@@ -61,6 +61,21 @@ pub enum ArgKind {
61
61
pub struct ArgType {
62
62
pub kind : ArgKind ,
63
63
/// Original LLVM type
64
+ pub original_ty : Type ,
65
+ /// Sizing LLVM type (pointers are opaque).
66
+ /// Unlike original_ty, this is guaranteed to be complete.
67
+ ///
68
+ /// For example, while we're computing the function pointer type in
69
+ /// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
70
+ /// The field type will likely end up being `void(%Foo)*`, but we cannot
71
+ /// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
72
+ /// until `%Foo` is completed by having all of its field types inserted,
73
+ /// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
74
+ /// with opaque ones, resulting in `{i8*}` for `Foo`.
75
+ /// ABI-specific logic can then look at the size, alignment and fields of
76
+ /// `{i8*}` in order to determine how the argument will be passed.
77
+ /// Only later will `original_ty` aka `%Foo` be used in the LLVM function
78
+ /// pointer type, without ever having introspected it.
64
79
pub ty : Type ,
65
80
/// Coerced LLVM Type
66
81
pub cast : Option < Type > ,
@@ -71,9 +86,10 @@ pub struct ArgType {
71
86
}
72
87
73
88
impl ArgType {
74
- fn new ( ty : Type ) -> ArgType {
89
+ fn new ( original_ty : Type , ty : Type ) -> ArgType {
75
90
ArgType {
76
91
kind : Direct ,
92
+ original_ty : original_ty,
77
93
ty : ty,
78
94
cast : None ,
79
95
pad : None ,
@@ -90,14 +106,6 @@ impl ArgType {
90
106
}
91
107
}
92
108
93
- fn c_type_of < ' a , ' tcx > ( cx : & CrateContext < ' a , ' tcx > , ty : Ty < ' tcx > ) -> Type {
94
- if ty. is_bool ( ) {
95
- Type :: i1 ( cx)
96
- } else {
97
- type_of:: type_of ( cx, ty)
98
- }
99
- }
100
-
101
109
/// Metadata describing how the arguments to a native function
102
110
/// should be passed in order to respect the native ABI.
103
111
///
@@ -147,13 +155,6 @@ impl FnType {
147
155
Aapcs => llvm:: CCallConv ,
148
156
} ;
149
157
150
- let rty = match sig. output {
151
- ty:: FnConverging ( ret_ty) if !return_type_is_void ( ccx, ret_ty) => {
152
- c_type_of ( ccx, ret_ty)
153
- }
154
- _ => Type :: void ( ccx)
155
- } ;
156
-
157
158
let mut inputs = & sig. inputs [ ..] ;
158
159
let extra_args = if abi == RustCall {
159
160
assert ! ( !sig. variadic && extra_args. is_empty( ) ) ;
@@ -173,30 +174,45 @@ impl FnType {
173
174
extra_args
174
175
} ;
175
176
177
+ let arg_of = |ty : Ty < ' tcx > | {
178
+ if ty. is_bool ( ) {
179
+ let llty = Type :: i1 ( ccx) ;
180
+ let mut arg = ArgType :: new ( llty, llty) ;
181
+ arg. attr = Some ( llvm:: Attribute :: ZExt ) ;
182
+ arg
183
+ } else {
184
+ ArgType :: new ( type_of:: type_of ( ccx, ty) ,
185
+ type_of:: sizing_type_of ( ccx, ty) )
186
+ }
187
+ } ;
188
+
189
+ let ret = match sig. output {
190
+ ty:: FnConverging ( ret_ty) if !return_type_is_void ( ccx, ret_ty) => {
191
+ arg_of ( ret_ty)
192
+ }
193
+ _ => ArgType :: new ( Type :: void ( ccx) , Type :: void ( ccx) )
194
+ } ;
195
+
176
196
let mut args = Vec :: with_capacity ( inputs. len ( ) + extra_args. len ( ) ) ;
177
197
for ty in inputs. iter ( ) . chain ( extra_args. iter ( ) ) {
178
- let llty = c_type_of ( ccx , ty) ;
198
+ let arg = arg_of ( ty) ;
179
199
if type_is_fat_ptr ( ccx. tcx ( ) , ty) {
180
- args. extend ( llty. field_types ( ) . into_iter ( ) . map ( ArgType :: new) ) ;
200
+ let original = arg. original_ty . field_types ( ) ;
201
+ let sizing = arg. ty . field_types ( ) ;
202
+ args. extend ( original. into_iter ( ) . zip ( sizing)
203
+ . map ( |( o, s) | ArgType :: new ( o, s) ) ) ;
181
204
} else {
182
- args. push ( ArgType :: new ( llty ) ) ;
205
+ args. push ( arg ) ;
183
206
}
184
207
}
185
208
186
209
let mut fty = FnType {
187
210
args : args,
188
- ret : ArgType :: new ( rty ) ,
211
+ ret : ret ,
189
212
variadic : sig. variadic ,
190
213
cconv : cconv
191
214
} ;
192
215
193
- // Add ZExt attributes to i1 arguments and returns.
194
- for arg in Some ( & mut fty. ret ) . into_iter ( ) . chain ( & mut fty. args ) {
195
- if arg. ty == Type :: i1 ( ccx) {
196
- arg. attr = Some ( llvm:: Attribute :: ZExt ) ;
197
- }
198
- }
199
-
200
216
if abi == Rust || abi == RustCall {
201
217
let fixup = |arg : & mut ArgType | {
202
218
if !arg. ty . is_aggregate ( ) {
@@ -213,13 +229,12 @@ impl FnType {
213
229
arg. cast = Some ( Type :: ix ( ccx, size * 8 ) ) ;
214
230
}
215
231
} ;
216
- if let ty :: FnConverging ( ret_ty ) = sig . output {
232
+ if fty . ret . ty != Type :: void ( ccx ) {
217
233
// Fat pointers are returned by-value.
218
- if !type_is_fat_ptr ( ccx. tcx ( ) , ret_ty) &&
219
- fty. ret . ty != Type :: void ( ccx) {
234
+ if !type_is_fat_ptr ( ccx. tcx ( ) , sig. output . unwrap ( ) ) {
220
235
fixup ( & mut fty. ret ) ;
221
236
}
222
- } ;
237
+ }
223
238
for arg in & mut fty. args {
224
239
fixup ( arg) ;
225
240
}
@@ -256,10 +271,10 @@ impl FnType {
256
271
let mut llargument_tys = Vec :: new ( ) ;
257
272
258
273
let llreturn_ty = if self . ret . is_indirect ( ) {
259
- llargument_tys. push ( self . ret . ty . ptr_to ( ) ) ;
274
+ llargument_tys. push ( self . ret . original_ty . ptr_to ( ) ) ;
260
275
Type :: void ( ccx)
261
276
} else {
262
- self . ret . cast . unwrap_or ( self . ret . ty )
277
+ self . ret . cast . unwrap_or ( self . ret . original_ty )
263
278
} ;
264
279
265
280
for arg in & self . args {
@@ -272,9 +287,9 @@ impl FnType {
272
287
}
273
288
274
289
let llarg_ty = if arg. is_indirect ( ) {
275
- arg. ty . ptr_to ( )
290
+ arg. original_ty . ptr_to ( )
276
291
} else {
277
- arg. cast . unwrap_or ( arg. ty )
292
+ arg. cast . unwrap_or ( arg. original_ty )
278
293
} ;
279
294
280
295
llargument_tys. push ( llarg_ty) ;
0 commit comments