https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/174655
>From 06a34c860273e0db966e2e8b31203f3cb179702e Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Tue, 6 Jan 2026 15:36:15 -0600 Subject: [PATCH 1/4] [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}} +} >From fc35ecc4c9982782ff98b1b524714580426ff5e4 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Wed, 7 Jan 2026 08:50:27 -0600 Subject: [PATCH 2/4] Sema test --- clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c b/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c index 43b2de4e31485..89c582c87c2b5 100644 --- a/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c +++ b/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c @@ -76,8 +76,10 @@ void test_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) { +void test_subgroup_shuffle(int i, float f, int *p, int [[clang::ext_vector_type(1)]] v) { __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(p, f); // expected-error{{1st argument must be a scalar type}} __builtin_spirv_subgroup_shuffle(i, f); // expected-error{{2nd argument must be a scalar integer}} + __builtin_spirv_subgroup_shuffle(i, v); // expected-error{{2nd argument must be a scalar integer}} } >From 14ed1ee85cd84264466310ec9b79e2814a412031 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Wed, 7 Jan 2026 08:51:10 -0600 Subject: [PATCH 3/4] nit --- clang/lib/CodeGen/TargetBuiltins/SPIR.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp index 1ea23e8c2195f..ff7b5fefedd19 100644 --- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp @@ -115,8 +115,7 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID, 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"); + Intrinsic::spv_wave_readlane, {X, Y}, nullptr, "spv.shuffle"); } case SPIRV::BI__builtin_spirv_num_workgroups: return Builder.CreateIntrinsic( >From 391b43b153049666b5577cdf037b36d3fe344803 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Wed, 7 Jan 2026 20:50:57 -0600 Subject: [PATCH 4/4] Update sema checking for int argument --- clang/lib/Sema/SemaSPIRV.cpp | 26 +++++++++++++------ .../CodeGenSPIRV/Builtins/ids_and_ranges.c | 8 ------ clang/test/CodeGenSPIRV/Builtins/subgroup.c | 11 ++++++++ .../test/SemaSPIRV/BuiltIns/ids_and_ranges.c | 8 ------ .../test/SemaSPIRV/BuiltIns/subgroup-errors.c | 15 +++++++++++ 5 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 clang/test/CodeGenSPIRV/Builtins/subgroup.c create mode 100644 clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp index 5f3dd71f28c67..28bc7d1447cb7 100644 --- a/clang/lib/Sema/SemaSPIRV.cpp +++ b/clang/lib/Sema/SemaSPIRV.cpp @@ -384,7 +384,12 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const TargetInfo &TI, if (SemaRef.checkArgCount(TheCall, 2)) return true; - ExprResult A = TheCall->getArg(0); + 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) @@ -393,14 +398,19 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const TargetInfo &TI, 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; + 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); diff --git a/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c b/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c index 7142dbbc2be73..bff850b3622b9 100644 --- a/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c +++ b/clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c @@ -104,11 +104,3 @@ [[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/CodeGenSPIRV/Builtins/subgroup.c b/clang/test/CodeGenSPIRV/Builtins/subgroup.c new file mode 100644 index 0000000000000..89ed48f82d37f --- /dev/null +++ b/clang/test/CodeGenSPIRV/Builtins/subgroup.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -O1 -triple spirv64 -fsycl-is-device -x c++ %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_cc1 -O1 -triple spirv64 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_cc1 -O1 -triple spirv32 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK + +// 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 89c582c87c2b5..0d98a552bb1b9 100644 --- a/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c +++ b/clang/test/SemaSPIRV/BuiltIns/ids_and_ranges.c @@ -75,11 +75,3 @@ 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, int [[clang::ext_vector_type(1)]] v) { - __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(p, f); // expected-error{{1st argument must be a scalar type}} - __builtin_spirv_subgroup_shuffle(i, f); // expected-error{{2nd argument must be a scalar integer}} - __builtin_spirv_subgroup_shuffle(i, v); // expected-error{{2nd argument must be a scalar integer}} -} diff --git a/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c b/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c new file mode 100644 index 0000000000000..550a3fda216f8 --- /dev/null +++ b/clang/test/SemaSPIRV/BuiltIns/subgroup-errors.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -O1 -Wno-unused-value -triple spirv64 -fsycl-is-device -verify %s -o - +// RUN: %clang_cc1 -O1 -Wno-unused-value -triple spirv64 -verify %s -cl-std=CL3.0 -x cl -o - +// RUN: %clang_cc1 -O1 -Wno-unused-value -triple spirv32 -verify %s -cl-std=CL3.0 -x cl -o - + +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}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
