]>
Commit | Line | Data |
---|---|---|
9346a6ac AL |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | //! Set and unset common attributes on LLVM values. | |
11 | ||
12 | use libc::{c_uint, c_ulonglong}; | |
13 | use llvm::{self, ValueRef, AttrHelper}; | |
c1a9b12d SL |
14 | use middle::ty; |
15 | use middle::infer; | |
62682a34 | 16 | use session::config::NoDebugInfo; |
7453a54e | 17 | use syntax::abi::Abi; |
b039eaaf SL |
18 | pub use syntax::attr::InlineAttr; |
19 | use syntax::ast; | |
e9174d1e | 20 | use rustc_front::hir; |
9346a6ac AL |
21 | use trans::base; |
22 | use trans::common; | |
23 | use trans::context::CrateContext; | |
24 | use trans::machine; | |
25 | use trans::type_of; | |
26 | ||
9346a6ac AL |
27 | /// Mark LLVM function to use provided inline heuristic. |
28 | #[inline] | |
29 | pub fn inline(val: ValueRef, inline: InlineAttr) { | |
30 | use self::InlineAttr::*; | |
31 | match inline { | |
d9579d0f AL |
32 | Hint => llvm::SetFunctionAttribute(val, llvm::Attribute::InlineHint), |
33 | Always => llvm::SetFunctionAttribute(val, llvm::Attribute::AlwaysInline), | |
34 | Never => llvm::SetFunctionAttribute(val, llvm::Attribute::NoInline), | |
9346a6ac | 35 | None => { |
d9579d0f AL |
36 | let attr = llvm::Attribute::InlineHint | |
37 | llvm::Attribute::AlwaysInline | | |
38 | llvm::Attribute::NoInline; | |
9346a6ac AL |
39 | unsafe { |
40 | llvm::LLVMRemoveFunctionAttr(val, attr.bits() as c_ulonglong) | |
41 | } | |
42 | }, | |
43 | }; | |
44 | } | |
45 | ||
46 | /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. | |
47 | #[inline] | |
48 | pub fn emit_uwtable(val: ValueRef, emit: bool) { | |
49 | if emit { | |
d9579d0f | 50 | llvm::SetFunctionAttribute(val, llvm::Attribute::UWTable); |
9346a6ac AL |
51 | } else { |
52 | unsafe { | |
d9579d0f AL |
53 | llvm::LLVMRemoveFunctionAttr( |
54 | val, | |
55 | llvm::Attribute::UWTable.bits() as c_ulonglong, | |
56 | ); | |
9346a6ac AL |
57 | } |
58 | } | |
59 | } | |
60 | ||
61 | /// Tell LLVM whether the function can or cannot unwind. | |
62 | #[inline] | |
9346a6ac AL |
63 | pub fn unwind(val: ValueRef, can_unwind: bool) { |
64 | if can_unwind { | |
65 | unsafe { | |
d9579d0f AL |
66 | llvm::LLVMRemoveFunctionAttr( |
67 | val, | |
68 | llvm::Attribute::NoUnwind.bits() as c_ulonglong, | |
69 | ); | |
9346a6ac AL |
70 | } |
71 | } else { | |
d9579d0f | 72 | llvm::SetFunctionAttribute(val, llvm::Attribute::NoUnwind); |
9346a6ac AL |
73 | } |
74 | } | |
75 | ||
76 | /// Tell LLVM whether it should optimise function for size. | |
77 | #[inline] | |
78 | #[allow(dead_code)] // possibly useful function | |
79 | pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { | |
80 | if optimize { | |
d9579d0f | 81 | llvm::SetFunctionAttribute(val, llvm::Attribute::OptimizeForSize); |
9346a6ac AL |
82 | } else { |
83 | unsafe { | |
d9579d0f AL |
84 | llvm::LLVMRemoveFunctionAttr( |
85 | val, | |
86 | llvm::Attribute::OptimizeForSize.bits() as c_ulonglong, | |
87 | ); | |
9346a6ac AL |
88 | } |
89 | } | |
90 | } | |
91 | ||
92 | /// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) | |
93 | /// attributes. | |
b039eaaf SL |
94 | pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) { |
95 | use syntax::attr::*; | |
9346a6ac AL |
96 | inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs)); |
97 | ||
62682a34 SL |
98 | // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a |
99 | // parameter. | |
100 | let no_fp_elim = (ccx.sess().opts.debuginfo != NoDebugInfo) || | |
101 | !ccx.sess().target.target.options.eliminate_frame_pointer; | |
102 | if no_fp_elim { | |
103 | unsafe { | |
104 | let attr = "no-frame-pointer-elim\0".as_ptr() as *const _; | |
105 | let val = "true\0".as_ptr() as *const _; | |
106 | llvm::LLVMAddFunctionAttrStringValue(llfn, | |
107 | llvm::FunctionIndex as c_uint, | |
108 | attr, val); | |
109 | } | |
110 | } | |
111 | ||
9346a6ac | 112 | for attr in attrs { |
e9174d1e | 113 | if attr.check_name("cold") { |
9346a6ac AL |
114 | unsafe { |
115 | llvm::LLVMAddFunctionAttribute(llfn, | |
116 | llvm::FunctionIndex as c_uint, | |
117 | llvm::ColdAttribute as u64) | |
118 | } | |
119 | } else if attr.check_name("allocator") { | |
d9579d0f | 120 | llvm::Attribute::NoAlias.apply_llfn(llvm::ReturnIndex as c_uint, llfn); |
e9174d1e SL |
121 | } else if attr.check_name("unwind") { |
122 | unwind(llfn, true); | |
9346a6ac AL |
123 | } |
124 | } | |
125 | } | |
126 | ||
127 | /// Composite function which converts function type into LLVM attributes for the function. | |
128 | pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx>) | |
129 | -> llvm::AttrBuilder { | |
130 | use middle::ty::{BrAnon, ReLateBound}; | |
131 | ||
132 | let function_type; | |
133 | let (fn_sig, abi, env_ty) = match fn_type.sty { | |
62682a34 | 134 | ty::TyBareFn(_, ref f) => (&f.sig, f.abi, None), |
c1a9b12d SL |
135 | ty::TyClosure(closure_did, ref substs) => { |
136 | let infcx = infer::normalizing_infer_ctxt(ccx.tcx(), &ccx.tcx().tables); | |
137 | function_type = infcx.closure_type(closure_did, substs); | |
9346a6ac | 138 | let self_type = base::self_type_for_closure(ccx, closure_did, fn_type); |
7453a54e | 139 | (&function_type.sig, Abi::RustCall, Some(self_type)) |
9346a6ac AL |
140 | } |
141 | _ => ccx.sess().bug("expected closure or function.") | |
142 | }; | |
143 | ||
c1a9b12d | 144 | let fn_sig = ccx.tcx().erase_late_bound_regions(fn_sig); |
92a42be0 | 145 | let fn_sig = infer::normalize_associated_type(ccx.tcx(), &fn_sig); |
9346a6ac AL |
146 | |
147 | let mut attrs = llvm::AttrBuilder::new(); | |
148 | let ret_ty = fn_sig.output; | |
149 | ||
150 | // These have an odd calling convention, so we need to manually | |
151 | // unpack the input ty's | |
152 | let input_tys = match fn_type.sty { | |
62682a34 | 153 | ty::TyClosure(..) => { |
7453a54e | 154 | assert!(abi == Abi::RustCall); |
9346a6ac AL |
155 | |
156 | match fn_sig.inputs[0].sty { | |
62682a34 | 157 | ty::TyTuple(ref inputs) => { |
9346a6ac | 158 | let mut full_inputs = vec![env_ty.expect("Missing closure environment")]; |
92a42be0 | 159 | full_inputs.extend_from_slice(inputs); |
9346a6ac AL |
160 | full_inputs |
161 | } | |
162 | _ => ccx.sess().bug("expected tuple'd inputs") | |
163 | } | |
164 | }, | |
7453a54e | 165 | ty::TyBareFn(..) if abi == Abi::RustCall => { |
9346a6ac AL |
166 | let mut inputs = vec![fn_sig.inputs[0]]; |
167 | ||
168 | match fn_sig.inputs[1].sty { | |
62682a34 | 169 | ty::TyTuple(ref t_in) => { |
92a42be0 | 170 | inputs.extend_from_slice(&t_in[..]); |
9346a6ac AL |
171 | inputs |
172 | } | |
173 | _ => ccx.sess().bug("expected tuple'd inputs") | |
174 | } | |
175 | } | |
176 | _ => fn_sig.inputs.clone() | |
177 | }; | |
178 | ||
179 | // Index 0 is the return value of the llvm func, so we start at 1 | |
62682a34 | 180 | let mut idx = 1; |
9346a6ac AL |
181 | if let ty::FnConverging(ret_ty) = ret_ty { |
182 | // A function pointer is called without the declaration | |
183 | // available, so we have to apply any attributes with ABI | |
184 | // implications directly to the call instruction. Right now, | |
185 | // the only attribute we need to worry about is `sret`. | |
186 | if type_of::return_uses_outptr(ccx, ret_ty) { | |
187 | let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, ret_ty)); | |
188 | ||
189 | // The outptr can be noalias and nocapture because it's entirely | |
190 | // invisible to the program. We also know it's nonnull as well | |
191 | // as how many bytes we can dereference | |
d9579d0f AL |
192 | attrs.arg(1, llvm::Attribute::StructRet) |
193 | .arg(1, llvm::Attribute::NoAlias) | |
194 | .arg(1, llvm::Attribute::NoCapture) | |
9346a6ac AL |
195 | .arg(1, llvm::DereferenceableAttribute(llret_sz)); |
196 | ||
197 | // Add one more since there's an outptr | |
62682a34 | 198 | idx += 1; |
9346a6ac AL |
199 | } else { |
200 | // The `noalias` attribute on the return value is useful to a | |
201 | // function ptr caller. | |
202 | match ret_ty.sty { | |
d9579d0f | 203 | // `Box` pointer return values never alias because ownership |
9346a6ac | 204 | // is transferred |
62682a34 | 205 | ty::TyBox(it) if common::type_is_sized(ccx.tcx(), it) => { |
d9579d0f | 206 | attrs.ret(llvm::Attribute::NoAlias); |
9346a6ac AL |
207 | } |
208 | _ => {} | |
209 | } | |
210 | ||
211 | // We can also mark the return value as `dereferenceable` in certain cases | |
212 | match ret_ty.sty { | |
213 | // These are not really pointers but pairs, (pointer, len) | |
c1a9b12d | 214 | ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) |
62682a34 | 215 | | ty::TyBox(inner) if common::type_is_sized(ccx.tcx(), inner) => { |
9346a6ac AL |
216 | let llret_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner)); |
217 | attrs.ret(llvm::DereferenceableAttribute(llret_sz)); | |
218 | } | |
219 | _ => {} | |
220 | } | |
221 | ||
62682a34 | 222 | if let ty::TyBool = ret_ty.sty { |
d9579d0f | 223 | attrs.ret(llvm::Attribute::ZExt); |
9346a6ac AL |
224 | } |
225 | } | |
226 | } | |
227 | ||
62682a34 | 228 | for &t in input_tys.iter() { |
9346a6ac | 229 | match t.sty { |
62682a34 | 230 | _ if type_of::arg_is_indirect(ccx, t) => { |
9346a6ac AL |
231 | let llarg_sz = machine::llsize_of_real(ccx, type_of::type_of(ccx, t)); |
232 | ||
233 | // For non-immediate arguments the callee gets its own copy of | |
234 | // the value on the stack, so there are no aliases. It's also | |
235 | // program-invisible so can't possibly capture | |
d9579d0f AL |
236 | attrs.arg(idx, llvm::Attribute::NoAlias) |
237 | .arg(idx, llvm::Attribute::NoCapture) | |
9346a6ac AL |
238 | .arg(idx, llvm::DereferenceableAttribute(llarg_sz)); |
239 | } | |
240 | ||
62682a34 | 241 | ty::TyBool => { |
d9579d0f | 242 | attrs.arg(idx, llvm::Attribute::ZExt); |
9346a6ac AL |
243 | } |
244 | ||
d9579d0f | 245 | // `Box` pointer parameters never alias because ownership is transferred |
62682a34 SL |
246 | ty::TyBox(inner) => { |
247 | attrs.arg(idx, llvm::Attribute::NoAlias); | |
248 | ||
249 | if common::type_is_sized(ccx.tcx(), inner) { | |
250 | let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner)); | |
251 | attrs.arg(idx, llvm::DereferenceableAttribute(llsz)); | |
252 | } else { | |
253 | attrs.arg(idx, llvm::NonNullAttribute); | |
c1a9b12d | 254 | if inner.is_trait() { |
62682a34 SL |
255 | attrs.arg(idx + 1, llvm::NonNullAttribute); |
256 | } | |
257 | } | |
9346a6ac AL |
258 | } |
259 | ||
62682a34 SL |
260 | ty::TyRef(b, mt) => { |
261 | // `&mut` pointer parameters never alias other parameters, or mutable global data | |
262 | // | |
263 | // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as | |
264 | // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely | |
265 | // on memory dependencies rather than pointer equality | |
c1a9b12d | 266 | let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe(); |
9346a6ac | 267 | |
7453a54e | 268 | if mt.mutbl != hir::MutMutable && !interior_unsafe { |
62682a34 SL |
269 | attrs.arg(idx, llvm::Attribute::NoAlias); |
270 | } | |
9346a6ac | 271 | |
e9174d1e | 272 | if mt.mutbl == hir::MutImmutable && !interior_unsafe { |
d9579d0f | 273 | attrs.arg(idx, llvm::Attribute::ReadOnly); |
9346a6ac AL |
274 | } |
275 | ||
62682a34 SL |
276 | // & pointer parameters are also never null and for sized types we also know |
277 | // exactly how many bytes we can dereference | |
278 | if common::type_is_sized(ccx.tcx(), mt.ty) { | |
279 | let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); | |
280 | attrs.arg(idx, llvm::DereferenceableAttribute(llsz)); | |
281 | } else { | |
282 | attrs.arg(idx, llvm::NonNullAttribute); | |
c1a9b12d | 283 | if mt.ty.is_trait() { |
62682a34 SL |
284 | attrs.arg(idx + 1, llvm::NonNullAttribute); |
285 | } | |
286 | } | |
287 | ||
288 | // When a reference in an argument has no named lifetime, it's | |
289 | // impossible for that reference to escape this function | |
290 | // (returned or stored beyond the call by a closure). | |
9346a6ac | 291 | if let ReLateBound(_, BrAnon(_)) = *b { |
d9579d0f | 292 | attrs.arg(idx, llvm::Attribute::NoCapture); |
9346a6ac AL |
293 | } |
294 | } | |
295 | ||
9346a6ac AL |
296 | _ => () |
297 | } | |
62682a34 SL |
298 | |
299 | if common::type_is_fat_ptr(ccx.tcx(), t) { | |
300 | idx += 2; | |
301 | } else { | |
302 | idx += 1; | |
303 | } | |
9346a6ac AL |
304 | } |
305 | ||
306 | attrs | |
307 | } |