-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DXIL] Define and generate DXILAttribute
and DXILProperty
#117072
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
In design discussion I used the terms 'function attribute' vs 'queryable attribute'. In this commit I change the terminology to Reviewer suggestions are very welcomed to this naming. |
473f6df
to
b157913
Compare
@llvm/pr-subscribers-tablegen @llvm/pr-subscribers-backend-directx Author: Finn Plummer (inbelic) Changes
Note: Resolves #114461 Patch is 124.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117072.diff 56 Files Affected:
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 36228a5e0dce18..8a44a6efd4bd33 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -266,18 +266,30 @@ def miss : DXILShaderStage;
def all_stages : DXILShaderStage;
// Denote support for DXIL Op to have been removed
def removed : DXILShaderStage;
+
// DXIL Op attributes
+// A function attribute denotes that there is a corresponding LLVM function
+// attribute that will be set when building the DXIL op. The mapping for
+// non-trivial cases is defined by setDXILAttribute in DXILOpBuilder.cpp
class DXILAttribute;
-def ReadOnly : DXILAttribute;
def ReadNone : DXILAttribute;
-def IsDerivative : DXILAttribute;
-def IsGradient : DXILAttribute;
-def IsFeedback : DXILAttribute;
-def IsWave : DXILAttribute;
-def NeedsUniformInputs : DXILAttribute;
-def IsBarrier : DXILAttribute;
+def ReadOnly : DXILAttribute;
+def NoDuplicate : DXILAttribute;
+def NoReturn : DXILAttribute;
+
+// A property is simply used to mark a DXIL op belongs to a sub-group of
+// DXIL ops, and it is used to query if a particular holds this property.
+// This is used for static analysis of DXIL ops.
+class DXILProperty;
+
+def IsBarrier : DXILProperty;
+def IsDerivative : DXILProperty;
+def IsGradient : DXILProperty;
+def IsFeedback : DXILProperty;
+def IsWave : DXILProperty;
+def RequiresUniformInputs : DXILProperty;
class Overloads<Version ver, list<DXILOpParamType> ols> {
Version dxil_version = ver;
@@ -291,7 +303,7 @@ class Stages<Version ver, list<DXILShaderStage> st> {
class Attributes<Version ver = DXIL1_0, list<DXILAttribute> attrs> {
Version dxil_version = ver;
- list<DXILAttribute> op_attrs = attrs;
+ list<DXILAttribute> fn_attrs = attrs;
}
// Abstraction DXIL Operation
@@ -322,6 +334,9 @@ class DXILOp<int opcode, DXILOpClass opclass> {
// Versioned attributes of operation
list<Attributes> attributes = [];
+
+ // List of properties. Default to no properties.
+ list<DXILProperty> properties = [];
}
// Concrete definitions of DXIL Operations
@@ -729,6 +744,7 @@ def CreateHandle : DXILOp<57, createHandle> {
let arguments = [Int8Ty, Int32Ty, Int32Ty, Int1Ty];
let result = HandleTy;
let stages = [Stages<DXIL1_0, [all_stages]>, Stages<DXIL1_6, [removed]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
def BufferLoad : DXILOp<68, bufferLoad> {
@@ -740,6 +756,7 @@ def BufferLoad : DXILOp<68, bufferLoad> {
[Overloads<DXIL1_0,
[ResRetHalfTy, ResRetFloatTy, ResRetInt16Ty, ResRetInt32Ty]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
def BufferStore : DXILOp<69, bufferStore> {
@@ -768,6 +785,7 @@ def CheckAccessFullyMapped : DXILOp<71, checkAccessFullyMapped> {
let result = Int1Ty;
let overloads = [Overloads<DXIL1_0, [Int32Ty]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
def Discard : DXILOp<82, discard> {
@@ -833,8 +851,8 @@ def Dot4AddI8Packed : DXILOp<163, dot4AddPacked> {
let LLVMIntrinsic = int_dx_dot4add_i8packed;
let arguments = [Int32Ty, Int32Ty, Int32Ty];
let result = Int32Ty;
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def Dot4AddU8Packed : DXILOp<164, dot4AddPacked> {
@@ -843,8 +861,8 @@ def Dot4AddU8Packed : DXILOp<164, dot4AddPacked> {
let LLVMIntrinsic = int_dx_dot4add_u8packed;
let arguments = [Int32Ty, Int32Ty, Int32Ty];
let result = Int32Ty;
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def AnnotateHandle : DXILOp<216, annotateHandle> {
@@ -852,6 +870,7 @@ def AnnotateHandle : DXILOp<216, annotateHandle> {
let arguments = [HandleTy, ResPropsTy];
let result = HandleTy;
let stages = [Stages<DXIL1_6, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def CreateHandleFromBinding : DXILOp<217, createHandleFromBinding> {
@@ -859,6 +878,7 @@ def CreateHandleFromBinding : DXILOp<217, createHandleFromBinding> {
let arguments = [ResBindTy, Int32Ty, Int1Ty];
let result = HandleTy;
let stages = [Stages<DXIL1_6, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def WaveActiveAnyTrue : DXILOp<113, waveAnyTrue> {
@@ -875,7 +895,6 @@ def WaveIsFirstLane : DXILOp<110, waveIsFirstLane> {
let arguments = [];
let result = Int1Ty;
let stages = [Stages<DXIL1_0, [all_stages]>];
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
@@ -885,7 +904,6 @@ def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
let result = OverloadTy;
let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy, DoubleTy, Int1Ty, Int16Ty, Int32Ty, Int64Ty]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
@@ -894,7 +912,7 @@ def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
let arguments = [];
let result = Int32Ty;
let stages = [Stages<DXIL1_0, [all_stages]>];
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
def WaveAllBitCount : DXILOp<135, waveAllOp> {
@@ -903,5 +921,4 @@ def WaveAllBitCount : DXILOp<135, waveAllOp> {
let arguments = [Int1Ty];
let result = Int32Ty;
let stages = [Stages<DXIL1_0, [all_stages]>];
- let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
diff --git a/llvm/lib/Target/DirectX/DXILConstants.h b/llvm/lib/Target/DirectX/DXILConstants.h
index 022cd57795a063..229401d6b271aa 100644
--- a/llvm/lib/Target/DirectX/DXILConstants.h
+++ b/llvm/lib/Target/DirectX/DXILConstants.h
@@ -30,6 +30,16 @@ enum class OpParamType : unsigned {
#include "DXILOperation.inc"
};
+enum class Attribute : unsigned {
+#define DXIL_ATTRIBUTE(Name) Name,
+#include "DXILOperation.inc"
+};
+
+enum class Property : unsigned {
+#define DXIL_PROPERTY(Name) Name,
+#include "DXILOperation.inc"
+};
+
} // namespace dxil
} // namespace llvm
diff --git a/llvm/lib/Target/DirectX/DXILOpBuilder.cpp b/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
index 5d5bb3eacace25..cae3f2ea43bf8e 100644
--- a/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
@@ -54,7 +54,7 @@ struct OpStage {
struct OpAttribute {
Version DXILVersion;
- uint32_t ValidAttrs;
+ llvm::SmallVector<dxil::Attribute> ValidAttrs;
};
static const char *getOverloadTypeName(OverloadKind Kind) {
@@ -159,6 +159,7 @@ struct OpCodeProperty {
llvm::SmallVector<OpOverload> Overloads;
llvm::SmallVector<OpStage> Stages;
llvm::SmallVector<OpAttribute> Attributes;
+ llvm::SmallVector<dxil::Property> Properties;
int OverloadParamIndex; // parameter index which control the overload.
// When < 0, should be only 1 overload type.
};
@@ -367,6 +368,20 @@ static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,
return std::nullopt;
}
+static void setDXILAttribute(CallInst *CI, dxil::Attribute Attr) {
+ switch (Attr) {
+ case dxil::Attribute::ReadNone:
+ return CI->setDoesNotAccessMemory();
+ case dxil::Attribute::ReadOnly:
+ return CI->setOnlyReadsMemory();
+ case dxil::Attribute::NoReturn:
+ return CI->setDoesNotReturn();
+ case dxil::Attribute::NoDuplicate:
+ return CI->setCannotDuplicate();
+ }
+ llvm_unreachable("Invalid function attribute specified for DXIL operation");
+}
+
namespace llvm {
namespace dxil {
@@ -461,7 +476,17 @@ Expected<CallInst *> DXILOpBuilder::tryCreateOp(dxil::OpCode OpCode,
OpArgs.push_back(IRB.getInt32(llvm::to_underlying(OpCode)));
OpArgs.append(Args.begin(), Args.end());
- return IRB.CreateCall(DXILFn, OpArgs, Name);
+ // Create the function call instruction
+ CallInst *CI = IRB.CreateCall(DXILFn, OpArgs, Name);
+
+ // We then need to attach available function attributes
+ for (auto OpAttr : Prop->Attributes)
+ if (VersionTuple(OpAttr.DXILVersion.Major, OpAttr.DXILVersion.Minor) <=
+ DXILVersion)
+ for (auto Attr : OpAttr.ValidAttrs)
+ setDXILAttribute(CI, Attr);
+
+ return CI;
}
CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,
diff --git a/llvm/test/CodeGen/DirectX/BufferLoad.ll b/llvm/test/CodeGen/DirectX/BufferLoad.ll
index 24d65fe1648c15..874c81df29b64a 100644
--- a/llvm/test/CodeGen/DirectX/BufferLoad.ll
+++ b/llvm/test/CodeGen/DirectX/BufferLoad.ll
@@ -16,7 +16,7 @@ define void @loadv4f32() {
; The temporary casts should all have been cleaned up
; CHECK-NOT: %dx.cast_handle
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR:]]
%data0 = call <4 x float> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 0)
@@ -33,7 +33,7 @@ define void @loadv4f32() {
call void @scalar_user(float %data0_0)
call void @scalar_user(float %data0_2)
- ; CHECK: [[DATA4:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 4, i32 undef)
+ ; CHECK: [[DATA4:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 4, i32 undef) #[[#ATTR]]
%data4 = call <4 x float> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 4)
@@ -47,7 +47,7 @@ define void @loadv4f32() {
; CHECK: insertelement <4 x float>
call void @vector_user(<4 x float> %data4)
- ; CHECK: [[DATA12:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 12, i32 undef)
+ ; CHECK: [[DATA12:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 12, i32 undef) #[[#ATTR]]
%data12 = call <4 x float> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 12)
@@ -69,7 +69,7 @@ define void @index_dynamic(i32 %bufindex, i32 %elemindex) {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[LOAD:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 %bufindex, i32 undef)
+ ; CHECK: [[LOAD:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 %bufindex, i32 undef) #[[#ATTR]]
%load = call <4 x float> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 %bufindex)
@@ -104,7 +104,7 @@ define void @loadf32() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call float @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", float, 0, 0, 0) %buffer, i32 0)
@@ -122,7 +122,7 @@ define void @loadv2f32() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v2f32_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call <2 x float> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <2 x float>, 0, 0, 0) %buffer, i32 0)
@@ -136,12 +136,12 @@ define void @loadv4f32_checkbit() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call {<4 x float>, i1} @llvm.dx.typedBufferLoad.checkbit.f32(
target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i32 0)
; CHECK: [[STATUS:%.*]] = extractvalue %dx.types.ResRet.f32 [[DATA0]], 4
- ; CHECK: [[MAPPED:%.*]] = call i1 @dx.op.checkAccessFullyMapped.i32(i32 71, i32 [[STATUS]]
+ ; CHECK: [[MAPPED:%.*]] = call i1 @dx.op.checkAccessFullyMapped.i32(i32 71, i32 [[STATUS]]) #[[#ATTR]]
%check = extractvalue {<4 x float>, i1} %data0, 1
; CHECK: call void @check_user(i1 [[MAPPED]])
@@ -157,7 +157,7 @@ define void @loadv4i32() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4i32_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i32 @dx.op.bufferLoad.i32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i32 @dx.op.bufferLoad.i32(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call <4 x i32> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) %buffer, i32 0)
@@ -171,7 +171,7 @@ define void @loadv4f16() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f16_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f16 @dx.op.bufferLoad.f16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.f16 @dx.op.bufferLoad.f16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call <4 x half> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x half>, 0, 0, 0) %buffer, i32 0)
@@ -185,9 +185,11 @@ define void @loadv4i16() {
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4i16_0_0_0(
i32 0, i32 0, i32 1, i32 0, i1 false)
- ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i16 @dx.op.bufferLoad.i16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef)
+ ; CHECK: [[DATA0:%.*]] = call %dx.types.ResRet.i16 @dx.op.bufferLoad.i16(i32 68, %dx.types.Handle [[HANDLE]], i32 0, i32 undef) #[[#ATTR]]
%data0 = call <4 x i16> @llvm.dx.typedBufferLoad(
target("dx.TypedBuffer", <4 x i16>, 0, 0, 0) %buffer, i32 0)
ret void
}
+
+; CHECK: attributes #[[#ATTR]] = {{{.*}} memory(read) {{.*}}}
diff --git a/llvm/test/CodeGen/DirectX/BufferStore.ll b/llvm/test/CodeGen/DirectX/BufferStore.ll
index 9ea7735be59c81..68849bc71edd22 100644
--- a/llvm/test/CodeGen/DirectX/BufferStore.ll
+++ b/llvm/test/CodeGen/DirectX/BufferStore.ll
@@ -17,7 +17,7 @@ define void @storefloat(<4 x float> %data, i32 %index) {
; CHECK: [[DATA0_1:%.*]] = extractelement <4 x float> %data, i32 1
; CHECK: [[DATA0_2:%.*]] = extractelement <4 x float> %data, i32 2
; CHECK: [[DATA0_3:%.*]] = extractelement <4 x float> %data, i32 3
- ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, float [[DATA0_0]], float [[DATA0_1]], float [[DATA0_2]], float [[DATA0_3]], i8 15)
+ ; CHECK: call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, float [[DATA0_0]], float [[DATA0_1]], float [[DATA0_2]], float [[DATA0_3]], i8 15){{$}}
call void @llvm.dx.typedBufferStore(
target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %buffer,
i32 %index, <4 x float> %data)
@@ -37,7 +37,7 @@ define void @storeint(<4 x i32> %data, i32 %index) {
; CHECK: [[DATA0_1:%.*]] = extractelement <4 x i32> %data, i32 1
; CHECK: [[DATA0_2:%.*]] = extractelement <4 x i32> %data, i32 2
; CHECK: [[DATA0_3:%.*]] = extractelement <4 x i32> %data, i32 3
- ; CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, i32 [[DATA0_0]], i32 [[DATA0_1]], i32 [[DATA0_2]], i32 [[DATA0_3]], i8 15)
+ ; CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, i32 [[DATA0_0]], i32 [[DATA0_1]], i32 [[DATA0_2]], i32 [[DATA0_3]], i8 15){{$}}
call void @llvm.dx.typedBufferStore(
target("dx.TypedBuffer", <4 x i32>, 1, 0, 0) %buffer,
i32 %index, <4 x i32> %data)
@@ -60,7 +60,7 @@ define void @storehalf(<4 x half> %data, i32 %index) {
; CHECK: [[DATA0_1:%.*]] = extractelement <4 x half> %data, i32 1
; CHECK: [[DATA0_2:%.*]] = extractelement <4 x half> %data, i32 2
; CHECK: [[DATA0_3:%.*]] = extractelement <4 x half> %data, i32 3
- ; CHECK: call void @dx.op.bufferStore.f16(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, half [[DATA0_0]], half [[DATA0_1]], half [[DATA0_2]], half [[DATA0_3]], i8 15)
+ ; CHECK: call void @dx.op.bufferStore.f16(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, half [[DATA0_0]], half [[DATA0_1]], half [[DATA0_2]], half [[DATA0_3]], i8 15){{$}}
call void @llvm.dx.typedBufferStore(
target("dx.TypedBuffer", <4 x half>, 1, 0, 0) %buffer,
i32 %index, <4 x half> %data)
@@ -83,7 +83,7 @@ define void @storei16(<4 x i16> %data, i32 %index) {
; CHECK: [[DATA0_1:%.*]] = extractelement <4 x i16> %data, i32 1
; CHECK: [[DATA0_2:%.*]] = extractelement <4 x i16> %data, i32 2
; CHECK: [[DATA0_3:%.*]] = extractelement <4 x i16> %data, i32 3
- ; CHECK: call void @dx.op.bufferStore.i16(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, i16 [[DATA0_0]], i16 [[DATA0_1]], i16 [[DATA0_2]], i16 [[DATA0_3]], i8 15)
+ ; CHECK: call void @dx.op.bufferStore.i16(i32 69, %dx.types.Handle [[HANDLE]], i32 %index, i32 undef, i16 [[DATA0_0]], i16 [[DATA0_1]], i16 [[DATA0_2]], i16 [[DATA0_3]], i8 15){{$}}
call void @llvm.dx.typedBufferStore(
target("dx.TypedBuffer", <4 x i16>, 1, 0, 0) %buffer,
i32 %index, <4 x i16> %data)
diff --git a/llvm/test/CodeGen/DirectX/CreateHandle.ll b/llvm/test/CodeGen/DirectX/CreateHandle.ll
index 40b3b2c7122722..6f1a17386db9b0 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandle.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandle.ll
@@ -19,14 +19,14 @@ define void @test_buffers() {
%typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
i32 3, i32 5, i32 1, i32 4, i1 false)
- ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 4, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 4, i1 false) #[[#ATTR:]]
; CHECK-NOT: @llvm.dx.cast.handle
; RWBuffer<int> Buf : register(u7, space2)
%typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t(
i32 2, i32 7, i32 1, i32 6, i1 false)
- ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 6, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 6, i1 false) #[[#ATTR]]
; Buffer<uint4> Buf[24] : register(t3, space5)
; Buffer<uint4> typed2 = Buf[4]
@@ -34,20 +34,20 @@ define void @test_buffers() {
%typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
i32 5, i32 3, i32 24, i32 7, i1 false)
- ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 3, i32 7, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 3, i32 7, i1 false) #[[#ATTR]]
; struct S { float4 a; uint4 b; };
; StructuredBuffer<S> Buf : register(t2, space4)
%struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
@llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
i32 4, i32 2, i32 1, i32 10, i1 true)
- ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 10, i1 true)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 10, i1 true) #[[#ATTR]]
; ByteAddressBuffer Buf : register(t8, space1)
%byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
@llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
i32 1, i32...
[truncated]
|
llvm/lib/Target/DirectX/DXIL.td
Outdated
class DXILProperty; | ||
|
||
def IsBarrier : DXILProperty; | ||
def IsDerivative : DXILProperty; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was set, but never actually used in hctdb.py. What was actually used is the is_gradient value. This change demonstrates it can be removed from DXC with no consequences: microsoft/DirectXShaderCompiler#7023
As such, I think we should remove this property
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore, do we want to undefine all the properties and only define them when an op that uses them is added? Currently all but isWave/isBarrier
. It would prevent having any other redundant properties by being easier to track.
- switch to using DXILAttribute to only denote function attributes - attribute enums can't be or'd together as is currently implemented, so we switch to using a list of attributes in DXILOpBuilder.cpp and DXILEmitter.cpp
- correct all uses of ReadOnly/ReadNone to be consistent with hctdb.py in DXC - all fix order of attributes in each op
- testcases are update to check updated attribute types - there are some CHECK-NOT tests to ensure that previously set attributes no longer emit an attribute
- define these properties in DXIL.td and DXILConstants.h - emit DXIL definitions as enumerations - emit some helper functions to query OpCodeProp for each property class
- instead of generating a query helper for each property, just use a general one for all property enums
- before this change, we would be required to define OpCodeProperty and all used structs to use hasProperty, which for all uses outside of DXILOpBuilder is not usable.
- remove `IsDerivative` property as it was not used in `hctdb.py`
1c40803
to
2e4d5c2
Compare
Rebased to resolve existing conflicts. I also added some more commits to this pr regarding formatting and |
- update existing wave ops to specify the `IsWave` property - update the `Barrier` op to specify the `IsBarrier` property
b99955f
to
147395e
Compare
Personally I think we should rip out all of the codegen for properties until we're actually doing something with them. It might be fine to have them in DXIL.td if we want to copy these over as we go, but the DXILEmitter stuff really shouldn't be there until we're using the properties in code. Note that this whole approach, for both attributes and properties, is continuing a bit of an antipattern that's already there. It would be much nicer if we used tablegen to generate tables rather than using is as a generic c++ code emitter. Consider, in DXILConstants.h we could define a structure instead of an enum: struct Properties {
#define DXIL_PROPERTY(Name) bool Name;
#include "DXILOperation.inc"
}; Then, in DXILOpBuilder rather than adding to the OpCodeProperty mess we could simpy have a function like: static void setDXILProperties(dxil::OpCode OpCode, CallInst *CI) {
dxil::Properties Props;
switch (OpCode) {
#define DXIL_OP_PROPERTIES(OpCode, ...) \
case OpCode: \
Props = {__VA_ARGS__}; \
break;
#include "DXILOperation.inc"
}
if (Props.IsBarrier)
...
if (Props.IsGradient)
...
if (Props.IsFeedback)
...
if (Props.IsWave)
...
if (Props.RequiresUniformInputs)
...
} Where instead of building a string of all of the properties we simply emit a table of bools:
A similar approach should probably be applied to attributes, but it's a bit more complicated if we want to have the versioning so I think it's reasonable to do that as part of a more general cleanup of DXILEmitter later. |
- convert to using a struct to store the active attributes to let us use a table of bools instead of a string list of attributes
- as noted, the codegen was not going to be used until a future pr - it is now removed and we restructure the properties to be a struct of bools, with the intention to remove any codegen and follow a similar pattern to the attributes in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one really small formatting nit.
llvm/utils/TableGen/DXILEmitter.cpp
Outdated
for (const Record *CR : Recs) { | ||
PropRecs.push_back(CR); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
for (const Record *CR : Recs) { | |
PropRecs.push_back(CR); | |
} | |
for (const Record *CR : Recs) | |
PropRecs.push_back(CR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote a few different comments before I resolved them myself. Following the way the versioned op attributes are matched was a bit confusing, but I'm convinced it's right now. What remains are attributes that match what we had in hctdb, but I wonder if they should be different. We can certainly consider that later.
@@ -783,6 +797,7 @@ def CreateHandle : DXILOp<57, createHandle> { | |||
let arguments = [Int8Ty, Int32Ty, Int32Ty, Int1Ty]; | |||
let result = HandleTy; | |||
let stages = [Stages<DXIL1_0, [all_stages]>, Stages<DXIL1_6, [removed]>]; | |||
let attributes = [Attributes<DXIL1_0, [ReadOnly]>]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is consistent with hctdb.py, but I'm not certain it's right. I can't really rationalize why this is RO while both CreateHandleFromBinding
and even CreateHandleFromHeap
are RN. I'm not suggesting changing it. At worst, if you're unsure, a comment here might give future generations a chance to revisit this when we might be better suited to answer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment to note this and will mention it in the new issue.
@@ -974,7 +996,7 @@ def WaveAllBitCount : DXILOp<135, waveAllOp> { | |||
let arguments = [Int1Ty]; | |||
let result = Int32Ty; | |||
let stages = [Stages<DXIL1_0, [all_stages]>]; | |||
let attributes = [Attributes<DXIL1_0, [ReadNone]>]; | |||
let properties = [IsWave]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all these wave ops could be readonly at the least, maybe we want to investigate that later though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that we could further investigate the correctness of the attributes. The goal of this PR was to get consistent behaviour with DXC, so I will file an issue to look into that.
- update nit from review comments - fix some typos in comments - add additional comments for clarity
Created an issue to track the additional work of verifying the current attributes specified in DXC: #123825 |
DXILAttribute
to denote a function attribute, compatible to how it was define in DXC/LLVM 3.7DXILAttribute
is emitted to be a struct of set attributes instead of an "or" of the enumsDXILAttribute
to LLVM function attributes inDXILOpBuilder.cpp
. A custom mapping is defined.llvm/test/CodeGen/DirectX
of all ops with attributes to match that attributes are setDXILProperty
to denote the other type of attributes from DXC used to query properties.DXILProperty
as a struct of set attributes.DXIL.td
to specify applicableDXILProperty
s on opsNote:
DXILProperty
was referred to as 'queryable attributes' in design discussion. Changed to property to allow for better expression inDXIL.td
Resolves #114461
Resolves #115912