Skip to content

Commit ace9310

Browse files
committed
builder: castless noop in ptr_offset_strided.
1 parent 4a15e51 commit ace9310

File tree

1 file changed

+60
-17
lines changed

1 file changed

+60
-17
lines changed

crates/rustc_codegen_spirv/src/builder/builder_methods.rs

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -675,15 +675,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
675675
/// where `T` is given by `stride_elem_ty` (named so for extra clarity).
676676
///
677677
/// This can produce legal SPIR-V by using 3 strategies:
678-
/// 1. `pointercast` for a constant offset of `0`
679-
/// - itself can succeed via `recover_access_chain_from_offset`
680-
/// - even if a specific cast is unsupported, legal SPIR-V can still be
681-
/// obtained, if all downstream uses rely on e.g. `strip_ptrcasts`
682-
/// - also used before the merge strategy (3.), to improve its chances
678+
/// 1. noop, i.e. returning `ptr` unmodified, comparable to a `pointercast`
679+
/// (but instead letting downstream users do any casts they might need,
680+
/// themselves - also, upstream untyped pointers mean that no pointer
681+
/// can be expected to have any specific pointee type)
683682
/// 2. `recover_access_chain_from_offset` for constant offsets
684683
/// (e.g. from `ptradd`/`inbounds_ptradd` used to access `struct` fields)
685684
/// 3. merging onto an array `OpAccessChain` with the same `stride_elem_ty`
686685
/// (possibly `&array[0]` from `pointercast` doing `*[T; N]` -> `*T`)
686+
///
687+
/// Also, `pointercast` (used downstream, or as part of strategy 3.) helps
688+
/// with producing legal SPIR-V, as it allows deferring whole casts chains,
689+
/// and has a couple success modes of its own:
690+
/// - itself can also use `recover_access_chain_from_offset`, supporting
691+
/// `struct`/array casts e.g. `*(T, U, ...)` -> `*T` / `*[T; N]` -> `*T`
692+
/// - even if a specific cast is unsupported, legal SPIR-V can still be
693+
/// later obtained (thanks to `SpirvValueKind::LogicalPtrCast`), if all
694+
/// uses of that cast rely on `pointercast` and/or `strip_ptrcasts`, e.g.:
695+
/// - another `ptr_offset_strided` call (with a different offset)
696+
/// - `adjust_pointer_for_typed_access`/`adjust_pointer_for_sized_access`
697+
/// (themselves used by e.g. loads, stores, copies, etc.)
698+
//
699+
// FIXME(eddyb) maybe move the above `pointercast` section to its own docs?
687700
#[instrument(level = "trace", skip(self), fields(ptr, stride_elem_ty = ?self.debug_type(stride_elem_ty), index, is_inbounds))]
688701
fn ptr_offset_strided(
689702
&mut self,
@@ -700,15 +713,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
700713
Some(idx_u64 * stride)
701714
});
702715

703-
// Strategy 1: an offset of `0` is always a noop, and `pointercast`
704-
// gets to use `SpirvValueKind::LogicalPtrCast`, which can later
705-
// be "undone" (by `strip_ptrcasts`), allowing more flexibility
706-
// downstream (instead of overeagerly "shrinking" the pointee).
716+
// Strategy 1: do nothing for a `0` offset (and `stride_elem_ty` can be
717+
// safely ignored, because any typed uses will `pointercast` if needed).
707718
if const_offset == Some(Size::ZERO) {
708-
trace!("ptr_offset_strided: strategy 1 picked: offset 0 => pointer cast");
719+
trace!("ptr_offset_strided: strategy 1 picked: offset 0 => noop");
709720

710-
// FIXME(eddyb) could this just `return ptr;`? what even breaks?
711-
return self.pointercast(ptr, self.type_ptr_to(stride_elem_ty));
721+
return ptr;
712722
}
713723

714724
// Strategy 2: try recovering an `OpAccessChain` from a constant offset.
@@ -3209,6 +3219,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
32093219
Bitcast(ID, ID),
32103220
CompositeExtract(ID, ID, u32),
32113221
InBoundsAccessChain(ID, ID, u32),
3222+
InBoundsAccessChain2(ID, ID, u32, u32),
32123223
Store(ID, ID),
32133224
Load(ID, ID),
32143225
CopyMemory(ID, ID),
@@ -3265,6 +3276,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
32653276
Inst::Unsupported(inst.class.opcode)
32663277
}
32673278
}
3279+
(Op::InBoundsAccessChain, Some(r), &[p, i, j]) => {
3280+
if let [Some(i), Some(j)] = [i, j].map(const_as_u32) {
3281+
Inst::InBoundsAccessChain2(r, p, i, j)
3282+
} else {
3283+
Inst::Unsupported(inst.class.opcode)
3284+
}
3285+
}
32683286
(Op::Store, None, &[p, v]) => Inst::Store(p, v),
32693287
(Op::Load, Some(r), &[p]) => Inst::Load(r, p),
32703288
(Op::CopyMemory, None, &[a, b]) => Inst::CopyMemory(a, b),
@@ -3456,20 +3474,45 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
34563474

34573475
let rev_copies_to_rt_args_array_src_ptrs: SmallVec<[_; 4]> =
34583476
(0..rt_args_count).rev().map(|rt_arg_idx| {
3459-
let copy_to_rt_args_array_insts = try_rev_take(4).ok_or_else(|| {
3477+
let mut copy_to_rt_args_array_insts = try_rev_take(3).ok_or_else(|| {
34603478
FormatArgsNotRecognized(
34613479
"[fmt::rt::Argument; N] copy: ran out of instructions".into(),
34623480
)
34633481
})?;
3482+
3483+
// HACK(eddyb) account for both the split and combined
3484+
// access chain cases that `inbounds_gep` can now cause.
3485+
if let Inst::InBoundsAccessChain(dst_field_ptr, dst_base_ptr, 0) =
3486+
copy_to_rt_args_array_insts[0]
3487+
{
3488+
if let Some(mut prev_insts) = try_rev_take(1) {
3489+
assert_eq!(prev_insts.len(), 1);
3490+
let prev_inst = prev_insts.pop().unwrap();
3491+
3492+
match prev_inst {
3493+
Inst::InBoundsAccessChain(
3494+
array_elem_ptr,
3495+
array_ptr,
3496+
idx,
3497+
) if dst_base_ptr == array_elem_ptr => {
3498+
copy_to_rt_args_array_insts[0] =
3499+
Inst::InBoundsAccessChain2(dst_field_ptr, array_ptr, idx, 0);
3500+
}
3501+
_ => {
3502+
// HACK(eddyb) don't lose the taken `prev_inst`.
3503+
copy_to_rt_args_array_insts.insert(0, prev_inst);
3504+
}
3505+
}
3506+
}
3507+
}
3508+
34643509
match copy_to_rt_args_array_insts[..] {
34653510
[
3466-
Inst::InBoundsAccessChain(array_slot, array_base, array_idx),
3467-
Inst::InBoundsAccessChain(dst_field_ptr, dst_base_ptr, 0),
3511+
Inst::InBoundsAccessChain2(dst_field_ptr, dst_array_base_ptr, array_idx, 0),
34683512
Inst::InBoundsAccessChain(src_field_ptr, src_base_ptr, 0),
34693513
Inst::CopyMemory(copy_dst, copy_src),
3470-
] if array_base == rt_args_array_ptr_id
3514+
] if dst_array_base_ptr == rt_args_array_ptr_id
34713515
&& array_idx as usize == rt_arg_idx
3472-
&& dst_base_ptr == array_slot
34733516
&& (copy_dst, copy_src) == (dst_field_ptr, src_field_ptr) =>
34743517
{
34753518
Ok(src_base_ptr)

0 commit comments

Comments
 (0)