Skip to content

Commit 8386a38

Browse files
authored
[HLSL] implement clamp intrinsic (#85424)
closes #70071 - `CGBuiltin.cpp` - Add the unsigned\generic clamp intrinsic emitter. - `IntrinsicsDirectX.td` - add the `dx.clamp` & `dx.uclamp` intrinsics - `DXILIntrinsicExpansion.cpp` - add the `clamp` instruction expansion while maintaining vector form. - `SemaChecking.cpp` - Add `clamp` builtin Sema Checks. - `Builtins.td` - add a `clamp` builtin - `hlsl_intrinsics.h` - add the `clamp` api Why `clamp` as instruction expansion for DXIL? 1. SPIR-V has a GLSL `clamp` extension via: - [FClamp](https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html#FClamp) - [UClamp](https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html#UClamp) - [SClamp](https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html#SClamp) 2. Further Clamp lowers to `min(max( x, min_range ), max_range)` which we have float, signed, & unsigned dixilOps.
1 parent 113214e commit 8386a38

File tree

19 files changed

+780
-19
lines changed

19 files changed

+780
-19
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4591,6 +4591,12 @@ def HLSLWaveActiveCountBits : LangBuiltin<"HLSL_LANG"> {
45914591
let Prototype = "unsigned int(bool)";
45924592
}
45934593

4594+
def HLSLClamp : LangBuiltin<"HLSL_LANG"> {
4595+
let Spellings = ["__builtin_hlsl_elementwise_clamp"];
4596+
let Attributes = [NoThrow, Const];
4597+
let Prototype = "void(...)";
4598+
}
4599+
45944600
def HLSLCreateHandle : LangBuiltin<"HLSL_LANG"> {
45954601
let Spellings = ["__builtin_hlsl_create_handle"];
45964602
let Attributes = [NoThrow, Const];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18048,6 +18048,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
1804818048
/*ReturnType=*/llvm::Type::getInt1Ty(getLLVMContext()),
1804918049
Intrinsic::dx_any, ArrayRef<Value *>{Op0}, nullptr, "dx.any");
1805018050
}
18051+
case Builtin::BI__builtin_hlsl_elementwise_clamp: {
18052+
Value *OpX = EmitScalarExpr(E->getArg(0));
18053+
Value *OpMin = EmitScalarExpr(E->getArg(1));
18054+
Value *OpMax = EmitScalarExpr(E->getArg(2));
18055+
18056+
QualType Ty = E->getArg(0)->getType();
18057+
bool IsUnsigned = false;
18058+
if (auto *VecTy = Ty->getAs<VectorType>())
18059+
Ty = VecTy->getElementType();
18060+
IsUnsigned = Ty->isUnsignedIntegerType();
18061+
return Builder.CreateIntrinsic(
18062+
/*ReturnType=*/OpX->getType(),
18063+
IsUnsigned ? Intrinsic::dx_uclamp : Intrinsic::dx_clamp,
18064+
ArrayRef<Value *>{OpX, OpMin, OpMax}, nullptr, "dx.clamp");
18065+
}
1805118066
case Builtin::BI__builtin_hlsl_dot: {
1805218067
Value *Op0 = EmitScalarExpr(E->getArg(0));
1805318068
Value *Op1 = EmitScalarExpr(E->getArg(1));

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,116 @@ double3 ceil(double3);
252252
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_ceil)
253253
double4 ceil(double4);
254254

255+
//===----------------------------------------------------------------------===//
256+
// clamp builtins
257+
//===----------------------------------------------------------------------===//
258+
259+
/// \fn T clamp(T X, T Min, T Max)
260+
/// \brief Clamps the specified value \a X to the specified
261+
/// minimum ( \a Min) and maximum ( \a Max) range.
262+
/// \param X A value to clamp.
263+
/// \param Min The specified minimum range.
264+
/// \param Max The specified maximum range.
265+
///
266+
/// Returns The clamped value for the \a X parameter.
267+
/// For values of -INF or INF, clamp will behave as expected.
268+
/// However for values of NaN, the results are undefined.
269+
270+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
271+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
272+
half clamp(half, half, half);
273+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
274+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
275+
half2 clamp(half2, half2, half2);
276+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
277+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
278+
half3 clamp(half3, half3, half3);
279+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
280+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
281+
half4 clamp(half4, half4, half4);
282+
283+
#ifdef __HLSL_ENABLE_16_BIT
284+
_HLSL_AVAILABILITY(shadermodel, 6.2)
285+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
286+
int16_t clamp(int16_t, int16_t, int16_t);
287+
_HLSL_AVAILABILITY(shadermodel, 6.2)
288+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
289+
int16_t2 clamp(int16_t2, int16_t2, int16_t2);
290+
_HLSL_AVAILABILITY(shadermodel, 6.2)
291+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
292+
int16_t3 clamp(int16_t3, int16_t3, int16_t3);
293+
_HLSL_AVAILABILITY(shadermodel, 6.2)
294+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
295+
int16_t4 clamp(int16_t4, int16_t4, int16_t4);
296+
297+
_HLSL_AVAILABILITY(shadermodel, 6.2)
298+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
299+
uint16_t clamp(uint16_t, uint16_t, uint16_t);
300+
_HLSL_AVAILABILITY(shadermodel, 6.2)
301+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
302+
uint16_t2 clamp(uint16_t2, uint16_t2, uint16_t2);
303+
_HLSL_AVAILABILITY(shadermodel, 6.2)
304+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
305+
uint16_t3 clamp(uint16_t3, uint16_t3, uint16_t3);
306+
_HLSL_AVAILABILITY(shadermodel, 6.2)
307+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
308+
uint16_t4 clamp(uint16_t4, uint16_t4, uint16_t4);
309+
#endif
310+
311+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
312+
int clamp(int, int, int);
313+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
314+
int2 clamp(int2, int2, int2);
315+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
316+
int3 clamp(int3, int3, int3);
317+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
318+
int4 clamp(int4, int4, int4);
319+
320+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
321+
uint clamp(uint, uint, uint);
322+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
323+
uint2 clamp(uint2, uint2, uint2);
324+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
325+
uint3 clamp(uint3, uint3, uint3);
326+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
327+
uint4 clamp(uint4, uint4, uint4);
328+
329+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
330+
int64_t clamp(int64_t, int64_t, int64_t);
331+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
332+
int64_t2 clamp(int64_t2, int64_t2, int64_t2);
333+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
334+
int64_t3 clamp(int64_t3, int64_t3, int64_t3);
335+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
336+
int64_t4 clamp(int64_t4, int64_t4, int64_t4);
337+
338+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
339+
uint64_t clamp(uint64_t, uint64_t, uint64_t);
340+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
341+
uint64_t2 clamp(uint64_t2, uint64_t2, uint64_t2);
342+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
343+
uint64_t3 clamp(uint64_t3, uint64_t3, uint64_t3);
344+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
345+
uint64_t4 clamp(uint64_t4, uint64_t4, uint64_t4);
346+
347+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
348+
float clamp(float, float, float);
349+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
350+
float2 clamp(float2, float2, float2);
351+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
352+
float3 clamp(float3, float3, float3);
353+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
354+
float4 clamp(float4, float4, float4);
355+
356+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
357+
double clamp(double, double, double);
358+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
359+
double2 clamp(double2, double2, double2);
360+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
361+
double3 clamp(double3, double3, double3);
362+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
363+
double4 clamp(double4, double4, double4);
364+
255365
//===----------------------------------------------------------------------===//
256366
// cos builtins
257367
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaChecking.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5499,6 +5499,17 @@ bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
54995499
return true;
55005500
break;
55015501
}
5502+
case Builtin::BI__builtin_hlsl_elementwise_clamp: {
5503+
if (checkArgCount(*this, TheCall, 3))
5504+
return true;
5505+
if (CheckVectorElementCallArgs(this, TheCall))
5506+
return true;
5507+
if (SemaBuiltinElementwiseTernaryMath(
5508+
TheCall, /*CheckForFloatArgs*/
5509+
TheCall->getArg(0)->getType()->hasFloatingRepresentation()))
5510+
return true;
5511+
break;
5512+
}
55025513
case Builtin::BI__builtin_hlsl_dot: {
55035514
if (checkArgCount(*this, TheCall, 2))
55045515
return true;
@@ -5547,7 +5558,9 @@ bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
55475558
return true;
55485559
if (CheckVectorElementCallArgs(this, TheCall))
55495560
return true;
5550-
if (SemaBuiltinElementwiseTernaryMath(TheCall, /*CheckForFloatArgs*/ false))
5561+
if (SemaBuiltinElementwiseTernaryMath(
5562+
TheCall, /*CheckForFloatArgs*/
5563+
TheCall->getArg(0)->getType()->hasFloatingRepresentation()))
55515564
return true;
55525565
}
55535566
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -disable-llvm-passes -o - | FileCheck %s
2+
3+
// CHECK-LABEL: builtin_test_clamp_int4
4+
// CHECK: %dx.clamp = call <4 x i32> @llvm.dx.clamp.v4i32(<4 x i32> %0, <4 x i32> %1, <4 x i32> %2)
5+
// CHECK: ret <4 x i32> %dx.clamp
6+
int4 builtin_test_clamp_int4(int4 p0, int4 p1, int4 p2) {
7+
return __builtin_hlsl_elementwise_clamp(p0, p1, p2);
8+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
2+
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
3+
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
4+
// RUN: --check-prefixes=CHECK,NATIVE_HALF
5+
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
6+
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
7+
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF
8+
9+
#ifdef __HLSL_ENABLE_16_BIT
10+
// NATIVE_HALF: define noundef i16 @
11+
// NATIVE_HALF: call i16 @llvm.dx.clamp.i16(
12+
int16_t test_clamp_short(int16_t p0, int16_t p1) { return clamp(p0, p1,p1); }
13+
// NATIVE_HALF: define noundef <2 x i16> @
14+
// NATIVE_HALF: call <2 x i16> @llvm.dx.clamp.v2i16(
15+
int16_t2 test_clamp_short2(int16_t2 p0, int16_t2 p1) { return clamp(p0, p1,p1); }
16+
// NATIVE_HALF: define noundef <3 x i16> @
17+
// NATIVE_HALF: call <3 x i16> @llvm.dx.clamp.v3i16
18+
int16_t3 test_clamp_short3(int16_t3 p0, int16_t3 p1) { return clamp(p0, p1,p1); }
19+
// NATIVE_HALF: define noundef <4 x i16> @
20+
// NATIVE_HALF: call <4 x i16> @llvm.dx.clamp.v4i16
21+
int16_t4 test_clamp_short4(int16_t4 p0, int16_t4 p1) { return clamp(p0, p1,p1); }
22+
23+
// NATIVE_HALF: define noundef i16 @
24+
// NATIVE_HALF: call i16 @llvm.dx.uclamp.i16(
25+
uint16_t test_clamp_ushort(uint16_t p0, uint16_t p1) { return clamp(p0, p1,p1); }
26+
// NATIVE_HALF: define noundef <2 x i16> @
27+
// NATIVE_HALF: call <2 x i16> @llvm.dx.uclamp.v2i16
28+
uint16_t2 test_clamp_ushort2(uint16_t2 p0, uint16_t2 p1) { return clamp(p0, p1,p1); }
29+
// NATIVE_HALF: define noundef <3 x i16> @
30+
// NATIVE_HALF: call <3 x i16> @llvm.dx.uclamp.v3i16
31+
uint16_t3 test_clamp_ushort3(uint16_t3 p0, uint16_t3 p1) { return clamp(p0, p1,p1); }
32+
// NATIVE_HALF: define noundef <4 x i16> @
33+
// NATIVE_HALF: call <4 x i16> @llvm.dx.uclamp.v4i16
34+
uint16_t4 test_clamp_ushort4(uint16_t4 p0, uint16_t4 p1) { return clamp(p0, p1,p1); }
35+
#endif
36+
37+
// CHECK: define noundef i32 @
38+
// CHECK: call i32 @llvm.dx.clamp.i32(
39+
int test_clamp_int(int p0, int p1) { return clamp(p0, p1,p1); }
40+
// CHECK: define noundef <2 x i32> @
41+
// CHECK: call <2 x i32> @llvm.dx.clamp.v2i32
42+
int2 test_clamp_int2(int2 p0, int2 p1) { return clamp(p0, p1,p1); }
43+
// CHECK: define noundef <3 x i32> @
44+
// CHECK: call <3 x i32> @llvm.dx.clamp.v3i32
45+
int3 test_clamp_int3(int3 p0, int3 p1) { return clamp(p0, p1,p1); }
46+
// CHECK: define noundef <4 x i32> @
47+
// CHECK: call <4 x i32> @llvm.dx.clamp.v4i32
48+
int4 test_clamp_int4(int4 p0, int4 p1) { return clamp(p0, p1,p1); }
49+
50+
// CHECK: define noundef i32 @
51+
// CHECK: call i32 @llvm.dx.uclamp.i32(
52+
int test_clamp_uint(uint p0, uint p1) { return clamp(p0, p1,p1); }
53+
// CHECK: define noundef <2 x i32> @
54+
// CHECK: call <2 x i32> @llvm.dx.uclamp.v2i32
55+
uint2 test_clamp_uint2(uint2 p0, uint2 p1) { return clamp(p0, p1,p1); }
56+
// CHECK: define noundef <3 x i32> @
57+
// CHECK: call <3 x i32> @llvm.dx.uclamp.v3i32
58+
uint3 test_clamp_uint3(uint3 p0, uint3 p1) { return clamp(p0, p1,p1); }
59+
// CHECK: define noundef <4 x i32> @
60+
// CHECK: call <4 x i32> @llvm.dx.uclamp.v4i32
61+
uint4 test_clamp_uint4(uint4 p0, uint4 p1) { return clamp(p0, p1,p1); }
62+
63+
// CHECK: define noundef i64 @
64+
// CHECK: call i64 @llvm.dx.clamp.i64(
65+
int64_t test_clamp_long(int64_t p0, int64_t p1) { return clamp(p0, p1,p1); }
66+
// CHECK: define noundef <2 x i64> @
67+
// CHECK: call <2 x i64> @llvm.dx.clamp.v2i64
68+
int64_t2 test_clamp_long2(int64_t2 p0, int64_t2 p1) { return clamp(p0, p1,p1); }
69+
// CHECK: define noundef <3 x i64> @
70+
// CHECK: call <3 x i64> @llvm.dx.clamp.v3i64
71+
int64_t3 test_clamp_long3(int64_t3 p0, int64_t3 p1) { return clamp(p0, p1,p1); }
72+
// CHECK: define noundef <4 x i64> @
73+
// CHECK: call <4 x i64> @llvm.dx.clamp.v4i64
74+
int64_t4 test_clamp_long4(int64_t4 p0, int64_t4 p1) { return clamp(p0, p1,p1); }
75+
76+
// CHECK: define noundef i64 @
77+
// CHECK: call i64 @llvm.dx.uclamp.i64(
78+
uint64_t test_clamp_long(uint64_t p0, uint64_t p1) { return clamp(p0, p1,p1); }
79+
// CHECK: define noundef <2 x i64> @
80+
// CHECK: call <2 x i64> @llvm.dx.uclamp.v2i64
81+
uint64_t2 test_clamp_long2(uint64_t2 p0, uint64_t2 p1) { return clamp(p0, p1,p1); }
82+
// CHECK: define noundef <3 x i64> @
83+
// CHECK: call <3 x i64> @llvm.dx.uclamp.v3i64
84+
uint64_t3 test_clamp_long3(uint64_t3 p0, uint64_t3 p1) { return clamp(p0, p1,p1); }
85+
// CHECK: define noundef <4 x i64> @
86+
// CHECK: call <4 x i64> @llvm.dx.uclamp.v4i64
87+
uint64_t4 test_clamp_long4(uint64_t4 p0, uint64_t4 p1) { return clamp(p0, p1,p1); }
88+
89+
// NATIVE_HALF: define noundef half @
90+
// NATIVE_HALF: call half @llvm.dx.clamp.f16(
91+
// NO_HALF: define noundef float @"?test_clamp_half
92+
// NO_HALF: call float @llvm.dx.clamp.f32(
93+
half test_clamp_half(half p0, half p1) { return clamp(p0, p1,p1); }
94+
// NATIVE_HALF: define noundef <2 x half> @
95+
// NATIVE_HALF: call <2 x half> @llvm.dx.clamp.v2f16
96+
// NO_HALF: define noundef <2 x float> @"?test_clamp_half2
97+
// NO_HALF: call <2 x float> @llvm.dx.clamp.v2f32(
98+
half2 test_clamp_half2(half2 p0, half2 p1) { return clamp(p0, p1,p1); }
99+
// NATIVE_HALF: define noundef <3 x half> @
100+
// NATIVE_HALF: call <3 x half> @llvm.dx.clamp.v3f16
101+
// NO_HALF: define noundef <3 x float> @"?test_clamp_half3
102+
// NO_HALF: call <3 x float> @llvm.dx.clamp.v3f32(
103+
half3 test_clamp_half3(half3 p0, half3 p1) { return clamp(p0, p1,p1); }
104+
// NATIVE_HALF: define noundef <4 x half> @
105+
// NATIVE_HALF: call <4 x half> @llvm.dx.clamp.v4f16
106+
// NO_HALF: define noundef <4 x float> @"?test_clamp_half4
107+
// NO_HALF: call <4 x float> @llvm.dx.clamp.v4f32(
108+
half4 test_clamp_half4(half4 p0, half4 p1) { return clamp(p0, p1,p1); }
109+
110+
// CHECK: define noundef float @"?test_clamp_float
111+
// CHECK: call float @llvm.dx.clamp.f32(
112+
float test_clamp_float(float p0, float p1) { return clamp(p0, p1,p1); }
113+
// CHECK: define noundef <2 x float> @"?test_clamp_float2
114+
// CHECK: call <2 x float> @llvm.dx.clamp.v2f32
115+
float2 test_clamp_float2(float2 p0, float2 p1) { return clamp(p0, p1,p1); }
116+
// CHECK: define noundef <3 x float> @"?test_clamp_float3
117+
// CHECK: call <3 x float> @llvm.dx.clamp.v3f32
118+
float3 test_clamp_float3(float3 p0, float3 p1) { return clamp(p0, p1,p1); }
119+
// CHECK: define noundef <4 x float> @"?test_clamp_float4
120+
// CHECK: call <4 x float> @llvm.dx.clamp.v4f32
121+
float4 test_clamp_float4(float4 p0, float4 p1) { return clamp(p0, p1,p1); }
122+
123+
// CHECK: define noundef double @
124+
// CHECK: call double @llvm.dx.clamp.f64(
125+
double test_clamp_double(double p0, double p1) { return clamp(p0, p1,p1); }
126+
// CHECK: define noundef <2 x double> @
127+
// CHECK: call <2 x double> @llvm.dx.clamp.v2f64
128+
double2 test_clamp_double2(double2 p0, double2 p1) { return clamp(p0, p1,p1); }
129+
// CHECK: define noundef <3 x double> @
130+
// CHECK: call <3 x double> @llvm.dx.clamp.v3f64
131+
double3 test_clamp_double3(double3 p0, double3 p1) { return clamp(p0, p1,p1); }
132+
// CHECK: define noundef <4 x double> @
133+
// CHECK: call <4 x double> @llvm.dx.clamp.v4f64
134+
double4 test_clamp_double4(double4 p0, double4 p1) { return clamp(p0, p1,p1); }

0 commit comments

Comments
 (0)