[clang] [llvm] [HLSL] Add HLSL pack and pack_clamp intrinsics (PR #228089)
Alexander Johnston via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 07:56:55 PDT 2026
https://github.com/Alexander-Johnston updated https://github.com/llvm/llvm-project/pull/228089
>From d499d664750792c840cf510f4b5ed466d17b25b2 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Thu, 1 Oct 2026 12:59:05 +0100
Subject: [PATCH 1/3] [HLSL] Add hlsl pack intrinsics to clang
---
clang/include/clang/Basic/Builtins.td | 24 +++++++++
clang/include/clang/Basic/HLSLIntrinsics.td | 36 +++++++++++++
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 24 +++++++++
clang/lib/CodeGen/CGHLSLRuntime.h | 4 ++
clang/lib/Sema/SemaHLSL.cpp | 52 +++++++++++++++++++
.../CodeGenHLSL/builtins/pack_clamp_s8.hlsl | 16 ++++++
.../CodeGenHLSL/builtins/pack_clamp_u8.hlsl | 16 ++++++
clang/test/CodeGenHLSL/builtins/pack_s8.hlsl | 16 ++++++
clang/test/CodeGenHLSL/builtins/pack_u8.hlsl | 16 ++++++
.../BuiltIns/pack_clamp_s8-errors.hlsl | 31 +++++++++++
.../BuiltIns/pack_clamp_u8-errors.hlsl | 31 +++++++++++
.../SemaHLSL/BuiltIns/pack_s8-errors.hlsl | 31 +++++++++++
.../SemaHLSL/BuiltIns/pack_u8-errors.hlsl | 31 +++++++++++
llvm/include/llvm/IR/IntrinsicsDirectX.td | 6 +++
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 4 ++
15 files changed, 338 insertions(+)
create mode 100644 clang/test/CodeGenHLSL/builtins/pack_clamp_s8.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/pack_clamp_u8.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/pack_s8.hlsl
create mode 100644 clang/test/CodeGenHLSL/builtins/pack_u8.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/pack_clamp_s8-errors.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/pack_clamp_u8-errors.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/pack_s8-errors.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/pack_u8-errors.hlsl
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 5480aed3d543972..dfdadc87a6ce02f 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5968,6 +5968,30 @@ def HLSLDdyFine : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLPackU8 : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_pack_u8"];
+ let Attributes = [NoThrow, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def HLSLPackS8 : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_pack_s8"];
+ let Attributes = [NoThrow, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def HLSLPackClampU8 : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_pack_clamp_u8"];
+ let Attributes = [NoThrow, CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def HLSLPackClampS8 : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_pack_clamp_s8"];
+ 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 4be051476395cdc..49e7f83a14472d7 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -1927,3 +1927,39 @@ let VaryingLongVector = 1;
let IsConvergent = 1;
let Availability = SM6_0;
}
+
+def hlsl_pack_u8 : HLSLOneArgBuiltin<"pack_u8", "__builtin_hlsl_pack_u8"> {
+ let VaryingTypes = [UInt16Ty, UIntTy];
+ let VaryingScalar = 0;
+ let VaryingVecSizes = [4];
+ let VaryingMatDims = [];
+ let ReturnType = UInt8PackedTy;
+ let Availability = SM6_6;
+}
+
+def hlsl_pack_s8 : HLSLOneArgBuiltin<"pack_s8", "__builtin_hlsl_pack_s8"> {
+ let VaryingTypes = [Int16Ty, IntTy];
+ let VaryingScalar = 0;
+ let VaryingVecSizes = [4];
+ let VaryingMatDims = [];
+ let ReturnType = Int8PackedTy;
+ let Availability = SM6_6;
+}
+
+def hlsl_pack_clamp_u8 : HLSLOneArgBuiltin<"pack_clamp_u8", "__builtin_hlsl_pack_clamp_u8"> {
+ let VaryingTypes = [Int16Ty, IntTy];
+ let VaryingScalar = 0;
+ let VaryingVecSizes = [4];
+ let VaryingMatDims = [];
+ let ReturnType = UInt8PackedTy;
+ let Availability = SM6_6;
+}
+
+def hlsl_pack_clamp_s8 : HLSLOneArgBuiltin<"pack_clamp_s8", "__builtin_hlsl_pack_clamp_s8"> {
+ let VaryingTypes = [Int16Ty, IntTy];
+ let VaryingScalar = 0;
+ let VaryingVecSizes = [4];
+ let VaryingMatDims = [];
+ let ReturnType = Int8PackedTy;
+ let Availability = SM6_6;
+}
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index aa26727e8ad17eb..9e75d33f29c3a0a 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1799,6 +1799,30 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
ArrayRef<Value *>{Op0}, nullptr,
"hlsl.ddy.fine");
}
+ case Builtin::BI__builtin_hlsl_pack_u8: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getPackU8Intrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Builder.getInt32Ty(), ID,
+ {Op0}, nullptr, "hlsl.pack.u8");
+ }
+ case Builtin::BI__builtin_hlsl_pack_s8: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getPackS8Intrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Builder.getInt32Ty(), ID,
+ {Op0}, nullptr, "hlsl.pack.s8");
+ }
+ case Builtin::BI__builtin_hlsl_pack_clamp_u8: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getPackClampU8Intrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Builder.getInt32Ty(), ID,
+ {Op0}, nullptr, "hlsl.pack.clamp.u8");
+ }
+ case Builtin::BI__builtin_hlsl_pack_clamp_s8: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ Intrinsic::ID ID = CGM.getHLSLRuntime().getPackClampS8Intrinsic();
+ return Builder.CreateIntrinsic(/*ReturnType=*/Builder.getInt32Ty(), ID,
+ {Op0}, nullptr, "hlsl.pack.clamp.s8");
+ }
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 11b3e0220dcfbab..267fce649779780 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(PackS8, pack_s8)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(PackU8, pack_u8)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(PackClampS8, pack_clamp_s8)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(PackClampU8, pack_clamp_u8)
//===----------------------------------------------------------------------===//
// End of reserved area for HLSL intrinsic getters.
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d06491b32ea1b2f..db446bee969fc6d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4952,6 +4952,58 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
getASTContext().UnsignedIntTy);
break;
}
+ case Builtin::BI__builtin_hlsl_pack_u8: {
+ if (SemaRef.checkArgCount(TheCall, 1))
+ return true;
+ const auto *VecTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
+ if (!VecTy || !VecTy->getElementType()->isUnsignedIntegerType() ||
+ (SemaRef.Context.getTypeSize(VecTy->getElementType()) != 16 &&
+ SemaRef.Context.getTypeSize(VecTy->getElementType()) != 32) ||
+ VecTy->getNumElements() != 4) {
+ SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
+ diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* unsigned integer */ 3 << /* no fp */ 0
+ << TheCall->getArg(0)->getType();
+ return true;
+ }
+ TheCall->setType(getASTContext().UInt8_4PackedTy);
+ break;
+ }
+ case Builtin::BI__builtin_hlsl_pack_clamp_u8: {
+ if (SemaRef.checkArgCount(TheCall, 1))
+ return true;
+ const auto *VecTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
+ if (!VecTy || !VecTy->getElementType()->isSignedIntegerType() ||
+ (SemaRef.Context.getTypeSize(VecTy->getElementType()) != 16 &&
+ SemaRef.Context.getTypeSize(VecTy->getElementType()) != 32) ||
+ VecTy->getNumElements() != 4) {
+ SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
+ diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* signed integer */ 2 << /* no fp */ 0
+ << TheCall->getArg(0)->getType();
+ return true;
+ }
+ TheCall->setType(getASTContext().UInt8_4PackedTy);
+ break;
+ }
+ case Builtin::BI__builtin_hlsl_pack_s8:
+ case Builtin::BI__builtin_hlsl_pack_clamp_s8: {
+ if (SemaRef.checkArgCount(TheCall, 1))
+ return true;
+ const auto *VecTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
+ if (!VecTy || !VecTy->getElementType()->isSignedIntegerType() ||
+ (SemaRef.Context.getTypeSize(VecTy->getElementType()) != 16 &&
+ SemaRef.Context.getTypeSize(VecTy->getElementType()) != 32) ||
+ VecTy->getNumElements() != 4) {
+ SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
+ diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* signed integer */ 2 << /* no fp */ 0
+ << TheCall->getArg(0)->getType();
+ return true;
+ }
+ TheCall->setType(getASTContext().Int8_4PackedTy);
+ break;
+ }
}
return false;
}
diff --git a/clang/test/CodeGenHLSL/builtins/pack_clamp_s8.hlsl b/clang/test/CodeGenHLSL/builtins/pack_clamp_s8.hlsl
new file mode 100644
index 000000000000000..80db19d467f1096
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/pack_clamp_s8.hlsl
@@ -0,0 +1,16 @@
+// 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 -DCALL=dx
+// 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 -DCALL=spv
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u16Dv4_s
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.clamp.s8.v4i16(<4 x i16> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+int8_t4_packed test_u16(int16_t4 val) { return pack_clamp_s8(val); }
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u32Dv4_i
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.clamp.s8.v4i32(<4 x i32> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+int8_t4_packed test_u32(int4 val) { return pack_clamp_s8(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/pack_clamp_u8.hlsl b/clang/test/CodeGenHLSL/builtins/pack_clamp_u8.hlsl
new file mode 100644
index 000000000000000..99beb20843ea8e2
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/pack_clamp_u8.hlsl
@@ -0,0 +1,16 @@
+// 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 -DCALL=dx
+// 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 -DCALL=spv
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u16Dv4_s
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.clamp.u8.v4i16(<4 x i16> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+uint8_t4_packed test_u16(int16_t4 val) { return pack_clamp_u8(val); }
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u32Dv4_i
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.clamp.u8.v4i32(<4 x i32> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+uint8_t4_packed test_u32(int4 val) { return pack_clamp_u8(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/pack_s8.hlsl b/clang/test/CodeGenHLSL/builtins/pack_s8.hlsl
new file mode 100644
index 000000000000000..4b90a2890a54ba5
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/pack_s8.hlsl
@@ -0,0 +1,16 @@
+// 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 -DCALL=dx
+// 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 -DCALL=spv
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_s16Dv4_s
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.s8.v4i16(<4 x i16> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+int8_t4_packed test_s16(int16_t4 val) { return pack_s8(val); }
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_s32Dv4_i
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.s8.v4i32(<4 x i32> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+int8_t4_packed test_s32(int4 val) { return pack_s8(val); }
diff --git a/clang/test/CodeGenHLSL/builtins/pack_u8.hlsl b/clang/test/CodeGenHLSL/builtins/pack_u8.hlsl
new file mode 100644
index 000000000000000..b4ea9e78b12b424
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/pack_u8.hlsl
@@ -0,0 +1,16 @@
+// 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 -DCALL=dx
+// 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 -DCALL=spv
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u16Dv4_t
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.u8.v4i16(<4 x i16> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+uint8_t4_packed test_u16(uint16_t4 val) { return pack_u8(val); }
+
+// CHECK-LABEL: define {{.*}} i32 @_Z8test_u32Dv4_j
+// CHECK: [[VAR:%.*]] = call i32 @llvm.[[CALL]].pack.u8.v4i32(<4 x i32> %{{.*}})
+// CHECK-NEXT: ret i32 [[VAR]]
+uint8_t4_packed test_u32(uint4 val) { return pack_u8(val); }
diff --git a/clang/test/SemaHLSL/BuiltIns/pack_clamp_s8-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/pack_clamp_s8-errors.hlsl
new file mode 100644
index 000000000000000..e9a0ce049b66377
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/pack_clamp_s8-errors.hlsl
@@ -0,0 +1,31 @@
+// 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_pack_clamp_s8();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+int8_t4_packed test_builtin_extra_args(int32_t4 p0) {
+ return __builtin_hlsl_pack_clamp_s8(p0, p0);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+int8_t4_packed test_builtin_64bit_arg(int64_t4 p0) {
+ return __builtin_hlsl_pack_clamp_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int64_t4' (aka 'vector<int64_t, 4>'))}}
+}
+
+int8_t4_packed test_builtin_float_vec_arg(float32_t4 p0) {
+ return __builtin_hlsl_pack_clamp_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'float32_t4' (aka 'vector<float32_t, 4>'))}}
+}
+
+int8_t4_packed test_builtin_wrong_vec_elems(int32_t3 p0) {
+ return __builtin_hlsl_pack_clamp_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int32_t3' (aka 'vector<int32_t, 3>'))}}
+}
+
+int8_t4_packed test_builtin_scalar_arg(int p0) {
+ return __builtin_hlsl_pack_clamp_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int')}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/pack_clamp_u8-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/pack_clamp_u8-errors.hlsl
new file mode 100644
index 000000000000000..d4675c0a5968eb6
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/pack_clamp_u8-errors.hlsl
@@ -0,0 +1,31 @@
+// 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_pack_clamp_u8();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+uint8_t4_packed test_builtin_extra_args(int32_t4 p0) {
+ return __builtin_hlsl_pack_clamp_u8(p0, p0);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+uint8_t4_packed test_builtin_64bit_arg(int64_t4 p0) {
+ return __builtin_hlsl_pack_clamp_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int64_t4' (aka 'vector<int64_t, 4>'))}}
+}
+
+uint8_t4_packed test_builtin_float_vec_arg(float32_t4 p0) {
+ return __builtin_hlsl_pack_clamp_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'float32_t4' (aka 'vector<float32_t, 4>'))}}
+}
+
+uint8_t4_packed test_builtin_wrong_vec_elems(int32_t3 p0) {
+ return __builtin_hlsl_pack_clamp_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int32_t3' (aka 'vector<int32_t, 3>'))}}
+}
+
+uint8_t4_packed test_builtin_scalar_arg(int p0) {
+ return __builtin_hlsl_pack_clamp_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int')}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/pack_s8-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/pack_s8-errors.hlsl
new file mode 100644
index 000000000000000..0f0a5a082ad7ee1
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/pack_s8-errors.hlsl
@@ -0,0 +1,31 @@
+// 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_pack_s8();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+int8_t4_packed test_builtin_extra_args(int32_t4 p0) {
+ return __builtin_hlsl_pack_s8(p0, p0);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+int8_t4_packed test_builtin_64bit_arg(int64_t4 p0) {
+ return __builtin_hlsl_pack_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int64_t4' (aka 'vector<int64_t, 4>'))}}
+}
+
+int8_t4_packed test_builtin_float_vec_arg(float32_t4 p0) {
+ return __builtin_hlsl_pack_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'float32_t4' (aka 'vector<float32_t, 4>'))}}
+}
+
+int8_t4_packed test_builtin_wrong_vec_elems(int32_t3 p0) {
+ return __builtin_hlsl_pack_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int32_t3' (aka 'vector<int32_t, 3>'))}}
+}
+
+int8_t4_packed test_builtin_scalar_arg(int p0) {
+ return __builtin_hlsl_pack_s8(p0);
+ // expected-error at -1 {{1st argument must be a vector of signed integer types (was 'int')}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/pack_u8-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/pack_u8-errors.hlsl
new file mode 100644
index 000000000000000..faf493103dae535
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/pack_u8-errors.hlsl
@@ -0,0 +1,31 @@
+// 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_pack_u8();
+ // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+uint8_t4_packed test_builtin_extra_args(uint32_t4 p0) {
+ return __builtin_hlsl_pack_u8(p0, p0);
+ // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+uint8_t4_packed test_builtin_64bit_arg(uint64_t4 p0) {
+ return __builtin_hlsl_pack_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of unsigned integer types (was 'uint64_t4' (aka 'vector<uint64_t, 4>'))}}
+}
+
+uint8_t4_packed test_builtin_float_vec_arg(float32_t4 p0) {
+ return __builtin_hlsl_pack_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of unsigned integer types (was 'float32_t4' (aka 'vector<float32_t, 4>'))}}
+}
+
+uint8_t4_packed test_builtin_wrong_vec_elems(uint32_t3 p0) {
+ return __builtin_hlsl_pack_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of unsigned integer types (was 'uint32_t3' (aka 'vector<uint32_t, 3>'))}}
+}
+
+uint8_t4_packed test_builtin_scalar_arg(uint p0) {
+ return __builtin_hlsl_pack_u8(p0);
+ // expected-error at -1 {{1st argument must be a vector of unsigned integer types (was 'uint' (aka 'unsigned int'))}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index e087216b822da32..68faf4c2e04734d 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -359,4 +359,10 @@ 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_pack_u8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+def int_dx_pack_s8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+def int_dx_pack_clamp_u8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+def int_dx_pack_clamp_s8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+
}
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 25b5a3c38546546..0f4685ca2582ecc 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -375,5 +375,9 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
def int_spv_unpackhalf2x16 : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_i32_ty], [IntrNoMem]>;
def int_spv_packhalf2x16 : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty], [IntrNoMem]>;
+ def int_spv_pack_u8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+ def int_spv_pack_s8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+ def int_spv_pack_clamp_u8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
+ def int_spv_pack_clamp_s8 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_vector_int_ty], [IntrNoMem]>;
}
>From 8454a247cd45bc96c76dcbfb0673d6d1f7d89275 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Thu, 1 Oct 2026 12:59:50 +0100
Subject: [PATCH 2/3] [DirectX] Add hlsl pack intrinsics to DirectX backend
---
llvm/lib/Target/DirectX/DXIL.td | 9 ++++++
llvm/lib/Target/DirectX/DXILOpLowering.cpp | 34 ++++++++++++++++++++++
llvm/test/CodeGen/DirectX/pack_clamp_s8.ll | 26 +++++++++++++++++
llvm/test/CodeGen/DirectX/pack_clamp_u8.ll | 26 +++++++++++++++++
llvm/test/CodeGen/DirectX/pack_s8.ll | 26 +++++++++++++++++
llvm/test/CodeGen/DirectX/pack_u8.ll | 26 +++++++++++++++++
6 files changed, 147 insertions(+)
create mode 100644 llvm/test/CodeGen/DirectX/pack_clamp_s8.ll
create mode 100644 llvm/test/CodeGen/DirectX/pack_clamp_u8.ll
create mode 100644 llvm/test/CodeGen/DirectX/pack_s8.ll
create mode 100644 llvm/test/CodeGen/DirectX/pack_u8.ll
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 5e63e94a1c069ad..be5a7dca7f44f9e 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -1560,3 +1560,12 @@ def CreateHandleFromHeap : DXILOp<218, createHandleFromHeap> {
let stages = [Stages<DXIL1_6, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
+
+def Pack4x8 : DXILOp<220, pack4x8> {
+ let Doc = "pack 4 integer values into a single 32 bit value";
+ let arguments = [Int8Ty, OverloadTy, OverloadTy, OverloadTy, OverloadTy];
+ let overloads = [Overloads<DXIL1_6, [Int16Ty, Int32Ty]>];
+ let result = Int32Ty;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
+ let attributes = [Attributes<DXIL1_6, [ReadNone]>];
+}
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 2807e58942fbd05..0c146d5de56c331 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -1359,6 +1359,30 @@ class OpLowerer {
});
}
+ [[nodiscard]] bool lowerPack(Function &F, uint32_t PackOpCode) {
+ IRBuilder<> &IRB = OpBuilder.getIRB();
+ Type *RetTy = IRB.getInt32Ty();
+ return replaceFunction(F, [&](CallInst *CI) -> Error {
+ IRB.SetInsertPoint(CI);
+ SmallVector<Value *, 5> Args;
+ Args.push_back(IRB.getInt8(PackOpCode));
+
+ // Disassemble the vector to fill args 1-5 of the pack op.
+ Value *VecArg = CI->getArgOperand(0);
+ for (int i = 1; i < 5; i++)
+ Args.push_back(IRB.CreateExtractElement(VecArg, i - 1));
+
+ Expected<CallInst *> OpCall =
+ OpBuilder.tryCreateOp(OpCode::Pack4x8, Args, CI->getName(), RetTy);
+ if (Error E = OpCall.takeError())
+ return E;
+
+ CI->replaceAllUsesWith(*OpCall);
+ CI->eraseFromParent();
+ return Error::success();
+ });
+ }
+
bool lowerIntrinsics() {
bool Updated = false;
bool HasErrors = false;
@@ -1482,6 +1506,16 @@ class OpLowerer {
case Intrinsic::is_fpclass:
HasErrors |= lowerIsFPClass(F);
break;
+ case Intrinsic::dx_pack_u8:
+ case Intrinsic::dx_pack_s8:
+ HasErrors |= lowerPack(F, 0);
+ break;
+ case Intrinsic::dx_pack_clamp_u8:
+ HasErrors |= lowerPack(F, 1);
+ break;
+ case Intrinsic::dx_pack_clamp_s8:
+ HasErrors |= lowerPack(F, 2);
+ break;
}
Updated = true;
}
diff --git a/llvm/test/CodeGen/DirectX/pack_clamp_s8.ll b/llvm/test/CodeGen/DirectX/pack_clamp_s8.ll
new file mode 100644
index 000000000000000..7829991991b0a65
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/pack_clamp_s8.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef i32 @pack_clamp_s8_16(<4 x i16> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i16> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i16> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i16> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i16> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i16(i32 220, i8 2, i16 [[one]], i16 [[two]], i16 [[three]], i16 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.clamp.s8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_clamp_s8_32(<4 x i32> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i32> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i32> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i32> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i32> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i32(i32 220, i8 2, i32 [[one]], i32 [[two]], i32 [[three]], i32 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.clamp.s8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.dx.pack.clamp.s8.v4i16(<4 x i16>)
+declare i32 @llvm.dx.pack.clamp.s8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/DirectX/pack_clamp_u8.ll b/llvm/test/CodeGen/DirectX/pack_clamp_u8.ll
new file mode 100644
index 000000000000000..b85530b06429dc7
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/pack_clamp_u8.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef i32 @pack_clamp_u8_16(<4 x i16> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i16> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i16> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i16> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i16> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i16(i32 220, i8 1, i16 [[one]], i16 [[two]], i16 [[three]], i16 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.clamp.u8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_clamp_u8_32(<4 x i32> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i32> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i32> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i32> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i32> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i32(i32 220, i8 1, i32 [[one]], i32 [[two]], i32 [[three]], i32 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.clamp.u8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.dx.pack.clamp.u8.v4i16(<4 x i16>)
+declare i32 @llvm.dx.pack.clamp.u8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/DirectX/pack_s8.ll b/llvm/test/CodeGen/DirectX/pack_s8.ll
new file mode 100644
index 000000000000000..c54798270d1f628
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/pack_s8.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef i32 @pack_s8_16(<4 x i16> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i16> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i16> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i16> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i16> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i16(i32 220, i8 0, i16 [[one]], i16 [[two]], i16 [[three]], i16 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.s8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_s8_32(<4 x i32> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i32> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i32> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i32> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i32> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i32(i32 220, i8 0, i32 [[one]], i32 [[two]], i32 [[three]], i32 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.s8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.dx.pack.s8.v4i16(<4 x i16>)
+declare i32 @llvm.dx.pack.s8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/DirectX/pack_u8.ll b/llvm/test/CodeGen/DirectX/pack_u8.ll
new file mode 100644
index 000000000000000..bd52047d5748898
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/pack_u8.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s
+
+define noundef i32 @pack_u8_16(<4 x i16> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i16> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i16> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i16> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i16> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i16(i32 220, i8 0, i16 [[one]], i16 [[two]], i16 [[three]], i16 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.u8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_u8_32(<4 x i32> noundef %a) {
+; CHECK: [[one:%.*]] = extractelement <4 x i32> %a, i64 0
+; CHECK: [[two:%.*]] = extractelement <4 x i32> %a, i64 1
+; CHECK: [[three:%.*]] = extractelement <4 x i32> %a, i64 2
+; CHECK: [[four:%.*]] = extractelement <4 x i32> %a, i64 3
+; CHECK: [[packed:%.*]] = call i32 @dx.op.pack4x8.i32(i32 220, i8 0, i32 [[one]], i32 [[two]], i32 [[three]], i32 [[four]])
+; CHECK: ret i32 [[packed]]
+ %packed = call i32 @llvm.dx.pack.u8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.dx.pack.u8.v4i16(<4 x i16>)
+declare i32 @llvm.dx.pack.u8.v4i32(<4 x i32>)
>From d347fc7eab301dc09029537407d52fb29aca9652 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Thu, 1 Oct 2026 13:00:15 +0100
Subject: [PATCH 3/3] [SPIRV] Add hlsl pack intrinsics to SPIRV backend
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 53 +++++++++++++++++++
.../SPIRV/hlsl-intrinsics/pack_clamp_s8.ll | 44 +++++++++++++++
.../SPIRV/hlsl-intrinsics/pack_clamp_u8.ll | 42 +++++++++++++++
.../CodeGen/SPIRV/hlsl-intrinsics/pack_s8.ll | 28 ++++++++++
.../CodeGen/SPIRV/hlsl-intrinsics/pack_u8.ll | 28 ++++++++++
5 files changed, 195 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_s8.ll
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_u8.ll
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_s8.ll
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_u8.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 143e1c0e3ef2abb..84eb442bdaeddd9 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 selectPackInst(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
+ const bool Signed, const bool Clamp) const;
// Utilities
Register buildI32Constant(uint32_t Val, MachineInstr &I,
SPIRVTypeInst ResType = nullptr) const;
@@ -5262,6 +5264,49 @@ bool SPIRVInstructionSelector::selectDerivativeInst(
return true;
}
+bool SPIRVInstructionSelector::selectPackInst(Register ResVReg,
+ SPIRVTypeInst ResType,
+ MachineInstr &I,
+ const bool Signed,
+ const bool Clamp) const {
+ MachineIRBuilder MIRBuilder(I);
+ Register SrcReg = I.getOperand(2).getReg();
+ SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(SrcReg);
+
+ // pack_clamp_ instructions require SClamp before performing SConvert
+ // limits are determined by if the pack_clamp is signed or not
+ if (Clamp) {
+ const unsigned ElemWidth = GR.getScalarOrVectorBitWidth(SrcType);
+ APInt Lower =
+ Signed ? APInt(ElemWidth, -128, true) : APInt::getZero(ElemWidth);
+ APInt Upper =
+ Signed ? APInt(ElemWidth, 127, true) : APInt(ElemWidth, 255, true);
+ bool ZeroAsNull = !STI.isShader();
+ Register LowerLimit =
+ GR.getOrCreateConstVector(Lower, I, SrcType, TII, ZeroAsNull);
+ Register UpperLimit =
+ GR.getOrCreateConstVector(Upper, I, SrcType, TII, ZeroAsNull);
+
+ Register ClampedReg = MRI->createVirtualRegister(GR.getRegClass(SrcType));
+ if (!selectExtInst(ClampedReg, SrcType, I, CL::s_clamp, GL::SClamp,
+ /*setMIFlags=*/true, /*useMISrc=*/false,
+ {SrcReg, LowerLimit, UpperLimit}))
+ return false;
+ SrcReg = ClampedReg;
+ }
+
+ // Narrow to an i8 vector, then bitcast. Convert sign doesn't matter here
+ SPIRVTypeInst I8Type = GR.getOrCreateSPIRVIntegerType(8, MIRBuilder);
+ SPIRVTypeInst I8x4Type =
+ GR.getOrCreateSPIRVVectorType(I8Type, 4, MIRBuilder, true);
+ Register I8x4Reg = MRI->createVirtualRegister(GR.getRegClass(I8x4Type));
+ auto ConvertOpcode = Signed ? SPIRV::OpSConvert : SPIRV::OpUConvert;
+ if (!selectOpWithSrcs(I8x4Reg, I8x4Type, I, {SrcReg}, ConvertOpcode))
+ return false;
+
+ return selectOpWithSrcs(ResVReg, ResType, I, {I8x4Reg}, SPIRV::OpBitcast);
+}
+
bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
@@ -5853,6 +5898,14 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
MIB.constrainAllUses(TII, TRI, RBI);
return true;
}
+ case Intrinsic::spv_pack_u8:
+ return selectPackInst(ResVReg, ResType, I, false, false);
+ case Intrinsic::spv_pack_s8:
+ return selectPackInst(ResVReg, ResType, I, true, false);
+ case Intrinsic::spv_pack_clamp_u8:
+ return selectPackInst(ResVReg, ResType, I, false, true);
+ case Intrinsic::spv_pack_clamp_s8:
+ return selectPackInst(ResVReg, ResType, I, true, true);
default:
return diagnoseUnsupported(I, "intrinsic selection not implemented.");
}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_s8.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_s8.ll
new file mode 100644
index 000000000000000..f6784e076265c89
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_s8.ll
@@ -0,0 +1,44 @@
+; 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: [[import:%.*]] = OpExtInstImport "GLSL.std.450"
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[int16x4:%.*]] = OpTypeVector [[int16]] 4
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+; 65408 is -128 for SClamp
+; CHECK-DAG: [[int16_65408:%.*]] = OpConstant [[int16]] 65408
+; CHECK-DAG: [[int16_65408x4:%.*]] = OpConstantComposite [[int16x4]] [[int16_65408]] [[int16_65408]] [[int16_65408]] [[int16_65408]]
+; CHECK-DAG: [[int16_127:%.*]] = OpConstant [[int16]] 127
+; CHECK-DAG: [[int16_127x4:%.*]] = OpConstantComposite [[int16x4]] [[int16_127]] [[int16_127]] [[int16_127]] [[int16_127]]
+
+; 4294967168 is -128 for SClamp
+; CHECK-DAG: [[int32_4294967168:%.*]] = OpConstant [[int32]] 4294967168
+; CHECK-DAG: [[int32_4294967168x4:%.*]] = OpConstantComposite [[int32x4]] [[int32_4294967168]] [[int32_4294967168]] [[int32_4294967168]] [[int32_4294967168]]
+; CHECK-DAG: [[int32_127:%.*]] = OpConstant [[int32]] 127
+; CHECK-DAG: [[int32_127x4:%.*]] = OpConstantComposite [[int32x4]] [[int32_127]] [[int32_127]] [[int32_127]] [[int32_127]]
+
+define noundef i32 @pack_clamp_s8_16(<4 x i16> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int16x4]]
+; CHECK: [[clamped:%.*]] = OpExtInst [[int16x4]] [[import]] SClamp [[in]] [[int16_65408x4]] [[int16_127x4]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int8x4]] [[clamped]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.clamp.s8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_clamp_s8_32(<4 x i32> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int32x4]]
+; CHECK: [[clamped:%.*]] = OpExtInst [[int32x4]] [[import]] SClamp [[in]] [[int32_4294967168x4]] [[int32_127x4]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int8x4]] [[clamped]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.clamp.s8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.spv.pack.clamp.s8.v4i16(<4 x i16>)
+declare i32 @llvm.spv.pack.clamp.s8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_u8.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_u8.ll
new file mode 100644
index 000000000000000..c5234e3283fee70
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_clamp_u8.ll
@@ -0,0 +1,42 @@
+; 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: [[import:%.*]] = OpExtInstImport "GLSL.std.450"
+
+; CHECK-DAG: [[int8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[int8x4:%.*]] = OpTypeVector [[int8]] 4
+; CHECK-DAG: [[int16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[int16x4:%.*]] = OpTypeVector [[int16]] 4
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+; CHECK-DAG: [[int16_0:%.*]] = OpConstant [[int16]] 0
+; CHECK-DAG: [[int16_0x4:%.*]] = OpConstantComposite [[int16x4]] [[int16_0]] [[int16_0]] [[int16_0]] [[int16_0]]
+; CHECK-DAG: [[int16_255:%.*]] = OpConstant [[int16]] 255
+; CHECK-DAG: [[int16_255x4:%.*]] = OpConstantComposite [[int16x4]] [[int16_255]] [[int16_255]] [[int16_255]] [[int16_255]]
+
+; CHECK-DAG: [[int32_0:%.*]] = OpConstant [[int32]] 0
+; CHECK-DAG: [[int32_0x4:%.*]] = OpConstantComposite [[int32x4]] [[int32_0]] [[int32_0]] [[int32_0]] [[int32_0]]
+; CHECK-DAG: [[int32_255:%.*]] = OpConstant [[int32]] 255
+; CHECK-DAG: [[int32_255x4:%.*]] = OpConstantComposite [[int32x4]] [[int32_255]] [[int32_255]] [[int32_255]] [[int32_255]]
+
+define noundef i32 @pack_clamp_u8_16(<4 x i16> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int16x4]]
+; CHECK: [[clamped:%.*]] = OpExtInst [[int16x4]] [[import]] SClamp [[in]] [[int16_0x4]] [[int16_255x4]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int8x4]] [[clamped]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.clamp.u8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_clamp_u8_32(<4 x i32> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int32x4]]
+; CHECK: [[clamped:%.*]] = OpExtInst [[int32x4]] [[import]] SClamp [[in]] [[int32_0x4]] [[int32_255x4]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int8x4]] [[clamped]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.clamp.u8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.spv.pack.clamp.u8.v4i16(<4 x i16>)
+declare i32 @llvm.spv.pack.clamp.u8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_s8.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_s8.ll
new file mode 100644
index 000000000000000..191382e66b6e570
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_s8.ll
@@ -0,0 +1,28 @@
+; 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
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+define noundef i32 @pack_s8_16(<4 x i16> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int16x4]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int8x4]] [[in]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.s8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_s8_32(<4 x i32> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int32x4]]
+; CHECK: [[converted:%.*]] = OpSConvert [[int8x4]] [[in]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.s8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.spv.pack.s8.v4i16(<4 x i16>)
+declare i32 @llvm.spv.pack.s8.v4i32(<4 x i32>)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_u8.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_u8.ll
new file mode 100644
index 000000000000000..e87e57984d967df
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/pack_u8.ll
@@ -0,0 +1,28 @@
+; 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
+; CHECK-DAG: [[int32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[int32x4:%.*]] = OpTypeVector [[int32]] 4
+
+define noundef i32 @pack_u8_16(<4 x i16> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int16x4]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int8x4]] [[in]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.u8.v4i16(<4 x i16> %a)
+ ret i32 %packed
+}
+
+define noundef i32 @pack_u8_32(<4 x i32> noundef %a) {
+; CHECK: [[in:%.*]] = OpFunctionParameter [[int32x4]]
+; CHECK: [[converted:%.*]] = OpUConvert [[int8x4]] [[in]]
+; CHECK: [[cast:%.*]] = OpBitcast [[int32]] [[converted]]
+ %packed = call i32 @llvm.spv.pack.u8.v4i32(<4 x i32> %a)
+ ret i32 %packed
+}
+
+declare i32 @llvm.spv.pack.u8.v4i16(<4 x i16>)
+declare i32 @llvm.spv.pack.u8.v4i32(<4 x i32>)
More information about the llvm-commits
mailing list