Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPIRV] Add support for cl_khr_extended_bit_ops #120571

Merged
merged 11 commits into from
Feb 11, 2025

Conversation

maarquitos14
Copy link
Contributor

This PR adds support for cl_khr_extended_bit_ops in SPIRV Backend. Note that cl_khr_extended_bit_ops only supports types in

char, charn, uchar, ucharn, short, shortn, ushort, ushortn, int, intn, uint, uintn, long, longn, ulong, and ulongn

where n is 2, 3, 4, 8, or 16.

Subsequent PRs will introduce support for non-standard bit width required by SPV_KHR_bit_instructions.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Dec 19, 2024

@llvm/pr-subscribers-backend-spir-v

Author: Marcos Maronas (maarquitos14)

Changes

This PR adds support for cl_khr_extended_bit_ops in SPIRV Backend. Note that cl_khr_extended_bit_ops only supports types in

char, charn, uchar, ucharn, short, shortn, ushort, ushortn, int, intn, uint, uintn, long, longn, ulong, and ulongn

where n is 2, 3, 4, 8, or 16.

Subsequent PRs will introduce support for non-standard bit width required by SPV_KHR_bit_instructions.


Patch is 63.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/120571.diff

4 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (+45)
  • (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.td (+11)
  • (added) llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_bit_instructions/cl_khr_extended_bit_ops.cl (+1191)
  • (modified) llvm/test/CodeGen/SPIRV/lit.local.cfg (+6)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 73dce230575d84..d64d9177b49219 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -983,6 +983,38 @@ static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
   return true;
 }
 
+/// Helper function for building extended bit operations.
+static bool buildExtendedBitOpsInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
+                             MachineIRBuilder &MIRBuilder,
+                             SPIRVGlobalRegistry *GR) {
+  const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
+  const auto *ST =
+      static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
+  if ((Opcode == SPIRV::OpBitFieldInsert ||
+       Opcode == SPIRV::OpBitFieldSExtract ||
+       Opcode == SPIRV::OpBitFieldUExtract ||
+       Opcode == SPIRV::OpBitReverse) &&
+      !ST->canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) {
+    std::string DiagMsg = std::string(Builtin->Name) +
+                          ": the builtin requires the following SPIR-V "
+                          "extension: SPV_KHR_bit_instructions";
+    report_fatal_error(DiagMsg.c_str(), false);
+  }
+
+  // Generate SPIRV instruction accordingly.
+  if (Call->isSpirvOp())
+    return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
+
+  // Generate the instruction.
+  auto MIB = MIRBuilder.buildInstr(Opcode)
+      .addDef(Call->ReturnRegister)
+      .addUse(GR->getSPIRVTypeID(Call->ReturnType));
+  for (unsigned i = 0; i < Call->Arguments.size(); ++i)
+    MIB.addUse(Call->Arguments[i]);
+
+  return true;
+}
+
 static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
   switch (dim) {
   case SPIRV::Dim::DIM_1D:
@@ -2041,6 +2073,17 @@ static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,
   }
 }
 
+static bool generateExtendedBitOpsInst(const SPIRV::IncomingCall *Call,
+                                MachineIRBuilder &MIRBuilder,
+                                SPIRVGlobalRegistry *GR) {
+  // Lookup the instruction opcode in the TableGen records.
+  const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
+  unsigned Opcode =
+      SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
+
+  return buildExtendedBitOpsInst(Call, Opcode, MIRBuilder, GR);
+}
+
 static bool buildNDRange(const SPIRV::IncomingCall *Call,
                          MachineIRBuilder &MIRBuilder,
                          SPIRVGlobalRegistry *GR) {
@@ -2628,6 +2671,8 @@ std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
     return generateKernelClockInst(Call.get(), MIRBuilder, GR);
   case SPIRV::CoopMatr:
     return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
+  case SPIRV::ExtendedBitOps:
+    return generateExtendedBitOpsInst(Call.get(), MIRBuilder, GR);
   }
   return false;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index e0dfc25723b0cc..9a146b331ffc10 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -64,6 +64,7 @@ def CastToPtr : BuiltinGroup;
 def Construct : BuiltinGroup;
 def CoopMatr : BuiltinGroup;
 def ICarryBorrow : BuiltinGroup;
+def ExtendedBitOps : BuiltinGroup;
 
 //===----------------------------------------------------------------------===//
 // Class defining a demangled builtin record. The information in the record
@@ -1441,6 +1442,16 @@ defm : DemangledNativeBuiltin<"__spirv_SatConvertSToU", OpenCL_std, Convert, 1,
 defm : DemangledNativeBuiltin<"__spirv_SatConvertUToS", OpenCL_std, Convert, 1, 1, OpSatConvertUToS>;
 defm : DemangledNativeBuiltin<"__spirv_ConvertUToPtr", OpenCL_std, Convert, 1, 1, OpConvertUToPtr>;
 
+// cl_khr_extended_bit_ops / SPV_KHR_bit_instructions
+defm : DemangledNativeBuiltin<"bitfield_insert", OpenCL_std, ExtendedBitOps, 4, 4, OpBitFieldInsert>;
+defm : DemangledNativeBuiltin<"__spirv_BitFieldInsert", OpenCL_std, ExtendedBitOps, 4, 4, OpBitFieldInsert>;
+defm : DemangledNativeBuiltin<"bitfield_extract_signed", OpenCL_std, ExtendedBitOps, 3, 3, OpBitFieldSExtract>;
+defm : DemangledNativeBuiltin<"__spirv_BitFieldSExtract", OpenCL_std, ExtendedBitOps, 3, 3, OpBitFieldSExtract>;
+defm : DemangledNativeBuiltin<"bitfield_extract_unsigned", OpenCL_std, ExtendedBitOps, 3, 3, OpBitFieldUExtract>;
+defm : DemangledNativeBuiltin<"__spirv_BitFieldUExtract", OpenCL_std, ExtendedBitOps, 3, 3, OpBitFieldUExtract>;
+defm : DemangledNativeBuiltin<"bit_reverse", OpenCL_std, ExtendedBitOps, 1, 1, OpBitReverse>;
+defm : DemangledNativeBuiltin<"__spirv_BitReverse", OpenCL_std, ExtendedBitOps, 1, 1, OpBitReverse>;
+
 // cl_intel_bfloat16_conversions / SPV_INTEL_bfloat16_conversion
 // Multiclass used to define at the same time both a demangled builtin records
 // and a corresponding convert builtin records.
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_bit_instructions/cl_khr_extended_bit_ops.cl b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_bit_instructions/cl_khr_extended_bit_ops.cl
new file mode 100644
index 00000000000000..4f7fd131f6f9a9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_bit_instructions/cl_khr_extended_bit_ops.cl
@@ -0,0 +1,1191 @@
+// RUN: %clang_cc1 -triple spir-unknown-unknown -O1 -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header -emit-llvm-bc %s -o %t.bc
+// RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %t.bc --spirv-ext=+SPV_KHR_bit_instructions -o - | FileCheck %s --check-prefix=CHECK-EXTENSION
+// RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %t.bc -o %t.spvt 2>&1 | FileCheck %s --check-prefix=CHECK-NO-EXTENSION
+
+// CHECK-SPIRV: Capability BitInstructions
+// CHECK-SPIRV: Extension "SPV_KHR_bit_instructions"
+// CHECK-NO-EXTENSION: LLVM ERROR: bitfield_insert: the builtin requires the following SPIR-V extension: SPV_KHR_bit_instructions
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_long:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_long:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_long]] %[[#insertinsert_long]]
+kernel void testInsert_long(long b, long i, global long *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ulong:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ulong:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ulong]] %[[#insertinsert_ulong]]
+kernel void testInsert_ulong(ulong b, ulong i, global ulong *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_int:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_int:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_int]] %[[#insertinsert_int]]
+kernel void testInsert_int(int b, int i, global int *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uint:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uint:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uint]] %[[#insertinsert_uint]]
+kernel void testInsert_uint(uint b, uint i, global uint *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_short:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_short:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_short]] %[[#insertinsert_short]]
+kernel void testInsert_short(short b, short i, global short *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ushort:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ushort:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ushort]] %[[#insertinsert_ushort]]
+kernel void testInsert_ushort(ushort b, ushort i, global ushort *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_long2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_long2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_long2]] %[[#insertinsert_long2]]
+kernel void testInsert_long2(long2 b, long2 i, global long2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ulong2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ulong2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ulong2]] %[[#insertinsert_ulong2]]
+kernel void testInsert_ulong2(ulong2 b, ulong2 i, global ulong2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_int2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_int2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_int2]] %[[#insertinsert_int2]]
+kernel void testInsert_int2(int2 b, int2 i, global int2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uint2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uint2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uint2]] %[[#insertinsert_uint2]]
+kernel void testInsert_uint2(uint2 b, uint2 i, global uint2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_short2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_short2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_short2]] %[[#insertinsert_short2]]
+kernel void testInsert_short2(short2 b, short2 i, global short2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ushort2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ushort2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ushort2]] %[[#insertinsert_ushort2]]
+kernel void testInsert_ushort2(ushort2 b, ushort2 i, global ushort2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_char2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_char2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_char2]] %[[#insertinsert_char2]]
+kernel void testInsert_char2(char2 b, char2 i, global char2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uchar2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uchar2:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uchar2]] %[[#insertinsert_uchar2]]
+kernel void testInsert_uchar2(uchar2 b, uchar2 i, global uchar2 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_long3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_long3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_long3]] %[[#insertinsert_long3]]
+kernel void testInsert_long3(long3 b, long3 i, global long3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ulong3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ulong3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ulong3]] %[[#insertinsert_ulong3]]
+kernel void testInsert_ulong3(ulong3 b, ulong3 i, global ulong3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_int3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_int3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_int3]] %[[#insertinsert_int3]]
+kernel void testInsert_int3(int3 b, int3 i, global int3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uint3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uint3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uint3]] %[[#insertinsert_uint3]]
+kernel void testInsert_uint3(uint3 b, uint3 i, global uint3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_short3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_short3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_short3]] %[[#insertinsert_short3]]
+kernel void testInsert_short3(short3 b, short3 i, global short3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ushort3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ushort3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ushort3]] %[[#insertinsert_ushort3]]
+kernel void testInsert_ushort3(ushort3 b, ushort3 i, global ushort3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_char3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_char3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_char3]] %[[#insertinsert_char3]]
+kernel void testInsert_char3(char3 b, char3 i, global char3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uchar3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uchar3:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uchar3]] %[[#insertinsert_uchar3]]
+kernel void testInsert_uchar3(uchar3 b, uchar3 i, global uchar3 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_long4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_long4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_long4]] %[[#insertinsert_long4]]
+kernel void testInsert_long4(long4 b, long4 i, global long4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ulong4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ulong4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ulong4]] %[[#insertinsert_ulong4]]
+kernel void testInsert_ulong4(ulong4 b, ulong4 i, global ulong4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_int4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_int4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_int4]] %[[#insertinsert_int4]]
+kernel void testInsert_int4(int4 b, int4 i, global int4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uint4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uint4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uint4]] %[[#insertinsert_uint4]]
+kernel void testInsert_uint4(uint4 b, uint4 i, global uint4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_short4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_short4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_short4]] %[[#insertinsert_short4]]
+kernel void testInsert_short4(short4 b, short4 i, global short4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ushort4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ushort4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ushort4]] %[[#insertinsert_ushort4]]
+kernel void testInsert_ushort4(ushort4 b, ushort4 i, global ushort4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_char4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_char4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_char4]] %[[#insertinsert_char4]]
+kernel void testInsert_char4(char4 b, char4 i, global char4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uchar4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uchar4:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uchar4]] %[[#insertinsert_uchar4]]
+kernel void testInsert_uchar4(uchar4 b, uchar4 i, global uchar4 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_long8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_long8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_long8]] %[[#insertinsert_long8]]
+kernel void testInsert_long8(long8 b, long8 i, global long8 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_ulong8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_ulong8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_ulong8]] %[[#insertinsert_ulong8]]
+kernel void testInsert_ulong8(ulong8 b, ulong8 i, global ulong8 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_int8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_int8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_int8]] %[[#insertinsert_int8]]
+kernel void testInsert_int8(int8 b, int8 i, global int8 *res) {
+  *res = bitfield_insert(b, i, 4, 2);
+}
+
+// CHECK-EXTENSION: %[[#]] = OpFunction %[[#]] None %[[#]]
+// CHECK-EXTENSION: %[[#insertbase_uint8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#insertinsert_uint8:]] = OpFunctionParameter %[[#]]
+// CHECK-EXTENSION: %[[#]] = OpBitFieldInsert %[[#]] %[[#insertbase_uint8]] %[[#insertinsert_uint8]]
+kernel void testInsert_uint8(uint8 b, uint8 i, global uint8 *res) {
+  *res = ...
[truncated]

Copy link

github-actions bot commented Dec 19, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

// RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %t.bc --spirv-ext=+SPV_KHR_bit_instructions -o - | FileCheck %s --check-prefix=CHECK-EXTENSION
// RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %t.bc -o %t.spvt 2>&1 | FileCheck %s --check-prefix=CHECK-NO-EXTENSION

// CHECK-SPIRV: Capability BitInstructions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this and the next should be CHECK-EXTENSION?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Thanks! Done in c3fe999.

@@ -0,0 +1,1191 @@
// RUN: %clang_cc1 -triple spir-unknown-unknown -O1 -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header -emit-llvm-bc %s -o %t.bc
// RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %t.bc --spirv-ext=+SPV_KHR_bit_instructions -o - | FileCheck %s --check-prefix=CHECK-EXTENSION
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to add smth like ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} for this test case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in c3fe999.

@maarquitos14
Copy link
Contributor Author

@michalpaszkowski @MrSidims ping :)

@@ -1441,6 +1442,16 @@ defm : DemangledNativeBuiltin<"__spirv_SatConvertSToU", OpenCL_std, Convert, 1,
defm : DemangledNativeBuiltin<"__spirv_SatConvertUToS", OpenCL_std, Convert, 1, 1, OpSatConvertUToS>;
defm : DemangledNativeBuiltin<"__spirv_ConvertUToPtr", OpenCL_std, Convert, 1, 1, OpConvertUToPtr>;

// cl_khr_extended_bit_ops / SPV_KHR_bit_instructions
defm : DemangledNativeBuiltin<"bitfield_insert", OpenCL_std, ExtendedBitOps, 4, 4, OpBitFieldInsert>;
defm : DemangledNativeBuiltin<"__spirv_BitFieldInsert", OpenCL_std, ExtendedBitOps, 4, 4, OpBitFieldInsert>;
Copy link
Contributor

@MrSidims MrSidims Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are SPIR-V friendly builtins tested anywhere? This PR doesn't add them and on my machine grep doesn't show any tests:

LLVM/llvm-project/llvm$ grep -rn BitFieldInsert
lib/CodeGen/GlobalISel/LegalizerHelper.cpp:3665:static Register buildBitFieldInsert(MachineIRBuilder &B,
lib/CodeGen/GlobalISel/LegalizerHelper.cpp:3739:    Register InsertedElt = buildBitFieldInsert(MIRBuilder, ExtractedElt,
lib/Target/SPIRV/SPIRVModuleAnalysis.cpp:1243:  case SPIRV::OpBitFieldInsert:
lib/Target/SPIRV/SPIRVInstrInfo.td:547:def OpBitFieldInsert: Op<201, (outs ID:$res),
lib/Target/SPIRV/SPIRVInstrInfo.td:549:                  "$res = OpBitFieldInsert $ty $base $insert $offset $count">;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They have been included in cl_khr_extended_bit_ops.ll.

std::string DiagMsg = std::string(Builtin->Name) +
": the builtin requires the following SPIR-V "
"extension: SPV_KHR_bit_instructions";
report_fatal_error(DiagMsg.c_str(), false);
Copy link
Contributor

@MrSidims MrSidims Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since __spirv_BitFieldInsert and other _spirv spellings for bit instructions are untested in the backend it's hard for me to understand whether this code would work correctly. My expectations are:

  1. if OpenCL builtin calls present in LLVM IR input to the backend and SPV_KHR_bit_instructions is not enabled, then the error must be emitted;
  2. if OpenCL builtin calls present in LLVM IR input to the backend and SPV_KHR_bit_instructions is enabled, then the error must not be emitted and BitInstructions capability must be generated;
  3. if SPIR-V friendly calls are in the module (aka no OpenCL calls), then (unless there is a mechanism of distinguishing between Kernel and Shader capabilities in the backend of which I'm not yet aware of) the error must not be emitted, BitInstructions must not be generated, Shader capability must present in the module.

Will this code ensure the above behavior (it feels like scenario 3 won't work as I described) or may be I'm missing something, and my expected behavior is incorrect?

Copy link
Contributor

@MrSidims MrSidims Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And for scenario 2 I don't expect Shader capability to appear (if no other shader specific instructions are used).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added new tests to check these expectations. All seems to be working according to your expectations.

Copy link
Contributor

@MrSidims MrSidims left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@@ -2743,9 +2743,90 @@ entry:

declare spir_func <16 x i8> @_Z11bit_reverseDv16_h(<16 x i8> noundef)

; Test SPIRV-friendly builtins.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is worth to out separately from cl_khr_extended_bit_ops.ll

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in ab0ef11

@maarquitos14
Copy link
Contributor Author

@michalpaszkowski ping :)

@VyacheslavLevytskyy VyacheslavLevytskyy merged commit a09beae into llvm:main Feb 11, 2025
8 checks passed
Copy link

@maarquitos14 Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
This PR adds support for `cl_khr_extended_bit_ops` in SPIRV Backend.
Note that `cl_khr_extended_bit_ops` only supports types in
```
char, charn, uchar, ucharn, short, shortn, ushort, ushortn, int, intn, uint, uintn, long, longn, ulong, and ulongn
```
where `n is 2, 3, 4, 8, or 16`.

Subsequent PRs will introduce support for non-standard bit width
required by `SPV_KHR_bit_instructions`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants