diff --git a/mlir/include/mlir/Dialect/DXSA/IR/DXSAOpBase.td b/mlir/include/mlir/Dialect/DXSA/IR/DXSAOpBase.td index 6f67ca4c3329..4a4de2dd5acf 100644 --- a/mlir/include/mlir/Dialect/DXSA/IR/DXSAOpBase.td +++ b/mlir/include/mlir/Dialect/DXSA/IR/DXSAOpBase.td @@ -28,6 +28,12 @@ class DXSA_Op traits = []> : // DXSA shared bases for ops with inline operands //===----------------------------------------------------------------------===// +class DXSA_NullaryOp : DXSA_Op { + let arguments = (ins); + let results = (outs); + let assemblyFormat = "attr-dict"; +} + class DXSA_UnaryOp : DXSA_Op { let arguments = (ins DXSA_DstOperandAttr:$dst, @@ -61,4 +67,10 @@ class DXSA_TernaryOp : DXSA_Op { "(`precise` $precise^)? $dst `,` $src0 `,` $src1 `,` $src2 attr-dict"; } +class DXSA_GsStreamIndexOp : DXSA_Op { + let arguments = (ins ConfinedAttr]>:$index); + let assemblyFormat = [{ $index attr-dict }]; +} + #endif // MLIR_DIALECT_DXSA_IR_DXSAOPBASE diff --git a/mlir/include/mlir/Dialect/DXSA/IR/DXSAOps.td b/mlir/include/mlir/Dialect/DXSA/IR/DXSAOps.td index 54e5b8ed7a33..0e1a80880649 100644 --- a/mlir/include/mlir/Dialect/DXSA/IR/DXSAOps.td +++ b/mlir/include/mlir/Dialect/DXSA/IR/DXSAOps.td @@ -14,6 +14,7 @@ include "mlir/Dialect/DXSA/IR/DXSATypes.td" include "mlir/Dialect/DXSA/IR/DXSAFPArithOps.td" include "mlir/Dialect/DXSA/IR/DXSAConditionOps.td" include "mlir/Dialect/DXSA/IR/DXSABitwiseOps.td" +include "mlir/Dialect/DXSA/IR/DXSATopologyOps.td" include "mlir/Dialect/DXSA/IR/DXSATypeConversionOps.td" include "mlir/Dialect/DXSA/IR/DXSAAtomicOps.td" include "mlir/IR/AttrTypeBase.td" @@ -1096,7 +1097,7 @@ def DXSA_DclSampler : DXSA_Op<"dcl_sampler"> { let hasVerifier = 1; } -def DXSA_DclStream : DXSA_Op<"dcl_stream"> { +def DXSA_DclStream : DXSA_GsStreamIndexOp<"dcl_stream"> { let summary = "declares a geometry shader (GS) output stream"; let description = [{ The `dxsa.dcl_stream` operation declares a geometry shader (GS) output stream. @@ -1109,9 +1110,6 @@ def DXSA_DclStream : DXSA_Op<"dcl_stream"> { dxsa.dcl_stream 0 ``` }]; - let arguments = (ins ConfinedAttr]>:$index); - let assemblyFormat = [{ $index attr-dict }]; } def DXSA_DclResource : DXSA_Op<"dcl_resource"> { diff --git a/mlir/include/mlir/Dialect/DXSA/IR/DXSATopologyOps.td b/mlir/include/mlir/Dialect/DXSA/IR/DXSATopologyOps.td new file mode 100644 index 000000000000..d93d267adf0d --- /dev/null +++ b/mlir/include/mlir/Dialect/DXSA/IR/DXSATopologyOps.td @@ -0,0 +1,154 @@ +//===- DXSATopologyOps.td - DXSA topology ops --------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Topology instructions of the DXSA dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_DXSA_IR_DXSATOPOLOGYOPS +#define MLIR_DIALECT_DXSA_IR_DXSATOPOLOGYOPS + +include "mlir/Dialect/DXSA/IR/DXSAOpBase.td" + +//===----------------------------------------------------------------------===// +// dxsa.emit +//===----------------------------------------------------------------------===// + +def DXSA_Emit : DXSA_NullaryOp<"emit"> { + let summary = "outputs a geometry-shader vertex from declared output registers"; + let description = [{ + The `dxsa.emit` operation reads every declared `o#` output register and + outputs a vertex from the geometry shader. Each `dxsa.emit` produces one + vertex; successive emits assemble primitives according to the output + topology declared by `dxsa.dcl_output_topology`. + + A geometry shader may issue `dxsa.emit` any number of times, including + inside flow control. If output streams have been declared with + `dxsa.dcl_stream`, `dxsa.emit_stream` must be used instead. + + Example: + + ```mlir + dxsa.emit + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// dxsa.cut +//===----------------------------------------------------------------------===// + +def DXSA_Cut : DXSA_NullaryOp<"cut"> { + let summary = "ends the current geometry-shader output strip"; + let description = [{ + The `dxsa.cut` operation ends the current output strip at the last emitted + vertex, so the next `dxsa.emit` begins a new strip. This only affects + linestrip and trianglestrip output topologies; for pointlist output, + `dxsa.cut` has no effect. + + A geometry shader may issue `dxsa.cut` any number of times, including + inside flow control. If output streams have been declared with + `dxsa.dcl_stream`, `dxsa.cut_stream` must be used instead. + + Example: + + ```mlir + dxsa.cut + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// dxsa.emit_then_cut +//===----------------------------------------------------------------------===// + +def DXSA_EmitThenCut : DXSA_NullaryOp<"emit_then_cut"> { + let summary = "outputs a geometry-shader vertex and ends the current strip"; + let description = [{ + The `dxsa.emit_then_cut` operation has the same effect as `dxsa.emit`, + immediately followed by `dxsa.cut`. + + A geometry shader may issue `dxsa.emit_then_cut` any number of times, + including inside flow control. If output streams have been declared with + `dxsa.dcl_stream`, `dxsa.emit_then_cut_stream` must be used instead. + + Example: + + ```mlir + dxsa.emit_then_cut + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// dxsa.emit_stream +//===----------------------------------------------------------------------===// + +def DXSA_EmitStream : DXSA_GsStreamIndexOp<"emit_stream"> { + let summary = "outputs a geometry-shader vertex on an output stream"; + let description = [{ + The `dxsa.emit_stream` operation reads every declared `o#` output register + for stream `$index` and outputs a vertex on that geometry-shader output + stream. Each `dxsa.emit_stream` produces one vertex; successive emits on + the same stream assemble primitives according to the output topology + declared for that stream. + + The `$index` must be in the range [0, 3]. + + Example: + + ```mlir + dxsa.emit_stream 0 + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// dxsa.cut_stream +//===----------------------------------------------------------------------===// + +def DXSA_CutStream : DXSA_GsStreamIndexOp<"cut_stream"> { + let summary = "ends the current strip on a geometry-shader output stream"; + let description = [{ + The `dxsa.cut_stream` operation ends the current output strip at the last + emitted vertex on stream `$index`, so the next emit on that stream begins a + new strip. This has the same semantics as `dxsa.cut`, scoped to the given + output stream. + + The `$index` must be in the range [0, 3]. + + Example: + + ```mlir + dxsa.cut_stream 0 + ``` + }]; +} + +//===----------------------------------------------------------------------===// +// dxsa.emit_then_cut_stream +//===----------------------------------------------------------------------===// + +def DXSA_EmitThenCutStream : DXSA_GsStreamIndexOp<"emit_then_cut_stream"> { + let summary = "outputs a vertex on a stream and ends the current strip"; + let description = [{ + The `dxsa.emit_then_cut_stream` operation has the same effect as + `dxsa.emit_stream`, immediately followed by `dxsa.cut_stream`, on stream + `$index`. + + The `$index` must be in the range [0, 3]. + + Example: + + ```mlir + dxsa.emit_then_cut_stream 0 + ``` + }]; +} + +#endif // MLIR_DIALECT_DXSA_IR_DXSATOPOLOGYOPS diff --git a/mlir/lib/Target/DXSA/BinaryParser.cpp b/mlir/lib/Target/DXSA/BinaryParser.cpp index b783e6be85f8..9c8ea1e5b75e 100644 --- a/mlir/lib/Target/DXSA/BinaryParser.cpp +++ b/mlir/lib/Target/DXSA/BinaryParser.cpp @@ -629,9 +629,9 @@ class DXBuilder { return dxsa::DclMaxOutputVertexCount::create(builder, loc, count); } - Instruction buildDclStream(uint32_t index, Location loc) { - return dxsa::DclStream::create(builder, loc, - builder.getI32IntegerAttr(index)); + template + Instruction buildGsStreamIndexOp(uint32_t index, Location loc) { + return OpT::create(builder, loc, builder.getI32IntegerAttr(index)); } Instruction buildDclInputPs(dxsa::InterpolationMode interpolationMode, @@ -1448,21 +1448,29 @@ class Parser { return builder.buildDclMaxOutputVertexCount(count, loc); } - FailureOr parseDclStream(Location loc) { - auto operand = parseDstOperand(); - FAILURE_IF_FAILED(operand); - if (operand->getType() != dxsa::OperandType::m) + template + FailureOr parseGsStreamIndex(SrcOrDstOperand operand, + Location loc) { + if (operand.getType() != dxsa::OperandType::m) return emitError(loc, "unexpected operand type: ") - << dxsa::stringifyOperandType(operand->getType()); - if (operand->getComponents().getValue() != dxsa::OperandComponents::none) + << dxsa::stringifyOperandType(operand.getType()); + if (operand.getComponents().getValue() != dxsa::OperandComponents::none) return emitError(loc, "unexpected operand components: ") << dxsa::stringifyOperandComponents( - operand->getComponents().getValue()); - auto indices = getRequiredImmIndices(*operand, loc); + operand.getComponents().getValue()); + auto indices = getRequiredImmIndices(operand, loc); FAILURE_IF_FAILED(indices); if (indices->size() != 1) return emitError(loc, "unsupported index dimension: ") << indices->size(); - return builder.buildDclStream((*indices)[0], loc); + return (*indices)[0]; + } + + FailureOr parseDclStream(Location loc) { + auto operand = parseDstOperand(); + FAILURE_IF_FAILED(operand); + auto index = parseGsStreamIndex(*operand, loc); + FAILURE_IF_FAILED(index); + return builder.buildGsStreamIndexOp(*index, loc); } FailureOr @@ -1650,8 +1658,9 @@ class Parser { } // Get plain immediates with no relative indices. + template FailureOr> - getRequiredImmIndices(dxsa::DstOperandAttr operand, Location loc) { + getRequiredImmIndices(SrcOrDstOperand operand, Location loc) { SmallVector indices; if (auto index = operand.getIndex()) for (dxsa::IndexAttr entry : index) { @@ -1695,6 +1704,18 @@ class Parser { *srcs); } + template + FailureOr decodeStreamIndexOp(size_t beginOffset, + uint32_t length, Location loc) { + auto operand = parseSrcOperand(); + FAILURE_IF_FAILED(operand); + auto index = parseGsStreamIndex(*operand, loc); + FAILURE_IF_FAILED(index); + if (failed(verifyInstructionLength(beginOffset, length))) + return failure(); + return builder.buildGsStreamIndexOp(*index, loc); + } + FailureOr parseDclInput(Location loc) { auto operand = parseDstOperand(); FAILURE_IF_FAILED(operand); @@ -2303,6 +2324,9 @@ class Parser { decodeOp(beginOffset, instructionLengthInTokens, modifier, \ getLocation()) +#define STREAM_INDEX_OP(OP) \ + decodeStreamIndexOp(beginOffset, instructionLengthInTokens, \ + getLocation()) switch (opcode) { // Floating-point arithmetic instructions @@ -2426,9 +2450,23 @@ class Parser { return PLAIN_OP(AtomicUMax, 1, 2, HasPreciseAttr::No); case D3D11_SB_OPCODE_ATOMIC_UMIN: return PLAIN_OP(AtomicUMin, 1, 2, HasPreciseAttr::No); + // Topology instructions + case D3D10_SB_OPCODE_EMIT: + return PLAIN_OP(Emit, 0, 0, HasPreciseAttr::No); + case D3D10_SB_OPCODE_EMITTHENCUT: + return PLAIN_OP(EmitThenCut, 0, 0, HasPreciseAttr::No); + case D3D10_SB_OPCODE_CUT: + return PLAIN_OP(Cut, 0, 0, HasPreciseAttr::No); + case D3D11_SB_OPCODE_EMIT_STREAM: + return STREAM_INDEX_OP(EmitStream); + case D3D11_SB_OPCODE_CUT_STREAM: + return STREAM_INDEX_OP(CutStream); + case D3D11_SB_OPCODE_EMITTHENCUT_STREAM: + return STREAM_INDEX_OP(EmitThenCutStream); } #undef SATURABLE_OP #undef PLAIN_OP +#undef STREAM_INDEX_OP SmallVector operands; for (unsigned i = 0; i < numOperands; ++i) { diff --git a/mlir/test/Target/DXSA/topology.test b/mlir/test/Target/DXSA/topology.test new file mode 100644 index 000000000000..738780827d8f --- /dev/null +++ b/mlir/test/Target/DXSA/topology.test @@ -0,0 +1,63 @@ +// RUN: mlir-translate --split-input-file --import-dxsa-hex %s | FileCheck %s +// RUN: mlir-translate --split-input-file --import-dxsa-hex %s | mlir-opt --split-input-file --verify-roundtrip + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.cut +// CHECK-NEXT: } +0x01000009 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit +// CHECK-NEXT: } +0x01000013 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit_then_cut +// CHECK-NEXT: } +0x01000014 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit_stream 0 +// CHECK-NEXT: } +0x03000075, 0x00110000, 0x00000000 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit_stream 3 +// CHECK-NEXT: } +0x03000075, 0x00110000, 0x00000003 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.cut_stream 0 +// CHECK-NEXT: } +0x03000076, 0x00110000, 0x00000000 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.cut_stream 3 +// CHECK-NEXT: } +0x03000076, 0x00110000, 0x00000003 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit_then_cut_stream 0 +// CHECK-NEXT: } +0x03000077, 0x00110000, 0x00000000 + +// ----- + +// CHECK-LABEL: dxsa.module { +// CHECK-NEXT: dxsa.emit_then_cut_stream 3 +// CHECK-NEXT: } +0x03000077, 0x00110000, 0x00000003 diff --git a/mlir/test/Target/DXSA/topology_stream_invalid.mlir b/mlir/test/Target/DXSA/topology_stream_invalid.mlir new file mode 100644 index 000000000000..d6cff05ec373 --- /dev/null +++ b/mlir/test/Target/DXSA/topology_stream_invalid.mlir @@ -0,0 +1,29 @@ +// RUN: mlir-opt %s -split-input-file -verify-diagnostics + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.emit_stream -1 + +// ----- + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.emit_stream 4 + +// ----- + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.cut_stream -1 + +// ----- + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.cut_stream 4 + +// ----- + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.emit_then_cut_stream -1 + +// ----- + +// expected-error@+1 {{attribute 'index' failed to satisfy constraint: 32-bit signless integer attribute whose value is non-negative whose maximum value is 3}} +dxsa.emit_then_cut_stream 4