https://github.com/jhuber6 created 
https://github.com/llvm/llvm-project/pull/174655

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.


>From 3351f9c7b1ee4d1287e7d1aabb56b376bfaf250b Mon Sep 17 00:00:00 2001
From: Joseph Huber <[email protected]>
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.
---
 .../clang/Basic/BuiltinsSPIRVCommon.td        |  2 ++
 clang/lib/CodeGen/TargetBuiltins/SPIR.cpp     |  9 +++++++
 clang/lib/Sema/SemaSPIRV.cpp                  | 26 +++++++++++++++++++
 .../CodeGenSPIRV/Builtins/ids_and_ranges.c    |  8 ++++++
 .../test/SemaSPIRV/BuiltIns/ids_and_ranges.c  |  6 +++++
 5 files changed, 51 insertions(+)

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}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to