[clang] [llvm] [HLSL][SPIRV] Add IntrConvergent to convergent intrinsics (PR #211256)
Tim Corringham via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 28 01:56:08 PDT 2026
https://github.com/tcorringham updated https://github.com/llvm/llvm-project/pull/211256
>From 9df964e1090328a8585ca87a24c41bf90f640ee5 Mon Sep 17 00:00:00 2001
From: Tim Corringham <tcorring at amd.com>
Date: Wed, 22 Jul 2026 11:08:50 +0100
Subject: [PATCH 1/3] [HLSL][SPIRV] Add IntrConvergent to convergent intrinsics
A number of spirv intrinsics need to run in convergent code flow
but LLVM -O3 could sometimes lower these into divergent control
flow as they were missing the IntrConvergent attribute.
This change adds the IntrConvergent attribute to these intrinsics,
and switches the builtin emission from `Builder.CreateIntrinsic`
to `EmitRuntimeCall` (via a helper function) so that the attribute
is preserved through codegen.
Tests check that the Convergent attribute is added where expected,
and that it survives a llvm-as/llvm-dis round-trip.
Assisted by Cursor
Fixes #211253
---
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 40 +++++++++++++------
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 12 +++---
.../spirv-resource-intrinsic-attributes.ll | 29 ++++++++++++++
llvm/unittests/IR/IntrinsicsTest.cpp | 26 +++++++++++-
4 files changed, 87 insertions(+), 20 deletions(-)
create mode 100644 llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 1bda113143e07..9cdf650f1d560 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -533,6 +533,19 @@ static Value *emitHlslClamp(CodeGenFunction &CGF, const CallExpr *E,
return Clamp;
}
+static CallInst *emitConvergentResourceIntrinsicCall(CodeGenFunction &CGF,
+ llvm::Type *RetTy,
+ Intrinsic::ID ID,
+ ArrayRef<Value *> Args) {
+ SmallVector<llvm::Type *> ArgTys;
+ ArgTys.reserve(Args.size());
+ for (Value *Arg : Args)
+ ArgTys.push_back(Arg->getType());
+ Function *IntrFn = Intrinsic::getOrInsertDeclaration(
+ &CGF.CGM.getModule(), ID, RetTy, ArgTys);
+ return CGF.EmitRuntimeCall(IntrFn, Args);
+}
+
static Value *emitGetDimensions(CodeGenFunction &CGF, const CallExpr *E,
unsigned IntrinsicID, unsigned NumRetComps,
bool HasLod) {
@@ -667,13 +680,13 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
llvm::Type *RetTy = ConvertType(E->getType());
if (E->getNumArgs() <= 4) {
- return Builder.CreateIntrinsic(
- RetTy, CGM.getHLSLRuntime().getSampleIntrinsic(), Args);
+ return emitConvergentResourceIntrinsicCall(
+ *this, RetTy, CGM.getHLSLRuntime().getSampleIntrinsic(), Args);
}
Args.push_back(emitHlslClamp(*this, E, 4));
- return Builder.CreateIntrinsic(
- RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
+ return emitConvergentResourceIntrinsicCall(
+ *this, RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
}
case Builtin::BI__builtin_hlsl_resource_sample_bias: {
Value *HandleOp = EmitScalarExpr(E->getArg(0));
@@ -692,13 +705,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT)));
llvm::Type *RetTy = ConvertType(E->getType());
- if (E->getNumArgs() <= 5)
- return Builder.CreateIntrinsic(
- RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
+ if (E->getNumArgs() <= 5) {
+ return emitConvergentResourceIntrinsicCall(
+ *this, RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
+ }
Args.push_back(emitHlslClamp(*this, E, 5));
- return Builder.CreateIntrinsic(
- RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
+ return emitConvergentResourceIntrinsicCall(
+ *this, RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
}
case Builtin::BI__builtin_hlsl_resource_sample_grad: {
Value *HandleOp = EmitScalarExpr(E->getArg(0));
@@ -827,8 +841,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *SamplerOp = EmitScalarExpr(E->getArg(1));
Value *CoordOp = EmitScalarExpr(E->getArg(2));
- return Builder.CreateIntrinsic(
- ConvertType(E->getType()),
+ return emitConvergentResourceIntrinsicCall(
+ *this, ConvertType(E->getType()),
CGM.getHLSLRuntime().getCalculateLodIntrinsic(),
{HandleOp, SamplerOp, CoordOp});
}
@@ -837,8 +851,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *SamplerOp = EmitScalarExpr(E->getArg(1));
Value *CoordOp = EmitScalarExpr(E->getArg(2));
- return Builder.CreateIntrinsic(
- ConvertType(E->getType()),
+ return emitConvergentResourceIntrinsicCall(
+ *this, ConvertType(E->getType()),
CGM.getHLSLRuntime().getCalculateLodUnclampedIntrinsic(),
{HandleOp, SamplerOp, CoordOp});
}
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index d948ef78b9584..b6debb2a0b74a 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -245,25 +245,25 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
def int_spv_resource_sample
: DefaultAttrsIntrinsic<
[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_sample_clamp
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_any_ty, llvm_float_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_samplebias
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_float_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_samplebias_clamp
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_float_ty, llvm_any_ty, llvm_float_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_samplegrad
: DefaultAttrsIntrinsic<[llvm_any_ty],
@@ -311,12 +311,12 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
def int_spv_resource_calculate_lod
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_calculate_lod_unclamped
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_spv_resource_gather
: DefaultAttrsIntrinsic<[llvm_any_ty],
diff --git a/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll b/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
new file mode 100644
index 0000000000000..6cb690411d8b0
--- /dev/null
+++ b/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
@@ -0,0 +1,29 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+; Verify SPIR-V resource intrinsics that use implicit derivatives are marked
+; convergent so LLVM does not sink them into divergent control flow.
+
+; CHECK: declare float @llvm.spv.resource.calculate.lod.f32.{{.*}} [[CONVERGENT:#[0-9]+]]
+declare float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+
+; CHECK: declare float @llvm.spv.resource.calculate.lod.unclamped.f32.{{.*}} [[CONVERGENT]]
+declare float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.sample.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>, float)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>, float)
+
+; Explicit-derivative sampling should remain non-convergent.
+; CHECK: declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.{{.*}} [[NON_CONVERGENT:#[0-9]+]]
+declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v3f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <2 x float>, <2 x float>, <2 x i32>)
+
+; CHECK: attributes [[CONVERGENT]] = { convergent nocallback nofree nosync nounwind willreturn memory(read) }
+; CHECK: attributes [[NON_CONVERGENT]] = { nocallback nofree nosync nounwind willreturn memory(read) }
diff --git a/llvm/unittests/IR/IntrinsicsTest.cpp b/llvm/unittests/IR/IntrinsicsTest.cpp
index d502bf591b7fa..c2a9fc5316fff 100644
--- a/llvm/unittests/IR/IntrinsicsTest.cpp
+++ b/llvm/unittests/IR/IntrinsicsTest.cpp
@@ -23,6 +23,7 @@
#include "llvm/IR/IntrinsicsPowerPC.h"
#include "llvm/IR/IntrinsicsRISCV.h"
#include "llvm/IR/IntrinsicsS390.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
#include "llvm/IR/IntrinsicsX86.h"
#include "llvm/IR/Module.h"
#include "gtest/gtest.h"
@@ -189,8 +190,31 @@ TEST_F(IntrinsicsTest, InstrProfInheritance) {
}
}
+TEST(IntrinsicAttributes, SPIRVResourceImplicitDerivativeIntrinsicsAreConvergent) {
+ using namespace Intrinsic;
+ LLVMContext Context;
+ static constexpr ID ConvergentResourceIntrinsics[] = {
+ spv_resource_sample,
+ spv_resource_sample_clamp,
+ spv_resource_samplebias,
+ spv_resource_samplebias_clamp,
+ spv_resource_calculate_lod,
+ spv_resource_calculate_lod_unclamped,
+ };
+ for (ID IntrID : ConvergentResourceIntrinsics) {
+ AttributeSet AS = getFnAttributes(Context, IntrID);
+ EXPECT_TRUE(AS.hasAttribute(Attribute::Convergent))
+ << "Intrinsic " << getName(IntrID) << " should be convergent";
+ }
+
+ AttributeSet SampleGradAttrs = getFnAttributes(Context, spv_resource_samplegrad);
+ EXPECT_FALSE(SampleGradAttrs.hasAttribute(Attribute::Convergent))
+ << "Intrinsic " << getName(spv_resource_samplegrad)
+ << " should not be convergent";
+}
+
// Check that getFnAttributes for intrinsics that do not have any function
-// attributes correcty returns an empty set.
+// attributes correctly returns an empty set.
TEST(IntrinsicAttributes, TestGetFnAttributesBug) {
using namespace Intrinsic;
LLVMContext Context;
>From 19e3b539dde0ff0c49b1bf5a3c0a51de1f35d464 Mon Sep 17 00:00:00 2001
From: Tim Corringham <tcorring at amd.com>
Date: Wed, 22 Jul 2026 14:54:59 +0100
Subject: [PATCH 2/3] Fix clang-format errors
Tidy up formatting errors - no functional change.
---
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 4 ++--
llvm/unittests/IR/IntrinsicsTest.cpp | 15 +++++++--------
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 9cdf650f1d560..2187f9de315f2 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -541,8 +541,8 @@ static CallInst *emitConvergentResourceIntrinsicCall(CodeGenFunction &CGF,
ArgTys.reserve(Args.size());
for (Value *Arg : Args)
ArgTys.push_back(Arg->getType());
- Function *IntrFn = Intrinsic::getOrInsertDeclaration(
- &CGF.CGM.getModule(), ID, RetTy, ArgTys);
+ Function *IntrFn = Intrinsic::getOrInsertDeclaration(&CGF.CGM.getModule(), ID,
+ RetTy, ArgTys);
return CGF.EmitRuntimeCall(IntrFn, Args);
}
diff --git a/llvm/unittests/IR/IntrinsicsTest.cpp b/llvm/unittests/IR/IntrinsicsTest.cpp
index c2a9fc5316fff..003b9fd61ceaf 100644
--- a/llvm/unittests/IR/IntrinsicsTest.cpp
+++ b/llvm/unittests/IR/IntrinsicsTest.cpp
@@ -190,16 +190,14 @@ TEST_F(IntrinsicsTest, InstrProfInheritance) {
}
}
-TEST(IntrinsicAttributes, SPIRVResourceImplicitDerivativeIntrinsicsAreConvergent) {
+TEST(IntrinsicAttributes,
+ SPIRVResourceImplicitDerivativeIntrinsicsAreConvergent) {
using namespace Intrinsic;
LLVMContext Context;
static constexpr ID ConvergentResourceIntrinsics[] = {
- spv_resource_sample,
- spv_resource_sample_clamp,
- spv_resource_samplebias,
- spv_resource_samplebias_clamp,
- spv_resource_calculate_lod,
- spv_resource_calculate_lod_unclamped,
+ spv_resource_sample, spv_resource_sample_clamp,
+ spv_resource_samplebias, spv_resource_samplebias_clamp,
+ spv_resource_calculate_lod, spv_resource_calculate_lod_unclamped,
};
for (ID IntrID : ConvergentResourceIntrinsics) {
AttributeSet AS = getFnAttributes(Context, IntrID);
@@ -207,7 +205,8 @@ TEST(IntrinsicAttributes, SPIRVResourceImplicitDerivativeIntrinsicsAreConvergent
<< "Intrinsic " << getName(IntrID) << " should be convergent";
}
- AttributeSet SampleGradAttrs = getFnAttributes(Context, spv_resource_samplegrad);
+ AttributeSet SampleGradAttrs =
+ getFnAttributes(Context, spv_resource_samplegrad);
EXPECT_FALSE(SampleGradAttrs.hasAttribute(Attribute::Convergent))
<< "Intrinsic " << getName(spv_resource_samplegrad)
<< " should not be convergent";
>From c092b71b89a30c4c4fb847ed3b60cbbea072edf6 Mon Sep 17 00:00:00 2001
From: Tim Corringham <tcorring at amd.com>
Date: Mon, 27 Jul 2026 19:10:07 +0100
Subject: [PATCH 3/3] Add IntrConvergent to DirectX resource intrinsics
Added IntrConvergent property to the DirextX intrinsics, and adjusted
the test case accordingly.
Renamed the helper function - a wider cleanup is debatable, and is
left as a separate exercise.
---
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 19 +++---
llvm/include/llvm/IR/IntrinsicsDirectX.td | 12 ++--
.../resource-intrinsic-attributes.ll | 59 +++++++++++++++++++
.../spirv-resource-intrinsic-attributes.ll | 29 ---------
4 files changed, 74 insertions(+), 45 deletions(-)
create mode 100644 llvm/test/Assembler/resource-intrinsic-attributes.ll
delete mode 100644 llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 2187f9de315f2..d6df16d65e550 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -533,10 +533,9 @@ static Value *emitHlslClamp(CodeGenFunction &CGF, const CallExpr *E,
return Clamp;
}
-static CallInst *emitConvergentResourceIntrinsicCall(CodeGenFunction &CGF,
- llvm::Type *RetTy,
- Intrinsic::ID ID,
- ArrayRef<Value *> Args) {
+static CallInst *emitResourceIntrinsic(CodeGenFunction &CGF, llvm::Type *RetTy,
+ Intrinsic::ID ID,
+ ArrayRef<Value *> Args) {
SmallVector<llvm::Type *> ArgTys;
ArgTys.reserve(Args.size());
for (Value *Arg : Args)
@@ -680,12 +679,12 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
llvm::Type *RetTy = ConvertType(E->getType());
if (E->getNumArgs() <= 4) {
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, RetTy, CGM.getHLSLRuntime().getSampleIntrinsic(), Args);
}
Args.push_back(emitHlslClamp(*this, E, 4));
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
}
case Builtin::BI__builtin_hlsl_resource_sample_bias: {
@@ -706,12 +705,12 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
llvm::Type *RetTy = ConvertType(E->getType());
if (E->getNumArgs() <= 5) {
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
}
Args.push_back(emitHlslClamp(*this, E, 5));
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
}
case Builtin::BI__builtin_hlsl_resource_sample_grad: {
@@ -841,7 +840,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *SamplerOp = EmitScalarExpr(E->getArg(1));
Value *CoordOp = EmitScalarExpr(E->getArg(2));
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, ConvertType(E->getType()),
CGM.getHLSLRuntime().getCalculateLodIntrinsic(),
{HandleOp, SamplerOp, CoordOp});
@@ -851,7 +850,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *SamplerOp = EmitScalarExpr(E->getArg(1));
Value *CoordOp = EmitScalarExpr(E->getArg(2));
- return emitConvergentResourceIntrinsicCall(
+ return emitResourceIntrinsic(
*this, ConvertType(E->getType()),
CGM.getHLSLRuntime().getCalculateLodUnclampedIntrinsic(),
{HandleOp, SamplerOp, CoordOp});
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 4dd86270f0d01..2b155701564f7 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -91,24 +91,24 @@ def int_dx_resource_getdimensions_levels_xy
def int_dx_resource_sample
: DefaultAttrsIntrinsic<
[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_sample_clamp
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_float_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_samplebias
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_float_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_samplebias_clamp
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty,
llvm_float_ty, llvm_any_ty, llvm_float_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_samplegrad
: DefaultAttrsIntrinsic<[llvm_any_ty],
@@ -137,12 +137,12 @@ def int_dx_resource_load_level
def int_dx_resource_calculate_lod
: DefaultAttrsIntrinsic<[llvm_float_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_calculate_lod_unclamped
: DefaultAttrsIntrinsic<[llvm_float_ty],
[llvm_any_ty, llvm_any_ty, llvm_any_ty],
- [IntrReadMem]>;
+ [IntrReadMem, IntrConvergent]>;
def int_dx_resource_samplecmp
: DefaultAttrsIntrinsic<[llvm_any_ty],
diff --git a/llvm/test/Assembler/resource-intrinsic-attributes.ll b/llvm/test/Assembler/resource-intrinsic-attributes.ll
new file mode 100644
index 0000000000000..6bb1fac70e4c5
--- /dev/null
+++ b/llvm/test/Assembler/resource-intrinsic-attributes.ll
@@ -0,0 +1,59 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+; Verify both DirectX and SPIR-V resource intrinsics that use implicit
+; derivatives are marked convergent so LLVM does not sink them into
+; divergent control flow.
+
+; CHECK: declare float @llvm.dx.resource.calculate.lod.{{.*}} [[CONVERGENT:#[0-9]+]]
+declare float @llvm.dx.resource.calculate.lod.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32(target("dx.Texture", <4 x float>, 0, 0, 0, 2), target("dx.Sampler", 0), <2 x float>)
+
+; CHECK: declare float @llvm.dx.resource.calculate.lod.unclamped.{{.*}} [[CONVERGENT]]
+declare float @llvm.dx.resource.calculate.lod.unclamped.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32(target("dx.Texture", <4 x float>, 0, 0, 0, 2), target("dx.Sampler", 0), <2 x float>)
+
+; CHECK: declare ptr @llvm.dx.resource.getpointer.p0.{{.*}} [[CONVERGENT_NO_MEM:#[0-9]+]]
+declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0), i32)
+
+; CHECK: declare ptr @llvm.dx.resource.getbasepointer.p0.{{.*}} [[CONVERGENT_NO_MEM]]
+declare ptr @llvm.dx.resource.getbasepointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0))
+
+; CHECK: declare <4 x float> @llvm.dx.resource.sample.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.dx.resource.sample.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2), target("dx.Sampler", 0), <2 x float>, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.dx.resource.sample.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.dx.resource.sample.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2), target("dx.Sampler", 0), <2 x float>, <2 x i32>, float)
+
+; CHECK: declare <4 x float> @llvm.dx.resource.samplebias.v4f32.{{.*}} [[CONVERGENT]]
+declare <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), target("dx.Sampler", 0), <2 x float>, float, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.dx.resource.samplebias.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <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), target("dx.Sampler", 0), <2 x float>, float, <2 x i32>, float)
+
+; Explicit-derivative sampling should remain non-convergent.
+; CHECK: declare <4 x float> @llvm.dx.resource.samplegrad.v4f32.{{.*}} [[NON_CONVERGENT:#[0-9]+]]
+declare <4 x float> @llvm.dx.resource.samplegrad.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2f32.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2), target("dx.Sampler", 0), <2 x float>, <2 x float>, <2 x float>, <2 x i32>)
+
+; CHECK: declare float @llvm.spv.resource.calculate.lod.f32.{{.*}} [[CONVERGENT]]
+declare float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+
+; CHECK: declare float @llvm.spv.resource.calculate.lod.unclamped.f32.{{.*}} [[CONVERGENT]]
+declare float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.sample.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>, float)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+
+; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.{{.*}} [[CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>, float)
+
+; Explicit-derivative sampling should remain non-convergent.
+; CHECK: declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.{{.*}} [[NON_CONVERGENT]]
+declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v3f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <2 x float>, <2 x float>, <2 x i32>)
+
+; CHECK: attributes [[CONVERGENT]] = { convergent nocallback nofree nosync nounwind willreturn memory(read) }
+; CHECK: attributes [[CONVERGENT_NO_MEM]] = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
+; CHECK: attributes [[NON_CONVERGENT]] = { nocallback nofree nosync nounwind willreturn memory(read) }
diff --git a/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll b/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
deleted file mode 100644
index 6cb690411d8b0..0000000000000
--- a/llvm/test/Assembler/spirv-resource-intrinsic-attributes.ll
+++ /dev/null
@@ -1,29 +0,0 @@
-; RUN: llvm-as < %s | llvm-dis | FileCheck %s
-
-; Verify SPIR-V resource intrinsics that use implicit derivatives are marked
-; convergent so LLVM does not sink them into divergent control flow.
-
-; CHECK: declare float @llvm.spv.resource.calculate.lod.f32.{{.*}} [[CONVERGENT:#[0-9]+]]
-declare float @llvm.spv.resource.calculate.lod.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
-
-; CHECK: declare float @llvm.spv.resource.calculate.lod.unclamped.f32.{{.*}} [[CONVERGENT]]
-declare float @llvm.spv.resource.calculate.lod.unclamped.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>)
-
-; CHECK: declare <4 x float> @llvm.spv.resource.sample.v4f32.{{.*}} [[CONVERGENT]]
-declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>)
-
-; CHECK: declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.{{.*}} [[CONVERGENT]]
-declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x i32>, float)
-
-; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.v4f32.{{.*}} [[CONVERGENT]]
-declare <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
-
-; CHECK: declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.{{.*}} [[CONVERGENT]]
-declare <4 x float> @llvm.spv.resource.samplebias.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>, float)
-
-; Explicit-derivative sampling should remain non-convergent.
-; CHECK: declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.{{.*}} [[NON_CONVERGENT:#[0-9]+]]
-declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v3f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <2 x float>, <2 x float>, <2 x i32>)
-
-; CHECK: attributes [[CONVERGENT]] = { convergent nocallback nofree nosync nounwind willreturn memory(read) }
-; CHECK: attributes [[NON_CONVERGENT]] = { nocallback nofree nosync nounwind willreturn memory(read) }
More information about the cfe-commits
mailing list