Skip to content

Commit 011b618

Browse files
authored
[DXIL] Define and generate DXILAttribute and DXILProperty (#117072)
- Redefines `DXILAttribute` to denote a function attribute, compatible to how it was define in DXC/LLVM 3.7 - Fix how `DXILAttribute` is emitted to be a struct of set attributes instead of an "or" of the enums - Implement the lowering of `DXILAttribute` to LLVM function attributes in `DXILOpBuilder.cpp`. A custom mapping is defined. - Audit all current ops to specify the correct attributes consistent with DXC. This is done here to allow for testing. - Update testcases in `llvm/test/CodeGen/DirectX` of all ops with attributes to match that attributes are set - Update testcases of ops that had previously incorrectly set attributes to check there is no attributes set - Defines `DXILProperty` to denote the other type of attributes from DXC used to query properties. - Emit `DXILProperty` as a struct of set attributes. - Updates `DXIL.td` to specify applicable `DXILProperty`s on ops Note: `DXILProperty` was referred to as 'queryable attributes' in design discussion. Changed to property to allow for better expression in `DXIL.td` Resolves #114461 Resolves #115912
1 parent 335f1a7 commit 011b618

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+558
-315
lines changed

llvm/lib/Target/DirectX/DXIL.td

+40-14
Original file line numberDiff line numberDiff line change
@@ -268,18 +268,29 @@ def miss : DXILShaderStage;
268268
def all_stages : DXILShaderStage;
269269
// Denote support for DXIL Op to have been removed
270270
def removed : DXILShaderStage;
271+
271272
// DXIL Op attributes
272273

274+
// A function attribute denotes that there is a corresponding LLVM function
275+
// attribute that will be set when building the DXIL op. The mapping is defined
276+
// by setDXILAttributes in DXILOpBuilder.cpp
273277
class DXILAttribute;
274278

275-
def ReadOnly : DXILAttribute;
276279
def ReadNone : DXILAttribute;
277-
def IsDerivative : DXILAttribute;
278-
def IsGradient : DXILAttribute;
279-
def IsFeedback : DXILAttribute;
280-
def IsWave : DXILAttribute;
281-
def NeedsUniformInputs : DXILAttribute;
282-
def IsBarrier : DXILAttribute;
280+
def ReadOnly : DXILAttribute;
281+
def NoDuplicate : DXILAttribute;
282+
def NoReturn : DXILAttribute;
283+
284+
// A property is simply used to mark that a DXIL op belongs to a sub-group of
285+
// DXIL ops, and it is used to query if a particular op holds this property.
286+
// This is used for the static analysis of DXIL ops.
287+
class DXILProperty;
288+
289+
def IsBarrier : DXILProperty;
290+
def IsGradient : DXILProperty;
291+
def IsFeedback : DXILProperty;
292+
def IsWave : DXILProperty;
293+
def RequiresUniformInputs : DXILProperty;
283294

284295
class Overloads<Version ver, list<DXILOpParamType> ols> {
285296
Version dxil_version = ver;
@@ -293,7 +304,7 @@ class Stages<Version ver, list<DXILShaderStage> st> {
293304

294305
class Attributes<Version ver = DXIL1_0, list<DXILAttribute> attrs> {
295306
Version dxil_version = ver;
296-
list<DXILAttribute> op_attrs = attrs;
307+
list<DXILAttribute> fn_attrs = attrs;
297308
}
298309

299310
defvar BarrierMode_DeviceMemoryBarrier = 2;
@@ -386,6 +397,9 @@ class DXILOp<int opcode, DXILOpClass opclass> {
386397

387398
// Versioned attributes of operation
388399
list<Attributes> attributes = [];
400+
401+
// List of properties. Default to no properties.
402+
list<DXILProperty> properties = [];
389403
}
390404

391405
// Concrete definitions of DXIL Operations
@@ -805,6 +819,10 @@ def CreateHandle : DXILOp<57, createHandle> {
805819
let arguments = [Int8Ty, Int32Ty, Int32Ty, Int1Ty];
806820
let result = HandleTy;
807821
let stages = [Stages<DXIL1_0, [all_stages]>, Stages<DXIL1_6, [removed]>];
822+
// NOTE: The ReadOnly attribute was set for consistency with DXC. However, it
823+
// seems like ReadNone may more appropiately describe it. So noted to
824+
// consider a change in the future
825+
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
808826
}
809827

810828
def BufferLoad : DXILOp<68, bufferLoad> {
@@ -816,6 +834,7 @@ def BufferLoad : DXILOp<68, bufferLoad> {
816834
[Overloads<DXIL1_0,
817835
[ResRetHalfTy, ResRetFloatTy, ResRetInt16Ty, ResRetInt32Ty]>];
818836
let stages = [Stages<DXIL1_0, [all_stages]>];
837+
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
819838
}
820839

821840
def BufferStore : DXILOp<69, bufferStore> {
@@ -844,6 +863,7 @@ def CheckAccessFullyMapped : DXILOp<71, checkAccessFullyMapped> {
844863
let result = Int1Ty;
845864
let overloads = [Overloads<DXIL1_0, [Int32Ty]>];
846865
let stages = [Stages<DXIL1_0, [all_stages]>];
866+
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
847867
}
848868

849869
def Discard : DXILOp<82, discard> {
@@ -955,8 +975,8 @@ def Dot4AddI8Packed : DXILOp<163, dot4AddPacked> {
955975
let intrinsics = [ IntrinSelect<int_dx_dot4add_i8packed> ];
956976
let arguments = [Int32Ty, Int32Ty, Int32Ty];
957977
let result = Int32Ty;
958-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
959978
let stages = [Stages<DXIL1_0, [all_stages]>];
979+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
960980
}
961981

962982
def Dot4AddU8Packed : DXILOp<164, dot4AddPacked> {
@@ -965,22 +985,24 @@ def Dot4AddU8Packed : DXILOp<164, dot4AddPacked> {
965985
let intrinsics = [ IntrinSelect<int_dx_dot4add_u8packed> ];
966986
let arguments = [Int32Ty, Int32Ty, Int32Ty];
967987
let result = Int32Ty;
968-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
969988
let stages = [Stages<DXIL1_0, [all_stages]>];
989+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
970990
}
971991

972992
def AnnotateHandle : DXILOp<216, annotateHandle> {
973993
let Doc = "annotate handle with resource properties";
974994
let arguments = [HandleTy, ResPropsTy];
975995
let result = HandleTy;
976996
let stages = [Stages<DXIL1_6, [all_stages]>];
997+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
977998
}
978999

9791000
def CreateHandleFromBinding : DXILOp<217, createHandleFromBinding> {
9801001
let Doc = "create resource handle from binding";
9811002
let arguments = [ResBindTy, Int32Ty, Int1Ty];
9821003
let result = HandleTy;
9831004
let stages = [Stages<DXIL1_6, [all_stages]>];
1005+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
9841006
}
9851007

9861008
def WaveActiveAllTrue : DXILOp<114, waveAllTrue> {
@@ -989,6 +1011,7 @@ def WaveActiveAllTrue : DXILOp<114, waveAllTrue> {
9891011
let arguments = [Int1Ty];
9901012
let result = Int1Ty;
9911013
let stages = [Stages<DXIL1_0, [all_stages]>];
1014+
let properties = [IsWave];
9921015
}
9931016

9941017
def WaveActiveAnyTrue : DXILOp<113, waveAnyTrue> {
@@ -997,6 +1020,7 @@ def WaveActiveAnyTrue : DXILOp<113, waveAnyTrue> {
9971020
let arguments = [Int1Ty];
9981021
let result = Int1Ty;
9991022
let stages = [Stages<DXIL1_0, [all_stages]>];
1023+
let properties = [IsWave];
10001024
}
10011025

10021026
def WaveActiveOp : DXILOp<119, waveActiveOp> {
@@ -1023,7 +1047,7 @@ def WaveIsFirstLane : DXILOp<110, waveIsFirstLane> {
10231047
let arguments = [];
10241048
let result = Int1Ty;
10251049
let stages = [Stages<DXIL1_0, [all_stages]>];
1026-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
1050+
let properties = [IsWave];
10271051
}
10281052

10291053
def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
@@ -1033,7 +1057,7 @@ def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
10331057
let result = OverloadTy;
10341058
let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy, DoubleTy, Int1Ty, Int16Ty, Int32Ty, Int64Ty]>];
10351059
let stages = [Stages<DXIL1_0, [all_stages]>];
1036-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
1060+
let properties = [IsWave];
10371061
}
10381062

10391063
def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
@@ -1042,7 +1066,8 @@ def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
10421066
let arguments = [];
10431067
let result = Int32Ty;
10441068
let stages = [Stages<DXIL1_0, [all_stages]>];
1045-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
1069+
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
1070+
let properties = [IsWave];
10461071
}
10471072

10481073
def WaveAllBitCount : DXILOp<135, waveAllOp> {
@@ -1051,7 +1076,7 @@ def WaveAllBitCount : DXILOp<135, waveAllOp> {
10511076
let arguments = [Int1Ty];
10521077
let result = Int32Ty;
10531078
let stages = [Stages<DXIL1_0, [all_stages]>];
1054-
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
1079+
let properties = [IsWave];
10551080
}
10561081

10571082
def Barrier : DXILOp<80, barrier> {
@@ -1066,4 +1091,5 @@ def Barrier : DXILOp<80, barrier> {
10661091
let result = VoidTy;
10671092
let stages = [Stages<DXIL1_0, [compute, library]>];
10681093
let attributes = [Attributes<DXIL1_0, []>];
1094+
let properties = [IsBarrier];
10691095
}

llvm/lib/Target/DirectX/DXILConstants.h

+22
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@ enum class OpParamType : unsigned {
3030
#include "DXILOperation.inc"
3131
};
3232

33+
struct Attributes {
34+
#define DXIL_ATTRIBUTE(Name) bool Name = false;
35+
#include "DXILOperation.inc"
36+
};
37+
38+
inline Attributes operator|(Attributes a, Attributes b) {
39+
Attributes c;
40+
#define DXIL_ATTRIBUTE(Name) c.Name = a.Name | b.Name;
41+
#include "DXILOperation.inc"
42+
return c;
43+
}
44+
45+
inline Attributes &operator|=(Attributes &a, Attributes &b) {
46+
a = a | b;
47+
return a;
48+
}
49+
50+
struct Properties {
51+
#define DXIL_PROPERTY(Name) bool Name = false;
52+
#include "DXILOperation.inc"
53+
};
54+
3355
} // namespace dxil
3456
} // namespace llvm
3557

llvm/lib/Target/DirectX/DXILOpBuilder.cpp

+62-7
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ struct OpStage {
5252
uint32_t ValidStages;
5353
};
5454

55-
struct OpAttribute {
56-
Version DXILVersion;
57-
uint32_t ValidAttrs;
58-
};
59-
6055
static const char *getOverloadTypeName(OverloadKind Kind) {
6156
switch (Kind) {
6257
case OverloadKind::HALF:
@@ -158,7 +153,6 @@ struct OpCodeProperty {
158153
unsigned OpCodeClassNameOffset;
159154
llvm::SmallVector<OpOverload> Overloads;
160155
llvm::SmallVector<OpStage> Stages;
161-
llvm::SmallVector<OpAttribute> Attributes;
162156
int OverloadParamIndex; // parameter index which control the overload.
163157
// When < 0, should be only 1 overload type.
164158
};
@@ -371,6 +365,61 @@ static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,
371365
return std::nullopt;
372366
}
373367

368+
// Helper function to pack an OpCode and VersionTuple into a uint64_t for use
369+
// in a switch statement
370+
constexpr static uint64_t computeSwitchEnum(dxil::OpCode OpCode,
371+
uint16_t VersionMajor,
372+
uint16_t VersionMinor) {
373+
uint64_t OpCodePack = (uint64_t)OpCode;
374+
return (OpCodePack << 32) | (VersionMajor << 16) | VersionMinor;
375+
}
376+
377+
// Retreive all the set attributes for a DXIL OpCode given the targeted
378+
// DXILVersion
379+
static dxil::Attributes getDXILAttributes(dxil::OpCode OpCode,
380+
VersionTuple DXILVersion) {
381+
// Instantiate all versions to iterate through
382+
SmallVector<Version> Versions = {
383+
#define DXIL_VERSION(MAJOR, MINOR) {MAJOR, MINOR},
384+
#include "DXILOperation.inc"
385+
};
386+
387+
dxil::Attributes Attributes;
388+
for (auto Version : Versions) {
389+
if (DXILVersion < VersionTuple(Version.Major, Version.Minor))
390+
continue;
391+
392+
// Switch through and match an OpCode with the specific version and set the
393+
// corresponding flag(s) if available
394+
switch (computeSwitchEnum(OpCode, Version.Major, Version.Minor)) {
395+
#define DXIL_OP_ATTRIBUTES(OpCode, VersionMajor, VersionMinor, ...) \
396+
case computeSwitchEnum(OpCode, VersionMajor, VersionMinor): { \
397+
auto Other = dxil::Attributes{__VA_ARGS__}; \
398+
Attributes |= Other; \
399+
break; \
400+
};
401+
#include "DXILOperation.inc"
402+
}
403+
}
404+
return Attributes;
405+
}
406+
407+
// Retreive the set of DXIL Attributes given the version and map them to an
408+
// llvm function attribute that is set onto the instruction
409+
static void setDXILAttributes(CallInst *CI, dxil::OpCode OpCode,
410+
VersionTuple DXILVersion) {
411+
dxil::Attributes Attributes = getDXILAttributes(OpCode, DXILVersion);
412+
if (Attributes.ReadNone)
413+
CI->setDoesNotAccessMemory();
414+
if (Attributes.ReadOnly)
415+
CI->setOnlyReadsMemory();
416+
if (Attributes.NoReturn)
417+
CI->setDoesNotReturn();
418+
if (Attributes.NoDuplicate)
419+
CI->setCannotDuplicate();
420+
return;
421+
}
422+
374423
namespace llvm {
375424
namespace dxil {
376425

@@ -465,7 +514,13 @@ Expected<CallInst *> DXILOpBuilder::tryCreateOp(dxil::OpCode OpCode,
465514
OpArgs.push_back(IRB.getInt32(llvm::to_underlying(OpCode)));
466515
OpArgs.append(Args.begin(), Args.end());
467516

468-
return IRB.CreateCall(DXILFn, OpArgs, Name);
517+
// Create the function call instruction
518+
CallInst *CI = IRB.CreateCall(DXILFn, OpArgs, Name);
519+
520+
// We then need to attach available function attributes
521+
setDXILAttributes(CI, OpCode, DXILVersion);
522+
523+
return CI;
469524
}
470525

471526
CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,

0 commit comments

Comments
 (0)