[llvm-branch-commits] [mlir] 6c69d3d - [MLIR][SPIRV] Add initial support for OpSpecConstantOp.

Lei Zhang via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Dec 8 06:13:53 PST 2020


Author: ergawy
Date: 2020-12-08T09:07:52-05:00
New Revision: 6c69d3d68e9a4438b47422e49c0f2d69e2b8e1b7

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

LOG: [MLIR][SPIRV] Add initial support for OpSpecConstantOp.

This commit adds initial support for SPIR-V OpSpecConstantOp
instruction. The following is introdcued:

- A new `spv.specConstantOperation` operation consisting of a single
region and of 2 operations within that regions (more details in the
docs of the op itself).
- A new `spv.yield` instruction that acts a terminator for
`spv.specConstantOperation`.

For now, the generic form of the new op is supported (i.e. no custom
parsing or printing). This will be done in a follow up patch.

Reviewed By: antiagainst

Differential Revision: https://reviews.llvm.org/D92232

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td
    mlir/lib/Dialect/SPIRV/SPIRVOps.cpp
    mlir/test/Dialect/SPIRV/structure-ops.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td b/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td
index 0b1f6d294ac0..b8e76c3662ec 100644
--- a/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td
@@ -607,6 +607,130 @@ def SPV_SpecConstantCompositeOp : SPV_Op<"specConstantComposite", [InModuleScope
 
   let autogenSerialization = 0;
 }
+
+def SPV_YieldOp : SPV_Op<"mlir.yield", [NoSideEffect, Terminator]> {
+  let summary = "Yields the result computed in `spv.SpecConstantOperation`'s"
+                "region back to the parent op.";
+
+  let description = [{
+    This op is a special terminator whose only purpose is to terminate
+    an `spv.SpecConstantOperation`'s enclosed region. It accepts a
+    single operand produced by the preceeding (and only other) instruction
+    in its parent block (see SPV_SpecConstantOperation for further
+    details). This op has no corresponding SPIR-V instruction.
+
+    ```
+    spv.mlir.yield ::= `spv.mlir.yield` ssa-id : spirv-type
+    ```
+
+    #### Example:
+    ```mlir
+    %0 = ... (some op supported by SPIR-V OpSpecConstantOp)
+    spv.mlir.yield %0
+    ```
+  }];
+
+  let arguments = (ins AnyType:$operand);
+
+  let results = (outs);
+
+  let hasOpcode = 0;
+
+  let autogenSerialization = 0;
+
+  let assemblyFormat = "attr-dict $operand `:` type($operand)";
+}
+
+def SPV_SpecConstantOperationOp : SPV_Op<"SpecConstantOperation", [
+                                         InFunctionScope, NoSideEffect,
+                                         IsolatedFromAbove]> {
+  let summary = "Declare a new specialization constant that results from doing an operation.";
+
+  let description = [{
+    This op declares a SPIR-V specialization constant that results from
+    doing an operation on other constants (specialization or otherwise).
+
+    In the `spv` dialect, this op is modelled as follows:
+
+    ```
+    spv-spec-constant-operation-op ::= `"spv.SpecConstantOperation"`
+                                         `(`ssa-id (`, ` ssa-id)`)`
+                                       `({`
+                                         ssa-id = spirv-op
+                                         `spv.mlir.yield` ssa-id
+                                       `})` `:` function-type
+    ```
+
+    In particular, an `spv.SpecConstantOperation` contains exactly one
+    region. In turn, that region, contains exactly 2 instructions:
+    - One of SPIR-V's instructions that are allowed within an
+    OpSpecConstantOp.
+    - An `spv.mlir.yield` instruction as the terminator.
+
+    The following SPIR-V instructions are valid:
+    - OpSConvert,
+    - OpUConvert,
+    - OpFConvert,
+    - OpSNegate,
+    - OpNot,
+    - OpIAdd,
+    - OpISub,
+    - OpIMul,
+    - OpUDiv,
+    - OpSDiv,
+    - OpUMod,
+    - OpSRem,
+    - OpSMod
+    - OpShiftRightLogical,
+    - OpShiftRightArithmetic,
+    - OpShiftLeftLogical
+    - OpBitwiseOr,
+    - OpBitwiseXor,
+    - OpBitwiseAnd
+    - OpVectorShuffle,
+    - OpCompositeExtract,
+    - OpCompositeInsert
+    - OpLogicalOr,
+    - OpLogicalAnd,
+    - OpLogicalNot,
+    - OpLogicalEqual,
+    - OpLogicalNotEqual
+    - OpSelect
+    - OpIEqual,
+    - OpINotEqual
+    - OpULessThan,
+    - OpSLessThan
+    - OpUGreaterThan,
+    - OpSGreaterThan
+    - OpULessThanEqual,
+    - OpSLessThanEqual
+    - OpUGreaterThanEqual,
+    - OpSGreaterThanEqual
+
+    TODO Add capability-specific ops when supported.
+
+    #### Example:
+    ```mlir
+    %0 = spv.constant 1: i32
+
+    %1 = "spv.SpecConstantOperation"(%0) ({
+      %ret = spv.IAdd %0, %0 : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32) -> i32
+    ```
+  }];
+
+  let arguments = (ins Variadic<AnyType>:$operands);
+
+  let results = (outs AnyType:$results);
+
+  let regions = (region SizedRegion<1>:$body);
+
+  let hasOpcode = 0;
+
+  let autogenSerialization = 0;
+}
+
 // -----
 
 #endif // SPIRV_STRUCTURE_OPS

diff  --git a/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp
index 776fbdf11c80..19e0f98f26e9 100644
--- a/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp
@@ -3395,6 +3395,87 @@ static LogicalResult verify(spirv::SpecConstantCompositeOp constOp) {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// spv.mlir.yield
+//===----------------------------------------------------------------------===//
+
+static LogicalResult verify(spirv::YieldOp yieldOp) {
+  Operation *parentOp = yieldOp.getParentOp();
+
+  if (!parentOp || !isa<spirv::SpecConstantOperationOp>(parentOp))
+    return yieldOp.emitOpError(
+        "expected parent op to be 'spv.SpecConstantOperation'");
+
+  Block &block = parentOp->getRegion(0).getBlocks().front();
+  Operation &enclosedOp = block.getOperations().front();
+
+  if (yieldOp.getOperand().getDefiningOp() != &enclosedOp)
+    return yieldOp.emitOpError(
+        "expected operand to be defined by preceeding op");
+
+  return success();
+}
+
+static ParseResult parseSpecConstantOperationOp(OpAsmParser &parser,
+                                                OperationState &state) {
+  // TODO: For now, only generic form is supported.
+  return failure();
+}
+
+static void print(spirv::SpecConstantOperationOp op, OpAsmPrinter &printer) {
+  // TODO
+  printer.printGenericOp(op);
+}
+
+static LogicalResult verify(spirv::SpecConstantOperationOp constOp) {
+  Block &block = constOp.getRegion().getBlocks().front();
+
+  if (block.getOperations().size() != 2)
+    return constOp.emitOpError("expected exactly 2 nested ops");
+
+  Operation &yieldOp = block.getOperations().back();
+
+  if (!isa<spirv::YieldOp>(yieldOp))
+    return constOp.emitOpError("expected terminator to be a yield op");
+
+  Operation &enclosedOp = block.getOperations().front();
+
+  // TODO Add a `UsableInSpecConstantOp` trait and mark ops from the list below
+  // with it instead.
+  if (!isa<spirv::SConvertOp, spirv::UConvertOp, spirv::FConvertOp,
+           spirv::SNegateOp, spirv::NotOp, spirv::IAddOp, spirv::ISubOp,
+           spirv::IMulOp, spirv::UDivOp, spirv::SDivOp, spirv::UModOp,
+           spirv::SRemOp, spirv::SModOp, spirv::ShiftRightLogicalOp,
+           spirv::ShiftRightArithmeticOp, spirv::ShiftLeftLogicalOp,
+           spirv::BitwiseOrOp, spirv::BitwiseXorOp, spirv::BitwiseAndOp,
+           spirv::CompositeExtractOp, spirv::CompositeInsertOp,
+           spirv::LogicalOrOp, spirv::LogicalAndOp, spirv::LogicalNotOp,
+           spirv::LogicalEqualOp, spirv::LogicalNotEqualOp, spirv::SelectOp,
+           spirv::IEqualOp, spirv::INotEqualOp, spirv::ULessThanOp,
+           spirv::SLessThanOp, spirv::UGreaterThanOp, spirv::SGreaterThanOp,
+           spirv::ULessThanEqualOp, spirv::SLessThanEqualOp,
+           spirv::UGreaterThanEqualOp, spirv::SGreaterThanEqualOp>(enclosedOp))
+    return constOp.emitOpError("invalid enclosed op");
+
+  if (enclosedOp.getNumOperands() != constOp.getOperands().size())
+    return constOp.emitOpError("invalid number of operands; expected ")
+           << enclosedOp.getNumOperands() << ", actual "
+           << constOp.getOperands().size();
+
+  if (enclosedOp.getNumOperands() != constOp.getRegion().getNumArguments())
+    return constOp.emitOpError("invalid number of region arguments; expected ")
+           << enclosedOp.getNumOperands() << ", actual "
+           << constOp.getRegion().getNumArguments();
+
+  for (auto operand : constOp.getOperands())
+    if (!isa<spirv::ConstantOp, spirv::SpecConstantOp,
+             spirv::SpecConstantCompositeOp, spirv::SpecConstantOperationOp>(
+            operand.getDefiningOp()))
+      return constOp.emitOpError("invalid operand");
+
+  return success();
+}
+
 namespace mlir {
 namespace spirv {
 

diff  --git a/mlir/test/Dialect/SPIRV/structure-ops.mlir b/mlir/test/Dialect/SPIRV/structure-ops.mlir
index 9d0a476c4763..89a30e23dec9 100644
--- a/mlir/test/Dialect/SPIRV/structure-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/structure-ops.mlir
@@ -757,3 +757,152 @@ spv.module Logical GLSL450 {
   // expected-error @+1 {{unsupported composite type}}
   spv.specConstantComposite @scc (@sc1) : !spv.coopmatrix<8x16xf32, Device>
 }
+//===----------------------------------------------------------------------===//
+// spv.SpecConstantOperation
+//===----------------------------------------------------------------------===//
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+    %2 = spv.constant 1: i32
+
+    %1 = "spv.SpecConstantOperation"(%0, %0) ({
+    ^bb(%lhs : i32, %rhs : i32):
+      %ret = spv.IAdd %lhs, %rhs : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32, i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+    %2 = spv.constant 1: i32
+
+    // expected-error @+1 {{invalid number of operands; expected 2, actual 1}}
+    %1 = "spv.SpecConstantOperation"(%0) ({
+    ^bb(%lhs : i32, %rhs : i32):
+      %ret = spv.IAdd %lhs, %rhs : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+    %2 = spv.constant 1: i32
+
+    // expected-error @+1 {{invalid number of region arguments; expected 2, actual 1}}
+    %1 = "spv.SpecConstantOperation"(%0, %0) ({
+    ^bb(%lhs : i32):
+      %ret = spv.IAdd %lhs, %lhs : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32, i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+    // expected-error @+1 {{expected parent op to be 'spv.SpecConstantOperation'}}
+    spv.mlir.yield %0 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+
+    %1 = "spv.SpecConstantOperation"(%0, %0) ({
+    ^bb(%lhs : i32, %rhs : i32):
+      %ret = spv.ISub %lhs, %rhs : i32
+      // expected-error @+1 {{expected operand to be defined by preceeding op}}
+      spv.mlir.yield %lhs : i32
+    }) : (i32, i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+
+    // expected-error @+1 {{expected exactly 2 nested ops}}
+    %1 = "spv.SpecConstantOperation"(%0, %0) ({
+    ^bb(%lhs : i32, %rhs : i32):
+      %ret = spv.IAdd %lhs, %rhs : i32
+      %ret2 = spv.IAdd %lhs, %rhs : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32, i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> i32 "None" {
+    %0 = spv.constant 1: i32
+
+    // expected-error @+1 {{expected terminator to be a yield op}}
+    %1 = "spv.SpecConstantOperation"(%0, %0) ({
+    ^bb(%lhs : i32, %rhs : i32):
+      %ret = spv.IAdd %lhs, %rhs : i32
+      spv.ReturnValue %ret : i32
+    }) : (i32, i32) -> i32
+
+    spv.ReturnValue %1 : i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> () "None" {
+    %0 = spv.Variable : !spv.ptr<i32, Function>
+
+    // expected-error @+1 {{invalid enclosed op}}
+    %2 = "spv.SpecConstantOperation"(%0) ({
+    ^bb(%arg0 : !spv.ptr<i32, Function>):
+      %ret = spv.Load "Function" %arg0 : i32
+      spv.mlir.yield %ret : i32
+    }) : (!spv.ptr<i32, Function>) -> i32
+  }
+}
+
+// -----
+
+spv.module Logical GLSL450 {
+  spv.func @foo() -> () "None" {
+    %0 = spv.Variable : !spv.ptr<i32, Function>
+    %1 = spv.Load "Function" %0 : i32
+
+    // expected-error @+1 {{invalid operand}}
+    %2 = "spv.SpecConstantOperation"(%1, %1) ({
+    ^bb(%lhs: i32, %rhs: i32):
+      %ret = spv.IAdd %lhs, %lhs : i32
+      spv.mlir.yield %ret : i32
+    }) : (i32, i32) -> i32
+  }
+}


        


More information about the llvm-branch-commits mailing list