[Mlir-commits] [mlir] f646f73 - [mlir][spirv] Add InBoundsAccessChain op (#216682)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Aug 17 11:20:35 PDT 2026


Author: Hsiangkai Wang
Date: 2026-08-17T19:20:30+01:00
New Revision: f646f7395fe1fbb05d78a42871cb72bc31e4d7f9

URL: https://github.com/llvm/llvm-project/commit/f646f7395fe1fbb05d78a42871cb72bc31e4d7f9
DIFF: https://github.com/llvm/llvm-project/commit/f646f7395fe1fbb05d78a42871cb72bc31e4d7f9.diff

LOG: [mlir][spirv] Add InBoundsAccessChain op (#216682)

Add the SPIR-V opcode, ODS definition, verifier hook, and
canonicalization support for spirv.InBoundsAccessChain.

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
    mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
    mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
    mlir/lib/Dialect/SPIRV/IR/SPIRVCanonicalization.cpp
    mlir/test/Dialect/SPIRV/IR/memory-ops.mlir
    mlir/test/Dialect/SPIRV/Transforms/canonicalize.mlir
    mlir/test/Target/SPIRV/memory-ops.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index 527e6082c5fcd..92653117a451a 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4493,6 +4493,7 @@ def SPIRV_OC_OpLoad                           : I32EnumAttrCase<"OpLoad", 61>;
 def SPIRV_OC_OpStore                          : I32EnumAttrCase<"OpStore", 62>;
 def SPIRV_OC_OpCopyMemory                     : I32EnumAttrCase<"OpCopyMemory", 63>;
 def SPIRV_OC_OpAccessChain                    : I32EnumAttrCase<"OpAccessChain", 65>;
+def SPIRV_OC_OpInBoundsAccessChain            : I32EnumAttrCase<"OpInBoundsAccessChain", 66>;
 def SPIRV_OC_OpPtrAccessChain                 : I32EnumAttrCase<"OpPtrAccessChain", 67>;
 def SPIRV_OC_OpInBoundsPtrAccessChain         : I32EnumAttrCase<"OpInBoundsPtrAccessChain", 70>;
 def SPIRV_OC_OpDecorate                       : I32EnumAttrCase<"OpDecorate", 71>;
@@ -4744,7 +4745,7 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpSpecConstantOp, SPIRV_OC_OpFunction, SPIRV_OC_OpFunctionParameter,
       SPIRV_OC_OpFunctionEnd, SPIRV_OC_OpFunctionCall, SPIRV_OC_OpVariable,
       SPIRV_OC_OpLoad, SPIRV_OC_OpStore, SPIRV_OC_OpCopyMemory,
-      SPIRV_OC_OpAccessChain, SPIRV_OC_OpPtrAccessChain,
+      SPIRV_OC_OpAccessChain, SPIRV_OC_OpInBoundsAccessChain, SPIRV_OC_OpPtrAccessChain,
       SPIRV_OC_OpInBoundsPtrAccessChain, SPIRV_OC_OpDecorate,
       SPIRV_OC_OpMemberDecorate, SPIRV_OC_OpVectorExtractDynamic,
       SPIRV_OC_OpVectorInsertDynamic, SPIRV_OC_OpVectorShuffle,

diff  --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
index e13909e8eeeae..5ae6c8af83003 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
@@ -81,6 +81,40 @@ def SPIRV_AccessChainOp : SPIRV_Op<"AccessChain", [Pure]> {
 
 // -----
 
+def SPIRV_InBoundsAccessChainOp : SPIRV_Op<"InBoundsAccessChain", [Pure]> {
+  let summary = [{
+    Create a pointer into a composite object that is known to stay within the
+    base object.
+  }];
+
+  let description = [{
+    Has the same operands, result, and type rules as `spirv.AccessChain`, with
+    the additional contract that the resulting pointer points within the base
+    object.
+  }];
+
+  let arguments = (ins
+    SPIRV_AnyPtr:$base_ptr,
+    Variadic<SPIRV_Integer>:$indices
+  );
+
+  let results = (outs
+    SPIRV_AnyPtr:$component_ptr
+  );
+
+  let builders = [OpBuilder<(ins "Value":$basePtr, "ValueRange":$indices)>];
+
+  let hasCanonicalizer = 1;
+
+  let hasCustomAssemblyFormat = 0;
+
+  let assemblyFormat = [{
+    $base_ptr `[` $indices `]` attr-dict `:` type($base_ptr) `,` type($indices) `->` type(results)
+  }];
+}
+
+// -----
+
 def SPIRV_CopyMemoryOp : SPIRV_Op<"CopyMemory", [DeclareOpInterfaceMethods<AlignmentAttrOpInterface>]> {
   let summary = [{
     Copy from the memory pointed to by Source to the memory pointed to by

diff  --git a/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp b/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
index f9c03bf3b88c0..602ba932f8838 100644
--- a/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
@@ -351,6 +351,21 @@ LogicalResult AccessChainOp::verify() {
   return verifyAccessChain(*this, getIndices());
 }
 
+//===----------------------------------------------------------------------===//
+// spirv.InBoundsAccessChainOp
+//===----------------------------------------------------------------------===//
+
+void InBoundsAccessChainOp::build(OpBuilder &builder, OperationState &state,
+                                  Value basePtr, ValueRange indices) {
+  Type type = getElementPtrType(basePtr.getType(), indices, state.location);
+  assert(type && "Unable to deduce return type based on basePtr and indices");
+  build(builder, state, type, basePtr, indices);
+}
+
+LogicalResult InBoundsAccessChainOp::verify() {
+  return verifyAccessChain(*this, getIndices());
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.LoadOp
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVCanonicalization.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVCanonicalization.cpp
index 2d5c4d7d3fd0e..acc38dffe4a2b 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVCanonicalization.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVCanonicalization.cpp
@@ -84,21 +84,20 @@ namespace {
 } // namespace
 
 //===----------------------------------------------------------------------===//
-// spirv.AccessChainOp
+// spirv.AccessChainOp / spirv.InBoundsAccessChainOp
 //===----------------------------------------------------------------------===//
 
 namespace {
 
-/// Combines chained `spirv::AccessChainOp` operations into one
-/// `spirv::AccessChainOp` operation.
-struct CombineChainedAccessChain final
-    : OpRewritePattern<spirv::AccessChainOp> {
-  using Base::Base;
+/// Combines chained SPIR-V access chain operations of the same kind into one.
+template <typename AccessChainOp>
+struct CombineChainedAccessChain final : OpRewritePattern<AccessChainOp> {
+  using OpRewritePattern<AccessChainOp>::OpRewritePattern;
 
-  LogicalResult matchAndRewrite(spirv::AccessChainOp accessChainOp,
+  LogicalResult matchAndRewrite(AccessChainOp accessChainOp,
                                 PatternRewriter &rewriter) const override {
     auto parentAccessChainOp =
-        accessChainOp.getBasePtr().getDefiningOp<spirv::AccessChainOp>();
+        accessChainOp.getBasePtr().template getDefiningOp<AccessChainOp>();
 
     if (!parentAccessChainOp) {
       return failure();
@@ -108,7 +107,7 @@ struct CombineChainedAccessChain final
     SmallVector<Value, 4> indices(parentAccessChainOp.getIndices());
     llvm::append_range(indices, accessChainOp.getIndices());
 
-    rewriter.replaceOpWithNewOp<spirv::AccessChainOp>(
+    rewriter.replaceOpWithNewOp<AccessChainOp>(
         accessChainOp, parentAccessChainOp.getBasePtr(), indices);
 
     return success();
@@ -118,7 +117,12 @@ struct CombineChainedAccessChain final
 
 void spirv::AccessChainOp::getCanonicalizationPatterns(
     RewritePatternSet &results, MLIRContext *context) {
-  results.add<CombineChainedAccessChain>(context);
+  results.add<CombineChainedAccessChain<spirv::AccessChainOp>>(context);
+}
+
+void spirv::InBoundsAccessChainOp::getCanonicalizationPatterns(
+    RewritePatternSet &results, MLIRContext *context) {
+  results.add<CombineChainedAccessChain<spirv::InBoundsAccessChainOp>>(context);
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/test/Dialect/SPIRV/IR/memory-ops.mlir b/mlir/test/Dialect/SPIRV/IR/memory-ops.mlir
index a3b96c698a344..c58f22cea5a4b 100644
--- a/mlir/test/Dialect/SPIRV/IR/memory-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/memory-ops.mlir
@@ -35,6 +35,17 @@ func.func @access_chain_2D_array_2(%arg0 : i32) -> () {
   return
 }
 
+//===----------------------------------------------------------------------===//
+// spirv.InBoundsAccessChain
+//===----------------------------------------------------------------------===//
+
+func.func @inbounds_access_chain(%arg0 : i32) -> () {
+  %0 = spirv.Variable : !spirv.ptr<!spirv.array<4xf32>, Function>
+  // CHECK: spirv.InBoundsAccessChain {{.*}}[{{.*}}] : !spirv.ptr<!spirv.array<4 x f32>, Function>
+  %1 = spirv.InBoundsAccessChain %0[%arg0] : !spirv.ptr<!spirv.array<4xf32>, Function>, i32 -> !spirv.ptr<f32, Function>
+  return
+}
+
 func.func @access_chain_rtarray(%arg0 : i32) -> () {
   %0 = spirv.Variable : !spirv.ptr<!spirv.rtarray<f32>, Function>
   // CHECK: spirv.AccessChain {{.*}}[{{.*}}] : !spirv.ptr<!spirv.rtarray<f32>, Function>

diff  --git a/mlir/test/Dialect/SPIRV/Transforms/canonicalize.mlir b/mlir/test/Dialect/SPIRV/Transforms/canonicalize.mlir
index 94d9c53db0bbc..e80145419f8a8 100644
--- a/mlir/test/Dialect/SPIRV/Transforms/canonicalize.mlir
+++ b/mlir/test/Dialect/SPIRV/Transforms/canonicalize.mlir
@@ -19,6 +19,25 @@ func.func @combine_full_access_chain() -> f32 {
 
 // -----
 
+//===----------------------------------------------------------------------===//
+// spirv.InBoundsAccessChain
+//===----------------------------------------------------------------------===//
+
+func.func @combine_full_inbounds_access_chain() -> f32 {
+  // CHECK: %[[INDEX:.*]] = spirv.Constant 0
+  // CHECK-NEXT: %[[VAR:.*]] = spirv.Variable
+  // CHECK-NEXT: %[[PTR:.*]] = spirv.InBoundsAccessChain %[[VAR]][%[[INDEX]], %[[INDEX]], %[[INDEX]]]
+  // CHECK-NEXT: spirv.Load "Function" %[[PTR]]
+  %c0 = spirv.Constant 0: i32
+  %0 = spirv.Variable : !spirv.ptr<!spirv.struct<(!spirv.array<4x!spirv.array<4xf32>>, !spirv.array<4xi32>)>, Function>
+  %1 = spirv.InBoundsAccessChain %0[%c0] : !spirv.ptr<!spirv.struct<(!spirv.array<4x!spirv.array<4xf32>>, !spirv.array<4xi32>)>, Function>, i32 -> !spirv.ptr<!spirv.array<4x!spirv.array<4xf32>>, Function>
+  %2 = spirv.InBoundsAccessChain %1[%c0, %c0] : !spirv.ptr<!spirv.array<4x!spirv.array<4xf32>>, Function>, i32, i32 -> !spirv.ptr<f32, Function>
+  %3 = spirv.Load "Function" %2 : f32
+  spirv.ReturnValue %3 : f32
+}
+
+// -----
+
 func.func @combine_access_chain_multi_use() -> !spirv.array<4xf32> {
   // CHECK: %[[INDEX:.*]] = spirv.Constant 0
   // CHECK-NEXT: %[[VAR:.*]] = spirv.Variable

diff  --git a/mlir/test/Target/SPIRV/memory-ops.mlir b/mlir/test/Target/SPIRV/memory-ops.mlir
index 2d18394818611..fa786f18aac18 100644
--- a/mlir/test/Target/SPIRV/memory-ops.mlir
+++ b/mlir/test/Target/SPIRV/memory-ops.mlir
@@ -40,6 +40,17 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage], []> {
 
 // -----
 
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage], []> {
+  spirv.func @inbounds_access_chain(%arg0 : !spirv.ptr<!spirv.array<4xf32>, Function>, %arg1 : i32) "None" {
+    // CHECK: {{%.*}} = spirv.InBoundsAccessChain {{%.*}}[{{%.*}}] : !spirv.ptr<!spirv.array<4 x f32>, Function>
+    %0 = spirv.InBoundsAccessChain %arg0[%arg1] : !spirv.ptr<!spirv.array<4xf32>, Function>, i32 -> !spirv.ptr<f32, Function>
+    %1 = spirv.Load "Function" %0 : f32
+    spirv.Return
+  }
+}
+
+// -----
+
 spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage], [SPV_KHR_storage_buffer_storage_class]> {
   spirv.func @load_store_zero_rank_float(%arg0: !spirv.ptr<!spirv.struct<(!spirv.array<1 x f32, stride=4> [0]), Block>, StorageBuffer>, %arg1: !spirv.ptr<!spirv.struct<(!spirv.array<1 x f32, stride=4> [0]), Block>, StorageBuffer>) "None" {
     // CHECK: [[LOAD_PTR:%.*]] = spirv.AccessChain {{%.*}}[{{%.*}}, {{%.*}}] : !spirv.ptr<!spirv.struct<(!spirv.array<1 x f32, stride=4> [0]), Block>, StorageBuffer>
@@ -122,4 +133,3 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.4, [Shader, Linkage], []> {
     spirv.Return
   }
 }
-


        


More information about the Mlir-commits mailing list