@@ -676,15 +676,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
676
676
/// where `T` is given by `stride_elem_ty` (named so for extra clarity).
677
677
///
678
678
/// 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)
684
683
/// 2. `recover_access_chain_from_offset` for constant offsets
685
684
/// (e.g. from `ptradd`/`inbounds_ptradd` used to access `struct` fields)
686
685
/// 3. merging onto an array `OpAccessChain` with the same `stride_elem_ty`
687
686
/// (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?
688
701
#[ instrument( level = "trace" , skip( self ) , fields( ptr, stride_elem_ty = ?self . debug_type( stride_elem_ty) , index, is_inbounds) ) ]
689
702
fn ptr_offset_strided (
690
703
& mut self ,
@@ -701,15 +714,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
701
714
Some ( idx_u64 * stride)
702
715
} ) ;
703
716
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).
708
719
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 " ) ;
710
721
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;
713
723
}
714
724
715
725
// Strategy 2: try recovering an `OpAccessChain` from a constant offset.
@@ -3210,6 +3220,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3210
3220
Bitcast ( ID , ID ) ,
3211
3221
CompositeExtract ( ID , ID , u32 ) ,
3212
3222
InBoundsAccessChain ( ID , ID , u32 ) ,
3223
+ InBoundsAccessChain2 ( ID , ID , u32 , u32 ) ,
3213
3224
Store ( ID , ID ) ,
3214
3225
Load ( ID , ID ) ,
3215
3226
CopyMemory ( ID , ID ) ,
@@ -3266,6 +3277,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3266
3277
Inst :: Unsupported ( inst. class . opcode )
3267
3278
}
3268
3279
}
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
+ }
3269
3287
( Op :: Store , None , & [ p, v] ) => Inst :: Store ( p, v) ,
3270
3288
( Op :: Load , Some ( r) , & [ p] ) => Inst :: Load ( r, p) ,
3271
3289
( Op :: CopyMemory , None , & [ a, b] ) => Inst :: CopyMemory ( a, b) ,
@@ -3457,20 +3475,45 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3457
3475
3458
3476
let rev_copies_to_rt_args_array_src_ptrs: SmallVec < [ _ ; 4 ] > =
3459
3477
( 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 ( || {
3461
3479
FormatArgsNotRecognized (
3462
3480
"[fmt::rt::Argument; N] copy: ran out of instructions" . into ( ) ,
3463
3481
)
3464
3482
} ) ?;
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
+
3465
3510
match copy_to_rt_args_array_insts[ ..] {
3466
3511
[
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 ) ,
3469
3513
Inst :: InBoundsAccessChain ( src_field_ptr, src_base_ptr, 0 ) ,
3470
3514
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
3472
3516
&& array_idx as usize == rt_arg_idx
3473
- && dst_base_ptr == array_slot
3474
3517
&& ( copy_dst, copy_src) == ( dst_field_ptr, src_field_ptr) =>
3475
3518
{
3476
3519
Ok ( src_base_ptr)
0 commit comments