@@ -680,3 +680,94 @@ pub fn fold_load_from_constant_variable(module: &mut Module) {
680680 }
681681 }
682682}
683+
684+ /// Eliminate the `OpBitcast` that arises from `*[T; N] → *RuntimeArray<T>` pointer casts.
685+ ///
686+ /// When a local array is coerced to a slice (`&[T;N] as &[T]`), codegen emits:
687+ /// ```text
688+ /// %elem0 = OpInBoundsAccessChain %arr 0 ; pointer to arr[0]
689+ /// %rta = OpBitcast %elem0 ; *RuntimeArray<T> (invalid in Logical SPIR-V)
690+ /// ```
691+ /// After inlining the slice-taking function, indexing becomes:
692+ /// ```text
693+ /// %ei = OpInBoundsAccessChain %rta i ; data[i]
694+ ///
695+ pub fn fold_array_bitcast_access_chain (
696+ types : & FxHashMap < Word , Instruction > ,
697+ function : & mut Function ,
698+ ) {
699+ let func_defs: FxHashMap < Word , Instruction > = function
700+ . all_inst_iter ( )
701+ . filter_map ( |inst| Some ( ( inst. result_id ?, inst. clone ( ) ) ) )
702+ . collect ( ) ;
703+
704+ // look up an ID in either function-local defs or module-level types/globals.
705+ let lookup = |id : Word | -> Option < & Instruction > {
706+ func_defs. get ( & id) . or_else ( || types. get ( & id) )
707+ } ;
708+
709+ for block in & mut function. blocks {
710+ for inst in & mut block. instructions {
711+ if !matches ! ( inst. class. opcode, Op :: AccessChain | Op :: InBoundsAccessChain ) {
712+ continue ;
713+ }
714+ if inst. operands . is_empty ( ) {
715+ continue ;
716+ }
717+ let base_id = inst. operands [ 0 ] . unwrap_id_ref ( ) ;
718+
719+ // base must be an OpBitcast
720+ let Some ( bitcast) = lookup ( base_id) else { continue } ;
721+ if bitcast. class . opcode != Op :: Bitcast {
722+ continue ;
723+ }
724+
725+ // bitcast result type must be *SC RuntimeArray<T>
726+ let Some ( bitcast_dst_ptr) = lookup ( bitcast. result_type . unwrap ( ) ) else { continue } ;
727+ if bitcast_dst_ptr. class . opcode != Op :: TypePointer {
728+ continue ;
729+ }
730+ let rta_type_id = bitcast_dst_ptr. operands [ 1 ] . unwrap_id_ref ( ) ;
731+ let Some ( rta_ty) = lookup ( rta_type_id) else { continue } ;
732+ if rta_ty. class . opcode != Op :: TypeRuntimeArray {
733+ continue ;
734+ }
735+ let rta_elem_ty = rta_ty. operands [ 0 ] . unwrap_id_ref ( ) ;
736+
737+ // bitcast source must be OpInBoundsAccessChain(arr, 0)
738+ let bitcast_src_id = bitcast. operands [ 0 ] . unwrap_id_ref ( ) ;
739+ let Some ( inner_ac) = lookup ( bitcast_src_id) else { continue } ;
740+ if inner_ac. class . opcode != Op :: InBoundsAccessChain {
741+ continue ;
742+ }
743+ // Exactly one index operand
744+ if inner_ac. operands . len ( ) != 2 {
745+ continue ;
746+ }
747+ // That index must be the constant 0
748+ let idx0_id = inner_ac. operands [ 1 ] . unwrap_id_ref ( ) ;
749+ let Some ( idx0) = lookup ( idx0_id) else { continue } ;
750+ if idx0. class . opcode != Op :: Constant {
751+ continue ;
752+ }
753+ if !matches ! ( idx0. operands[ 0 ] , Operand :: LiteralBit32 ( 0 ) ) {
754+ continue ;
755+ }
756+
757+ // inner AccessChain result type must be *SC T where T == rta_elem_ty
758+ let Some ( inner_dst_ptr) = lookup ( inner_ac. result_type . unwrap ( ) ) else { continue } ;
759+ if inner_dst_ptr. class . opcode != Op :: TypePointer {
760+ continue ;
761+ }
762+ let elem_ty = inner_dst_ptr. operands [ 1 ] . unwrap_id_ref ( ) ;
763+ if elem_ty != rta_elem_ty {
764+ continue ;
765+ }
766+
767+ // AccessChain(Bitcast(InBoundsAccessChain(arr, 0)), i)
768+ // Replace base with arr — the dead bitcast and intermediate AC are cleaned by DCE.
769+ let arr_id = inner_ac. operands [ 0 ] . unwrap_id_ref ( ) ;
770+ inst. operands [ 0 ] = Operand :: IdRef ( arr_id) ;
771+ }
772+ }
773+ }
0 commit comments