Skip to content

Commit c6f533e

Browse files
committed
[HLSL] Implementation lerp intrinsic
This is the start of implementing the lerp intrinsic https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp Builtins.td - defines the builtin hlsl_intrinsics.h - defines the lerp api DiagnosticSemaKinds.td - needed a new error to be inclusive for more than two operands. CGBuiltin.cpp - add the lerp intrinsic lowering SemaChecking.cpp - type checks for lerp builtin IntrinsicsDirectX.td - define the lerp intrinsic this change implements the first half of #70102
1 parent e9cdd16 commit c6f533e

File tree

10 files changed

+388
-41
lines changed

10 files changed

+388
-41
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,12 @@ def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> {
45304530
let Prototype = "void(...)";
45314531
}
45324532

4533+
def HLSLLerp : LangBuiltin<"HLSL_LANG"> {
4534+
let Spellings = ["__builtin_hlsl_lerp"];
4535+
let Attributes = [NoThrow, Const];
4536+
let Prototype = "void(...)";
4537+
}
4538+
45334539
// Builtins for XRay.
45344540
def XRayCustomEvent : Builtin {
45354541
let Spellings = ["__xray_customevent"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10266,6 +10266,11 @@ def err_block_on_vm : Error<
1026610266
def err_sizeless_nonlocal : Error<
1026710267
"non-local variable with sizeless type %0">;
1026810268

10269+
def err_vec_builtin_non_vector_all : Error<
10270+
"all arguments to %0 must be vectors">;
10271+
def err_vec_builtin_incompatible_vector_all : Error<
10272+
"all arguments to %0 must have vectors of the same type">;
10273+
1026910274
def err_vec_builtin_non_vector : Error<
1027010275
"first two arguments to %0 must be vectors">;
1027110276
def err_vec_builtin_incompatible_vector : Error<

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18006,6 +18006,43 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
1800618006
/*ReturnType*/ T0->getScalarType(), Intrinsic::dx_dot,
1800718007
ArrayRef<Value *>{Op0, Op1}, nullptr, "dx.dot");
1800818008
} break;
18009+
case Builtin::BI__builtin_hlsl_lerp: {
18010+
Value *X = EmitScalarExpr(E->getArg(0));
18011+
Value *Y = EmitScalarExpr(E->getArg(1));
18012+
Value *S = EmitScalarExpr(E->getArg(2));
18013+
llvm::Type *Xty = X->getType();
18014+
llvm::Type *Yty = Y->getType();
18015+
llvm::Type *Sty = S->getType();
18016+
if (!Xty->isVectorTy() && !Yty->isVectorTy() && !Sty->isVectorTy()) {
18017+
if (Xty->isFloatingPointTy()) {
18018+
auto V = Builder.CreateFSub(Y, X);
18019+
V = Builder.CreateFMul(S, V);
18020+
return Builder.CreateFAdd(X, V, "dx.lerp");
18021+
}
18022+
llvm_unreachable("Scalar Lerp is only supported on floats.");
18023+
}
18024+
// A VectorSplat should have happened
18025+
assert(Xty->isVectorTy() && Yty->isVectorTy() && Sty->isVectorTy() &&
18026+
"Lerp of vector and scalar is not supported.");
18027+
18028+
[[maybe_unused]] auto *XVecTy =
18029+
E->getArg(0)->getType()->getAs<VectorType>();
18030+
[[maybe_unused]] auto *YVecTy =
18031+
E->getArg(1)->getType()->getAs<VectorType>();
18032+
[[maybe_unused]] auto *SVecTy =
18033+
E->getArg(2)->getType()->getAs<VectorType>();
18034+
// A HLSLVectorTruncation should have happend
18035+
assert(XVecTy->getNumElements() == YVecTy->getNumElements() &&
18036+
XVecTy->getNumElements() == SVecTy->getNumElements() &&
18037+
"Lerp requires vectors to be of the same size.");
18038+
assert(XVecTy->getElementType()->isRealFloatingType() &&
18039+
XVecTy->getElementType() == YVecTy->getElementType() &&
18040+
XVecTy->getElementType() == SVecTy->getElementType() &&
18041+
"Lerp requires float vectors to be of the same type.");
18042+
return Builder.CreateIntrinsic(
18043+
/*ReturnType*/ Xty, Intrinsic::dx_lerp, ArrayRef<Value *>{X, Y, S},
18044+
nullptr, "dx.lerp");
18045+
}
1800918046
}
1801018047
return nullptr;
1801118048
}

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,42 @@ double3 floor(double3);
317317
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_floor)
318318
double4 floor(double4);
319319

320+
//===----------------------------------------------------------------------===//
321+
// lerp builtins
322+
//===----------------------------------------------------------------------===//
323+
324+
/// \fn T lerp(T x, T y, T s)
325+
/// \brief Returns the linear interpolation of x to y by s.
326+
/// \param x [in] The first-floating point value.
327+
/// \param y [in] The second-floating point value.
328+
/// \param s [in] A value that linearly interpolates between the x parameter and
329+
/// the y parameter.
330+
///
331+
/// Linear interpolation is based on the following formula: x*(1-s) + y*s which
332+
/// can equivalently be written as x + s(y-x).
333+
334+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
335+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
336+
half lerp(half, half, half);
337+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
338+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
339+
half2 lerp(half2, half2, half2);
340+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
341+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
342+
half3 lerp(half3, half3, half3);
343+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
344+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
345+
half4 lerp(half4, half4, half4);
346+
347+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
348+
float lerp(float, float, float);
349+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
350+
float2 lerp(float2, float2, float2);
351+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
352+
float3 lerp(float3, float3, float3);
353+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
354+
float4 lerp(float4, float4, float4);
355+
320356
//===----------------------------------------------------------------------===//
321357
// log builtins
322358
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaChecking.cpp

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5169,49 +5169,63 @@ bool Sema::CheckPPCMMAType(QualType Type, SourceLocation TypeLoc) {
51695169
bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) {
51705170
assert(TheCall->getNumArgs() > 1);
51715171
ExprResult A = TheCall->getArg(0);
5172-
ExprResult B = TheCall->getArg(1);
5172+
51735173
QualType ArgTyA = A.get()->getType();
5174-
QualType ArgTyB = B.get()->getType();
5174+
51755175
auto *VecTyA = ArgTyA->getAs<VectorType>();
5176-
auto *VecTyB = ArgTyB->getAs<VectorType>();
51775176
SourceLocation BuiltinLoc = TheCall->getBeginLoc();
5178-
if (VecTyA == nullptr && VecTyB == nullptr)
5179-
return false;
51805177

5181-
if (VecTyA && VecTyB) {
5182-
bool retValue = false;
5183-
if (VecTyA->getElementType() != VecTyB->getElementType()) {
5184-
// Note: type promotion is intended to be handeled via the intrinsics
5185-
// and not the builtin itself.
5186-
S->Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_incompatible_vector)
5187-
<< TheCall->getDirectCallee()
5188-
<< SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc());
5189-
retValue = true;
5190-
}
5191-
if (VecTyA->getNumElements() != VecTyB->getNumElements()) {
5192-
// if we get here a HLSLVectorTruncation is needed.
5193-
S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector)
5194-
<< TheCall->getDirectCallee()
5195-
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
5196-
TheCall->getArg(1)->getEndLoc());
5197-
retValue = true;
5198-
}
5178+
for (unsigned i = 1; i < TheCall->getNumArgs(); ++i) {
5179+
ExprResult B = TheCall->getArg(i);
5180+
QualType ArgTyB = B.get()->getType();
5181+
auto *VecTyB = ArgTyB->getAs<VectorType>();
5182+
if (VecTyA == nullptr && VecTyB == nullptr)
5183+
return false;
51995184

5200-
if (retValue)
5201-
TheCall->setType(VecTyA->getElementType());
5185+
if (VecTyA && VecTyB) {
5186+
bool retValue = false;
5187+
if (VecTyA->getElementType() != VecTyB->getElementType()) {
5188+
// Note: type promotion is intended to be handeled via the intrinsics
5189+
// and not the builtin itself.
5190+
S->Diag(TheCall->getBeginLoc(),
5191+
diag::err_vec_builtin_incompatible_vector_all)
5192+
<< TheCall->getDirectCallee()
5193+
<< SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc());
5194+
retValue = true;
5195+
}
5196+
if (VecTyA->getNumElements() != VecTyB->getNumElements()) {
5197+
// if we get here a HLSLVectorTruncation is needed.
5198+
S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector_all)
5199+
<< TheCall->getDirectCallee()
5200+
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
5201+
TheCall->getArg(1)->getEndLoc());
5202+
retValue = true;
5203+
}
5204+
5205+
if (!retValue)
5206+
TheCall->setType(VecTyA->getElementType());
52025207

5203-
return retValue;
5208+
return retValue;
5209+
}
52045210
}
52055211

52065212
// Note: if we get here one of the args is a scalar which
52075213
// requires a VectorSplat on Arg0 or Arg1
5208-
S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector)
5214+
S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector_all)
52095215
<< TheCall->getDirectCallee()
52105216
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
52115217
TheCall->getArg(1)->getEndLoc());
52125218
return true;
52135219
}
52145220

5221+
bool checkAllArgsAreFloatRepresnations(CallExpr *TheCall) {
5222+
for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) {
5223+
if (!TheCall->getArg(0)->getType()->hasFloatingRepresentation())
5224+
return true;
5225+
}
5226+
return false;
5227+
}
5228+
52155229
// Note: returning true in this case results in CheckBuiltinFunctionCall
52165230
// returning an ExprError
52175231
bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
@@ -5225,6 +5239,17 @@ bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
52255239
return true;
52265240
break;
52275241
}
5242+
case Builtin::BI__builtin_hlsl_lerp: {
5243+
if (checkArgCount(*this, TheCall, 3))
5244+
return true;
5245+
if (CheckVectorElementCallArgs(this, TheCall))
5246+
return true;
5247+
if (SemaBuiltinElementwiseTernaryMath(TheCall))
5248+
return true;
5249+
if (checkAllArgsAreFloatRepresnations(TheCall))
5250+
return true;
5251+
break;
5252+
}
52285253
}
52295254
return false;
52305255
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
4+
5+
// CHECK-LABEL: builtin_lerp_half_scalar
6+
// CHECK: %3 = fsub double %conv1, %conv
7+
// CHECK: %4 = fmul double %conv2, %3
8+
// CHECK: %dx.lerp = fadd double %conv, %4
9+
// CHECK: %conv3 = fptrunc double %dx.lerp to half
10+
// CHECK: ret half %conv3
11+
half builtin_lerp_half_scalar (half p0) {
12+
return __builtin_hlsl_lerp ( p0, p0, p0 );
13+
}
14+
15+
// CHECK-LABEL: builtin_lerp_float_scalar
16+
// CHECK: %3 = fsub double %conv1, %conv
17+
// CHECK: %4 = fmul double %conv2, %3
18+
// CHECK: %dx.lerp = fadd double %conv, %4
19+
// CHECK: %conv3 = fptrunc double %dx.lerp to float
20+
// CHECK: ret float %conv3
21+
float builtin_lerp_float_scalar ( float p0) {
22+
return __builtin_hlsl_lerp ( p0, p0, p0 );
23+
}
24+
25+
// CHECK-LABEL: builtin_lerp_half_vector
26+
// CHECK: %dx.lerp = call <3 x half> @llvm.dx.lerp.v3f16(<3 x half> %0, <3 x half> %1, <3 x half> %2)
27+
// CHECK: ret <3 x half> %dx.lerp
28+
half3 builtin_lerp_half_vector (half3 p0) {
29+
return __builtin_hlsl_lerp ( p0, p0, p0 );
30+
}
31+
32+
// CHECK-LABEL: builtin_lerp_floar_vector
33+
// CHECK: %dx.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %2)
34+
// CHECK: ret <2 x float> %dx.lerp
35+
float2 builtin_lerp_floar_vector ( float2 p0) {
36+
return __builtin_hlsl_lerp ( p0, p0, p0 );
37+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// RUN: %clang_cc1 -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 -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: %3 = fsub half %1, %0
10+
// NATIVE_HALF: %4 = fmul half %2, %3
11+
// NATIVE_HALF: %dx.lerp = fadd half %0, %4
12+
// NATIVE_HALF: ret half %dx.lerp
13+
// NO_HALF: %3 = fsub float %1, %0
14+
// NO_HALF: %4 = fmul float %2, %3
15+
// NO_HALF: %dx.lerp = fadd float %0, %4
16+
// NO_HALF: ret float %dx.lerp
17+
half test_lerp_half ( half p0) {
18+
return lerp ( p0, p0, p0 );
19+
}
20+
21+
// NATIVE_HALF: %dx.lerp = call <2 x half> @llvm.dx.lerp.v2f16(<2 x half> %0, <2 x half> %1, <2 x half> %2)
22+
// NATIVE_HALF: ret <2 x half> %dx.lerp
23+
// NO_HALF: %dx.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %2)
24+
// NO_HALF: ret <2 x float> %dx.lerp
25+
half2 test_lerp_half2 ( half2 p0, half2 p1 ) {
26+
return lerp ( p0, p0, p0 );
27+
}
28+
29+
// NATIVE_HALF: %dx.lerp = call <3 x half> @llvm.dx.lerp.v3f16(<3 x half> %0, <3 x half> %1, <3 x half> %2)
30+
// NATIVE_HALF: ret <3 x half> %dx.lerp
31+
// NO_HALF: %dx.lerp = call <3 x float> @llvm.dx.lerp.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %2)
32+
// NO_HALF: ret <3 x float> %dx.lerp
33+
half3 test_lerp_half3 ( half3 p0, half3 p1 ) {
34+
return lerp ( p0, p0, p0 );
35+
}
36+
37+
// NATIVE_HALF: %dx.lerp = call <4 x half> @llvm.dx.lerp.v4f16(<4 x half> %0, <4 x half> %1, <4 x half> %2)
38+
// NATIVE_HALF: ret <4 x half> %dx.lerp
39+
// NO_HALF: %dx.lerp = call <4 x float> @llvm.dx.lerp.v4f32(<4 x float> %0, <4 x float> %1, <4 x float> %2)
40+
// NO_HALF: ret <4 x float> %dx.lerp
41+
half4 test_lerp_half4 ( half4 p0, half4 p1 ) {
42+
return lerp ( p0, p0, p0 );
43+
}
44+
45+
// CHECK: %3 = fsub float %1, %0
46+
// CHECK: %4 = fmul float %2, %3
47+
// CHECK: %dx.lerp = fadd float %0, %4
48+
// CHECK: ret float %dx.lerp
49+
float test_lerp_float ( float p0, float p1 ) {
50+
return lerp ( p0, p0, p0 );
51+
}
52+
53+
// CHECK: %dx.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %2)
54+
// CHECK: ret <2 x float> %dx.lerp
55+
float2 test_lerp_float2 ( float2 p0, float2 p1 ) {
56+
return lerp ( p0, p0, p0 );
57+
}
58+
59+
// CHECK: %dx.lerp = call <3 x float> @llvm.dx.lerp.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %2)
60+
// CHECK: ret <3 x float> %dx.lerp
61+
float3 test_lerp_float3 ( float3 p0, float3 p1 ) {
62+
return lerp ( p0, p0, p0 );
63+
}
64+
65+
// CHECK: %dx.lerp = call <4 x float> @llvm.dx.lerp.v4f32(<4 x float> %0, <4 x float> %1, <4 x float> %2)
66+
// CHECK: ret <4 x float> %dx.lerp
67+
float4 test_lerp_float4 ( float4 p0, float4 p1) {
68+
return lerp ( p0, p0, p0 );
69+
}
70+
71+
// CHECK: %dx.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %splat.splat, <2 x float> %1, <2 x float> %2)
72+
// CHECK: ret <2 x float> %dx.lerp
73+
float2 test_lerp_float2_splat ( float p0, float2 p1 ) {
74+
return lerp( p0, p1, p1 );
75+
}
76+
77+
// CHECK: %dx.lerp = call <3 x float> @llvm.dx.lerp.v3f32(<3 x float> %splat.splat, <3 x float> %1, <3 x float> %2)
78+
// CHECK: ret <3 x float> %dx.lerp
79+
float3 test_lerp_float3_splat ( float p0, float3 p1 ) {
80+
return lerp( p0, p1, p1 );
81+
}
82+
83+
// CHECK: %dx.lerp = call <4 x float> @llvm.dx.lerp.v4f32(<4 x float> %splat.splat, <4 x float> %1, <4 x float> %2)
84+
// CHECK: ret <4 x float> %dx.lerp
85+
float4 test_lerp_float4_splat ( float p0, float4 p1 ) {
86+
return lerp( p0, p1, p1 );
87+
}
88+
89+
// CHECK: %conv = sitofp i32 %2 to float
90+
// CHECK: %splat.splatinsert = insertelement <2 x float> poison, float %conv, i64 0
91+
// CHECK: %splat.splat = shufflevector <2 x float> %splat.splatinsert, <2 x float> poison, <2 x i32> zeroinitializer
92+
// CHECK: %dx.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %0, <2 x float> %1, <2 x float> %splat.splat)
93+
// CHECK: ret <2 x float> %dx.lerp
94+
float2 test_lerp_float2_int_splat ( float2 p0, int p1 ) {
95+
return lerp ( p0, p0, p1 );
96+
}
97+
98+
// CHECK: %conv = sitofp i32 %2 to float
99+
// CHECK: %splat.splatinsert = insertelement <3 x float> poison, float %conv, i64 0
100+
// CHECK: %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> poison, <3 x i32> zeroinitializer
101+
// CHECK: %dx.lerp = call <3 x float> @llvm.dx.lerp.v3f32(<3 x float> %0, <3 x float> %1, <3 x float> %splat.splat)
102+
// CHECK: ret <3 x float> %dx.lerp
103+
float3 test_lerp_float3_int_splat ( float3 p0, int p1 ) {
104+
return lerp ( p0, p0, p1 );
105+
}

0 commit comments

Comments
 (0)