@@ -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