9
9
// except according to those terms.
10
10
//! Set and unset common attributes on LLVM values.
11
11
12
+ use libc:: { c_uint, c_ulonglong} ;
12
13
use llvm:: { self , ValueRef , AttrHelper } ;
14
+ use middle:: ty:: { self , ClosureTyper } ;
15
+ use syntax:: abi;
13
16
use syntax:: ast;
14
- use syntax:: attr:: InlineAttr ;
15
- pub use syntax:: attr:: InlineAttr :: * ;
17
+ pub use syntax:: attr:: InlineAttr ;
18
+ use trans:: base;
19
+ use trans:: common;
16
20
use trans:: context:: CrateContext ;
17
-
18
- use libc :: { c_uint , c_ulonglong } ;
21
+ use trans :: machine ;
22
+ use trans :: type_of ;
19
23
20
24
/// Mark LLVM function to use split stack.
21
25
#[ inline]
@@ -33,11 +37,12 @@ pub fn split_stack(val: ValueRef, set: bool) {
33
37
/// Mark LLVM function to use provided inline heuristic.
34
38
#[ inline]
35
39
pub fn inline ( val : ValueRef , inline : InlineAttr ) {
40
+ use self :: InlineAttr :: * ;
36
41
match inline {
37
- InlineHint => llvm:: SetFunctionAttribute ( val, llvm:: InlineHintAttribute ) ,
38
- InlineAlways => llvm:: SetFunctionAttribute ( val, llvm:: AlwaysInlineAttribute ) ,
39
- InlineNever => llvm:: SetFunctionAttribute ( val, llvm:: NoInlineAttribute ) ,
40
- InlineNone => {
42
+ Hint => llvm:: SetFunctionAttribute ( val, llvm:: InlineHintAttribute ) ,
43
+ Always => llvm:: SetFunctionAttribute ( val, llvm:: AlwaysInlineAttribute ) ,
44
+ Never => llvm:: SetFunctionAttribute ( val, llvm:: NoInlineAttribute ) ,
45
+ None => {
41
46
let attr = llvm:: InlineHintAttribute |
42
47
llvm:: AlwaysInlineAttribute |
43
48
llvm:: NoInlineAttribute ;
@@ -88,7 +93,7 @@ pub fn set_optimize_for_size(val: ValueRef, optimize: bool) {
88
93
89
94
/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
90
95
/// attributes.
91
- pub fn convert_fn_attrs_to_llvm ( ccx : & CrateContext , attrs : & [ ast:: Attribute ] , llfn : ValueRef ) {
96
+ pub fn from_fn_attrs ( ccx : & CrateContext , attrs : & [ ast:: Attribute ] , llfn : ValueRef ) {
92
97
use syntax:: attr:: * ;
93
98
inline ( llfn, find_inline_attr ( Some ( ccx. sess ( ) . diagnostic ( ) ) , attrs) ) ;
94
99
@@ -110,3 +115,173 @@ pub fn convert_fn_attrs_to_llvm(ccx: &CrateContext, attrs: &[ast::Attribute], ll
110
115
}
111
116
}
112
117
}
118
+
119
+ /// Composite function which converts function type into LLVM attributes for the function.
120
+ pub fn from_fn_type < ' a , ' tcx > ( ccx : & CrateContext < ' a , ' tcx > , fn_type : ty:: Ty < ' tcx > )
121
+ -> llvm:: AttrBuilder {
122
+ use middle:: ty:: { BrAnon , ReLateBound } ;
123
+
124
+ let function_type;
125
+ let ( fn_sig, abi, env_ty) = match fn_type. sty {
126
+ ty:: ty_bare_fn( _, ref f) => ( & f. sig , f. abi , None ) ,
127
+ ty:: ty_closure( closure_did, substs) => {
128
+ let typer = common:: NormalizingClosureTyper :: new ( ccx. tcx ( ) ) ;
129
+ function_type = typer. closure_type ( closure_did, substs) ;
130
+ let self_type = base:: self_type_for_closure ( ccx, closure_did, fn_type) ;
131
+ ( & function_type. sig , abi:: RustCall , Some ( self_type) )
132
+ }
133
+ _ => ccx. sess ( ) . bug ( "expected closure or function." )
134
+ } ;
135
+
136
+ let fn_sig = ty:: erase_late_bound_regions ( ccx. tcx ( ) , fn_sig) ;
137
+
138
+ let mut attrs = llvm:: AttrBuilder :: new ( ) ;
139
+ let ret_ty = fn_sig. output ;
140
+
141
+ // These have an odd calling convention, so we need to manually
142
+ // unpack the input ty's
143
+ let input_tys = match fn_type. sty {
144
+ ty:: ty_closure( ..) => {
145
+ assert ! ( abi == abi:: RustCall ) ;
146
+
147
+ match fn_sig. inputs [ 0 ] . sty {
148
+ ty:: ty_tup( ref inputs) => {
149
+ let mut full_inputs = vec ! [ env_ty. expect( "Missing closure environment" ) ] ;
150
+ full_inputs. push_all ( inputs) ;
151
+ full_inputs
152
+ }
153
+ _ => ccx. sess ( ) . bug ( "expected tuple'd inputs" )
154
+ }
155
+ } ,
156
+ ty:: ty_bare_fn( ..) if abi == abi:: RustCall => {
157
+ let mut inputs = vec ! [ fn_sig. inputs[ 0 ] ] ;
158
+
159
+ match fn_sig. inputs [ 1 ] . sty {
160
+ ty:: ty_tup( ref t_in) => {
161
+ inputs. push_all ( & t_in[ ..] ) ;
162
+ inputs
163
+ }
164
+ _ => ccx. sess ( ) . bug ( "expected tuple'd inputs" )
165
+ }
166
+ }
167
+ _ => fn_sig. inputs . clone ( )
168
+ } ;
169
+
170
+ // Index 0 is the return value of the llvm func, so we start at 1
171
+ let mut first_arg_offset = 1 ;
172
+ if let ty:: FnConverging ( ret_ty) = ret_ty {
173
+ // A function pointer is called without the declaration
174
+ // available, so we have to apply any attributes with ABI
175
+ // implications directly to the call instruction. Right now,
176
+ // the only attribute we need to worry about is `sret`.
177
+ if type_of:: return_uses_outptr ( ccx, ret_ty) {
178
+ let llret_sz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, ret_ty) ) ;
179
+
180
+ // The outptr can be noalias and nocapture because it's entirely
181
+ // invisible to the program. We also know it's nonnull as well
182
+ // as how many bytes we can dereference
183
+ attrs. arg ( 1 , llvm:: StructRetAttribute )
184
+ . arg ( 1 , llvm:: NoAliasAttribute )
185
+ . arg ( 1 , llvm:: NoCaptureAttribute )
186
+ . arg ( 1 , llvm:: DereferenceableAttribute ( llret_sz) ) ;
187
+
188
+ // Add one more since there's an outptr
189
+ first_arg_offset += 1 ;
190
+ } else {
191
+ // The `noalias` attribute on the return value is useful to a
192
+ // function ptr caller.
193
+ match ret_ty. sty {
194
+ // `~` pointer return values never alias because ownership
195
+ // is transferred
196
+ ty:: ty_uniq( it) if !common:: type_is_sized ( ccx. tcx ( ) , it) => { }
197
+ ty:: ty_uniq( _) => {
198
+ attrs. ret ( llvm:: NoAliasAttribute ) ;
199
+ }
200
+ _ => { }
201
+ }
202
+
203
+ // We can also mark the return value as `dereferenceable` in certain cases
204
+ match ret_ty. sty {
205
+ // These are not really pointers but pairs, (pointer, len)
206
+ ty:: ty_uniq( it) |
207
+ ty:: ty_rptr( _, ty:: mt { ty : it, .. } ) if !common:: type_is_sized ( ccx. tcx ( ) , it) => { }
208
+ ty:: ty_uniq( inner) | ty:: ty_rptr( _, ty:: mt { ty : inner, .. } ) => {
209
+ let llret_sz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, inner) ) ;
210
+ attrs. ret ( llvm:: DereferenceableAttribute ( llret_sz) ) ;
211
+ }
212
+ _ => { }
213
+ }
214
+
215
+ if let ty:: ty_bool = ret_ty. sty {
216
+ attrs. ret ( llvm:: ZExtAttribute ) ;
217
+ }
218
+ }
219
+ }
220
+
221
+ for ( idx, & t) in input_tys. iter ( ) . enumerate ( ) . map ( |( i, v) | ( i + first_arg_offset, v) ) {
222
+ match t. sty {
223
+ // this needs to be first to prevent fat pointers from falling through
224
+ _ if !common:: type_is_immediate ( ccx, t) => {
225
+ let llarg_sz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, t) ) ;
226
+
227
+ // For non-immediate arguments the callee gets its own copy of
228
+ // the value on the stack, so there are no aliases. It's also
229
+ // program-invisible so can't possibly capture
230
+ attrs. arg ( idx, llvm:: NoAliasAttribute )
231
+ . arg ( idx, llvm:: NoCaptureAttribute )
232
+ . arg ( idx, llvm:: DereferenceableAttribute ( llarg_sz) ) ;
233
+ }
234
+
235
+ ty:: ty_bool => {
236
+ attrs. arg ( idx, llvm:: ZExtAttribute ) ;
237
+ }
238
+
239
+ // `~` pointer parameters never alias because ownership is transferred
240
+ ty:: ty_uniq( inner) => {
241
+ let llsz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, inner) ) ;
242
+
243
+ attrs. arg ( idx, llvm:: NoAliasAttribute )
244
+ . arg ( idx, llvm:: DereferenceableAttribute ( llsz) ) ;
245
+ }
246
+
247
+ // `&mut` pointer parameters never alias other parameters, or mutable global data
248
+ //
249
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
250
+ // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on
251
+ // memory dependencies rather than pointer equality
252
+ ty:: ty_rptr( b, mt) if mt. mutbl == ast:: MutMutable ||
253
+ !ty:: type_contents ( ccx. tcx ( ) , mt. ty ) . interior_unsafe ( ) => {
254
+
255
+ let llsz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, mt. ty ) ) ;
256
+ attrs. arg ( idx, llvm:: NoAliasAttribute )
257
+ . arg ( idx, llvm:: DereferenceableAttribute ( llsz) ) ;
258
+
259
+ if mt. mutbl == ast:: MutImmutable {
260
+ attrs. arg ( idx, llvm:: ReadOnlyAttribute ) ;
261
+ }
262
+
263
+ if let ReLateBound ( _, BrAnon ( _) ) = * b {
264
+ attrs. arg ( idx, llvm:: NoCaptureAttribute ) ;
265
+ }
266
+ }
267
+
268
+ // When a reference in an argument has no named lifetime, it's impossible for that
269
+ // reference to escape this function (returned or stored beyond the call by a closure).
270
+ ty:: ty_rptr( & ReLateBound ( _, BrAnon ( _) ) , mt) => {
271
+ let llsz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, mt. ty ) ) ;
272
+ attrs. arg ( idx, llvm:: NoCaptureAttribute )
273
+ . arg ( idx, llvm:: DereferenceableAttribute ( llsz) ) ;
274
+ }
275
+
276
+ // & pointer parameters are also never null and we know exactly how
277
+ // many bytes we can dereference
278
+ ty:: ty_rptr( _, mt) => {
279
+ let llsz = machine:: llsize_of_real ( ccx, type_of:: type_of ( ccx, mt. ty ) ) ;
280
+ attrs. arg ( idx, llvm:: DereferenceableAttribute ( llsz) ) ;
281
+ }
282
+ _ => ( )
283
+ }
284
+ }
285
+
286
+ attrs
287
+ }
0 commit comments