Skip to content

Commit 7aa4079

Browse files
committed
auto merge of #15998 : luqmana/rust/nmnnbd, r=thestinger
LLVM recently added a new attribute, dereferenceable: http://reviews.llvm.org/D4449 >This patch adds a dereferencable attribute. In some sense, this is a companion to the nonnull attribute, but specifies that the pointer is known to be dereferencable in the same sense as a pointer generated by alloca is known to be dereferencable. With rust, everywhere that we previously marked `nonnull` we can actually mark as `dereferenceable` (which implies nonnull) since we know the size. That is, except for one case: when generating calls for TyVisitor. It seems like we haven't substituted the self type (so we have `ty_param`) and just treat it as an opaque pointer so I just left that bit as nonnull. With this, LLVM can for example hoist a load out of a loop where it previously couldn't: ```Rust pub fn baz(c: &uint, n: uint) -> uint { let mut res = 0; for i in range(0, n) { if i > 0 { res += *c * i; } } res } ``` Before: ```llvm define i64 @baz(i64* noalias nocapture nonnull readonly, i64) unnamed_addr #0 { entry-block: br label %for_loopback.outer for_loopback.outer: ; preds = %then-block-33-, %entry-block %.ph = phi i64 [ %.lcssa, %then-block-33- ], [ 0, %entry-block ] %res.0.ph = phi i64 [ %8, %then-block-33- ], [ 0, %entry-block ] br label %for_loopback for_exit: ; preds = %for_loopback %res.0.ph.lcssa = phi i64 [ %res.0.ph, %for_loopback ] ret i64 %res.0.ph.lcssa for_loopback: ; preds = %for_loopback.outer, %for_body %2 = phi i64 [ %4, %for_body ], [ %.ph, %for_loopback.outer ] %3 = icmp ult i64 %2, %1 br i1 %3, label %for_body, label %for_exit for_body: ; preds = %for_loopback %4 = add i64 %2, 1 %5 = icmp eq i64 %2, 0 br i1 %5, label %for_loopback, label %then-block-33- then-block-33-: ; preds = %for_body %.lcssa = phi i64 [ %4, %for_body ] %.lcssa15 = phi i64 [ %2, %for_body ] %6 = load i64* %0, align 8 ; <------- this load %7 = mul i64 %6, %.lcssa15 %8 = add i64 %7, %res.0.ph br label %for_loopback.outer } ``` After: ```llvm define i64 @baz(i64* noalias nocapture readonly dereferenceable(8), i64) unnamed_addr #0 { entry-block: %2 = load i64* %0, align 8 ; <------- load once instead br label %for_loopback.outer for_loopback.outer: ; preds = %then-block-33-, %entry-block %.ph = phi i64 [ %.lcssa, %then-block-33- ], [ 0, %entry-block ] %res.0.ph = phi i64 [ %8, %then-block-33- ], [ 0, %entry-block ] br label %for_loopback for_exit: ; preds = %for_loopback %res.0.ph.lcssa = phi i64 [ %res.0.ph, %for_loopback ] ret i64 %res.0.ph.lcssa for_loopback: ; preds = %for_loopback.outer, %for_body %3 = phi i64 [ %5, %for_body ], [ %.ph, %for_loopback.outer ] %4 = icmp ult i64 %3, %1 br i1 %4, label %for_body, label %for_exit for_body: ; preds = %for_loopback %5 = add i64 %3, 1 %6 = icmp eq i64 %3, 0 br i1 %6, label %for_loopback, label %then-block-33- then-block-33-: ; preds = %for_body %.lcssa = phi i64 [ %5, %for_body ] %.lcssa15 = phi i64 [ %3, %for_body ] %7 = mul i64 %2, %.lcssa15 %8 = add i64 %7, %res.0.ph br label %for_loopback.outer } ```
2 parents 50c62b4 + a78c0f1 commit 7aa4079

File tree

10 files changed

+237
-81
lines changed

10 files changed

+237
-81
lines changed

src/librustc/middle/trans/base.rs

+77-45
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,7 @@ pub fn decl_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> ValueRef {
281281

282282
let llfn = decl_fn(ccx, name, llvm::CCallConv, llfty, output);
283283
let attrs = get_fn_llvm_attributes(ccx, fn_ty);
284-
for &(idx, attr) in attrs.iter() {
285-
unsafe {
286-
llvm::LLVMAddFunctionAttribute(llfn, idx as c_uint, attr);
287-
}
288-
}
284+
attrs.apply_llfn(llfn);
289285

290286
llfn
291287
}
@@ -962,7 +958,7 @@ pub fn invoke<'a>(
962958
llargs.as_slice(),
963959
normal_bcx.llbb,
964960
landing_pad,
965-
attributes.as_slice());
961+
Some(attributes));
966962
return (llresult, normal_bcx);
967963
} else {
968964
debug!("calling {} at {}", llfn, bcx.llbb);
@@ -975,7 +971,7 @@ pub fn invoke<'a>(
975971
None => debuginfo::clear_source_location(bcx.fcx)
976972
};
977973

978-
let llresult = Call(bcx, llfn, llargs.as_slice(), attributes.as_slice());
974+
let llresult = Call(bcx, llfn, llargs.as_slice(), Some(attributes));
979975
return (llresult, bcx);
980976
}
981977
}
@@ -1081,7 +1077,7 @@ pub fn call_lifetime_start(cx: &Block, ptr: ValueRef) {
10811077
let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type()));
10821078
let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
10831079
let lifetime_start = ccx.get_intrinsic(&"llvm.lifetime.start");
1084-
Call(cx, lifetime_start, [llsize, ptr], []);
1080+
Call(cx, lifetime_start, [llsize, ptr], None);
10851081
}
10861082

10871083
pub fn call_lifetime_end(cx: &Block, ptr: ValueRef) {
@@ -1095,7 +1091,7 @@ pub fn call_lifetime_end(cx: &Block, ptr: ValueRef) {
10951091
let llsize = C_u64(ccx, machine::llsize_of_alloc(ccx, val_ty(ptr).element_type()));
10961092
let ptr = PointerCast(cx, ptr, Type::i8p(ccx));
10971093
let lifetime_end = ccx.get_intrinsic(&"llvm.lifetime.end");
1098-
Call(cx, lifetime_end, [llsize, ptr], []);
1094+
Call(cx, lifetime_end, [llsize, ptr], None);
10991095
}
11001096

11011097
pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) {
@@ -1111,7 +1107,7 @@ pub fn call_memcpy(cx: &Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef,
11111107
let size = IntCast(cx, n_bytes, ccx.int_type);
11121108
let align = C_i32(ccx, align as i32);
11131109
let volatile = C_bool(ccx, false);
1114-
Call(cx, memcpy, [dst_ptr, src_ptr, size, align, volatile], []);
1110+
Call(cx, memcpy, [dst_ptr, src_ptr, size, align, volatile], None);
11151111
}
11161112

11171113
pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) {
@@ -1156,7 +1152,7 @@ fn memzero(b: &Builder, llptr: ValueRef, ty: Type) {
11561152
let size = machine::llsize_of(ccx, ty);
11571153
let align = C_i32(ccx, llalign_of_min(ccx, ty) as i32);
11581154
let volatile = C_bool(ccx, false);
1159-
b.call(llintrinsicfn, [llptr, llzeroval, size, align, volatile], []);
1155+
b.call(llintrinsicfn, [llptr, llzeroval, size, align, volatile], None);
11601156
}
11611157

11621158
pub fn alloc_ty(bcx: &Block, t: ty::t, name: &str) -> ValueRef {
@@ -2040,7 +2036,7 @@ fn register_fn(ccx: &CrateContext,
20402036
}
20412037

20422038
pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
2043-
-> Vec<(uint, u64)> {
2039+
-> llvm::AttrBuilder {
20442040
use middle::ty::{BrAnon, ReLateBound};
20452041

20462042
let (fn_sig, abi, has_env) = match ty::get(fn_ty).sty {
@@ -2056,31 +2052,33 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
20562052
_ => fail!("expected closure or function.")
20572053
};
20582054

2055+
// Since index 0 is the return value of the llvm func, we start
2056+
// at either 1 or 2 depending on whether there's an env slot or not
2057+
let mut first_arg_offset = if has_env { 2 } else { 1 };
2058+
let mut attrs = llvm::AttrBuilder::new();
2059+
let ret_ty = fn_sig.output;
2060+
20592061
// These have an odd calling convention, so we skip them for now.
20602062
//
20612063
// FIXME(pcwalton): We don't have to skip them; just untuple the result.
20622064
if abi == RustCall {
2063-
return Vec::new()
2065+
return attrs;
20642066
}
20652067

2066-
// Since index 0 is the return value of the llvm func, we start
2067-
// at either 1 or 2 depending on whether there's an env slot or not
2068-
let mut first_arg_offset = if has_env { 2 } else { 1 };
2069-
let mut attrs = Vec::new();
2070-
let ret_ty = fn_sig.output;
2071-
20722068
// A function pointer is called without the declaration
20732069
// available, so we have to apply any attributes with ABI
20742070
// implications directly to the call instruction. Right now,
20752071
// the only attribute we need to worry about is `sret`.
20762072
if type_of::return_uses_outptr(ccx, ret_ty) {
2077-
attrs.push((1, llvm::StructRetAttribute as u64));
2073+
let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
20782074

20792075
// The outptr can be noalias and nocapture because it's entirely
2080-
// invisible to the program. We can also mark it as nonnull
2081-
attrs.push((1, llvm::NoAliasAttribute as u64));
2082-
attrs.push((1, llvm::NoCaptureAttribute as u64));
2083-
attrs.push((1, llvm::NonNullAttribute as u64));
2076+
// invisible to the program. We also know it's nonnull as well
2077+
// as how many bytes we can dereference
2078+
attrs.arg(1, llvm::StructRetAttribute)
2079+
.arg(1, llvm::NoAliasAttribute)
2080+
.arg(1, llvm::NoCaptureAttribute)
2081+
.arg(1, llvm::DereferenceableAttribute(llret_sz));
20842082

20852083
// Add one more since there's an outptr
20862084
first_arg_offset += 1;
@@ -2094,27 +2092,28 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
20942092
ty::ty_str | ty::ty_vec(..) | ty::ty_trait(..) => true, _ => false
20952093
} => {}
20962094
ty::ty_uniq(_) => {
2097-
attrs.push((llvm::ReturnIndex as uint, llvm::NoAliasAttribute as u64));
2095+
attrs.ret(llvm::NoAliasAttribute);
20982096
}
20992097
_ => {}
21002098
}
21012099

2102-
// We can also mark the return value as `nonnull` in certain cases
2100+
// We can also mark the return value as `dereferenceable` in certain cases
21032101
match ty::get(ret_ty).sty {
21042102
// These are not really pointers but pairs, (pointer, len)
21052103
ty::ty_uniq(it) |
21062104
ty::ty_rptr(_, ty::mt { ty: it, .. }) if match ty::get(it).sty {
21072105
ty::ty_str | ty::ty_vec(..) | ty::ty_trait(..) => true, _ => false
21082106
} => {}
2109-
ty::ty_uniq(_) | ty::ty_rptr(_, _) => {
2110-
attrs.push((llvm::ReturnIndex as uint, llvm::NonNullAttribute as u64));
2107+
ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => {
2108+
let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
2109+
attrs.ret(llvm::DereferenceableAttribute(llret_sz));
21112110
}
21122111
_ => {}
21132112
}
21142113

21152114
match ty::get(ret_ty).sty {
21162115
ty::ty_bool => {
2117-
attrs.push((llvm::ReturnIndex as uint, llvm::ZExtAttribute as u64));
2116+
attrs.ret(llvm::ZExtAttribute);
21182117
}
21192118
_ => {}
21202119
}
@@ -2124,44 +2123,77 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
21242123
match ty::get(t).sty {
21252124
// this needs to be first to prevent fat pointers from falling through
21262125
_ if !type_is_immediate(ccx, t) => {
2126+
let llarg_sz = llsize_of_real(ccx, type_of::type_of(ccx, t));
2127+
21272128
// For non-immediate arguments the callee gets its own copy of
21282129
// the value on the stack, so there are no aliases. It's also
21292130
// program-invisible so can't possibly capture
2130-
attrs.push((idx, llvm::NoAliasAttribute as u64));
2131-
attrs.push((idx, llvm::NoCaptureAttribute as u64));
2132-
attrs.push((idx, llvm::NonNullAttribute as u64));
2131+
attrs.arg(idx, llvm::NoAliasAttribute)
2132+
.arg(idx, llvm::NoCaptureAttribute)
2133+
.arg(idx, llvm::DereferenceableAttribute(llarg_sz));
21332134
}
2135+
21342136
ty::ty_bool => {
2135-
attrs.push((idx, llvm::ZExtAttribute as u64));
2137+
attrs.arg(idx, llvm::ZExtAttribute);
21362138
}
2139+
21372140
// `~` pointer parameters never alias because ownership is transferred
2138-
ty::ty_uniq(_) => {
2139-
attrs.push((idx, llvm::NoAliasAttribute as u64));
2140-
attrs.push((idx, llvm::NonNullAttribute as u64));
2141+
ty::ty_uniq(inner) => {
2142+
let llsz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
2143+
2144+
attrs.arg(idx, llvm::NoAliasAttribute)
2145+
.arg(idx, llvm::DereferenceableAttribute(llsz));
2146+
}
2147+
2148+
// The visit glue deals only with opaque pointers so we don't
2149+
// actually know the concrete type of Self thus we don't know how
2150+
// many bytes to mark as dereferenceable so instead we just mark
2151+
// it as nonnull which still holds true
2152+
ty::ty_rptr(b, ty::mt { ty: it, mutbl }) if match ty::get(it).sty {
2153+
ty::ty_param(_) => true, _ => false
2154+
} && mutbl == ast::MutMutable => {
2155+
attrs.arg(idx, llvm::NoAliasAttribute)
2156+
.arg(idx, llvm::NonNullAttribute);
2157+
2158+
match b {
2159+
ReLateBound(_, BrAnon(_)) => {
2160+
attrs.arg(idx, llvm::NoCaptureAttribute);
2161+
}
2162+
_ => {}
2163+
}
21412164
}
2165+
21422166
// `&mut` pointer parameters never alias other parameters, or mutable global data
21432167
// `&` pointer parameters never alias either (for LLVM's purposes) as long as the
21442168
// interior is safe
21452169
ty::ty_rptr(b, mt) if mt.mutbl == ast::MutMutable ||
21462170
!ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => {
2147-
attrs.push((idx, llvm::NoAliasAttribute as u64));
2148-
attrs.push((idx, llvm::NonNullAttribute as u64));
2171+
2172+
let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
2173+
attrs.arg(idx, llvm::NoAliasAttribute)
2174+
.arg(idx, llvm::DereferenceableAttribute(llsz));
2175+
21492176
match b {
21502177
ReLateBound(_, BrAnon(_)) => {
2151-
attrs.push((idx, llvm::NoCaptureAttribute as u64));
2178+
attrs.arg(idx, llvm::NoCaptureAttribute);
21522179
}
21532180
_ => {}
21542181
}
21552182
}
2183+
21562184
// When a reference in an argument has no named lifetime, it's impossible for that
21572185
// reference to escape this function (returned or stored beyond the call by a closure).
2158-
ty::ty_rptr(ReLateBound(_, BrAnon(_)), _) => {
2159-
attrs.push((idx, llvm::NoCaptureAttribute as u64));
2160-
attrs.push((idx, llvm::NonNullAttribute as u64));
2186+
ty::ty_rptr(ReLateBound(_, BrAnon(_)), mt) => {
2187+
let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
2188+
attrs.arg(idx, llvm::NoCaptureAttribute)
2189+
.arg(idx, llvm::DereferenceableAttribute(llsz));
21612190
}
2162-
// & pointer parameters are never null
2163-
ty::ty_rptr(_, _) => {
2164-
attrs.push((idx, llvm::NonNullAttribute as u64));
2191+
2192+
// & pointer parameters are also never null and we know exactly how
2193+
// many bytes we can dereference
2194+
ty::ty_rptr(_, mt) => {
2195+
let llsz = llsize_of_real(ccx, type_of::type_of(ccx, mt.ty));
2196+
attrs.arg(idx, llvm::DereferenceableAttribute(llsz));
21652197
}
21662198
_ => ()
21672199
}

src/librustc/middle/trans/build.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#![allow(non_snake_case_functions)]
1313

1414
use llvm;
15-
use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect};
15+
use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder};
1616
use llvm::{Opcode, IntPredicate, RealPredicate};
1717
use llvm::{ValueRef, BasicBlockRef};
1818
use middle::trans::common::*;
@@ -113,7 +113,7 @@ pub fn Invoke(cx: &Block,
113113
args: &[ValueRef],
114114
then: BasicBlockRef,
115115
catch: BasicBlockRef,
116-
attributes: &[(uint, u64)])
116+
attributes: Option<AttrBuilder>)
117117
-> ValueRef {
118118
if cx.unreachable.get() {
119119
return C_null(Type::i8(cx.ccx()));
@@ -681,13 +681,13 @@ pub fn InlineAsmCall(cx: &Block, asm: *const c_char, cons: *const c_char,
681681
}
682682

683683
pub fn Call(cx: &Block, fn_: ValueRef, args: &[ValueRef],
684-
attributes: &[(uint, u64)]) -> ValueRef {
684+
attributes: Option<AttrBuilder>) -> ValueRef {
685685
if cx.unreachable.get() { return _UndefReturn(cx, fn_); }
686686
B(cx).call(fn_, args, attributes)
687687
}
688688

689689
pub fn CallWithConv(cx: &Block, fn_: ValueRef, args: &[ValueRef], conv: CallConv,
690-
attributes: &[(uint, u64)]) -> ValueRef {
690+
attributes: Option<AttrBuilder>) -> ValueRef {
691691
if cx.unreachable.get() { return _UndefReturn(cx, fn_); }
692692
B(cx).call_with_conv(fn_, args, conv, attributes)
693693
}

src/librustc/middle/trans/builder.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#![allow(dead_code)] // FFI wrappers
1212

1313
use llvm;
14-
use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect};
14+
use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder};
1515
use llvm::{Opcode, IntPredicate, RealPredicate, False};
1616
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef};
1717
use middle::trans::base;
@@ -155,7 +155,7 @@ impl<'a> Builder<'a> {
155155
args: &[ValueRef],
156156
then: BasicBlockRef,
157157
catch: BasicBlockRef,
158-
attributes: &[(uint, u64)])
158+
attributes: Option<AttrBuilder>)
159159
-> ValueRef {
160160
self.count_insn("invoke");
161161

@@ -174,8 +174,9 @@ impl<'a> Builder<'a> {
174174
then,
175175
catch,
176176
noname());
177-
for &(idx, attr) in attributes.iter() {
178-
llvm::LLVMAddCallSiteAttribute(v, idx as c_uint, attr);
177+
match attributes {
178+
Some(a) => a.apply_callsite(v),
179+
None => {}
179180
}
180181
v
181182
}
@@ -777,7 +778,7 @@ impl<'a> Builder<'a> {
777778
c, noname(), False, False)
778779
}
779780
});
780-
self.call(asm, [], []);
781+
self.call(asm, [], None);
781782
}
782783
}
783784

@@ -802,12 +803,12 @@ impl<'a> Builder<'a> {
802803
unsafe {
803804
let v = llvm::LLVMInlineAsm(
804805
fty.to_ref(), asm, cons, volatile, alignstack, dia as c_uint);
805-
self.call(v, inputs, [])
806+
self.call(v, inputs, None)
806807
}
807808
}
808809

809810
pub fn call(&self, llfn: ValueRef, args: &[ValueRef],
810-
attributes: &[(uint, u64)]) -> ValueRef {
811+
attributes: Option<AttrBuilder>) -> ValueRef {
811812
self.count_insn("call");
812813

813814
debug!("Call {} with args ({})",
@@ -820,15 +821,16 @@ impl<'a> Builder<'a> {
820821
unsafe {
821822
let v = llvm::LLVMBuildCall(self.llbuilder, llfn, args.as_ptr(),
822823
args.len() as c_uint, noname());
823-
for &(idx, attr) in attributes.iter() {
824-
llvm::LLVMAddCallSiteAttribute(v, idx as c_uint, attr);
824+
match attributes {
825+
Some(a) => a.apply_callsite(v),
826+
None => {}
825827
}
826828
v
827829
}
828830
}
829831

830832
pub fn call_with_conv(&self, llfn: ValueRef, args: &[ValueRef],
831-
conv: CallConv, attributes: &[(uint, u64)]) -> ValueRef {
833+
conv: CallConv, attributes: Option<AttrBuilder>) -> ValueRef {
832834
self.count_insn("callwithconv");
833835
let v = self.call(llfn, args, attributes);
834836
llvm::SetInstructionCallConv(v, conv);

src/librustc/middle/trans/closure.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
588588
}
589589
llargs.extend(args.iter().map(|arg| arg.val));
590590

591-
let retval = Call(bcx, fn_ptr, llargs.as_slice(), []);
591+
let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
592592
if type_is_zero_size(ccx, f.sig.output) || fcx.llretptr.get().is_some() {
593593
RetVoid(bcx);
594594
} else {

src/librustc/middle/trans/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ fn trans_index<'a>(bcx: &'a Block<'a>,
553553
let expected = Call(bcx,
554554
expect,
555555
[bounds_check, C_bool(ccx, false)],
556-
[]);
556+
None);
557557
bcx = with_cond(bcx, expected, |bcx| {
558558
controlflow::trans_fail_bounds_check(bcx,
559559
index_expr.span,

0 commit comments

Comments
 (0)