[clang] [SPIR-V] Add clang builtin for subgroup shuffles (PR #174655)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 6 13:42:57 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Joseph Huber (jhuber6)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/174655.diff
5 Files Affected:
- (modified) clang/include/clang/Basic/BuiltinsSPIRVCommon.td (+2)
- (modified) clang/lib/CodeGen/TargetBuiltins/SPIR.cpp (+9)
- (modified) clang/lib/Sema/SemaSPIRV.cpp (+26)
- (modified) clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c (+8)
- (modified) clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c (+6)
``````````diff
diff --git a/clang/include/clang/Basic/BuiltinsSPIRVCommon.td b/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
index d2ef6f99a0502..e31758b889d3d 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRVCommon.td
@@ -21,3 +21,5 @@ def subgroup_local_invocation_id : SPIRVBuiltin<"uint32_t()", [NoThrow, Const]>;
def distance : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def length : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
def smoothstep : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
+
+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..1ea23e8c2195f 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -109,6 +109,15 @@ 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, ArrayRef<Value *>{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..5f3dd71f28c67 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -380,6 +380,32 @@ 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 = TheCall->getArg(0);
+ 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 = TheCall->getArg(1);
+ QualType ArgTyB = B.get()->getType();
+ if (!ArgTyB->isIntegerType()) {
+ SemaRef.Diag(B.get()->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << /* ordinal */ 2 << /* scalar */ 1 << /* int */ 1 << /* no fp */ 0
+ << ArgTyB;
+ return true;
+ }
+
+ QualType RetTy = ArgTyA;
+ TheCall->setType(RetTy);
+ break;
+ }
}
return false;
}
diff --git a/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c b/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c
index bff850b3622b9..7142dbbc2be73 100644
--- a/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c
+++ b/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c
@@ -104,3 +104,11 @@
[[clang::sycl_external]] unsigned int test_subgroup_local_invocation_id() {
return __builtin_spirv_subgroup_local_invocation_id();
}
+
+// 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/ids_and_ranges.c b/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c
index 0d98a552bb1b9..43b2de4e31485 100644
--- a/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c
+++ b/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c
@@ -75,3 +75,9 @@ void test_subgroup_local_invocation_id() {
__builtin_spirv_subgroup_local_invocation_id();
__builtin_spirv_subgroup_local_invocation_id(0); // expected-error{{too many arguments to function call, expected 0, have 1}}
}
+
+void test_subgroup_shuffle(int i, float f, int *p) {
+ __builtin_spirv_subgroup_shuffle(f, i);
+ __builtin_spirv_subgroup_shuffle(p, i); // expected-error{{1st argument must be a scalar type}}
+ __builtin_spirv_subgroup_shuffle(i, f); // expected-error{{2nd argument must be a scalar integer}}
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/174655
More information about the cfe-commits
mailing list