From 6c69d3d68e9a4438b47422e49c0f2d69e2b8e1b7 Mon Sep 17 00:00:00 2001 From: ergawy Date: Tue, 8 Dec 2020 09:02:02 -0500 Subject: [PATCH] [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 --- .../mlir/Dialect/SPIRV/SPIRVStructureOps.td | 124 +++++++++++++++++ mlir/lib/Dialect/SPIRV/SPIRVOps.cpp | 81 +++++++++++ mlir/test/Dialect/SPIRV/structure-ops.mlir | 149 +++++++++++++++++++++ 3 files changed, 354 insertions(+) diff --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td b/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td index 0b1f6d2..b8e76c3 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:$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 776fbdf..19e0f98 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(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(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(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( + 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 9d0a476..89a30e2 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 + + // expected-error @+1 {{invalid enclosed op}} + %2 = "spv.SpecConstantOperation"(%0) ({ + ^bb(%arg0 : !spv.ptr): + %ret = spv.Load "Function" %arg0 : i32 + spv.mlir.yield %ret : i32 + }) : (!spv.ptr) -> i32 + } +} + +// ----- + +spv.module Logical GLSL450 { + spv.func @foo() -> () "None" { + %0 = spv.Variable : !spv.ptr + %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 + } +} -- 2.7.4