Skip to content

Commit e9fd01b

Browse files
committed
Implement support to compile HLSL intrinsic "saturate" to DXIL
Add SPIRV Codegen support to transform saturate(x) to clamp(x, 0.0, 1.0) Add tests for DXIL and SPIRV CodeGen.
1 parent c7a54bf commit e9fd01b

File tree

15 files changed

+597
-17
lines changed

15 files changed

+597
-17
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4745,6 +4745,12 @@ def HLSLRSqrt : LangBuiltin<"HLSL_LANG"> {
47454745
let Prototype = "void(...)";
47464746
}
47474747

4748+
def HLSLSaturate : LangBuiltin<"HLSL_LANG"> {
4749+
let Spellings = ["__builtin_hlsl_elementwise_saturate"];
4750+
let Attributes = [NoThrow, Const];
4751+
let Prototype = "void(...)";
4752+
}
4753+
47484754
// Builtins for XRay.
47494755
def XRayCustomEvent : Builtin {
47504756
let Spellings = ["__xray_customevent"];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18667,6 +18667,15 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
1866718667
/*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getRsqrtIntrinsic(),
1866818668
ArrayRef<Value *>{Op0}, nullptr, "hlsl.rsqrt");
1866918669
}
18670+
case Builtin::BI__builtin_hlsl_elementwise_saturate: {
18671+
Value *Op0 = EmitScalarExpr(E->getArg(0));
18672+
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
18673+
llvm_unreachable("saturate operand must have a float representation");
18674+
return Builder.CreateIntrinsic(
18675+
/*ReturnType=*/Op0->getType(),
18676+
CGM.getHLSLRuntime().getSaturateIntrinsic(), ArrayRef<Value *>{Op0},
18677+
nullptr, "hlsl.saturate");
18678+
}
1867018679
case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
1867118680
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
1867218681
llvm::FunctionType::get(IntTy, {}, false), "__hlsl_wave_get_lane_index",

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class CGHLSLRuntime {
7979
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
8080
GENERATE_HLSL_INTRINSIC_FUNCTION(Normalize, normalize)
8181
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
82+
GENERATE_HLSL_INTRINSIC_FUNCTION(Saturate, saturate)
8283
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)
8384

8485
//===----------------------------------------------------------------------===//

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ float4 lerp(float4, float4, float4);
916916
/// \brief Returns the length of the specified floating-point vector.
917917
/// \param x [in] The vector of floats, or a scalar float.
918918
///
919-
/// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + …).
919+
/// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + ...).
920920

921921
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
922922
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
@@ -1564,6 +1564,45 @@ float3 round(float3);
15641564
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_roundeven)
15651565
float4 round(float4);
15661566

1567+
//===----------------------------------------------------------------------===//
1568+
// saturate builtins
1569+
//===----------------------------------------------------------------------===//
1570+
1571+
/// \fn T saturate(T Val)
1572+
/// \brief Returns input value, \a Val, clamped within the range of 0.0f
1573+
/// to 1.0f. \param Val The input value.
1574+
1575+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
1576+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1577+
half saturate(half);
1578+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
1579+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1580+
half2 saturate(half2);
1581+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
1582+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1583+
half3 saturate(half3);
1584+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
1585+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1586+
half4 saturate(half4);
1587+
1588+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1589+
float saturate(float);
1590+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1591+
float2 saturate(float2);
1592+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1593+
float3 saturate(float3);
1594+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1595+
float4 saturate(float4);
1596+
1597+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1598+
double saturate(double);
1599+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1600+
double2 saturate(double2);
1601+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1602+
double3 saturate(double3);
1603+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
1604+
double4 saturate(double4);
1605+
15671606
//===----------------------------------------------------------------------===//
15681607
// sin builtins
15691608
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ static bool isLegalTypeForHLSLSV_DispatchThreadID(QualType T) {
356356
return true;
357357
}
358358

359-
void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
359+
void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
360360
auto *VD = cast<ValueDecl>(D);
361361
if (!isLegalTypeForHLSLSV_DispatchThreadID(VD->getType())) {
362362
Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
@@ -1045,6 +1045,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
10451045
return true;
10461046
break;
10471047
}
1048+
case Builtin::BI__builtin_hlsl_elementwise_saturate:
10481049
case Builtin::BI__builtin_hlsl_elementwise_rcp: {
10491050
if (CheckAllArgsHaveFloatRepresentation(&SemaRef, TheCall))
10501051
return true;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
// NATIVE_HALF: define noundef half @
10+
// NATIVE_HALF: call half @llvm.dx.saturate.f16(
11+
// NO_HALF: define noundef float @"?test_saturate_half
12+
// NO_HALF: call float @llvm.dx.saturate.f32(
13+
half test_saturate_half(half p0) { return saturate(p0); }
14+
// NATIVE_HALF: define noundef <2 x half> @
15+
// NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
16+
// NO_HALF: define noundef <2 x float> @"?test_saturate_half2
17+
// NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
18+
half2 test_saturate_half2(half2 p0) { return saturate(p0); }
19+
// NATIVE_HALF: define noundef <3 x half> @
20+
// NATIVE_HALF: call <3 x half> @llvm.dx.saturate.v3f16
21+
// NO_HALF: define noundef <3 x float> @"?test_saturate_half3
22+
// NO_HALF: call <3 x float> @llvm.dx.saturate.v3f32(
23+
half3 test_saturate_half3(half3 p0) { return saturate(p0); }
24+
// NATIVE_HALF: define noundef <4 x half> @
25+
// NATIVE_HALF: call <4 x half> @llvm.dx.saturate.v4f16
26+
// NO_HALF: define noundef <4 x float> @"?test_saturate_half4
27+
// NO_HALF: call <4 x float> @llvm.dx.saturate.v4f32(
28+
half4 test_saturate_half4(half4 p0) { return saturate(p0); }
29+
30+
// CHECK: define noundef float @"?test_saturate_float
31+
// CHECK: call float @llvm.dx.saturate.f32(
32+
float test_saturate_float(float p0) { return saturate(p0); }
33+
// CHECK: define noundef <2 x float> @"?test_saturate_float2
34+
// CHECK: call <2 x float> @llvm.dx.saturate.v2f32
35+
float2 test_saturate_float2(float2 p0) { return saturate(p0); }
36+
// CHECK: define noundef <3 x float> @"?test_saturate_float3
37+
// CHECK: call <3 x float> @llvm.dx.saturate.v3f32
38+
float3 test_saturate_float3(float3 p0) { return saturate(p0); }
39+
// CHECK: define noundef <4 x float> @"?test_saturate_float4
40+
// CHECK: call <4 x float> @llvm.dx.saturate.v4f32
41+
float4 test_saturate_float4(float4 p0) { return saturate(p0); }
42+
43+
// CHECK: define noundef double @
44+
// CHECK: call double @llvm.dx.saturate.f64(
45+
double test_saturate_double(double p0) { return saturate(p0); }
46+
// CHECK: define noundef <2 x double> @
47+
// CHECK: call <2 x double> @llvm.dx.saturate.v2f64
48+
double2 test_saturate_double2(double2 p0) { return saturate(p0); }
49+
// CHECK: define noundef <3 x double> @
50+
// CHECK: call <3 x double> @llvm.dx.saturate.v3f64
51+
double3 test_saturate_double3(double3 p0) { return saturate(p0); }
52+
// CHECK: define noundef <4 x double> @
53+
// CHECK: call <4 x double> @llvm.dx.saturate.v4f64
54+
double4 test_saturate_double4(double4 p0) { return saturate(p0); }
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify -verify-ignore-unexpected -Werror
2+
3+
float2 test_no_arg() {
4+
return saturate();
5+
// expected-error@-1 {{no matching function for call to 'saturate'}}
6+
}
7+
8+
float2 test_too_many_arg(float2 p0) {
9+
return saturate(p0, p0, p0, p0);
10+
// expected-error@-1 {{no matching function for call to 'saturate'}}
11+
}
12+
13+
float2 test_saturate_vector_size_mismatch(float3 p0) {
14+
return saturate(p0);
15+
// expected-error@-1 {{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>'}}
16+
}
17+
18+
float2 test_saturate_float2_int_splat(int p0) {
19+
return saturate(p0);
20+
// expected-error@-1 {{call to 'saturate' is ambiguous}}
21+
}
22+
23+
float2 test_saturate_int_vect_to_float_vec_promotion(int2 p0) {
24+
return saturate(p0);
25+
// expected-error@-1 {{call to 'saturate' is ambiguous}}
26+
}
27+
28+
float test_saturate_bool_type_promotion(bool p0) {
29+
return saturate(p0);
30+
// expected-error@-1 {{call to 'saturate' is ambiguous}}
31+
}

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def int_dx_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
3434
def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
3535
def int_dx_clamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
3636
def int_dx_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
37+
def int_dx_saturate : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
3738

3839
def int_dx_dot2 :
3940
Intrinsic<[LLVMVectorElementType<0>],

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ let TargetPrefix = "spv" in {
6161
def int_spv_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
6262
def int_spv_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
6363
def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
64-
def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
64+
def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
6565
[IntrNoMem, IntrWillReturn] >;
6666
def int_spv_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty]>;
6767
def int_spv_normalize : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
6868
def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
69+
def int_spv_saturate : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
6970
}

llvm/lib/Target/DirectX/DXIL.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ def Abs : DXILOp<6, unary> {
325325
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
326326
}
327327

328+
def Saturate : DXILOp<7, unary> {
329+
let Doc = "Clamps a single or double precision floating point value to [0.0f...1.0f].";
330+
let LLVMIntrinsic = int_dx_saturate;
331+
let arguments = [overloadTy];
332+
let result = overloadTy;
333+
let overloads = [Overloads<DXIL1_0, [halfTy, floatTy, doubleTy]>];
334+
let stages = [Stages<DXIL1_0, [all_stages]>];
335+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
336+
}
337+
328338
def IsInf : DXILOp<9, isSpecialFloat> {
329339
let Doc = "Determines if the specified value is infinite.";
330340
let LLVMIntrinsic = int_dx_isinf;

llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/STLExtras.h"
1616
#include "llvm/ADT/SmallVector.h"
1717
#include "llvm/CodeGen/Passes.h"
18+
#include "llvm/IR/DerivedTypes.h"
1819
#include "llvm/IR/IRBuilder.h"
1920
#include "llvm/IR/Instruction.h"
2021
#include "llvm/IR/Instructions.h"
@@ -46,6 +47,7 @@ static bool isIntrinsicExpansion(Function &F) {
4647
case Intrinsic::dx_normalize:
4748
case Intrinsic::dx_sdot:
4849
case Intrinsic::dx_udot:
50+
case Intrinsic::dx_saturate:
4951
return true;
5052
}
5153
return false;

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
247247
bool selectNormalize(Register ResVReg, const SPIRVType *ResType,
248248
MachineInstr &I) const;
249249

250+
bool selectSaturate(Register ResVReg, const SPIRVType *ResType,
251+
MachineInstr &I) const;
252+
250253
bool selectSpvThreadId(Register ResVReg, const SPIRVType *ResType,
251254
MachineInstr &I) const;
252255

@@ -259,6 +262,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
259262
Register buildZerosValF(const SPIRVType *ResType, MachineInstr &I) const;
260263
Register buildOnesVal(bool AllOnes, const SPIRVType *ResType,
261264
MachineInstr &I) const;
265+
Register buildOnesValF(const SPIRVType *ResType, MachineInstr &I) const;
262266

263267
bool wrapIntoSpecConstantOp(MachineInstr &I,
264268
SmallVector<Register> &CompositeArgs) const;
@@ -1285,6 +1289,34 @@ static unsigned getBoolCmpOpcode(unsigned PredNum) {
12851289
}
12861290
}
12871291

1292+
static APFloat getZeroFP(const Type *LLVMFloatTy) {
1293+
if (!LLVMFloatTy)
1294+
return APFloat::getZero(APFloat::IEEEsingle());
1295+
switch (LLVMFloatTy->getScalarType()->getTypeID()) {
1296+
case Type::HalfTyID:
1297+
return APFloat::getZero(APFloat::IEEEhalf());
1298+
default:
1299+
case Type::FloatTyID:
1300+
return APFloat::getZero(APFloat::IEEEsingle());
1301+
case Type::DoubleTyID:
1302+
return APFloat::getZero(APFloat::IEEEdouble());
1303+
}
1304+
}
1305+
1306+
static APFloat getOneFP(const Type *LLVMFloatTy) {
1307+
if (!LLVMFloatTy)
1308+
return APFloat::getOne(APFloat::IEEEsingle());
1309+
switch (LLVMFloatTy->getScalarType()->getTypeID()) {
1310+
case Type::HalfTyID:
1311+
return APFloat::getOne(APFloat::IEEEhalf());
1312+
default:
1313+
case Type::FloatTyID:
1314+
return APFloat::getOne(APFloat::IEEEsingle());
1315+
case Type::DoubleTyID:
1316+
return APFloat::getOne(APFloat::IEEEdouble());
1317+
}
1318+
}
1319+
12881320
bool SPIRVInstructionSelector::selectAnyOrAll(Register ResVReg,
12891321
const SPIRVType *ResType,
12901322
MachineInstr &I,
@@ -1446,6 +1478,28 @@ bool SPIRVInstructionSelector::selectRsqrt(Register ResVReg,
14461478
.constrainAllUses(TII, TRI, RBI);
14471479
}
14481480

1481+
/// Transform saturate(x) to clamp(x, 0.0f, 1.0f) as SPIRV
1482+
/// does not have a saturate builtin.
1483+
bool SPIRVInstructionSelector::selectSaturate(Register ResVReg,
1484+
const SPIRVType *ResType,
1485+
MachineInstr &I) const {
1486+
assert(I.getNumOperands() == 3);
1487+
assert(I.getOperand(2).isReg());
1488+
MachineBasicBlock &BB = *I.getParent();
1489+
Register VZero = buildZerosValF(ResType, I);
1490+
Register VOne = buildOnesValF(ResType, I);
1491+
1492+
return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
1493+
.addDef(ResVReg)
1494+
.addUse(GR.getSPIRVTypeID(ResType))
1495+
.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
1496+
.addImm(GL::FClamp)
1497+
.addUse(I.getOperand(2).getReg())
1498+
.addUse(VZero)
1499+
.addUse(VOne)
1500+
.constrainAllUses(TII, TRI, RBI);
1501+
}
1502+
14491503
bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
14501504
const SPIRVType *ResType,
14511505
MachineInstr &I) const {
@@ -1724,20 +1778,6 @@ Register SPIRVInstructionSelector::buildZerosVal(const SPIRVType *ResType,
17241778
return GR.getOrCreateConstInt(0, I, ResType, TII, ZeroAsNull);
17251779
}
17261780

1727-
static APFloat getZeroFP(const Type *LLVMFloatTy) {
1728-
if (!LLVMFloatTy)
1729-
return APFloat::getZero(APFloat::IEEEsingle());
1730-
switch (LLVMFloatTy->getScalarType()->getTypeID()) {
1731-
case Type::HalfTyID:
1732-
return APFloat::getZero(APFloat::IEEEhalf());
1733-
default:
1734-
case Type::FloatTyID:
1735-
return APFloat::getZero(APFloat::IEEEsingle());
1736-
case Type::DoubleTyID:
1737-
return APFloat::getZero(APFloat::IEEEdouble());
1738-
}
1739-
}
1740-
17411781
Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType,
17421782
MachineInstr &I) const {
17431783
// OpenCL uses nulls for Zero. In HLSL we don't use null constants.
@@ -1748,6 +1788,16 @@ Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType,
17481788
return GR.getOrCreateConstFP(VZero, I, ResType, TII, ZeroAsNull);
17491789
}
17501790

1791+
Register SPIRVInstructionSelector::buildOnesValF(const SPIRVType *ResType,
1792+
MachineInstr &I) const {
1793+
// OpenCL uses nulls for Zero. In HLSL we don't use null constants.
1794+
bool ZeroAsNull = STI.isOpenCLEnv();
1795+
APFloat VOne = getOneFP(GR.getTypeForSPIRVType(ResType));
1796+
if (ResType->getOpcode() == SPIRV::OpTypeVector)
1797+
return GR.getOrCreateConstVector(VOne, I, ResType, TII, ZeroAsNull);
1798+
return GR.getOrCreateConstFP(VOne, I, ResType, TII, ZeroAsNull);
1799+
}
1800+
17511801
Register SPIRVInstructionSelector::buildOnesVal(bool AllOnes,
17521802
const SPIRVType *ResType,
17531803
MachineInstr &I) const {
@@ -2181,6 +2231,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
21812231
Size = 0;
21822232
BuildMI(BB, I, I.getDebugLoc(), TII.get(Op)).addUse(PtrReg).addImm(Size);
21832233
} break;
2234+
case Intrinsic::spv_saturate:
2235+
return selectSaturate(ResVReg, ResType, I);
21842236
default: {
21852237
std::string DiagMsg;
21862238
raw_string_ostream OS(DiagMsg);

0 commit comments

Comments
 (0)