@@ -16,13 +16,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
1616use rustc_middle:: ty:: Ty ;
1717use rustc_session:: config;
1818pub use rustc_target:: abi:: call:: * ;
19- use rustc_target:: abi:: { self , HasDataLayout , Int } ;
19+ use rustc_target:: abi:: { self , HasDataLayout , Int , Size } ;
2020pub use rustc_target:: spec:: abi:: Abi ;
2121use rustc_target:: spec:: SanitizerSet ;
2222
2323use libc:: c_uint;
2424use smallvec:: SmallVec ;
2525
26+ use std:: cmp;
27+
2628pub trait ArgAttributesExt {
2729 fn apply_attrs_to_llfn ( & self , idx : AttributePlace , cx : & CodegenCx < ' _ , ' _ > , llfn : & Value ) ;
2830 fn apply_attrs_to_callsite (
@@ -130,42 +132,36 @@ impl LlvmType for Reg {
130132impl LlvmType for CastTarget {
131133 fn llvm_type < ' ll > ( & self , cx : & CodegenCx < ' ll , ' _ > ) -> & ' ll Type {
132134 let rest_ll_unit = self . rest . unit . llvm_type ( cx) ;
133- let ( rest_count, rem_bytes ) = if self . rest . unit . size . bytes ( ) == 0 {
134- ( 0 , 0 )
135+ let rest_count = if self . rest . total == Size :: ZERO {
136+ 0
135137 } else {
136- (
137- self . rest . total . bytes ( ) / self . rest . unit . size . bytes ( ) ,
138- self . rest . total . bytes ( ) % self . rest . unit . size . bytes ( ) ,
139- )
138+ assert_ne ! (
139+ self . rest. unit. size,
140+ Size :: ZERO ,
141+ "total size {:?} cannot be divided into units of zero size" ,
142+ self . rest. total
143+ ) ;
144+ if self . rest . total . bytes ( ) % self . rest . unit . size . bytes ( ) != 0 {
145+ assert_eq ! ( self . rest. unit. kind, RegKind :: Integer , "only int regs can be split" ) ;
146+ }
147+ self . rest . total . bytes ( ) . div_ceil ( self . rest . unit . size . bytes ( ) )
140148 } ;
141149
150+ // Simplify to a single unit or an array if there's no prefix.
151+ // This produces the same layout, but using a simpler type.
142152 if self . prefix . iter ( ) . all ( |x| x. is_none ( ) ) {
143- // Simplify to a single unit when there is no prefix and size <= unit size
144- if self . rest . total <= self . rest . unit . size {
153+ if rest_count == 1 {
145154 return rest_ll_unit;
146155 }
147156
148- // Simplify to array when all chunks are the same size and type
149- if rem_bytes == 0 {
150- return cx. type_array ( rest_ll_unit, rest_count) ;
151- }
152- }
153-
154- // Create list of fields in the main structure
155- let mut args: Vec < _ > = self
156- . prefix
157- . iter ( )
158- . flat_map ( |option_reg| option_reg. map ( |reg| reg. llvm_type ( cx) ) )
159- . chain ( ( 0 ..rest_count) . map ( |_| rest_ll_unit) )
160- . collect ( ) ;
161-
162- // Append final integer
163- if rem_bytes != 0 {
164- // Only integers can be really split further.
165- assert_eq ! ( self . rest. unit. kind, RegKind :: Integer ) ;
166- args. push ( cx. type_ix ( rem_bytes * 8 ) ) ;
157+ return cx. type_array ( rest_ll_unit, rest_count) ;
167158 }
168159
160+ // Generate a struct type with the prefix and the "rest" arguments.
161+ let prefix_args =
162+ self . prefix . iter ( ) . flat_map ( |option_reg| option_reg. map ( |reg| reg. llvm_type ( cx) ) ) ;
163+ let rest_args = ( 0 ..rest_count) . map ( |_| rest_ll_unit) ;
164+ let args: Vec < _ > = prefix_args. chain ( rest_args) . collect ( ) ;
169165 cx. type_struct ( & args, false )
170166 }
171167}
@@ -215,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
215211 bug ! ( "unsized `ArgAbi` must be handled through `store_fn_arg`" ) ;
216212 }
217213 PassMode :: Cast { cast, pad_i32 : _ } => {
218- // FIXME(eddyb): Figure out when the simpler Store is safe, clang
219- // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
220- let can_store_through_cast_ptr = false ;
221- if can_store_through_cast_ptr {
222- bx. store ( val, dst. llval , self . layout . align . abi ) ;
223- } else {
224- // The actual return type is a struct, but the ABI
225- // adaptation code has cast it into some scalar type. The
226- // code that follows is the only reliable way I have
227- // found to do a transform like i64 -> {i32,i32}.
228- // Basically we dump the data onto the stack then memcpy it.
229- //
230- // Other approaches I tried:
231- // - Casting rust ret pointer to the foreign type and using Store
232- // is (a) unsafe if size of foreign type > size of rust type and
233- // (b) runs afoul of strict aliasing rules, yielding invalid
234- // assembly under -O (specifically, the store gets removed).
235- // - Truncating foreign type to correct integral type and then
236- // bitcasting to the struct type yields invalid cast errors.
237-
238- // We instead thus allocate some scratch space...
239- let scratch_size = cast. size ( bx) ;
240- let scratch_align = cast. align ( bx) ;
241- let llscratch = bx. alloca ( cast. llvm_type ( bx) , scratch_align) ;
242- bx. lifetime_start ( llscratch, scratch_size) ;
243-
244- // ... where we first store the value...
245- bx. store ( val, llscratch, scratch_align) ;
246-
247- // ... and then memcpy it to the intended destination.
248- bx. memcpy (
249- dst. llval ,
250- self . layout . align . abi ,
251- llscratch,
252- scratch_align,
253- bx. const_usize ( self . layout . size . bytes ( ) ) ,
254- MemFlags :: empty ( ) ,
255- ) ;
256-
257- bx. lifetime_end ( llscratch, scratch_size) ;
258- }
214+ // The ABI mandates that the value is passed as a different struct representation.
215+ // Spill and reload it from the stack to convert from the ABI representation to
216+ // the Rust representation.
217+ let scratch_size = cast. size ( bx) ;
218+ let scratch_align = cast. align ( bx) ;
219+ // Note that the ABI type may be either larger or smaller than the Rust type,
220+ // due to the presence or absence of trailing padding. For example:
221+ // - On some ABIs, the Rust layout { f64, f32, <f32 padding> } may omit padding
222+ // when passed by value, making it smaller.
223+ // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
224+ // when passed by value, making it larger.
225+ let copy_bytes = cmp:: min ( scratch_size. bytes ( ) , self . layout . size . bytes ( ) ) ;
226+ // Allocate some scratch space...
227+ let llscratch = bx. alloca ( cast. llvm_type ( bx) , scratch_align) ;
228+ bx. lifetime_start ( llscratch, scratch_size) ;
229+ // ...store the value...
230+ bx. store ( val, llscratch, scratch_align) ;
231+ // ... and then memcpy it to the intended destination.
232+ bx. memcpy (
233+ dst. llval ,
234+ self . layout . align . abi ,
235+ llscratch,
236+ scratch_align,
237+ bx. const_usize ( copy_bytes) ,
238+ MemFlags :: empty ( ) ,
239+ ) ;
240+ bx. lifetime_end ( llscratch, scratch_size) ;
259241 }
260242 _ => {
261243 OperandRef :: from_immediate_or_packed_pair ( bx, val, self . layout ) . val . store ( bx, dst) ;
0 commit comments