[clang] [SPIR-V] Add clang builtin for subgroup shuffles (PR #174655)
Joseph Huber via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 8 10:03:00 PST 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/174655
>From f590d17d6a1540fc3fdc03fc3e6fc6dfa18c964f Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 6 Jan 2026 15:36:15 -0600
Subject: [PATCH] [SPIR-V] Add clang builtin for subgroup shuffles
Summary:
This is an attempt to begin filling out some missing pieces to allow
more generic compute code to use SPIR-V flavored builtins. This should
provide the basic shuffle operation. The next most important one is the
ballot, but I don't think we have an IR intrinsic for that yet.
I don't know SPIR-V very well so let me know if this is the proper
function with the proper semantic checks.
Sema test
nit
Update sema checking for int argument
---
.../clang/Basic/BuiltinsSPIRVCommon.td | 1 +
clang/lib/CodeGen/TargetBuiltins/SPIR.cpp | 8 +++++
clang/lib/Sema/SemaSPIRV.cpp | 36 +++++++++++++++++++
clang/test/CodeGenSPIRV/Builtins/subgroup.c | 10 +++++-
.../test/SemaSPIRV/BuiltIns/subgroup-errors.c | 12 +++++++
5 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRVCommon.td b/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
index 495851ed1727a..7765cf0e5dbc9 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
@@ -23,3 +23,4 @@ def length : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def smoothstep : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
def subgroup_ballot : SPIRVBuiltin<"_ExtVector<4, uint32_t>(bool)", [NoThrow, Const]>;
+def subgroup_shuffle : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 9b0ca3eb0035a..ff7b5fefedd19 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -109,6 +109,14 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
Call->addRetAttr(llvm::Attribute::AttrKind::NoUndef);
return Call;
}
+ case SPIRV::BI__builtin_spirv_subgroup_shuffle: {
+ Value *X = EmitScalarExpr(E->getArg(0));
+ Value *Y = EmitScalarExpr(E->getArg(1));
+ assert(E->getArg(1)->getType()->hasIntegerRepresentation());
+ return Builder.CreateIntrinsic(
+ /*ReturnType=*/getTypes().ConvertType(E->getArg(0)->getType()),
+ Intrinsic::spv_wave_readlane, {X, Y}, nullptr, "spv.shuffle");
+ }
case SPIRV::BI__builtin_spirv_num_workgroups:
return Builder.CreateIntrinsic(
/*ReturnType=*/getTypes().ConvertType(E->getType()),
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index fa30e149c209a..8c2af5053bde2 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -380,6 +380,42 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const TargetInfo &TI,
TheCall->setType(RetTy);
break;
}
+ case SPIRV::BI__builtin_spirv_subgroup_shuffle: {
+ if (SemaRef.checkArgCount(TheCall, 2))
+ return true;
+
+ ExprResult A =
+ SemaRef.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+ if (A.isInvalid())
+ return true;
+ TheCall->setArg(0, A.get());
+
+ QualType ArgTyA = A.get()->getType();
+ if (!ArgTyA->isIntegerType() && !ArgTyA->isFloatingType()) {
+ SemaRef.Diag(A.get()->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << /* ordinal */ 1 << /* scalar */ 1 << /* no int */ 0
+ << /* no fp */ 0 << ArgTyA;
+ return true;
+ }
+
+ ExprResult B =
+ SemaRef.DefaultFunctionArrayLvalueConversion(TheCall->getArg(1));
+ if (B.isInvalid())
+ return true;
+
+ QualType Uint32Ty =
+ SemaRef.getASTContext().getIntTypeForBitwidth(32,
+ /*Signed=*/false);
+ ExprResult ResB = SemaRef.PerformImplicitConversion(
+ B.get(), Uint32Ty, AssignmentAction::Passing);
+ if (ResB.isInvalid())
+ return true;
+ TheCall->setArg(1, ResB.get());
+
+ QualType RetTy = ArgTyA;
+ TheCall->setType(RetTy);
+ break;
+ }
}
return false;
}
diff --git a/clang/test/CodeGenSPIRV/Builtins/subgroup.c b/clang/test/CodeGenSPIRV/Builtins/subgroup.c
index 2ae2013c3c23e..05f272c32786a 100644
--- a/clang/test/CodeGenSPIRV/Builtins/subgroup.c
+++ b/clang/test/CodeGenSPIRV/Builtins/subgroup.c
@@ -10,6 +10,14 @@ typedef unsigned __attribute__((ext_vector_type(4))) int4;
// CHECK: @{{.*}}test_subgroup_shuffle{{.*}}(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call <4 x i32> @llvm.spv.wave.ballot(i1 %i)
-[[clang::sycl_external]] int4 test_subgroup_shuffle(_Bool i) {
+[[clang::sycl_external]] int4 test_subgroup_ballot(_Bool i) {
return __builtin_spirv_subgroup_ballot(i);
}
+
+// CHECK: @{{.*}}test_subgroup_shuffle{{.*}}(
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: tail call float @llvm.spv.wave.readlane.f32(float %f, i32 %i)
+//
+[[clang::sycl_external]] float test_subgroup_shuffle(float f, int i) {
+ return __builtin_spirv_subgroup_shuffle(f, i);
+}
diff --git a/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c b/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c
index 5ef9f499efd31..ea41121b58c59 100644
--- a/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c
+++ b/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c
@@ -11,3 +11,15 @@ void ballot(_Bool c) {
x = __builtin_spirv_subgroup_ballot(x); // expected-error{{parameter of incompatible type}}
int y = __builtin_spirv_subgroup_ballot(c); // expected-error{{with an expression of incompatible type}}
}
+
+void shuffle() {
+ int x = 0;
+ long long l = 0;
+ float f = 0;
+ int [[clang::ext_vector_type(1)]] v;
+ (void)__builtin_spirv_subgroup_shuffle(x, x);
+ (void)__builtin_spirv_subgroup_shuffle(f, f);
+ (void)__builtin_spirv_subgroup_shuffle(x, x, x); // expected-error{{too many arguments to function call, expected 2, have 3}}
+ (void)__builtin_spirv_subgroup_shuffle(v, f); // expected-error{{1st argument must be a scalar type}}
+ (void)__builtin_spirv_subgroup_shuffle(f, v); // expected-error{{to parameter of incompatible type}}
+}
More information about the cfe-commits
mailing list