Skip to content

Commit 48d94f6

Browse files
committed
builder: castless noop in ptr_offset_strided.
1 parent 3396a98 commit 48d94f6

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

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

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

715725
// Strategy 2: try recovering an `OpAccessChain` from a constant offset.
@@ -3210,6 +3220,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
32103220
Bitcast(ID, ID),
32113221
CompositeExtract(ID, ID, u32),
32123222
InBoundsAccessChain(ID, ID, u32),
3223+
InBoundsAccessChain2(ID, ID, u32, u32),
32133224
Store(ID, ID),
32143225
Load(ID, ID),
32153226
CopyMemory(ID, ID),
@@ -3266,6 +3277,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
32663277
Inst::Unsupported(inst.class.opcode)
32673278
}
32683279
}
3280+
(Op::InBoundsAccessChain, Some(r), &[p, i, j]) => {
3281+
if let [Some(i), Some(j)] = [i, j].map(const_as_u32) {
3282+
Inst::InBoundsAccessChain2(r, p, i, j)
3283+
} else {
3284+
Inst::Unsupported(inst.class.opcode)
3285+
}
3286+
}
32693287
(Op::Store, None, &[p, v]) => Inst::Store(p, v),
32703288
(Op::Load, Some(r), &[p]) => Inst::Load(r, p),
32713289
(Op::CopyMemory, None, &[a, b]) => Inst::CopyMemory(a, b),
@@ -3457,20 +3475,45 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
34573475

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

0 commit comments

Comments
 (0)