Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mlir/include/mlir/Dialect/DXSA/IR/DXSAOpBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class DXSA_Op<string mnemonic, list<Trait> traits = []> :
// DXSA shared bases for ops with inline operands
//===----------------------------------------------------------------------===//

class DXSA_NullaryOp<string mnemonic> : DXSA_Op<mnemonic> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is identical to what is in #194; I'm not sure which PR had it first and where the review comment should go. Regardless, the same comment applies here.

let arguments = (ins);
let results = (outs);
let assemblyFormat = "attr-dict";
}

class DXSA_UnaryOp<string mnemonic> : DXSA_Op<mnemonic> {
let arguments = (ins
DXSA_DstOperandAttr:$dst,
Expand Down Expand Up @@ -61,4 +67,10 @@ class DXSA_TernaryOp<string mnemonic> : DXSA_Op<mnemonic> {
"(`precise` $precise^)? $dst `,` $src0 `,` $src1 `,` $src2 attr-dict";
}

class DXSA_GsStreamIndexOp<string mnemonic> : DXSA_Op<mnemonic> {
let arguments = (ins ConfinedAttr<I32Attr,
[IntNonNegative, IntMaxValue<3>]>:$index);
let assemblyFormat = [{ $index attr-dict }];
}

#endif // MLIR_DIALECT_DXSA_IR_DXSAOPBASE
6 changes: 2 additions & 4 deletions mlir/include/mlir/Dialect/DXSA/IR/DXSAOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand All @@ -1109,9 +1110,6 @@ def DXSA_DclStream : DXSA_Op<"dcl_stream"> {
dxsa.dcl_stream 0
```
}];
let arguments = (ins ConfinedAttr<I32Attr,
[IntNonNegative, IntMaxValue<3>]>:$index);
let assemblyFormat = [{ $index attr-dict }];
}

def DXSA_DclResource : DXSA_Op<"dcl_resource"> {
Expand Down
154 changes: 154 additions & 0 deletions mlir/include/mlir/Dialect/DXSA/IR/DXSATopologyOps.td
Original file line number Diff line number Diff line change
@@ -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"> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Microsoft call this emitThenCut rather than emit_then_cut, likewise for emitThenCut_stream, is it possible to use their name or does it need to be this?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instruction names in Microsoft's specification are formed according to different naming rules, rather than following a single uniform convention. We decided to use snake_case for operation names to maintain consistency across the dialect.

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
64 changes: 51 additions & 13 deletions mlir/lib/Target/DXSA/BinaryParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename OpT>
Instruction buildGsStreamIndexOp(uint32_t index, Location loc) {
return OpT::create(builder, loc, builder.getI32IntegerAttr(index));
}

Instruction buildDclInputPs(dxsa::InterpolationMode interpolationMode,
Expand Down Expand Up @@ -1448,21 +1448,29 @@ class Parser {
return builder.buildDclMaxOutputVertexCount(count, loc);
}

FailureOr<Instruction> parseDclStream(Location loc) {
auto operand = parseDstOperand();
FAILURE_IF_FAILED(operand);
if (operand->getType() != dxsa::OperandType::m)
template <typename SrcOrDstOperand>
FailureOr<uint32_t> 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<Instruction> parseDclStream(Location loc) {
auto operand = parseDstOperand();
FAILURE_IF_FAILED(operand);
auto index = parseGsStreamIndex(*operand, loc);
FAILURE_IF_FAILED(index);
return builder.buildGsStreamIndexOp<dxsa::DclStream>(*index, loc);
}

FailureOr<dxsa::InterpolationMode>
Expand Down Expand Up @@ -1650,8 +1658,9 @@ class Parser {
}

// Get plain immediates with no relative indices.
template <typename SrcOrDstOperand>
FailureOr<SmallVector<uint32_t, 3>>
getRequiredImmIndices(dxsa::DstOperandAttr operand, Location loc) {
getRequiredImmIndices(SrcOrDstOperand operand, Location loc) {
SmallVector<uint32_t, 3> indices;
if (auto index = operand.getIndex())
for (dxsa::IndexAttr entry : index) {
Expand Down Expand Up @@ -1695,6 +1704,18 @@ class Parser {
*srcs);
}

template <typename OpT>
FailureOr<Instruction> 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<OpT>(*index, loc);
}

FailureOr<Instruction> parseDclInput(Location loc) {
auto operand = parseDstOperand();
FAILURE_IF_FAILED(operand);
Expand Down Expand Up @@ -2303,6 +2324,9 @@ class Parser {
decodeOp<dxsa::MNEMONIC, dxsa::MNEMONIC, HAS_PRECISE, NUM_DST_OPERANDS, \
NUM_SRC_OPERANDS>(beginOffset, instructionLengthInTokens, modifier, \
getLocation())
#define STREAM_INDEX_OP(OP) \
decodeStreamIndexOp<dxsa::OP>(beginOffset, instructionLengthInTokens, \
getLocation())

switch (opcode) {
// Floating-point arithmetic instructions
Expand Down Expand Up @@ -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<Operand, 8> operands;
for (unsigned i = 0; i < numOperands; ++i) {
Expand Down
63 changes: 63 additions & 0 deletions mlir/test/Target/DXSA/topology.test
Original file line number Diff line number Diff line change
@@ -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
Loading