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