Skip to content

Commit 56240f0

Browse files
committed
start of lerp intrinsic
1 parent 777ac46 commit 56240f0

File tree

10 files changed

+381
-41
lines changed

10 files changed

+381
-41
lines changed

clang/include/clang/Basic/Builtins.td

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

4539+
def HLSLLerp : LangBuiltin<"HLSL_LANG"> {
4540+
let Spellings = ["__builtin_hlsl_lerp"];
4541+
let Attributes = [NoThrow, Const];
4542+
let Prototype = "void(...)";
4543+
}
4544+
45394545
// Builtins for XRay.
45404546
def XRayCustomEvent : Builtin {
45414547
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18007,6 +18007,46 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
1800718007
/*ReturnType*/ T0->getScalarType(), Intrinsic::dx_dot,
1800818008
ArrayRef<Value *>{Op0, Op1}, nullptr, "dx.dot");
1800918009
} break;
18010+
case Builtin::BI__builtin_hlsl_lerp: {
18011+
Value *X = EmitScalarExpr(E->getArg(0));
18012+
Value *Y = EmitScalarExpr(E->getArg(1));
18013+
Value *S = EmitScalarExpr(E->getArg(2));
18014+
llvm::Type *Xty = X->getType();
18015+
llvm::Type *Yty = Y->getType();
18016+
llvm::Type *Sty = S->getType();
18017+
if (!Xty->isVectorTy() && !Yty->isVectorTy() && !Sty->isVectorTy()) {
18018+
if (Xty->isFloatingPointTy()) {
18019+
auto V = Builder.CreateFSub(Y, X);
18020+
V = Builder.CreateFMul(S, V);
18021+
return Builder.CreateFAdd(X, V, "dx.lerp");
18022+
}
18023+
// DXC does this via casting to float should we do the same thing?
18024+
if (Xty->isIntegerTy()) {
18025+
auto V = Builder.CreateSub(Y, X);
18026+
V = Builder.CreateMul(S, V);
18027+
return Builder.CreateAdd(X, V, "dx.lerp");
18028+
}
18029+
// Bools should have been promoted
18030+
llvm_unreachable("Scalar Lerp is only supported on ints and floats.");
18031+
}
18032+
// A VectorSplat should have happened
18033+
assert(Xty->isVectorTy() && Yty->isVectorTy() && Sty->isVectorTy() &&
18034+
"Lerp of vector and scalar is not supported.");
18035+
18036+
[[maybe_unused]] auto *XVecTy =
18037+
E->getArg(0)->getType()->getAs<VectorType>();
18038+
[[maybe_unused]] auto *YVecTy =
18039+
E->getArg(1)->getType()->getAs<VectorType>();
18040+
[[maybe_unused]] auto *SVecTy =
18041+
E->getArg(2)->getType()->getAs<VectorType>();
18042+
// A HLSLVectorTruncation should have happend
18043+
assert(XVecTy->getNumElements() == YVecTy->getNumElements() &&
18044+
SVecTy->getNumElements() &&
18045+
"Lerp requires vectors to be of the same size.");
18046+
return Builder.CreateIntrinsic(
18047+
/*ReturnType*/ Xty, Intrinsic::dx_lerp, ArrayRef<Value *>{X, Y, S},
18048+
nullptr, "dx.lerp");
18049+
}
1801018050
}
1801118051
return nullptr;
1801218052
}

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: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5191,43 +5191,49 @@ bool Sema::CheckPPCMMAType(QualType Type, SourceLocation TypeLoc) {
51915191
bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) {
51925192
assert(TheCall->getNumArgs() > 1);
51935193
ExprResult A = TheCall->getArg(0);
5194-
ExprResult B = TheCall->getArg(1);
5194+
51955195
QualType ArgTyA = A.get()->getType();
5196-
QualType ArgTyB = B.get()->getType();
5196+
51975197
auto *VecTyA = ArgTyA->getAs<VectorType>();
5198-
auto *VecTyB = ArgTyB->getAs<VectorType>();
51995198
SourceLocation BuiltinLoc = TheCall->getBeginLoc();
5200-
if (VecTyA == nullptr && VecTyB == nullptr)
5201-
return false;
52025199

5203-
if (VecTyA && VecTyB) {
5204-
bool retValue = false;
5205-
if (VecTyA->getElementType() != VecTyB->getElementType()) {
5206-
// Note: type promotion is intended to be handeled via the intrinsics
5207-
// and not the builtin itself.
5208-
S->Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_incompatible_vector)
5209-
<< TheCall->getDirectCallee()
5210-
<< SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc());
5211-
retValue = true;
5212-
}
5213-
if (VecTyA->getNumElements() != VecTyB->getNumElements()) {
5214-
// if we get here a HLSLVectorTruncation is needed.
5215-
S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector)
5216-
<< TheCall->getDirectCallee()
5217-
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
5218-
TheCall->getArg(1)->getEndLoc());
5219-
retValue = true;
5220-
}
5200+
for (unsigned i = 1; i < TheCall->getNumArgs(); ++i) {
5201+
ExprResult B = TheCall->getArg(i);
5202+
QualType ArgTyB = B.get()->getType();
5203+
auto *VecTyB = ArgTyB->getAs<VectorType>();
5204+
if (VecTyA == nullptr && VecTyB == nullptr)
5205+
return false;
5206+
5207+
if (VecTyA && VecTyB) {
5208+
bool retValue = false;
5209+
if (VecTyA->getElementType() != VecTyB->getElementType()) {
5210+
// Note: type promotion is intended to be handeled via the intrinsics
5211+
// and not the builtin itself.
5212+
S->Diag(TheCall->getBeginLoc(),
5213+
diag::err_vec_builtin_incompatible_vector_all)
5214+
<< TheCall->getDirectCallee()
5215+
<< SourceRange(A.get()->getBeginLoc(), B.get()->getEndLoc());
5216+
retValue = true;
5217+
}
5218+
if (VecTyA->getNumElements() != VecTyB->getNumElements()) {
5219+
// if we get here a HLSLVectorTruncation is needed.
5220+
S->Diag(BuiltinLoc, diag::err_vec_builtin_incompatible_vector_all)
5221+
<< TheCall->getDirectCallee()
5222+
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
5223+
TheCall->getArg(1)->getEndLoc());
5224+
retValue = true;
5225+
}
52215226

5222-
if (retValue)
5223-
TheCall->setType(VecTyA->getElementType());
5227+
if (!retValue)
5228+
TheCall->setType(VecTyA->getElementType());
52245229

5225-
return retValue;
5230+
return retValue;
5231+
}
52265232
}
52275233

52285234
// Note: if we get here one of the args is a scalar which
52295235
// requires a VectorSplat on Arg0 or Arg1
5230-
S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector)
5236+
S->Diag(BuiltinLoc, diag::err_vec_builtin_non_vector_all)
52315237
<< TheCall->getDirectCallee()
52325238
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
52335239
TheCall->getArg(1)->getEndLoc());
@@ -5247,6 +5253,15 @@ bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
52475253
return true;
52485254
break;
52495255
}
5256+
case Builtin::BI__builtin_hlsl_lerp: {
5257+
if (checkArgCount(*this, TheCall, 3))
5258+
return true;
5259+
if (CheckVectorElementCallArgs(this, TheCall))
5260+
return true;
5261+
if (SemaBuiltinElementwiseTernaryMath(TheCall))
5262+
return true;
5263+
break;
5264+
}
52505265
}
52515266
return false;
52525267
}
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)