[clang] [llvm] [HLSL] Add HLSL unpack intrinsics (PR #228518)

Alexander Johnston via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 2 09:49:00 PDT 2026


https://github.com/Alexander-Johnston created https://github.com/llvm/llvm-project/pull/228518

These HLSL intrinsics unpack a packed type (int8_t4_packed or uint8_t4_packed) into an appropriate 4 element vector, as specified by the specific unpack intrinsic used.

>From b6b4e2e4bbe3101b219bc0aac760b03738ce5219 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Fri, 2 Oct 2026 17:29:10 +0100
Subject: [PATCH 1/3] [DirectX] Add HLSL unpack intrinsics to DirectX backend

---
 llvm/include/llvm/IR/IntrinsicsDirectX.td |  5 ++++
 llvm/lib/Target/DirectX/DXIL.td           | 35 +++++++++++++++++++++++
 llvm/test/CodeGen/DirectX/unpack_s8s16.ll | 18 ++++++++++++
 llvm/test/CodeGen/DirectX/unpack_s8s32.ll | 18 ++++++++++++
 llvm/test/CodeGen/DirectX/unpack_u8u16.ll | 18 ++++++++++++
 llvm/test/CodeGen/DirectX/unpack_u8u32.ll | 18 ++++++++++++
 6 files changed, 112 insertions(+)
 create mode 100644 llvm/test/CodeGen/DirectX/unpack_s8s16.ll
 create mode 100644 llvm/test/CodeGen/DirectX/unpack_s8s32.ll
 create mode 100644 llvm/test/CodeGen/DirectX/unpack_u8u16.ll
 create mode 100644 llvm/test/CodeGen/DirectX/unpack_u8u32.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index e087216b822da3..740e9b22a210ea 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -359,4 +359,9 @@ def int_dx_store_output
           [llvm_i32_ty /*SigElementId*/, llvm_i32_ty /*RowIndex*/,
            llvm_i8_ty /*ColIndex*/, llvm_any_ty /*Value*/],
           [IntrConvergent]>;
+
+def int_dx_unpack_u8u16 : DefaultAttrsIntrinsic<[llvm_i16_ty, llvm_i16_ty, llvm_i16_ty, llvm_i16_ty], [llvm_i32_ty], [IntrNoMem]>;
+def int_dx_unpack_u8u32 : DefaultAttrsIntrinsic<[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>;
+def int_dx_unpack_s8s16 : DefaultAttrsIntrinsic<[llvm_i16_ty, llvm_i16_ty, llvm_i16_ty, llvm_i16_ty], [llvm_i32_ty], [IntrNoMem]>;
+def int_dx_unpack_s8s32 : DefaultAttrsIntrinsic<[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>;
 }
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 5e63e94a1c069a..ecc126ff7eff1f 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -329,6 +329,9 @@ defvar QuadOpKind_ReadAcrossX = 0;
 defvar QuadOpKind_ReadAcrossY = 1;
 defvar QuadOpKind_ReadAcrossDiagonal = 2;
 
+defvar Unpack_Unsigned = 0;
+defvar Unpack_Signed = 1;
+
 // Intrinsic arg selection
 class IntrinArgSelectType;
 def IntrinArgSelect_Index : IntrinArgSelectType;
@@ -1560,3 +1563,35 @@ def CreateHandleFromHeap : DXILOp<218, createHandleFromHeap> {
   let stages = [Stages<DXIL1_6, [all_stages]>];
   let attributes = [Attributes<DXIL1_0, [ReadNone]>];
 }
+
+def Unpack4x8 : DXILOp<219, unpack4x8> {
+  let Doc = "unpack 4 integer values from a single 32 bit value";
+  let intrinsics = [
+    IntrinSelect<int_dx_unpack_u8u16,
+                 [
+                   IntrinArgI8<Unpack_Unsigned>,
+                   IntrinArgIndex<0>
+                 ]>,
+    IntrinSelect<int_dx_unpack_u8u32,
+                 [
+                   IntrinArgI8<Unpack_Unsigned>,
+                   IntrinArgIndex<0>
+                 ]>,
+    IntrinSelect<int_dx_unpack_s8s16,
+                 [
+                   IntrinArgI8<Unpack_Signed>,
+                   IntrinArgIndex<0>
+                 ]>,
+    IntrinSelect<int_dx_unpack_s8s32,
+                 [
+                   IntrinArgI8<Unpack_Signed>,
+                   IntrinArgIndex<0>
+                 ]>,
+  ];
+
+  let arguments = [Int8Ty, Int32Ty];
+  let result = OverloadTy;
+  let overloads = [Overloads<DXIL1_6, [Int16Ty, Int32Ty]>];
+  let stages = [Stages<DXIL1_6, [all_stages]>];
+  let attributes = [Attributes<DXIL1_6, [ReadNone]>];
+}
diff --git a/llvm/test/CodeGen/DirectX/unpack_s8s16.ll b/llvm/test/CodeGen/DirectX/unpack_s8s16.ll
new file mode 100644
index 00000000000000..d65db0c83faf50
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/unpack_s8s16.ll
@@ -0,0 +1,18 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef <4 x i16> @test_unpack_s8s16(i32 noundef %a) {
+  ; CHECK: %{{.*}} = call { i16, i16, i16, i16 } @dx.op.unpack4x8.i16(i32 219, i8 1, i32 {{.*}})
+  %unpacked = call { i16, i16, i16, i16 } @llvm.dx.unpack.s8s16(i32 %a)
+  %1 = extractvalue { i16, i16, i16, i16 } %unpacked, 0
+  %2 = extractvalue { i16, i16, i16, i16 } %unpacked, 1
+  %3 = extractvalue { i16, i16, i16, i16 } %unpacked, 2
+  %4 = extractvalue { i16, i16, i16, i16 } %unpacked, 3
+  %5 = insertelement <4 x i16> poison, i16 %1, i32 0
+  %6 = insertelement <4 x i16> %5, i16 %2, i32 1
+  %7 = insertelement <4 x i16> %6, i16 %3, i32 2
+  %8 = insertelement <4 x i16> %7, i16 %4, i32 3
+  ret <4 x i16> %8
+}
+
+; CHECK-DAG: declare { i16, i16, i16, i16 } @dx.op.unpack4x8.i16(i32, i8, i32)
+declare { i16, i16, i16, i16 } @llvm.dx.unpack.s8s16(i32)
diff --git a/llvm/test/CodeGen/DirectX/unpack_s8s32.ll b/llvm/test/CodeGen/DirectX/unpack_s8s32.ll
new file mode 100644
index 00000000000000..22d7736fa2a1d2
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/unpack_s8s32.ll
@@ -0,0 +1,18 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef <4 x i32> @test_unpack_s8s32(i32 noundef %a) {
+  ; CHECK: %{{.*}} = call { i32, i32, i32, i32 } @dx.op.unpack4x8.i32(i32 219, i8 1, i32 {{.*}})
+  %unpacked = call { i32, i32, i32, i32 } @llvm.dx.unpack.s8s32(i32 %a)
+  %1 = extractvalue { i32, i32, i32, i32 } %unpacked, 0
+  %2 = extractvalue { i32, i32, i32, i32 } %unpacked, 1
+  %3 = extractvalue { i32, i32, i32, i32 } %unpacked, 2
+  %4 = extractvalue { i32, i32, i32, i32 } %unpacked, 3
+  %5 = insertelement <4 x i32> poison, i32 %1, i32 0
+  %6 = insertelement <4 x i32> %5, i32 %2, i32 1
+  %7 = insertelement <4 x i32> %6, i32 %3, i32 2
+  %8 = insertelement <4 x i32> %7, i32 %4, i32 3
+  ret <4 x i32> %8
+}
+
+; CHECK-DAG: declare { i32, i32, i32, i32 } @dx.op.unpack4x8.i32(i32, i8, i32)
+declare { i32, i32, i32, i32 } @llvm.dx.unpack.s8s32(i32)
diff --git a/llvm/test/CodeGen/DirectX/unpack_u8u16.ll b/llvm/test/CodeGen/DirectX/unpack_u8u16.ll
new file mode 100644
index 00000000000000..34ee17bdf6dda0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/unpack_u8u16.ll
@@ -0,0 +1,18 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef <4 x i16> @test_unpack_u8u16(i32 noundef %a) {
+  ; CHECK: %{{.*}} = call { i16, i16, i16, i16 } @dx.op.unpack4x8.i16(i32 219, i8 0, i32 {{.*}})
+  %unpacked = call { i16, i16, i16, i16 } @llvm.dx.unpack.u8u16(i32 %a)
+  %1 = extractvalue { i16, i16, i16, i16 } %unpacked, 0
+  %2 = extractvalue { i16, i16, i16, i16 } %unpacked, 1
+  %3 = extractvalue { i16, i16, i16, i16 } %unpacked, 2
+  %4 = extractvalue { i16, i16, i16, i16 } %unpacked, 3
+  %5 = insertelement <4 x i16> poison, i16 %1, i32 0
+  %6 = insertelement <4 x i16> %5, i16 %2, i32 1
+  %7 = insertelement <4 x i16> %6, i16 %3, i32 2
+  %8 = insertelement <4 x i16> %7, i16 %4, i32 3
+  ret <4 x i16> %8
+}
+
+; CHECK-DAG: declare { i16, i16, i16, i16 } @dx.op.unpack4x8.i16(i32, i8, i32)
+declare { i16, i16, i16, i16 } @llvm.dx.unpack.u8u16(i32)
diff --git a/llvm/test/CodeGen/DirectX/unpack_u8u32.ll b/llvm/test/CodeGen/DirectX/unpack_u8u32.ll
new file mode 100644
index 00000000000000..3943e487bef4f0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/unpack_u8u32.ll
@@ -0,0 +1,18 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef <4 x i32> @test_unpack_u8u32(i32 noundef %a) {
+  ; CHECK: %{{.*}} = call { i32, i32, i32, i32 } @dx.op.unpack4x8.i32(i32 219, i8 0, i32 {{.*}})
+  %unpacked = call { i32, i32, i32, i32 } @llvm.dx.unpack.u8u32(i32 %a)
+  %1 = extractvalue { i32, i32, i32, i32 } %unpacked, 0
+  %2 = extractvalue { i32, i32, i32, i32 } %unpacked, 1
+  %3 = extractvalue { i32, i32, i32, i32 } %unpacked, 2
+  %4 = extractvalue { i32, i32, i32, i32 } %unpacked, 3
+  %5 = insertelement <4 x i32> poison, i32 %1, i32 0
+  %6 = insertelement <4 x i32> %5, i32 %2, i32 1
+  %7 = insertelement <4 x i32> %6, i32 %3, i32 2
+  %8 = insertelement <4 x i32> %7, i32 %4, i32 3
+  ret <4 x i32> %8
+}
+
+; CHECK-DAG: declare { i32, i32, i32, i32 } @dx.op.unpack4x8.i32(i32, i8, i32)
+declare { i32, i32, i32, i32 } @llvm.dx.unpack.u8u32(i32)

>From 9c7c571228a08c80134fbc77bc9ba5dab3a51961 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Fri, 2 Oct 2026 17:31:52 +0100
Subject: [PATCH 2/3] [SPIRV] Add HLSL unpack intrinsics to SPIRV backend

---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  4 +++
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 26 +++++++++++++++++++
 .../SPIRV/hlsl-intrinsics/unpack_s8s16.ll     | 17 ++++++++++++
 .../SPIRV/hlsl-intrinsics/unpack_s8s32.ll     | 17 ++++++++++++
 .../SPIRV/hlsl-intrinsics/unpack_u8u16.ll     | 17 ++++++++++++
 .../SPIRV/hlsl-intrinsics/unpack_u8u32.ll     | 17 ++++++++++++
 6 files changed, 98 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s16.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s32.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u16.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u32.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 25b5a3c3854654..380b32c66d5579 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -376,4 +376,8 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
   def int_spv_packhalf2x16 : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty], [IntrNoMem]>;
 
 
+  def int_spv_unpack_u8u16 : DefaultAttrsIntrinsic<[llvm_v4i16_ty], [llvm_i32_ty], [IntrNoMem]>;
+  def int_spv_unpack_u8u32 : DefaultAttrsIntrinsic<[llvm_v4i32_ty], [llvm_i32_ty], [IntrNoMem]>;
+  def int_spv_unpack_s8s16 : DefaultAttrsIntrinsic<[llvm_v4i16_ty], [llvm_i32_ty], [IntrNoMem]>;
+  def int_spv_unpack_s8s32 : DefaultAttrsIntrinsic<[llvm_v4i32_ty], [llvm_i32_ty], [IntrNoMem]>;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 143e1c0e3ef2ab..a6bdb6a8096a24 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -485,6 +485,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
                    MachineInstr &I) const;
   bool selectDerivativeInst(Register ResVReg, SPIRVTypeInst ResType,
                             MachineInstr &I, const unsigned DPdOpCode) const;
+  bool selectUnpackInst(Register ResVReg, SPIRVTypeInst ResType,
+                        MachineInstr &I, const bool Signed) const;
   // Utilities
   Register buildI32Constant(uint32_t Val, MachineInstr &I,
                             SPIRVTypeInst ResType = nullptr) const;
@@ -5262,6 +5264,24 @@ bool SPIRVInstructionSelector::selectDerivativeInst(
   return true;
 }
 
+bool SPIRVInstructionSelector::selectUnpackInst(Register ResVReg,
+                                                SPIRVTypeInst ResType,
+                                                MachineInstr &I,
+                                                const bool Signed) const {
+  MachineIRBuilder MIRBuilder(I);
+  SPIRVTypeInst I8Type = GR.getOrCreateSPIRVIntegerType(8, MIRBuilder);
+  SPIRVTypeInst I8x4Type =
+      GR.getOrCreateSPIRVVectorType(I8Type, 4, MIRBuilder, true);
+
+  Register I8x4Reg = MRI->createVirtualRegister(GR.getRegClass(I8x4Type));
+  if (!selectOpWithSrcs(I8x4Reg, I8x4Type, I, {I.getOperand(2).getReg()},
+                        SPIRV::OpBitcast))
+    return false;
+
+  unsigned ConvertOpcode = Signed ? SPIRV::OpSConvert : SPIRV::OpUConvert;
+  return selectOpWithSrcs(ResVReg, ResType, I, {I8x4Reg}, ConvertOpcode);
+}
+
 bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
                                                SPIRVTypeInst ResType,
                                                MachineInstr &I) const {
@@ -5853,6 +5873,12 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     MIB.constrainAllUses(TII, TRI, RBI);
     return true;
   }
+  case Intrinsic::spv_unpack_u8u16:
+  case Intrinsic::spv_unpack_u8u32:
+    return selectUnpackInst(ResVReg, ResType, I, false);
+  case Intrinsic::spv_unpack_s8s16:
+  case Intrinsic::spv_unpack_s8s32:
+    return selectUnpackInst(ResVReg, ResType, I, true);
   default:
     return diagnoseUnsupported(I, "intrinsic selection not implemented.");
   }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s16.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s16.ll
new file mode 100644
index 00000000000000..2d494cca2ec883
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s16.ll
@@ -0,0 +1,17 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[int16x4:%.*]] = OpTypeVector [[int16]] 4
+
+define noundef <4 x i16> @unpack_s8s16(i32 noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter
+; CHECK: [[cast:%.*]] = OpBitcast [[int8x4]] [[in]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int16x4]] [[cast]]
+  %unpacked = call <4 x i16> @llvm.spv.unpack.s8s16(i32 %a)
+  ret <4 x i16> %unpacked
+}
+
+declare <4 x i16> @llvm.spv.unpack.s8s16(i32)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s32.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s32.ll
new file mode 100644
index 00000000000000..6745b053c2e487
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_s8s32.ll
@@ -0,0 +1,17 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+define noundef <4 x i32> @unpack_s8s32(i32 noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter
+; CHECK: [[cast:%.*]] = OpBitcast [[int8x4]] [[in]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int32x4]] [[cast]]
+  %unpacked = call <4 x i32> @llvm.spv.unpack.s8s32(i32 %a)
+  ret <4 x i32> %unpacked
+}
+
+declare <4 x i32> @llvm.spv.unpack.s8s32(i32)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u16.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u16.ll
new file mode 100644
index 00000000000000..8c0ce6265215e6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u16.ll
@@ -0,0 +1,17 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[int16x4:%.*]] = OpTypeVector [[int16]] 4
+
+define noundef <4 x i16> @unpack_u8u16(i32 noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter
+; CHECK: [[cast:%.*]] = OpBitcast [[int8x4]] [[in]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int16x4]] [[cast]]
+  %unpacked = call <4 x i16> @llvm.spv.unpack.u8u16(i32 %a)
+  ret <4 x i16> %unpacked
+}
+
+declare <4 x i16> @llvm.spv.unpack.u8u16(i32)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u32.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u32.ll
new file mode 100644
index 00000000000000..a4952950a800dc
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/unpack_u8u32.ll
@@ -0,0 +1,17 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+define noundef <4 x i32> @unpack_u8u32(i32 noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter
+; CHECK: [[cast:%.*]] = OpBitcast [[int8x4]] [[in]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int32x4]] [[cast]]
+  %unpacked = call <4 x i32> @llvm.spv.unpack.u8u32(i32 %a)
+  ret <4 x i32> %unpacked
+}
+
+declare <4 x i32> @llvm.spv.unpack.u8u32(i32)

>From abd051e5531fac3c39973a0862ab94eeee8ae458 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Fri, 2 Oct 2026 17:32:32 +0100
Subject: [PATCH 3/3] [HLSL] Add HLSL unpack intrinsics to Clang

These intrinsics unpack a packed type (int8_t4_packed or uint8_t4_packed)
into an appropriate 4 element vector.
---
 clang/include/clang/Basic/Builtins.td         | 24 ++++++++
 clang/include/clang/Basic/HLSLIntrinsics.td   | 24 ++++++++
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          | 56 +++++++++++++++++++
 clang/lib/CodeGen/CGHLSLRuntime.h             |  4 ++
 clang/lib/Sema/SemaHLSL.cpp                   | 43 ++++++++++++++
 .../CodeGenHLSL/builtins/unpack_s8s16.hlsl    | 21 +++++++
 .../CodeGenHLSL/builtins/unpack_s8s32.hlsl    | 21 +++++++
 .../CodeGenHLSL/builtins/unpack_u8u16.hlsl    | 21 +++++++
 .../CodeGenHLSL/builtins/unpack_u8u32.hlsl    | 21 +++++++
 .../BuiltIns/unpack_s8s16-errors.hlsl         | 21 +++++++
 .../BuiltIns/unpack_s8s32-errors.hlsl         | 21 +++++++
 .../BuiltIns/unpack_u8u16-errors.hlsl         | 21 +++++++
 .../BuiltIns/unpack_u8u32-errors.hlsl         | 21 +++++++
 13 files changed, 319 insertions(+)
 create mode 100644 clang/test/CodeGenHLSL/builtins/unpack_s8s16.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/unpack_s8s32.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/unpack_u8u16.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/unpack_u8u32.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/unpack_s8s16-errors.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/unpack_s8s32-errors.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/unpack_u8u16-errors.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/unpack_u8u32-errors.hlsl

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 7a59aaa133d522..319403c343c1f9 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5973,6 +5973,30 @@ def HLSLDdyFine : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void(...)";
 }
 
+def HLSLUnpackU8U16 : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_unpack_u8u16"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
+def HLSLUnpackU8U32 : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_unpack_u8u32"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
+def HLSLUnpackS8S16 : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_unpack_s8s16"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
+def HLSLUnpackS8S32 : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_unpack_s8s32"];
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
 // Builtins for XRay.
 def XRayCustomEvent : Builtin {
   let Spellings = ["__xray_customevent"];
diff --git a/clang/include/clang/Basic/HLSLIntrinsics.td b/clang/include/clang/Basic/HLSLIntrinsics.td
index 4be051476395cd..0a503ee766e972 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -1927,3 +1927,27 @@ let VaryingLongVector = 1;
 let IsConvergent = 1;
 let Availability = SM6_0;
 }
+
+def hlsl_unpack_u8u16 : HLSLBuiltin<"unpack_u8u16", "__builtin_hlsl_unpack_u8u16"> {
+  let Args = [UInt8PackedTy];
+  let ReturnType = VectorType<UInt16Ty, 4>;
+  let Availability = SM6_6;
+}
+
+def hlsl_unpack_u8u32 : HLSLBuiltin<"unpack_u8u32", "__builtin_hlsl_unpack_u8u32"> {
+  let Args = [UInt8PackedTy];
+  let ReturnType = VectorType<UIntTy, 4>;
+  let Availability = SM6_6;
+}
+
+def hlsl_unpack_s8s16 : HLSLBuiltin<"unpack_s8s16", "__builtin_hlsl_unpack_s8s16"> {
+  let Args = [Int8PackedTy];
+  let ReturnType = VectorType<Int16Ty, 4>;
+  let Availability = SM6_6;
+}
+
+def hlsl_unpack_s8s32 : HLSLBuiltin<"unpack_s8s32", "__builtin_hlsl_unpack_s8s32"> {
+  let Args = [Int8PackedTy];
+  let ReturnType = VectorType<IntTy, 4>;
+  let Availability = SM6_6;
+}
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 7ffadee3086a5c..158166214a52bb 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -416,6 +416,42 @@ static Value *handleInterlockedCompareOp(CodeGenFunction &CGF,
   return Original;
 }
 
+static Value *handleHLSLUnpackOp(CodeGenFunction &CGF, const CallExpr *E,
+                                 unsigned BuiltinID, const Twine &Name,
+                                 const bool Short) {
+  Value *Op0 = CGF.EmitScalarExpr(E->getArg(0));
+  IntegerType *IntSizeType =
+      Short ? CGF.Builder.getInt16Ty() : CGF.Builder.getInt32Ty();
+
+  if (CGF.CGM.getTarget().getTriple().isDXIL()) {
+    // DXIL Intrinsic returns struct of 4 i32 or i16
+    // We have to call intrinsic then reassemble these to a vector
+    StructType *StructType =
+        StructType::get(CGF.getLLVMContext(),
+                        {IntSizeType, IntSizeType, IntSizeType, IntSizeType});
+    Value *StructVal = CGF.Builder.CreateIntrinsic(
+        /*ReturnType=*/StructType, BuiltinID, {Op0}, nullptr, Name);
+
+    llvm::Type *VecType = FixedVectorType::get(IntSizeType, 4);
+    Value *VecVal = PoisonValue::get(VecType);
+    for (unsigned I = 0; I < 4; ++I) {
+      Value *Elt = CGF.Builder.CreateExtractValue(StructVal, I);
+      VecVal =
+          CGF.Builder.CreateInsertElement(VecVal, Elt, CGF.Builder.getInt32(I));
+    }
+
+    return VecVal;
+  }
+
+  if (CGF.CGM.getTarget().getTriple().isSPIRV()) {
+    FixedVectorType *RetTy = FixedVectorType::get(IntSizeType, 4);
+    return CGF.Builder.CreateIntrinsic(/*ReturnType=*/RetTy, BuiltinID, {Op0},
+                                       nullptr, Name);
+  }
+
+  llvm_unreachable("Unpack_ is only supported for DXIL and SPIRV targets");
+}
+
 static Value *emitBufferStride(CodeGenFunction *CGF, const Expr *HandleExpr,
                                LValue &Stride) {
   // Figure out the stride of the buffer elements from the handle type.
@@ -1803,6 +1839,26 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
                                    ArrayRef<Value *>{Op0}, nullptr,
                                    "hlsl.ddy.fine");
   }
+  case Builtin::BI__builtin_hlsl_unpack_u8u16: {
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getUnpackU8U16Intrinsic();
+    return handleHLSLUnpackOp(*this, E, ID, "hlsl.unpack.u8u16",
+                              /*Short=*/true);
+  }
+  case Builtin::BI__builtin_hlsl_unpack_u8u32: {
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getUnpackU8U32Intrinsic();
+    return handleHLSLUnpackOp(*this, E, ID, "hlsl.unpack.u8u32",
+                              /*Short=*/false);
+  }
+  case Builtin::BI__builtin_hlsl_unpack_s8s16: {
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getUnpackS8S16Intrinsic();
+    return handleHLSLUnpackOp(*this, E, ID, "hlsl.unpack.s8s16",
+                              /*Short=*/true);
+  }
+  case Builtin::BI__builtin_hlsl_unpack_s8s32: {
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getUnpackS8S32Intrinsic();
+    return handleHLSLUnpackOp(*this, E, ID, "hlsl.unpack.s8s32",
+                              /*Short=*/false);
+  }
   case Builtin::BI__builtin_get_spirv_spec_constant_bool:
   case Builtin::BI__builtin_get_spirv_spec_constant_short:
   case Builtin::BI__builtin_get_spirv_spec_constant_ushort:
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 11b3e0220dcfba..e6b7fe1bb3a663 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -214,6 +214,10 @@ class CGHLSLRuntime {
   GENERATE_HLSL_INTRINSIC_FUNCTION(DdyCoarse, ddy_coarse)
   GENERATE_HLSL_INTRINSIC_FUNCTION(DdxFine, ddx_fine)
   GENERATE_HLSL_INTRINSIC_FUNCTION(DdyFine, ddy_fine)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(UnpackU8U16, unpack_u8u16)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(UnpackU8U32, unpack_u8u32)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(UnpackS8S16, unpack_s8s16)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(UnpackS8S32, unpack_s8s32)
 
   //===----------------------------------------------------------------------===//
   // End of reserved area for HLSL intrinsic getters.
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 73a6c8ae9672ab..df2c4f44ab18e6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4998,6 +4998,49 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
                                getASTContext().UnsignedIntTy);
     break;
   }
+  case Builtin::BI__builtin_hlsl_unpack_u8u16: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+    if (CheckArgTypeMatches(&SemaRef, TheCall->getArg(0),
+                            getASTContext().UInt8_4PackedTy))
+      return true;
+    QualType RetTy =
+        SemaRef.Context.getExtVectorType(getASTContext().UnsignedShortTy, 4);
+    TheCall->setType(RetTy);
+    break;
+  }
+  case Builtin::BI__builtin_hlsl_unpack_u8u32: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+    if (CheckArgTypeMatches(&SemaRef, TheCall->getArg(0),
+                            getASTContext().UInt8_4PackedTy))
+      return true;
+    QualType RetTy =
+        SemaRef.Context.getExtVectorType(getASTContext().UnsignedIntTy, 4);
+    TheCall->setType(RetTy);
+    break;
+  }
+  case Builtin::BI__builtin_hlsl_unpack_s8s16: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+    if (CheckArgTypeMatches(&SemaRef, TheCall->getArg(0),
+                            getASTContext().Int8_4PackedTy))
+      return true;
+    QualType RetTy =
+        SemaRef.Context.getExtVectorType(getASTContext().ShortTy, 4);
+    TheCall->setType(RetTy);
+    break;
+  }
+  case Builtin::BI__builtin_hlsl_unpack_s8s32: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+    if (CheckArgTypeMatches(&SemaRef, TheCall->getArg(0),
+                            getASTContext().Int8_4PackedTy))
+      return true;
+    QualType RetTy = SemaRef.Context.getExtVectorType(getASTContext().IntTy, 4);
+    TheCall->setType(RetTy);
+    break;
+  }
   }
   return false;
 }
diff --git a/clang/test/CodeGenHLSL/builtins/unpack_s8s16.hlsl b/clang/test/CodeGenHLSL/builtins/unpack_s8s16.hlsl
new file mode 100644
index 00000000000000..d613b5a50b566c
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/unpack_s8s16.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple dxil-pc-shadermodel6.6-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-int16-type -fnative-half-type -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple spirv-pc-vulkan-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-int16-type -fnative-half-type -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-SPV
+
+// CHECK: define {{.*}} <4 x i16> @_Z10test_s8s16u14int8_t4_packed
+// CHECK-DXIL: [[VAR:%.*]] = call { i16, i16, i16, i16 } @llvm.dx.unpack.s8s16(i32 %{{.*}})
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 0
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 1
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 2
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 3
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: ret <4 x i16>
+// CHECK-SPV: [[VAR:%.*]] = call <4 x i16> @llvm.spv.unpack.s8s16(i32 %{{.*}})
+// CHECK-SPV: ret <4 x i16> [[VAR]]
+int16_t4 test_s8s16(int8_t4_packed val) { return unpack_s8s16(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/unpack_s8s32.hlsl b/clang/test/CodeGenHLSL/builtins/unpack_s8s32.hlsl
new file mode 100644
index 00000000000000..7f45a21cacdfd4
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/unpack_s8s32.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple dxil-pc-shadermodel6.6-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple spirv-pc-vulkan-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-SPV
+
+// CHECK: define {{.*}} <4 x i32> @_Z10test_s8s32u14int8_t4_packed
+// CHECK-DXIL: [[VAR:%.*]] = call { i32, i32, i32, i32 } @llvm.dx.unpack.s8s32(i32 %{{.*}})
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 0
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 1
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 2
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 3
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: ret <4 x i32>
+// CHECK-SPV: [[VAR:%.*]] = call <4 x i32> @llvm.spv.unpack.s8s32(i32 %{{.*}})
+// CHECK-SPV: ret <4 x i32> [[VAR]]
+int32_t4 test_s8s32(int8_t4_packed val) { return unpack_s8s32(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/unpack_u8u16.hlsl b/clang/test/CodeGenHLSL/builtins/unpack_u8u16.hlsl
new file mode 100644
index 00000000000000..9f3a0c5b773323
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/unpack_u8u16.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple dxil-pc-shadermodel6.6-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-int16-type -fnative-half-type -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple spirv-pc-vulkan-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-int16-type -fnative-half-type -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-SPV
+
+// CHECK: define {{.*}} <4 x i16> @_Z10test_u8u16u15uint8_t4_packed
+// CHECK-DXIL: [[VAR:%.*]] = call { i16, i16, i16, i16 } @llvm.dx.unpack.u8u16(i32 %{{.*}})
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 0
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 1
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 2
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: %{{.*}} = extractvalue { i16, i16, i16, i16 } [[VAR]], 3
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i16>
+// CHECK-DXIL: ret <4 x i16>
+// CHECK-SPV: [[VAR:%.*]] = call <4 x i16> @llvm.spv.unpack.u8u16(i32 %{{.*}})
+// CHECK-SPV: ret <4 x i16> [[VAR]]
+uint16_t4 test_u8u16(uint8_t4_packed val) { return unpack_u8u16(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/unpack_u8u32.hlsl b/clang/test/CodeGenHLSL/builtins/unpack_u8u32.hlsl
new file mode 100644
index 00000000000000..be7f18e457377c
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/unpack_u8u32.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple dxil-pc-shadermodel6.6-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple spirv-pc-vulkan-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s -check-prefix=CHECK,CHECK-SPV
+
+// CHECK: define {{.*}} <4 x i32> @_Z10test_u8u32u15uint8_t4_packed
+// CHECK-DXIL: [[VAR:%.*]] = call { i32, i32, i32, i32 } @llvm.dx.unpack.u8u32(i32 %{{.*}})
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 0
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 1
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 2
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: %{{.*}} = extractvalue { i32, i32, i32, i32 } [[VAR]], 3
+// CHECK-DXIL: %{{.*}} = insertelement <4 x i32>
+// CHECK-DXIL: ret <4 x i32>
+// CHECK-SPV: [[VAR:%.*]] = call <4 x i32> @llvm.spv.unpack.u8u32(i32 %{{.*}})
+// CHECK-SPV: ret <4 x i32> [[VAR]]
+uint32_t4 test_u8u32(uint8_t4_packed val) { return unpack_u8u32(val); }
diff --git a/clang/test/SemaHLSL/BuiltIns/unpack_s8s16-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/unpack_s8s16-errors.hlsl
new file mode 100644
index 00000000000000..d038e462bf3adc
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/unpack_s8s16-errors.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -fnative-int16-type -emit-llvm-only -disable-llvm-passes -verify
+
+void test_builtin_no_args() {
+  __builtin_hlsl_unpack_s8s16();
+  // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+int16_t4 test_builtin_extra_args(int8_t4_packed p0) {
+  return __builtin_hlsl_unpack_s8s16(p0, p0);
+  // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+int16_t4 test_builtin_int_arg(int p0) {
+  return __builtin_hlsl_unpack_s8s16(p0);
+  // expected-error at -1 {{passing 'int' to parameter of incompatible type 'int8_t4_packed'}}
+}
+
+int16_t4 test_builtin_float_arg(float p0) {
+  return __builtin_hlsl_unpack_s8s16(p0);
+  // expected-error at -1 {{passing 'float' to parameter of incompatible type 'int8_t4_packed'}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/unpack_s8s32-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/unpack_s8s32-errors.hlsl
new file mode 100644
index 00000000000000..ba6d7cbac2ce91
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/unpack_s8s32-errors.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify
+
+void test_builtin_no_args() {
+  __builtin_hlsl_unpack_s8s32();
+  // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+int32_t4 test_builtin_extra_args(int8_t4_packed p0) {
+  return __builtin_hlsl_unpack_s8s32(p0, p0);
+  // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+int32_t4 test_builtin_int_arg(int p0) {
+  return __builtin_hlsl_unpack_s8s32(p0);
+  // expected-error at -1 {{passing 'int' to parameter of incompatible type 'int8_t4_packed'}}
+}
+
+int32_t4 test_builtin_float_arg(float p0) {
+  return __builtin_hlsl_unpack_s8s32(p0);
+  // expected-error at -1 {{passing 'float' to parameter of incompatible type 'int8_t4_packed'}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/unpack_u8u16-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/unpack_u8u16-errors.hlsl
new file mode 100644
index 00000000000000..6b8b0b549ce232
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/unpack_u8u16-errors.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -fnative-int16-type -emit-llvm-only -disable-llvm-passes -verify
+
+void test_builtin_no_args() {
+  __builtin_hlsl_unpack_u8u16();
+  // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+uint16_t4 test_builtin_extra_args(uint8_t4_packed p0) {
+  return __builtin_hlsl_unpack_u8u16(p0, p0);
+  // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+uint16_t4 test_builtin_int_arg(int p0) {
+  return __builtin_hlsl_unpack_u8u16(p0);
+  // expected-error at -1 {{passing 'int' to parameter of incompatible type 'uint8_t4_packed'}}
+}
+
+uint16_t4 test_builtin_float_arg(float p0) {
+  return __builtin_hlsl_unpack_u8u16(p0);
+  // expected-error at -1 {{passing 'float' to parameter of incompatible type 'uint8_t4_packed'}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/unpack_u8u32-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/unpack_u8u32-errors.hlsl
new file mode 100644
index 00000000000000..49bbbd267c8d3b
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/unpack_u8u32-errors.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify
+
+void test_builtin_no_args() {
+  __builtin_hlsl_unpack_u8u32();
+  // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+uint32_t4 test_builtin_extra_args(uint8_t4_packed p0) {
+  return __builtin_hlsl_unpack_u8u32(p0, p0);
+  // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+uint32_t4 test_builtin_int_arg(int p0) {
+  return __builtin_hlsl_unpack_u8u32(p0);
+  // expected-error at -1 {{passing 'int' to parameter of incompatible type 'uint8_t4_packed'}}
+}
+
+uint32_t4 test_builtin_float_arg(float p0) {
+  return __builtin_hlsl_unpack_u8u32(p0);
+  // expected-error at -1 {{passing 'float' to parameter of incompatible type 'uint8_t4_packed'}}
+}



More information about the cfe-commits mailing list