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