Skip to content

Commit 9a5b3a1

Browse files
authored
[DXIL] Add GroupMemoryBarrierWithGroupSync intrinsic (#111884)
fixes #112974 partially fixes #70103 ### Changes - Added new tablegen based way of lowering dx intrinsics to DXIL ops. - Added int_dx_group_memory_barrier_with_group_sync intrinsic in IntrinsicsDirectX.td - Added expansion for int_dx_group_memory_barrier_with_group_sync in DXILIntrinsicExpansion.cpp` - Added DXIL backend test case ### Related PRs * [[clang][HLSL] Add GroupMemoryBarrierWithGroupSync intrinsic #111883](#111883) * [[SPIRV] Add GroupMemoryBarrierWithGroupSync intrinsic #111888](#111888)
1 parent f964514 commit 9a5b3a1

File tree

5 files changed

+209
-22
lines changed

5 files changed

+209
-22
lines changed

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,6 @@ def int_dx_step : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, L
9292
def int_dx_splitdouble : DefaultAttrsIntrinsic<[llvm_anyint_ty, LLVMMatchType<0>],
9393
[LLVMScalarOrSameVectorWidth<0, llvm_double_ty>], [IntrNoMem]>;
9494
def int_dx_radians : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
95+
96+
def int_dx_group_memory_barrier_with_group_sync : DefaultAttrsIntrinsic<[], [], []>;
9597
}

llvm/lib/Target/DirectX/DXIL.td

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,43 @@ class Attributes<Version ver = DXIL1_0, list<DXILAttribute> attrs> {
294294
list<DXILAttribute> op_attrs = attrs;
295295
}
296296

297+
class DXILConstant<int value_> {
298+
int value = value_;
299+
}
300+
301+
defset list<DXILConstant> BarrierModes = {
302+
def BarrierMode_DeviceMemoryBarrier : DXILConstant<2>;
303+
def BarrierMode_DeviceMemoryBarrierWithGroupSync : DXILConstant<3>;
304+
def BarrierMode_GroupMemoryBarrier : DXILConstant<8>;
305+
def BarrierMode_GroupMemoryBarrierWithGroupSync : DXILConstant<9>;
306+
def BarrierMode_AllMemoryBarrier : DXILConstant<10>;
307+
def BarrierMode_AllMemoryBarrierWithGroupSync : DXILConstant<11>;
308+
}
309+
310+
// Intrinsic arg selection
311+
class Arg {
312+
int index = -1;
313+
DXILConstant value;
314+
bit is_i8 = 0;
315+
bit is_i32 = 0;
316+
}
317+
class ArgSelect<int index_> : Arg {
318+
let index = index_;
319+
}
320+
class ArgI32<DXILConstant value_> : Arg {
321+
let value = value_;
322+
let is_i32 = 1;
323+
}
324+
class ArgI8<DXILConstant value_> : Arg {
325+
let value = value_;
326+
let is_i8 = 1;
327+
}
328+
329+
class IntrinsicSelect<Intrinsic intrinsic_, list<Arg> args_> {
330+
Intrinsic intrinsic = intrinsic_;
331+
list<Arg> args = args_;
332+
}
333+
297334
// Abstraction DXIL Operation
298335
class DXILOp<int opcode, DXILOpClass opclass> {
299336
// A short description of the operation
@@ -308,6 +345,9 @@ class DXILOp<int opcode, DXILOpClass opclass> {
308345
// LLVM Intrinsic DXIL Operation maps to
309346
Intrinsic LLVMIntrinsic = ?;
310347

348+
// Non-trivial LLVM Intrinsics DXIL Operation maps to
349+
list<IntrinsicSelect> intrinsic_selects = [];
350+
311351
// Result type of the op
312352
DXILOpParamType result;
313353

@@ -829,3 +869,17 @@ def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
829869
let stages = [Stages<DXIL1_0, [all_stages]>];
830870
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
831871
}
872+
873+
def Barrier : DXILOp<80, barrier> {
874+
let Doc = "inserts a memory barrier in the shader";
875+
let intrinsic_selects = [
876+
IntrinsicSelect<
877+
int_dx_group_memory_barrier_with_group_sync,
878+
[ ArgI32<BarrierMode_GroupMemoryBarrierWithGroupSync> ]>,
879+
];
880+
881+
let arguments = [Int32Ty];
882+
let result = VoidTy;
883+
let stages = [Stages<DXIL1_0, [compute, library]>];
884+
let attributes = [Attributes<DXIL1_0, []>];
885+
}

llvm/lib/Target/DirectX/DXILOpLowering.cpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,43 @@ class OpLowerer {
106106
return false;
107107
}
108108

109-
[[nodiscard]]
110-
bool replaceFunctionWithOp(Function &F, dxil::OpCode DXILOp) {
109+
struct ArgSelect {
110+
enum class Type {
111+
Index,
112+
I8,
113+
I32,
114+
};
115+
Type Type = Type::Index;
116+
int Value = -1;
117+
};
118+
119+
[[nodiscard]] bool replaceFunctionWithOp(Function &F, dxil::OpCode DXILOp,
120+
ArrayRef<ArgSelect> ArgSelects) {
111121
bool IsVectorArgExpansion = isVectorArgExpansion(F);
112122
return replaceFunction(F, [&](CallInst *CI) -> Error {
113-
SmallVector<Value *> Args;
114123
OpBuilder.getIRB().SetInsertPoint(CI);
115-
if (IsVectorArgExpansion) {
116-
SmallVector<Value *> NewArgs = argVectorFlatten(CI, OpBuilder.getIRB());
117-
Args.append(NewArgs.begin(), NewArgs.end());
118-
} else
124+
SmallVector<Value *> Args;
125+
if (ArgSelects.size()) {
126+
for (const ArgSelect &A : ArgSelects) {
127+
switch (A.Type) {
128+
case ArgSelect::Type::Index:
129+
Args.push_back(CI->getArgOperand(A.Value));
130+
break;
131+
case ArgSelect::Type::I8:
132+
Args.push_back(OpBuilder.getIRB().getInt8((uint8_t)A.Value));
133+
break;
134+
case ArgSelect::Type::I32:
135+
Args.push_back(OpBuilder.getIRB().getInt32(A.Value));
136+
break;
137+
default:
138+
llvm_unreachable("Invalid type of intrinsic arg select.");
139+
}
140+
}
141+
} else if (IsVectorArgExpansion) {
142+
Args = argVectorFlatten(CI, OpBuilder.getIRB());
143+
} else {
119144
Args.append(CI->arg_begin(), CI->arg_end());
145+
}
120146

121147
Expected<CallInst *> OpCall =
122148
OpBuilder.tryCreateOp(DXILOp, Args, CI->getName(), F.getReturnType());
@@ -583,9 +609,10 @@ class OpLowerer {
583609
switch (ID) {
584610
default:
585611
continue;
586-
#define DXIL_OP_INTRINSIC(OpCode, Intrin) \
612+
#define DXIL_OP_INTRINSIC(OpCode, Intrin, ...) \
587613
case Intrin: \
588-
HasErrors |= replaceFunctionWithOp(F, OpCode); \
614+
HasErrors |= \
615+
replaceFunctionWithOp(F, OpCode, ArrayRef<ArgSelect>{__VA_ARGS__}); \
589616
break;
590617
#include "DXILOperation.inc"
591618
case Intrinsic::dx_handle_fromBinding:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library < %s | FileCheck %s
2+
3+
define void @test_group_memory_barrier_with_group_sync() {
4+
entry:
5+
; CHECK: call void @dx.op.barrier(i32 80, i32 9)
6+
call void @llvm.dx.group.memory.barrier.with.group.sync()
7+
ret void
8+
}

llvm/utils/TableGen/DXILEmitter.cpp

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ using namespace llvm::dxil;
3232

3333
namespace {
3434

35+
struct DXILArgSelect {
36+
enum class Type {
37+
Index,
38+
I32,
39+
I8,
40+
};
41+
Type Type = Type::Index;
42+
int Value = -1;
43+
};
44+
struct DXILIntrinsicSelect {
45+
StringRef Intrinsic;
46+
SmallVector<DXILArgSelect, 4> Args;
47+
};
48+
3549
struct DXILOperationDesc {
3650
std::string OpName; // name of DXIL operation
3751
int OpCode; // ID of DXIL operation
@@ -42,8 +56,7 @@ struct DXILOperationDesc {
4256
SmallVector<const Record *> OverloadRecs;
4357
SmallVector<const Record *> StageRecs;
4458
SmallVector<const Record *> AttrRecs;
45-
StringRef Intrinsic; // The llvm intrinsic map to OpName. Default is "" which
46-
// means no map exists
59+
SmallVector<DXILIntrinsicSelect> IntrinsicSelects;
4760
SmallVector<StringRef, 4>
4861
ShaderStages; // shader stages to which this applies, empty for all.
4962
int OverloadParamIndex; // Index of parameter with overload type.
@@ -71,6 +84,21 @@ static void AscendingSortByVersion(std::vector<const Record *> &Recs) {
7184
});
7285
}
7386

87+
/// Take a `int_{intrinsic_name}` and return just the intrinsic_name part if
88+
/// available. Otherwise return the empty string.
89+
static StringRef GetIntrinsicName(const RecordVal *RV) {
90+
if (RV && RV->getValue()) {
91+
if (const DefInit *DI = dyn_cast<DefInit>(RV->getValue())) {
92+
auto *IntrinsicDef = DI->getDef();
93+
auto DefName = IntrinsicDef->getName();
94+
assert(DefName.starts_with("int_") && "invalid intrinsic name");
95+
// Remove the int_ from intrinsic name.
96+
return DefName.substr(4);
97+
}
98+
}
99+
return "";
100+
}
101+
74102
/// Construct an object using the DXIL Operation records specified
75103
/// in DXIL.td. This serves as the single source of reference of
76104
/// the information extracted from the specified Record R, for
@@ -157,14 +185,63 @@ DXILOperationDesc::DXILOperationDesc(const Record *R) {
157185
OpName);
158186
}
159187

160-
const RecordVal *RV = R->getValue("LLVMIntrinsic");
161-
if (RV && RV->getValue()) {
162-
if (const DefInit *DI = dyn_cast<DefInit>(RV->getValue())) {
163-
auto *IntrinsicDef = DI->getDef();
164-
auto DefName = IntrinsicDef->getName();
165-
assert(DefName.starts_with("int_") && "invalid intrinsic name");
166-
// Remove the int_ from intrinsic name.
167-
Intrinsic = DefName.substr(4);
188+
{
189+
DXILIntrinsicSelect IntrSelect;
190+
IntrSelect.Intrinsic = GetIntrinsicName(R->getValue("LLVMIntrinsic"));
191+
if (IntrSelect.Intrinsic.size())
192+
IntrinsicSelects.emplace_back(std::move(IntrSelect));
193+
}
194+
195+
auto IntrinsicSelectRecords = R->getValueAsListOfDefs("intrinsic_selects");
196+
if (IntrinsicSelectRecords.size()) {
197+
if (IntrinsicSelects.size()) {
198+
PrintFatalError(
199+
R, Twine("LLVMIntrinsic and intrinsic_selects cannot be both "
200+
"defined for DXIL operation - ") +
201+
OpName);
202+
} else {
203+
for (const Record *R : IntrinsicSelectRecords) {
204+
DXILIntrinsicSelect IntrSelect;
205+
IntrSelect.Intrinsic = GetIntrinsicName(R->getValue("intrinsic"));
206+
auto Args = R->getValueAsListOfDefs("args");
207+
for (const Record *Arg : Args) {
208+
bool IsI8 = Arg->getValueAsBit("is_i8");
209+
bool IsI32 = Arg->getValueAsBit("is_i32");
210+
int Index = Arg->getValueAsInt("index");
211+
const Record *ValueRec = Arg->getValueAsOptionalDef("value");
212+
213+
DXILArgSelect ArgSelect;
214+
if (IsI8) {
215+
if (!ValueRec) {
216+
PrintFatalError(R, Twine("'value' must be defined for i8 "
217+
"ArgSelect for DXIL operation - ") +
218+
OpName);
219+
}
220+
ArgSelect.Type = DXILArgSelect::Type::I8;
221+
ArgSelect.Value = ValueRec->getValueAsInt("value");
222+
} else if (IsI32) {
223+
if (!ValueRec) {
224+
PrintFatalError(R, Twine("'value' must be defined for i32 "
225+
"ArgSelect for DXIL operation - ") +
226+
OpName);
227+
}
228+
ArgSelect.Type = DXILArgSelect::Type::I32;
229+
ArgSelect.Value = ValueRec->getValueAsInt("value");
230+
} else {
231+
if (Index < 0) {
232+
PrintFatalError(
233+
R, Twine("Index in ArgSelect<index> must be equal to or "
234+
"greater than 0 for DXIL operation - ") +
235+
OpName);
236+
}
237+
ArgSelect.Type = DXILArgSelect::Type::Index;
238+
ArgSelect.Value = Index;
239+
}
240+
241+
IntrSelect.Args.emplace_back(std::move(ArgSelect));
242+
}
243+
IntrinsicSelects.emplace_back(std::move(IntrSelect));
244+
}
168245
}
169246
}
170247
}
@@ -377,10 +454,29 @@ static void emitDXILIntrinsicMap(ArrayRef<DXILOperationDesc> Ops,
377454
OS << "#ifdef DXIL_OP_INTRINSIC\n";
378455
OS << "\n";
379456
for (const auto &Op : Ops) {
380-
if (Op.Intrinsic.empty())
457+
if (Op.IntrinsicSelects.empty()) {
381458
continue;
382-
OS << "DXIL_OP_INTRINSIC(dxil::OpCode::" << Op.OpName
383-
<< ", Intrinsic::" << Op.Intrinsic << ")\n";
459+
}
460+
for (const DXILIntrinsicSelect &MappedIntr : Op.IntrinsicSelects) {
461+
OS << "DXIL_OP_INTRINSIC(dxil::OpCode::" << Op.OpName
462+
<< ", Intrinsic::" << MappedIntr.Intrinsic;
463+
for (const DXILArgSelect &ArgSelect : MappedIntr.Args) {
464+
OS << ", (ArgSelect { ";
465+
switch (ArgSelect.Type) {
466+
case DXILArgSelect::Type::Index:
467+
OS << "ArgSelect::Type::Index, ";
468+
break;
469+
case DXILArgSelect::Type::I8:
470+
OS << "ArgSelect::Type::I8, ";
471+
break;
472+
case DXILArgSelect::Type::I32:
473+
OS << "ArgSelect::Type::I32, ";
474+
break;
475+
}
476+
OS << ArgSelect.Value << "})";
477+
}
478+
OS << ")\n";
479+
}
384480
}
385481
OS << "\n";
386482
OS << "#undef DXIL_OP_INTRINSIC\n";

0 commit comments

Comments
 (0)