[DirectX] Implement lowering of llvm.dx.resource.samplebias to the SampleBias DXIL Op#199745
[DirectX] Implement lowering of llvm.dx.resource.samplebias to the SampleBias DXIL Op#199745Icohedron wants to merge 4 commits into
Conversation
Assisted-by: Claude Opus 4.6
Assisted-by: Claude Opus 4.6
|
@llvm/pr-subscribers-backend-directx Author: Deric C. (Icohedron) ChangesFixes #192548 This PR implements the lowering of the Although I reckon that other Assisted-by: Claude Opus 4.6 Full diff: https://github.com/llvm/llvm-project/pull/199745.diff 3 Files Affected:
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 30dab507bbaf4..1caa32de2f328 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -880,6 +880,23 @@ def CBufferLoadLegacy : DXILOp<59, cbufferLoadLegacy> {
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
+def SampleBias : DXILOp<61, sampleBias> {
+ let Doc = "samples a texture after applying the input bias to the mip level";
+ // Handle, Sampler, Coord0, Coord1, Coord2, Coord3,
+ // Offset0, Offset1, Offset2, Bias, Clamp
+ let arguments = [HandleTy, HandleTy, FloatTy, FloatTy, FloatTy, FloatTy,
+ Int32Ty, Int32Ty, Int32Ty, FloatTy, FloatTy];
+ let result = OverloadTy;
+ let overloads =
+ [Overloads<DXIL1_0, [ResRetHalfTy, ResRetFloatTy]>,
+ Overloads<DXIL1_7,
+ [ResRetHalfTy, ResRetFloatTy, ResRetInt16Ty, ResRetInt32Ty]>];
+ let stages = [Stages<DXIL1_0, [library, pixel]>,
+ Stages<DXIL1_6, [library, pixel, compute, amplification, mesh,
+ node]>];
+ let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
+}
+
def TextureLoad : DXILOp<66, textureLoad> {
let Doc = "reads from a texture resource";
// Handle, MipLevelOrSampleCount, Coord0, Coord1, Coord2, Offset0, Offset1, Offset2
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index b4d95dc66d3cc..502cc00b422d9 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -644,6 +644,50 @@ class OpLowerer {
});
}
+ [[nodiscard]] bool lowerSampleBias(Function &F, bool HasClamp) {
+ IRBuilder<> &IRB = OpBuilder.getIRB();
+ Type *Int32Ty = IRB.getInt32Ty();
+ Type *FloatTy = IRB.getFloatTy();
+
+ return replaceFunction(F, [&](CallInst *CI) -> Error {
+ IRB.SetInsertPoint(CI);
+
+ Value *Handle =
+ createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
+ Value *Sampler =
+ createTmpHandleCast(CI->getArgOperand(1), OpBuilder.getHandleType());
+ Value *Coords = CI->getArgOperand(2);
+ Value *Bias = CI->getArgOperand(3);
+ Value *Offsets = CI->getArgOperand(4);
+ Value *Clamp = HasClamp ? CI->getArgOperand(5) : UndefValue::get(FloatTy);
+
+ Type *OldTy = CI->getType();
+ Type *NewRetTy = OpBuilder.getResRetType(OldTy->getScalarType());
+
+ Value *UndefF = UndefValue::get(FloatTy);
+ Value *UndefI = UndefValue::get(Int32Ty);
+ // Args: Handle, Sampler, Coord0..3, Offset0..2, Bias, Clamp
+ std::array<Value *, 11> Args{Handle, Sampler, UndefF, UndefF,
+ UndefF, UndefF, UndefI, UndefI,
+ UndefI, Bias, Clamp};
+
+ // Copy coordinates into Args[2..5].
+ extractElementsIntoArgs(IRB, Args, 2, Coords, 4);
+ // Copy offsets into Args[6..8] if non-zero.
+ if (auto *C = dyn_cast<Constant>(Offsets); !C || !C->isNullValue())
+ extractElementsIntoArgs(IRB, Args, 6, Offsets, 3);
+
+ Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
+ OpCode::SampleBias, Args, CI->getName(), NewRetTy);
+ if (Error E = OpCall.takeError())
+ return E;
+ if (Error E = replaceResRetUses(CI, *OpCall, /*HasCheckBit=*/false))
+ return E;
+
+ return Error::success();
+ });
+ }
+
[[nodiscard]] bool lowerRawBufferLoad(Function &F) {
const DataLayout &DL = F.getDataLayout();
IRBuilder<> &IRB = OpBuilder.getIRB();
@@ -1061,6 +1105,12 @@ class OpLowerer {
case Intrinsic::dx_resource_load_level:
HasErrors |= lowerTextureLoad(F);
break;
+ case Intrinsic::dx_resource_samplebias:
+ HasErrors |= lowerSampleBias(F, /*HasClamp=*/false);
+ break;
+ case Intrinsic::dx_resource_samplebias_clamp:
+ HasErrors |= lowerSampleBias(F, /*HasClamp=*/true);
+ break;
case Intrinsic::dx_resource_store_typedbuffer:
HasErrors |= lowerBufferStore(F, /*IsRaw=*/false);
break;
diff --git a/llvm/test/CodeGen/DirectX/SampleBias.ll b/llvm/test/CodeGen/DirectX/SampleBias.ll
new file mode 100644
index 0000000000000..b7520687c3233
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/SampleBias.ll
@@ -0,0 +1,223 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.7-pixel"
+
+declare void @use_float4(<4 x float>)
+declare void @use_float(float)
+declare void @use_half4(<4 x half>)
+declare void @use_int4(<4 x i32>)
+
+; Test basic SampleBias on a Texture2D with float4 result.
+; CHECK-LABEL: define void @samplebias_texture2d_float4(
+define void @samplebias_texture2d_float4(<2 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_2t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> zeroinitializer)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias with clamp on a Texture2D.
+; CHECK-LABEL: define void @samplebias_texture2d_with_clamp(
+define void @samplebias_texture2d_with_clamp(<2 x float> %coords, float %bias, float %clamp) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_2t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float %clamp)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> zeroinitializer, float %clamp)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias with non-zero offsets on a Texture2D.
+; CHECK-LABEL: define void @samplebias_texture2d_with_offset(
+define void @samplebias_texture2d_with_offset(<2 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_2t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 1, i32 -2, i32 undef, float %bias, float undef)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> <i32 1, i32 -2>)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias with both offset and clamp on a Texture2D.
+; CHECK-LABEL: define void @samplebias_texture2d_with_offset_and_clamp(
+define void @samplebias_texture2d_with_offset_and_clamp(<2 x float> %coords, float %bias, float %clamp) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_2t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 3, i32 -1, i32 undef, float %bias, float %clamp)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> <i32 3, i32 -1>, float %clamp)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias on a Texture1D (scalar coordinate, scalar offset).
+; CHECK-LABEL: define void @samplebias_texture1d_float4(
+define void @samplebias_texture1d_float4(float %coord, float %bias) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 1)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_1t(
+ i32 0, i32 3, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %coord, float undef, float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_1t.tdx.Sampler_0t.f32.i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 1) %texture,
+ target("dx.Sampler", 0) %sampler,
+ float %coord, float %bias, i32 0)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias on a Texture3D (3-component coordinates).
+; CHECK-LABEL: define void @samplebias_texture3d_float4(
+define void @samplebias_texture3d_float4(<3 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", <4 x float>, 0, 0, 0, 4)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f32_0_0_0_4t(
+ i32 0, i32 4, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <3 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <3 x float> %coords, i64 1
+ ; CHECK: %[[COORD2:.*]] = extractelement <3 x float> %coords, i64 2
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float %[[COORD2]], float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call <4 x float>
+ @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_4t.tdx.Sampler_0t.v3f32.v3i32(
+ target("dx.Texture", <4 x float>, 0, 0, 0, 4) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <3 x float> %coords, float %bias, <3 x i32> zeroinitializer)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float4(<4 x float> %data)
+ ret void
+}
+
+; Test SampleBias with a scalar return type on a Texture2D.
+; CHECK-LABEL: define void @samplebias_texture2d_scalar(
+define void @samplebias_texture2d_scalar(<2 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", float, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_f32_0_0_0_2t(
+ i32 0, i32 1, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f32 @dx.op.sampleBias.f32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call float
+ @llvm.dx.resource.samplebias.f32.tdx.Texture_f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", float, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> zeroinitializer)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f32 %[[SAMPLE]], 0
+ call void @use_float(float %data)
+ ret void
+}
+
+; Test SampleBias with half-precision result type on a Texture2D.
+; CHECK-LABEL: define void @samplebias_texture2d_half4(
+define void @samplebias_texture2d_half4(<2 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", <4 x half>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4f16_0_0_0_2t(
+ i32 0, i32 5, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.f16 @dx.op.sampleBias.f16(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call <4 x half>
+ @llvm.dx.resource.samplebias.v4f16.tdx.Texture_v4f16_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x half>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> zeroinitializer)
+
+ ; CHECK: extractvalue %dx.types.ResRet.f16 %[[SAMPLE]], 0
+ call void @use_half4(<4 x half> %data)
+ ret void
+}
+
+; Test SampleBias with integer result type on a Texture2D (requires SM 6.7).
+; CHECK-LABEL: define void @samplebias_texture2d_int4(
+define void @samplebias_texture2d_int4(<2 x float> %coords, float %bias) {
+ %texture = call target("dx.Texture", <4 x i32>, 0, 0, 0, 2)
+ @llvm.dx.resource.handlefrombinding.tdx.Texture_v4i32_0_0_0_2t(
+ i32 0, i32 6, i32 1, i32 0, ptr null)
+ %sampler = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefrombinding.tdx.Sampler_0t(
+ i32 0, i32 0, i32 1, i32 0, ptr null)
+
+ ; CHECK: %[[COORD0:.*]] = extractelement <2 x float> %coords, i64 0
+ ; CHECK: %[[COORD1:.*]] = extractelement <2 x float> %coords, i64 1
+ ; CHECK: %[[SAMPLE:.*]] = call %dx.types.ResRet.i32 @dx.op.sampleBias.i32(i32 61, %dx.types.Handle %{{.*}}, %dx.types.Handle %{{.*}}, float %[[COORD0]], float %[[COORD1]], float undef, float undef, i32 undef, i32 undef, i32 undef, float %bias, float undef)
+ %data = call <4 x i32>
+ @llvm.dx.resource.samplebias.v4i32.tdx.Texture_v4i32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(
+ target("dx.Texture", <4 x i32>, 0, 0, 0, 2) %texture,
+ target("dx.Sampler", 0) %sampler,
+ <2 x float> %coords, float %bias, <2 x i32> zeroinitializer)
+
+ ; CHECK: extractvalue %dx.types.ResRet.i32 %[[SAMPLE]], 0
+ call void @use_int4(<4 x i32> %data)
+ ret void
+}
|
You can test this locally with the following command:git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)' 'HEAD~1' HEAD llvm/test/CodeGen/DirectX/SampleBias.ll llvm/lib/Target/DirectX/DXILOpLowering.cppThe following files introduce new uses of undef:
Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields In tests, avoid using For example, this is considered a bad practice: define void @fn() {
...
br i1 undef, ...
}Please use the following instead: define void @fn(i1 %cond) {
...
br i1 %cond, ...
}Please refer to the Undefined Behavior Manual for more information. |
joaosaffran
left a comment
There was a problem hiding this comment.
LGTM, a really small nit, feel free to ignore if you disagree wih it
… Offset test Assisted-by: Claude Opus 4.6
…llow logic Assisted-by: Claude Opus 4.6
Fixes #192548
This PR implements the lowering of the
llvm.dx.resource.samplebiasintrinsic to theSampleBiasDXIL Op.Although I reckon that other
lowerSample*functions inDXILOpLowering.cppwill have shared logic, this is the first one to be implemented. Consolidating common logic between futurelowerSample*functions can be left to a later PR implementing the second or otherlowerSample*function.Assisted-by: Claude Opus 4.6