@@ -10,15 +10,23 @@ use crate::type_::Type;
1010use  crate :: type_of:: LayoutLlvmExt ; 
1111use  crate :: value:: Value ; 
1212
13+ fn  round_up_to_alignment < ' ll > ( 
14+     bx :  & mut  Builder < ' _ ,  ' ll ,  ' _ > , 
15+     mut  value :  & ' ll  Value , 
16+     align :  Align , 
17+ )  -> & ' ll  Value  { 
18+     value = bx. add ( value,  bx. cx ( ) . const_i32 ( align. bytes ( )  as  i32  - 1 ) ) ; 
19+     return  bx. and ( value,  bx. cx ( ) . const_i32 ( -( align. bytes ( )  as  i32 ) ) ) ; 
20+ } 
21+ 
1322fn  round_pointer_up_to_alignment < ' ll > ( 
1423    bx :  & mut  Builder < ' _ ,  ' ll ,  ' _ > , 
1524    addr :  & ' ll  Value , 
1625    align :  Align , 
1726    ptr_ty :  & ' ll  Type , 
1827)  -> & ' ll  Value  { 
1928    let  mut  ptr_as_int = bx. ptrtoint ( addr,  bx. cx ( ) . type_isize ( ) ) ; 
20-     ptr_as_int = bx. add ( ptr_as_int,  bx. cx ( ) . const_i32 ( align. bytes ( )  as  i32  - 1 ) ) ; 
21-     ptr_as_int = bx. and ( ptr_as_int,  bx. cx ( ) . const_i32 ( -( align. bytes ( )  as  i32 ) ) ) ; 
29+     ptr_as_int = round_up_to_alignment ( bx,  ptr_as_int,  align) ; 
2230    bx. inttoptr ( ptr_as_int,  ptr_ty) 
2331} 
2432
@@ -270,6 +278,106 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
270278    bx. load ( val_type,  val_addr,  layout. align . abi ) 
271279} 
272280
281+ fn  emit_xtensa_va_arg < ' ll ,  ' tcx > ( 
282+     bx :  & mut  Builder < ' _ ,  ' ll ,  ' tcx > , 
283+     list :  OperandRef < ' tcx ,  & ' ll  Value > , 
284+     target_ty :  Ty < ' tcx > , 
285+ )  -> & ' ll  Value  { 
286+     // Implementation of va_arg for Xtensa. There doesn't seem to be an authoritative source for 
287+     // this, other than "what GCC does". 
288+     // 
289+     // The va_list type has three fields: 
290+     // struct __va_list_tag { 
291+     //   int32_t *va_stk; // Arguments passed on the stack 
292+     //   int32_t *va_reg; // Arguments passed in registers, saved to memory by the prologue. 
293+     //   int32_t va_ndx; // Offset into the arguments, in bytes 
294+     // }; 
295+     // 
296+     // The first 24 bytes (equivalent to 6 registers) come from va_reg, the rest from va_stk. 
297+     // Thus if va_ndx is less than 24, the next va_arg *may* read from va_reg, 
298+     // otherwise it must come from va_stk. 
299+     // 
300+     // Primitive arguments are never split between registers and the stack. For example, if loading an 8 byte 
301+     // primitive value and va_ndx = 20, we instead bump the offset and read everything from va_stk. 
302+     let  va_list_addr = list. immediate ( ) ; 
303+     // FIXME: handle multi-field structs that split across regsave/stack? 
304+     let  layout = bx. cx . layout_of ( target_ty) ; 
305+     let  from_stack = bx. append_sibling_block ( "va_arg.from_stack" ) ; 
306+     let  from_regsave = bx. append_sibling_block ( "va_arg.from_regsave" ) ; 
307+     let  end = bx. append_sibling_block ( "va_arg.end" ) ; 
308+ 
309+     // (*va).va_ndx 
310+     let  va_reg_offset = 4 ; 
311+     let  va_ndx_offset = va_reg_offset + 4 ; 
312+     let  offset_ptr =
313+         bx. inbounds_gep ( bx. type_i8 ( ) ,  va_list_addr,  & [ bx. cx . const_usize ( va_ndx_offset) ] ) ; 
314+ 
315+     let  offset = bx. load ( bx. type_i32 ( ) ,  offset_ptr,  bx. tcx ( ) . data_layout . i32_align . abi ) ; 
316+     let  offset = round_up_to_alignment ( bx,  offset,  layout. align . abi ) ; 
317+ 
318+     let  slot_size = layout. size . align_to ( Align :: from_bytes ( 4 ) . unwrap ( ) ) . bytes ( )  as  i32 ; 
319+ 
320+     // Update the offset in va_list, by adding the slot's size. 
321+     let  offset_next = bx. add ( offset,  bx. const_i32 ( slot_size) ) ; 
322+ 
323+     // Figure out where to look for our value. We do that by checking the end of our slot (offset_next). 
324+     // If that is within the regsave area, then load from there. Otherwise load from the stack area. 
325+     let  regsave_size = bx. const_i32 ( 24 ) ; 
326+     let  use_regsave = bx. icmp ( IntPredicate :: IntULE ,  offset_next,  regsave_size) ; 
327+     bx. cond_br ( use_regsave,  from_regsave,  from_stack) ; 
328+ 
329+     bx. switch_to_block ( from_regsave) ; 
330+     // update va_ndx 
331+     bx. store ( offset_next,  offset_ptr,  bx. tcx ( ) . data_layout . pointer_align . abi ) ; 
332+ 
333+     // (*va).va_reg 
334+     let  regsave_area_ptr =
335+         bx. inbounds_gep ( bx. type_i8 ( ) ,  va_list_addr,  & [ bx. cx . const_usize ( va_reg_offset) ] ) ; 
336+     let  regsave_area =
337+         bx. load ( bx. type_ptr ( ) ,  regsave_area_ptr,  bx. tcx ( ) . data_layout . pointer_align . abi ) ; 
338+     let  regsave_value_ptr = bx. inbounds_gep ( bx. type_i8 ( ) ,  regsave_area,  & [ offset] ) ; 
339+     bx. br ( end) ; 
340+ 
341+     bx. switch_to_block ( from_stack) ; 
342+ 
343+     // The first time we switch from regsave to stack we needs to adjust our offsets a bit. 
344+     // va_stk is set up such that the first stack argument is always at va_stk + 32. 
345+     // The corrected offset is written back into the va_list struct. 
346+ 
347+     // let offset_corrected = cmp::max(offset, 32); 
348+     let  stack_offset_start = bx. const_i32 ( 32 ) ; 
349+     let  needs_correction = bx. icmp ( IntPredicate :: IntULE ,  offset,  stack_offset_start) ; 
350+     let  offset_corrected = bx. select ( needs_correction,  stack_offset_start,  offset) ; 
351+ 
352+     // let offset_next_corrected = offset_corrected + slot_size; 
353+     // va_ndx = offset_next_corrected; 
354+     let  offset_next_corrected = bx. add ( offset_next,  bx. const_i32 ( slot_size) ) ; 
355+     // update va_ndx 
356+     bx. store ( offset_next_corrected,  offset_ptr,  bx. tcx ( ) . data_layout . pointer_align . abi ) ; 
357+ 
358+     // let stack_value_ptr = unsafe { (*va).va_stk.byte_add(offset_corrected) }; 
359+     let  stack_area_ptr = bx. inbounds_gep ( bx. type_i8 ( ) ,  va_list_addr,  & [ bx. cx . const_usize ( 0 ) ] ) ; 
360+     let  stack_area = bx. load ( bx. type_ptr ( ) ,  stack_area_ptr,  bx. tcx ( ) . data_layout . pointer_align . abi ) ; 
361+     let  stack_value_ptr = bx. inbounds_gep ( bx. type_i8 ( ) ,  stack_area,  & [ offset_corrected] ) ; 
362+     bx. br ( end) ; 
363+ 
364+     bx. switch_to_block ( end) ; 
365+ 
366+     // On big-endian, for values smaller than the slot size we'd have to align the read to the end 
367+     // of the slot rather than the start. While the ISA and GCC support big-endian, all the Xtensa 
368+     // targets supported by rustc are litte-endian so don't worry about it. 
369+ 
370+     // if from_regsave { 
371+     //     unsafe { *regsave_value_ptr } 
372+     // } else { 
373+     //     unsafe { *stack_value_ptr } 
374+     // } 
375+     assert ! ( bx. tcx( ) . sess. target. endian == Endian :: Little ) ; 
376+     let  value_ptr =
377+         bx. phi ( bx. type_ptr ( ) ,  & [ regsave_value_ptr,  stack_value_ptr] ,  & [ from_regsave,  from_stack] ) ; 
378+     return  bx. load ( layout. llvm_type ( bx) ,  value_ptr,  layout. align . abi ) ; 
379+ } 
380+ 
273381pub ( super )  fn  emit_va_arg < ' ll ,  ' tcx > ( 
274382    bx :  & mut  Builder < ' _ ,  ' ll ,  ' tcx > , 
275383    addr :  OperandRef < ' tcx ,  & ' ll  Value > , 
@@ -302,6 +410,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
302410            let  indirect:  bool  = target_ty_size > 8  || !target_ty_size. is_power_of_two ( ) ; 
303411            emit_ptr_va_arg ( bx,  addr,  target_ty,  indirect,  Align :: from_bytes ( 8 ) . unwrap ( ) ,  false ) 
304412        } 
413+         "xtensa"  => emit_xtensa_va_arg ( bx,  addr,  target_ty) , 
305414        // For all other architecture/OS combinations fall back to using 
306415        // the LLVM va_arg instruction. 
307416        // https://llvm.org/docs/LangRef.html#va-arg-instruction 
0 commit comments