@@ -10,15 +10,23 @@ use crate::type_::Type;
10
10
use crate::type_of::LayoutLlvmExt;
11
11
use crate::value::Value;
12
12
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
+
13
22
fn round_pointer_up_to_alignment<'ll>(
14
23
bx: &mut Builder<'_, 'll, '_>,
15
24
addr: &'ll Value,
16
25
align: Align,
17
26
ptr_ty: &'ll Type,
18
27
) -> &'ll Value {
19
28
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);
22
30
bx.inttoptr(ptr_as_int, ptr_ty)
23
31
}
24
32
@@ -270,6 +278,106 @@ fn emit_s390x_va_arg<'ll, 'tcx>(
270
278
bx.load(val_type, val_addr, layout.align.abi)
271
279
}
272
280
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
+
273
381
pub(super) fn emit_va_arg<'ll, 'tcx>(
274
382
bx: &mut Builder<'_, 'll, 'tcx>,
275
383
addr: OperandRef<'tcx, &'ll Value>,
@@ -302,6 +410,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
302
410
let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
303
411
emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false)
304
412
}
413
+ "xtensa" => emit_xtensa_va_arg(bx, addr, target_ty),
305
414
// For all other architecture/OS combinations fall back to using
306
415
// the LLVM va_arg instruction.
307
416
// https://llvm.org/docs/LangRef.html#va-arg-instruction
0 commit comments