@@ -11,15 +11,23 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
11
11
use rustc_middle:: ty:: Ty ;
12
12
use rustc_target:: abi:: { Align , Endian , HasDataLayout , Size } ;
13
13
14
+ fn round_up_to_alignment < ' ll > (
15
+ bx : & mut Builder < ' _ , ' ll , ' _ > ,
16
+ mut value : & ' ll Value ,
17
+ align : Align ,
18
+ ) -> & ' ll Value {
19
+ value = bx. add ( value, bx. cx ( ) . const_i32 ( align. bytes ( ) as i32 - 1 ) ) ;
20
+ return bx. and ( value, bx. cx ( ) . const_i32 ( -( align. bytes ( ) as i32 ) ) ) ;
21
+ }
22
+
14
23
fn round_pointer_up_to_alignment < ' ll > (
15
24
bx : & mut Builder < ' _ , ' ll , ' _ > ,
16
25
addr : & ' ll Value ,
17
26
align : Align ,
18
27
ptr_ty : & ' ll Type ,
19
28
) -> & ' ll Value {
20
29
let mut ptr_as_int = bx. ptrtoint ( addr, bx. cx ( ) . type_isize ( ) ) ;
21
- ptr_as_int = bx. add ( ptr_as_int, bx. cx ( ) . const_i32 ( align. bytes ( ) as i32 - 1 ) ) ;
22
- ptr_as_int = bx. and ( ptr_as_int, bx. cx ( ) . const_i32 ( -( align. bytes ( ) as i32 ) ) ) ;
30
+ ptr_as_int = round_up_to_alignment ( bx, ptr_as_int, align) ;
23
31
bx. inttoptr ( ptr_as_int, ptr_ty)
24
32
}
25
33
@@ -249,6 +257,90 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
249
257
bx. load ( val_type, val_addr, layout. align . abi )
250
258
}
251
259
260
+ fn emit_xtensa_va_arg < ' ll , ' tcx > (
261
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
262
+ list : OperandRef < ' tcx , & ' ll Value > ,
263
+ target_ty : Ty < ' tcx > ,
264
+ ) -> & ' ll Value {
265
+ // Implementation of va_arg for Xtensa. There doesn't seem to be an authoritative source for
266
+ // this, other than "what GCC does".
267
+ //
268
+ // The va_list type has three fields:
269
+ // struct __va_list_tag {
270
+ // int32_t *va_stk; // Arguments passed on the stack
271
+ // int32_t *va_reg; // Arguments passed in registers, saved to memory by the prologue.
272
+ // int32_t va_ndx; // Offset into the arguments, in bytes
273
+ // };
274
+ //
275
+ // Whether an argument is loaded from va_stk or va_reg depends on the value of va_ndx.
276
+ // The first 24 bytes (equivalent to 6 registers) come from va_reg, the rest from va_stk.
277
+ //
278
+ // Arguments are never split between registers and the stack. For example, if loading an 8 byte
279
+ // value and va_ndx = 20, we instead bump the offset and read everything from va_stk.
280
+ let va_list_addr = list. immediate ( ) ;
281
+ let va_list_layout = list. deref ( bx. cx ) . layout ;
282
+ let va_list_ty = va_list_layout. llvm_type ( bx) ;
283
+
284
+ let layout = bx. cx . layout_of ( target_ty) ;
285
+ let from_stack = bx. append_sibling_block ( "va_arg.from_stack" ) ;
286
+ let from_regsave = bx. append_sibling_block ( "va_arg.from_regsave" ) ;
287
+ let end = bx. append_sibling_block ( "va_arg.end" ) ;
288
+
289
+ let offset_ptr =
290
+ bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 2 ) ) ;
291
+ let mut offset = bx. load ( bx. type_i32 ( ) , offset_ptr, bx. tcx ( ) . data_layout . i32_align . abi ) ;
292
+
293
+ let slot_size = layout. size . align_to ( Align :: from_bytes ( 4 ) . unwrap ( ) ) . bytes ( ) as i32 ;
294
+ if layout. align . abi . bytes ( ) > 4 {
295
+ offset = round_up_to_alignment ( bx, offset, layout. align . abi ) ;
296
+ }
297
+
298
+ // Update the offset in va_list, by adding the slot's size.
299
+ let offset_next = bx. add ( offset, bx. const_i32 ( slot_size) ) ;
300
+ bx. store ( offset_next, offset_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
301
+
302
+ // Figure out where to look for our value. We do that by checking the end of our slot (offset_next).
303
+ // If that is within the regsave area, then load from there. Otherwise load from the stack area.
304
+ let regsave_size = bx. const_i32 ( 24 ) ;
305
+ let use_regsave = bx. icmp ( IntPredicate :: IntULE , offset_next, regsave_size) ;
306
+ bx. cond_br ( use_regsave, from_regsave, from_stack) ;
307
+
308
+ bx. switch_to_block ( from_regsave) ;
309
+ let regsave_area_ptr =
310
+ bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 1 ) ) ;
311
+ let regsave_area =
312
+ bx. load ( bx. type_ptr ( ) , regsave_area_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
313
+ let regsave_value_ptr = bx. inbounds_gep ( bx. type_i8 ( ) , regsave_area, & [ offset] ) ;
314
+ bx. br ( end) ;
315
+
316
+ bx. switch_to_block ( from_stack) ;
317
+
318
+ // The first time we switch from regsave to stack we needs to adjust our offsets a bit.
319
+ // va_stk is set up such that the first stack argument is always at va_stk + 32.
320
+ // The corrected offset is written back into the va_list struct.
321
+ let needs_correction = bx. icmp ( IntPredicate :: IntULE , offset, regsave_size) ;
322
+ let offset_corrected = bx. select ( needs_correction, bx. const_i32 ( 32 ) , offset) ;
323
+ let offset_next_corrected =
324
+ bx. select ( needs_correction, bx. const_i32 ( 32 + slot_size) , offset_next) ;
325
+ bx. store ( offset_next_corrected, offset_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
326
+
327
+ let stack_area_ptr =
328
+ bx. struct_gep ( va_list_ty, va_list_addr, va_list_layout. llvm_field_index ( bx. cx , 0 ) ) ;
329
+ let stack_area = bx. load ( bx. type_ptr ( ) , stack_area_ptr, bx. tcx ( ) . data_layout . pointer_align . abi ) ;
330
+ let stack_value_ptr = bx. inbounds_gep ( bx. type_i8 ( ) , stack_area, & [ offset_corrected] ) ;
331
+ bx. br ( end) ;
332
+
333
+ bx. switch_to_block ( end) ;
334
+
335
+ // On big-endian, for values smaller than the slot size we'd have to align the read to the end
336
+ // of the slot rather than the start. While the ISA and GCC support big-endian, all the Xtensa
337
+ // targets supported by rustc are litte-endian so don't worry about it.
338
+ assert ! ( bx. tcx( ) . sess. target. endian == Endian :: Little ) ;
339
+ let value_ptr =
340
+ bx. phi ( bx. type_ptr ( ) , & [ regsave_value_ptr, stack_value_ptr] , & [ from_regsave, from_stack] ) ;
341
+ return bx. load ( layout. llvm_type ( bx) , value_ptr, layout. align . abi ) ;
342
+ }
343
+
252
344
pub ( super ) fn emit_va_arg < ' ll , ' tcx > (
253
345
bx : & mut Builder < ' _ , ' ll , ' tcx > ,
254
346
addr : OperandRef < ' tcx , & ' ll Value > ,
@@ -281,6 +373,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
281
373
let indirect: bool = target_ty_size > 8 || !target_ty_size. is_power_of_two ( ) ;
282
374
emit_ptr_va_arg ( bx, addr, target_ty, indirect, Align :: from_bytes ( 8 ) . unwrap ( ) , false )
283
375
}
376
+ "xtensa" => emit_xtensa_va_arg ( bx, addr, target_ty) ,
284
377
// For all other architecture/OS combinations fall back to using
285
378
// the LLVM va_arg instruction.
286
379
// https://llvm.org/docs/LangRef.html#va-arg-instruction
0 commit comments