[clang] [llvm] [HLSL][SPIRV] Added clamp intrinsic (PR #113394)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 22 16:27:05 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Adam Yang (adam-yang)

<details>
<summary>Changes</summary>

Fixes #<!-- -->88052

- Added the following intrinsics:
  - `int_spv_uclamp`
  - `int_spv_sclamp`
  - `int_spv_fclamp`
- Update the clamp.hlsl unit tests to include SPIRV
- Added the SPIRV specific tests

---

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


8 Files Affected:

- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+22-6) 
- (modified) clang/test/CodeGenHLSL/builtins/clamp.hlsl (+92-82) 
- (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+3) 
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+6) 
- (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clamp-vec.ll (+130) 
- (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clamp.ll (+122) 
- (added) llvm/test/CodeGen/SPIRV/opencl/clamp-vec.ll (+132) 
- (added) llvm/test/CodeGen/SPIRV/opencl/clamp.ll (+124) 


``````````diff
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1ad950798c2118..f6f6366cdccf1c 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -18661,14 +18661,30 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Value *OpMax = EmitScalarExpr(E->getArg(2));
 
     QualType Ty = E->getArg(0)->getType();
-    bool IsUnsigned = false;
     if (auto *VecTy = Ty->getAs<VectorType>())
       Ty = VecTy->getElementType();
-    IsUnsigned = Ty->isUnsignedIntegerType();
-    return Builder.CreateIntrinsic(
-        /*ReturnType=*/OpX->getType(),
-        IsUnsigned ? Intrinsic::dx_uclamp : Intrinsic::dx_clamp,
-        ArrayRef<Value *>{OpX, OpMin, OpMax}, nullptr, "dx.clamp");
+    bool IsUnsigned = Ty->isUnsignedIntegerType();
+    switch (CGM.getTarget().getTriple().getArch()) {
+    case llvm::Triple::dxil: {
+      return Builder.CreateIntrinsic(
+          /*ReturnType=*/OpX->getType(),
+          IsUnsigned ? Intrinsic::dx_uclamp : Intrinsic::dx_clamp,
+          ArrayRef<Value *>{OpX, OpMin, OpMax}, nullptr, "dx.clamp");
+    } break;
+    case llvm::Triple::spirv: {
+      Intrinsic::ID Intr = Intrinsic::spv_sclamp;
+      if (Ty->isFloatingType()) {
+        Intr = Intrinsic::spv_fclamp;
+      } else if (IsUnsigned) {
+        Intr = Intrinsic::spv_uclamp;
+      }
+      return Builder.CreateIntrinsic(OpX->getType(), Intr,
+                                     ArrayRef<Value *>{OpX, OpMin, OpMax},
+                                     nullptr, "spv.clamp");
+    } break;
+    default:
+      llvm_unreachable("Intrinsic clamp not supported by target architecture");
+    }
   }
   case Builtin::BI__builtin_hlsl_cross: {
     Value *Op0 = EmitScalarExpr(E->getArg(0));
diff --git a/clang/test/CodeGenHLSL/builtins/clamp.hlsl b/clang/test/CodeGenHLSL/builtins/clamp.hlsl
index af8f6b9733a071..806e786ae70931 100644
--- a/clang/test/CodeGenHLSL/builtins/clamp.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/clamp.hlsl
@@ -1,133 +1,143 @@
 // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
 // RUN:  -fnative-half-type -emit-llvm -disable-llvm-passes -o - | \
-// RUN:  FileCheck %s --check-prefixes=CHECK,NATIVE_HALF
+// RUN:  FileCheck %s --check-prefixes=CHECK,NATIVE_HALF \
+// RUN:  -DSCLAMP="dx.clamp" -DUCLAMP="dx.uclamp" -DFCLAMP="dx.clamp" -DFNATTR="noundef"
 // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
 // RUN:  -emit-llvm -disable-llvm-passes -o - | \
-// RUN:  FileCheck %s --check-prefixes=CHECK,NO_HALF
+// RUN:  FileCheck %s --check-prefixes=CHECK,NO_HALF \
+// RUN:  -DSCLAMP="dx.clamp" -DUCLAMP="dx.uclamp" -DFCLAMP="dx.clamp" -DFNATTR="noundef"
+// RUN: %clang_cc1 -finclude-default-header -triple spirv-unknown-vulkan-compute %s \
+// RUN:  -fnative-half-type -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK,NATIVE_HALF \
+// RUN:  -DSCLAMP="spv.sclamp" -DUCLAMP="spv.uclamp" -DFCLAMP="spv.fclamp" -DFNATTR="spir_func noundef"
+// RUN: %clang_cc1 -finclude-default-header -triple spirv-unknown-vulkan-compute %s \
+// RUN:  -emit-llvm -disable-llvm-passes -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK,NO_HALF \
+// RUN:  -DSCLAMP="spv.sclamp" -DUCLAMP="spv.uclamp" -DFCLAMP="spv.fclamp" -DFNATTR="spir_func noundef"
 
 #ifdef __HLSL_ENABLE_16_BIT
-// NATIVE_HALF-LABEL: define noundef i16 @_Z16test_clamp_short
-// NATIVE_HALF: call i16 @llvm.dx.clamp.i16(
+// NATIVE_HALF: define [[FNATTR]] i16 @_Z16test_clamp_short
+// NATIVE_HALF: call i16 @llvm.[[SCLAMP]].i16(
 int16_t test_clamp_short(int16_t p0, int16_t p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <2 x i16> @_Z17test_clamp_short2
-// NATIVE_HALF: call <2 x i16> @llvm.dx.clamp.v2i16(
+// NATIVE_HALF: define [[FNATTR]] <2 x i16> @_Z17test_clamp_short2
+// NATIVE_HALF: call <2 x i16> @llvm.[[SCLAMP]].v2i16(
 int16_t2 test_clamp_short2(int16_t2 p0, int16_t2 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <3 x i16> @_Z17test_clamp_short3
-// NATIVE_HALF: call <3 x i16> @llvm.dx.clamp.v3i16
+// NATIVE_HALF: define [[FNATTR]] <3 x i16> @_Z17test_clamp_short3
+// NATIVE_HALF: call <3 x i16> @llvm.[[SCLAMP]].v3i16
 int16_t3 test_clamp_short3(int16_t3 p0, int16_t3 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <4 x i16> @_Z17test_clamp_short4
-// NATIVE_HALF: call <4 x i16> @llvm.dx.clamp.v4i16
+// NATIVE_HALF: define [[FNATTR]] <4 x i16> @_Z17test_clamp_short4
+// NATIVE_HALF: call <4 x i16> @llvm.[[SCLAMP]].v4i16
 int16_t4 test_clamp_short4(int16_t4 p0, int16_t4 p1) { return clamp(p0, p1,p1); }
 
-// NATIVE_HALF-LABEL: define noundef i16 @_Z17test_clamp_ushort
-// NATIVE_HALF: call i16 @llvm.dx.uclamp.i16(
+// NATIVE_HALF: define [[FNATTR]] i16 @_Z17test_clamp_ushort
+// NATIVE_HALF: call i16 @llvm.[[UCLAMP]].i16(
 uint16_t test_clamp_ushort(uint16_t p0, uint16_t p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <2 x i16> @_Z18test_clamp_ushort2
-// NATIVE_HALF: call <2 x i16> @llvm.dx.uclamp.v2i16
+// NATIVE_HALF: define [[FNATTR]] <2 x i16> @_Z18test_clamp_ushort2
+// NATIVE_HALF: call <2 x i16> @llvm.[[UCLAMP]].v2i16
 uint16_t2 test_clamp_ushort2(uint16_t2 p0, uint16_t2 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <3 x i16> @_Z18test_clamp_ushort3
-// NATIVE_HALF: call <3 x i16> @llvm.dx.uclamp.v3i16
+// NATIVE_HALF: define [[FNATTR]] <3 x i16> @_Z18test_clamp_ushort3
+// NATIVE_HALF: call <3 x i16> @llvm.[[UCLAMP]].v3i16
 uint16_t3 test_clamp_ushort3(uint16_t3 p0, uint16_t3 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <4 x i16> @_Z18test_clamp_ushort4
-// NATIVE_HALF: call <4 x i16> @llvm.dx.uclamp.v4i16
+// NATIVE_HALF: define [[FNATTR]] <4 x i16> @_Z18test_clamp_ushort4
+// NATIVE_HALF: call <4 x i16> @llvm.[[UCLAMP]].v4i16
 uint16_t4 test_clamp_ushort4(uint16_t4 p0, uint16_t4 p1) { return clamp(p0, p1,p1); }
 #endif
 
-// CHECK-LABEL: define noundef i32 @_Z14test_clamp_int
-// CHECK: call i32 @llvm.dx.clamp.i32(
+// CHECK: define [[FNATTR]] i32 @_Z14test_clamp_int
+// CHECK: call i32 @llvm.[[SCLAMP]].i32(
 int test_clamp_int(int p0, int p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x i32> @_Z15test_clamp_int2
-// CHECK: call <2 x i32> @llvm.dx.clamp.v2i32
+// CHECK: define [[FNATTR]] <2 x i32> @_Z15test_clamp_int2
+// CHECK: call <2 x i32> @llvm.[[SCLAMP]].v2i32
 int2 test_clamp_int2(int2 p0, int2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x i32> @_Z15test_clamp_int3
-// CHECK: call <3 x i32> @llvm.dx.clamp.v3i32
+// CHECK: define [[FNATTR]] <3 x i32> @_Z15test_clamp_int3
+// CHECK: call <3 x i32> @llvm.[[SCLAMP]].v3i32
 int3 test_clamp_int3(int3 p0, int3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x i32> @_Z15test_clamp_int4
-// CHECK: call <4 x i32> @llvm.dx.clamp.v4i32
+// CHECK: define [[FNATTR]] <4 x i32> @_Z15test_clamp_int4
+// CHECK: call <4 x i32> @llvm.[[SCLAMP]].v4i32
 int4 test_clamp_int4(int4 p0, int4 p1) { return clamp(p0, p1,p1); }
 
-// CHECK-LABEL: define noundef i32 @_Z15test_clamp_uint
-// CHECK: call i32 @llvm.dx.uclamp.i32(
+// CHECK: define [[FNATTR]] i32 @_Z15test_clamp_uint
+// CHECK: call i32 @llvm.[[UCLAMP]].i32(
 int test_clamp_uint(uint p0, uint p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x i32> @_Z16test_clamp_uint2
-// CHECK: call <2 x i32> @llvm.dx.uclamp.v2i32
+// CHECK: define [[FNATTR]] <2 x i32> @_Z16test_clamp_uint2
+// CHECK: call <2 x i32> @llvm.[[UCLAMP]].v2i32
 uint2 test_clamp_uint2(uint2 p0, uint2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x i32> @_Z16test_clamp_uint3
-// CHECK: call <3 x i32> @llvm.dx.uclamp.v3i32
+// CHECK: define [[FNATTR]] <3 x i32> @_Z16test_clamp_uint3
+// CHECK: call <3 x i32> @llvm.[[UCLAMP]].v3i32
 uint3 test_clamp_uint3(uint3 p0, uint3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x i32> @_Z16test_clamp_uint4
-// CHECK: call <4 x i32> @llvm.dx.uclamp.v4i32
+// CHECK: define [[FNATTR]] <4 x i32> @_Z16test_clamp_uint4
+// CHECK: call <4 x i32> @llvm.[[UCLAMP]].v4i32
 uint4 test_clamp_uint4(uint4 p0, uint4 p1) { return clamp(p0, p1,p1); }
 
-// CHECK-LABEL: define noundef i64 @_Z15test_clamp_long
-// CHECK: call i64 @llvm.dx.clamp.i64(
+// CHECK: define [[FNATTR]] i64 @_Z15test_clamp_long
+// CHECK: call i64 @llvm.[[SCLAMP]].i64(
 int64_t test_clamp_long(int64_t p0, int64_t p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x i64> @_Z16test_clamp_long2
-// CHECK: call <2 x i64> @llvm.dx.clamp.v2i64
+// CHECK: define [[FNATTR]] <2 x i64> @_Z16test_clamp_long2
+// CHECK: call <2 x i64> @llvm.[[SCLAMP]].v2i64
 int64_t2 test_clamp_long2(int64_t2 p0, int64_t2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x i64> @_Z16test_clamp_long3
-// CHECK: call <3 x i64> @llvm.dx.clamp.v3i64
+// CHECK: define [[FNATTR]] <3 x i64> @_Z16test_clamp_long3
+// CHECK: call <3 x i64> @llvm.[[SCLAMP]].v3i64
 int64_t3 test_clamp_long3(int64_t3 p0, int64_t3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x i64> @_Z16test_clamp_long4
-// CHECK: call <4 x i64> @llvm.dx.clamp.v4i64
+// CHECK: define [[FNATTR]] <4 x i64> @_Z16test_clamp_long4
+// CHECK: call <4 x i64> @llvm.[[SCLAMP]].v4i64
 int64_t4 test_clamp_long4(int64_t4 p0, int64_t4 p1) { return clamp(p0, p1,p1); }
 
-// CHECK-LABEL: define noundef i64 @_Z16test_clamp_ulong
-// CHECK: call i64 @llvm.dx.uclamp.i64(
+// CHECK: define [[FNATTR]] i64 @_Z16test_clamp_ulong
+// CHECK: call i64 @llvm.[[UCLAMP]].i64(
 uint64_t test_clamp_ulong(uint64_t p0, uint64_t p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x i64> @_Z17test_clamp_ulong2
-// CHECK: call <2 x i64> @llvm.dx.uclamp.v2i64
+// CHECK: define [[FNATTR]] <2 x i64> @_Z17test_clamp_ulong2
+// CHECK: call <2 x i64> @llvm.[[UCLAMP]].v2i64
 uint64_t2 test_clamp_ulong2(uint64_t2 p0, uint64_t2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x i64> @_Z17test_clamp_ulong3
-// CHECK: call <3 x i64> @llvm.dx.uclamp.v3i64
+// CHECK: define [[FNATTR]] <3 x i64> @_Z17test_clamp_ulong3
+// CHECK: call <3 x i64> @llvm.[[UCLAMP]].v3i64
 uint64_t3 test_clamp_ulong3(uint64_t3 p0, uint64_t3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x i64> @_Z17test_clamp_ulong4
-// CHECK: call <4 x i64> @llvm.dx.uclamp.v4i64
+// CHECK: define [[FNATTR]] <4 x i64> @_Z17test_clamp_ulong4
+// CHECK: call <4 x i64> @llvm.[[UCLAMP]].v4i64
 uint64_t4 test_clamp_ulong4(uint64_t4 p0, uint64_t4 p1) { return clamp(p0, p1,p1); }
 
-// NATIVE_HALF-LABEL: define noundef half @_Z15test_clamp_half
-// NATIVE_HALF: call half @llvm.dx.clamp.f16(
-// NO_HALF-LABEL: define noundef float @_Z15test_clamp_half
-// NO_HALF: call float @llvm.dx.clamp.f32(
+// NATIVE_HALF: define [[FNATTR]] half @_Z15test_clamp_half
+// NATIVE_HALF: call half @llvm.[[FCLAMP]].f16(
+// NO_HALF: define [[FNATTR]] float @_Z15test_clamp_half
+// NO_HALF: call float @llvm.[[FCLAMP]].f32(
 half test_clamp_half(half p0, half p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <2 x half> @_Z16test_clamp_half2
-// NATIVE_HALF: call <2 x half> @llvm.dx.clamp.v2f16
-// NO_HALF-LABEL: define noundef <2 x float> @_Z16test_clamp_half2
-// NO_HALF: call <2 x float> @llvm.dx.clamp.v2f32(
+// NATIVE_HALF: define [[FNATTR]] <2 x half> @_Z16test_clamp_half2
+// NATIVE_HALF: call <2 x half> @llvm.[[FCLAMP]].v2f16
+// NO_HALF: define [[FNATTR]] <2 x float> @_Z16test_clamp_half2
+// NO_HALF: call <2 x float> @llvm.[[FCLAMP]].v2f32(
 half2 test_clamp_half2(half2 p0, half2 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <3 x half> @_Z16test_clamp_half3
-// NATIVE_HALF: call <3 x half> @llvm.dx.clamp.v3f16
-// NO_HALF-LABEL: define noundef <3 x float> @_Z16test_clamp_half3
-// NO_HALF: call <3 x float> @llvm.dx.clamp.v3f32(
+// NATIVE_HALF: define [[FNATTR]] <3 x half> @_Z16test_clamp_half3
+// NATIVE_HALF: call <3 x half> @llvm.[[FCLAMP]].v3f16
+// NO_HALF: define [[FNATTR]] <3 x float> @_Z16test_clamp_half3
+// NO_HALF: call <3 x float> @llvm.[[FCLAMP]].v3f32(
 half3 test_clamp_half3(half3 p0, half3 p1) { return clamp(p0, p1,p1); }
-// NATIVE_HALF-LABEL: define noundef <4 x half> @_Z16test_clamp_half4
-// NATIVE_HALF: call <4 x half> @llvm.dx.clamp.v4f16
-// NO_HALF-LABEL: define noundef <4 x float> @_Z16test_clamp_half4
-// NO_HALF: call <4 x float> @llvm.dx.clamp.v4f32(
+// NATIVE_HALF: define [[FNATTR]] <4 x half> @_Z16test_clamp_half4
+// NATIVE_HALF: call <4 x half> @llvm.[[FCLAMP]].v4f16
+// NO_HALF: define [[FNATTR]] <4 x float> @_Z16test_clamp_half4
+// NO_HALF: call <4 x float> @llvm.[[FCLAMP]].v4f32(
 half4 test_clamp_half4(half4 p0, half4 p1) { return clamp(p0, p1,p1); }
 
-// CHECK-LABEL: define noundef float @_Z16test_clamp_float
-// CHECK: call float @llvm.dx.clamp.f32(
+// CHECK: define [[FNATTR]] float @_Z16test_clamp_float
+// CHECK: call float @llvm.[[FCLAMP]].f32(
 float test_clamp_float(float p0, float p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x float> @_Z17test_clamp_float2
-// CHECK: call <2 x float> @llvm.dx.clamp.v2f32
+// CHECK: define [[FNATTR]] <2 x float> @_Z17test_clamp_float2
+// CHECK: call <2 x float> @llvm.[[FCLAMP]].v2f32
 float2 test_clamp_float2(float2 p0, float2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x float> @_Z17test_clamp_float3
-// CHECK: call <3 x float> @llvm.dx.clamp.v3f32
+// CHECK: define [[FNATTR]] <3 x float> @_Z17test_clamp_float3
+// CHECK: call <3 x float> @llvm.[[FCLAMP]].v3f32
 float3 test_clamp_float3(float3 p0, float3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x float> @_Z17test_clamp_float4
-// CHECK: call <4 x float> @llvm.dx.clamp.v4f32
+// CHECK: define [[FNATTR]] <4 x float> @_Z17test_clamp_float4
+// CHECK: call <4 x float> @llvm.[[FCLAMP]].v4f32
 float4 test_clamp_float4(float4 p0, float4 p1) { return clamp(p0, p1,p1); }
 
-// CHECK-LABEL: define noundef double @_Z17test_clamp_double
-// CHECK: call double @llvm.dx.clamp.f64(
+// CHECK: define [[FNATTR]] double @_Z17test_clamp_double
+// CHECK: call double @llvm.[[FCLAMP]].f64(
 double test_clamp_double(double p0, double p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <2 x double> @_Z18test_clamp_double2
-// CHECK: call <2 x double> @llvm.dx.clamp.v2f64
+// CHECK: define [[FNATTR]] <2 x double> @_Z18test_clamp_double2
+// CHECK: call <2 x double> @llvm.[[FCLAMP]].v2f64
 double2 test_clamp_double2(double2 p0, double2 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <3 x double> @_Z18test_clamp_double3
-// CHECK: call <3 x double> @llvm.dx.clamp.v3f64
+// CHECK: define [[FNATTR]] <3 x double> @_Z18test_clamp_double3
+// CHECK: call <3 x double> @llvm.[[FCLAMP]].v3f64
 double3 test_clamp_double3(double3 p0, double3 p1) { return clamp(p0, p1,p1); }
-// CHECK-LABEL: define noundef <4 x double> @_Z18test_clamp_double4
-// CHECK: call <4 x double> @llvm.dx.clamp.v4f64
+// CHECK: define [[FNATTR]] <4 x double> @_Z18test_clamp_double4
+// CHECK: call <4 x double> @llvm.[[FCLAMP]].v4f64
 double4 test_clamp_double4(double4 p0, double4 p1) { return clamp(p0, p1,p1); }
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 6df2eb156a0774..b19d2a299a6eeb 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -87,6 +87,9 @@ let TargetPrefix = "spv" in {
   def int_spv_wave_readlane : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, llvm_i32_ty], [IntrConvergent, IntrNoMem]>;
   def int_spv_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty], [IntrNoMem]>;
   def int_spv_radians : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
+  def int_spv_uclamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
+  def int_spv_sclamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
+  def int_spv_fclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
 
   // Create resource handle given the binding information. Returns a 
   // type appropriate for the kind of resource given the set id, binding id,
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index d9377fe4b91a1a..ca668941d0231d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2559,6 +2559,12 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   } break;
   case Intrinsic::spv_saturate:
     return selectSaturate(ResVReg, ResType, I);
+  case Intrinsic::spv_fclamp:
+    return selectExtInst(ResVReg, ResType, I, CL::fclamp, GL::FClamp);
+  case Intrinsic::spv_uclamp:
+    return selectExtInst(ResVReg, ResType, I, CL::u_clamp, GL::UClamp);
+  case Intrinsic::spv_sclamp:
+    return selectExtInst(ResVReg, ResType, I, CL::s_clamp, GL::SClamp);
   case Intrinsic::spv_wave_is_first_lane: {
     SPIRVType *IntTy = GR.getOrCreateSPIRVIntegerType(32, I, TII);
     return BuildMI(BB, I, I.getDebugLoc(),
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clamp-vec.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clamp-vec.ll
new file mode 100644
index 00000000000000..cd0111c399abf7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clamp-vec.ll
@@ -0,0 +1,130 @@
+; RUN: llc  -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#op_ext:]] = OpExtInstImport "GLSL.std.450"
+
+; CHECK-DAG: %[[#float_64:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+
+; CHECK-DAG: %[[#int_64:]] = OpTypeInt 64
+; CHECK-DAG: %[[#int_32:]] = OpTypeInt 32
+; CHECK-DAG: %[[#int_16:]] = OpTypeInt 16
+
+; CHECK-DAG: %[[#vec4_float_64:]] = OpTypeVector %[[#float_64]] 4
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+
+; CHECK-DAG: %[[#vec4_int_64:]] = OpTypeVector %[[#int_64]] 4
+; CHECK-DAG: %[[#vec4_int_32:]] = OpTypeVector %[[#int_32]] 4
+; CHECK-DAG: %[[#vec4_int_16:]] = OpTypeVector %[[#int_16]] 4
+
+; CHECK-LABEL: Begin function test_sclamp_v4i16
+define noundef <4 x i16> @test_sclamp_v4i16(<4 x i16> noundef %a, <4 x i16> noundef %b, <4 x i16> noundef %c) {
+entry:
+  ; CHECK: %[[#vec4_i16_arg0:]] = OpFunctionParameter %[[#vec4_int_16]]
+  ; CHECK: %[[#vec4_i16_arg1:]] = OpFunctionParameter %[[#vec4_int_16]]
+  ; CHECK: %[[#vec4_i16_arg2:]] = OpFunctionParameter %[[#vec4_int_16]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_int_16]] %[[#op_ext]] SClamp %[[#vec4_i16_arg0]] %[[#vec4_i16_arg1]] %[[#vec4_i16_arg2]]
+  %0 = call <4 x i16> @llvm.spv.sclamp.v4i16(<4 x i16> %a, <4 x i16> %b, <4 x i16> %c)
+  ret <4 x i16> %0
+}
+
+; CHECK-LABEL: Begin function test_sclamp_v4i32
+define noundef <4 x i32> @test_sclamp_v4i32(<4 x i32> noundef %a, <4 x i32> noundef %b, <4 x i32> noundef %c) {
+entry:
+  ; CHECK: %[[#vec4_i32_arg0:]] = OpFunctionParameter %[[#vec4_int_32]]
+  ; CHECK: %[[#vec4_i32_arg1:]] = OpFunctionParameter %[[#vec4_int_32]]
+  ; CHECK: %[[#vec4_i32_arg2:]] = OpFunctionParameter %[[#vec4_int_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_int_32]] %[[#op_ext]] SClamp %[[#vec4_i32_arg0]] %[[#vec4_i32_arg1]] %[[#vec4_i32_arg2]]
+  %0 = call <4 x i32> @llvm.spv.sclamp.v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %c)
+  ret <4 x i32> %0
+}
+
+; CHECK-LABEL: Begin function test_sclamp_v4i64
+define noundef <4 x i64> @test_sclamp_v4i64(<4 x i64> noundef %a, <4 x i64> noundef %b, <4 x i64> noundef %c) {
+entry:
+  ; CHECK: %[[#vec4_i64_arg0:]] = OpFunctionParameter %[[#vec4_int_64]]
+  ; CHECK: %[[#vec4_i64_arg1:]] = OpFunctionParameter %[[#vec4_int_64]]
+  ; CHECK: %[[#vec4_i64_arg2:]] = OpFunctionParameter %[[#vec4_int_64]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_int_64]] %[[#op_ext]] SClamp %[[#vec4_i64_arg0]] %[[#vec4_i64_arg1]] %[[#vec4_i64_arg2]]
+  %0 = call <4 x i64> @llvm.spv.sclamp.v4i64(<4 x i64> %a, <4 x i64> %b, <4 x i64> %c)
+...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/113394


More information about the cfe-commits mailing list