Skip to content

Commit 40c1d50

Browse files
authored
[HLSL] Implement the smoothstep intrinsic (llvm#132288)
Closes llvm#99156. Tasks completed: - Implement `smoothstep` using HLSL source in `hlsl_intrinsics.h` - Implement the `smoothstep` SPIR-V target built-in in `clang/include/clang/Basic/BuiltinsSPIRV.td` - Add sema checks for `smoothstep` to `CheckSPIRVBuiltinFunctionCall` in `clang/lib/Sema/SemaSPIRV.cpp` - Add codegen for spv `smoothstep` to `EmitSPIRVBuiltinExpr` in `clang/lib/CodeGen/TargetBuiltins/SPIR.cpp` - Add codegen tests to `clang/test/CodeGenHLSL/builtins/smoothstep.hlsl` - Add spv codegen test to `clang/test/CodeGenSPIRV/Builtins/smoothstep.c` - Add sema tests to `clang/test/SemaHLSL/BuiltIns/smoothstep-errors.hlsl` - Add spv sema tests to `clang/test/SemaSPIRV/BuiltIns/smoothstep-errors.c` - Create the `int_spv_smoothstep` intrinsic in `IntrinsicsSPIRV.td` - In SPIRVInstructionSelector.cpp create the `smoothstep` lowering and map it to `int_spv_smoothstep` in `SPIRVInstructionSelector::selectIntrinsic` - Create SPIR-V backend test case in `llvm/test/CodeGen/SPIRV/hlsl-intrinsics/smoothstep.ll` - Create SPIR-V backend test case in `llvm/test/CodeGen/SPIRV/opencl/smoothstep.ll`
1 parent 1e03408 commit 40c1d50

File tree

13 files changed

+581
-0
lines changed

13 files changed

+581
-0
lines changed

clang/include/clang/Basic/BuiltinsSPIRV.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ def SPIRVReflect : Builtin {
2525
let Attributes = [NoThrow, Const];
2626
let Prototype = "void(...)";
2727
}
28+
29+
def SPIRVSmoothStep : Builtin {
30+
let Spellings = ["__builtin_spirv_smoothstep"];
31+
let Attributes = [NoThrow, Const, CustomTypeChecking];
32+
let Prototype = "void(...)";
33+
}

clang/lib/CodeGen/TargetBuiltins/SPIR.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
5858
/*ReturnType=*/I->getType(), Intrinsic::spv_reflect,
5959
ArrayRef<Value *>{I, N}, nullptr, "spv.reflect");
6060
}
61+
case SPIRV::BI__builtin_spirv_smoothstep: {
62+
Value *Min = EmitScalarExpr(E->getArg(0));
63+
Value *Max = EmitScalarExpr(E->getArg(1));
64+
Value *X = EmitScalarExpr(E->getArg(2));
65+
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
66+
E->getArg(1)->getType()->hasFloatingRepresentation() &&
67+
E->getArg(2)->getType()->hasFloatingRepresentation() &&
68+
"SmoothStep operands must have a float representation");
69+
return Builder.CreateIntrinsic(
70+
/*ReturnType=*/Min->getType(), Intrinsic::spv_smoothstep,
71+
ArrayRef<Value *>{Min, Max, X}, /*FMFSource=*/nullptr,
72+
"spv.smoothstep");
73+
}
6174
}
6275
return nullptr;
6376
}

clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,26 @@ constexpr vector<T, N> fmod_vec_impl(vector<T, N> X, vector<T, N> Y) {
8181
#endif
8282
}
8383

84+
template <typename T> constexpr T smoothstep_impl(T Min, T Max, T X) {
85+
#if (__has_builtin(__builtin_spirv_smoothstep))
86+
return __builtin_spirv_smoothstep(Min, Max, X);
87+
#else
88+
T S = saturate((X - Min) / (Max - Min));
89+
return (3 - 2 * S) * S * S;
90+
#endif
91+
}
92+
93+
template <typename T, int N>
94+
constexpr vector<T, N> smoothstep_vec_impl(vector<T, N> Min, vector<T, N> Max,
95+
vector<T, N> X) {
96+
#if (__has_builtin(__builtin_spirv_smoothstep))
97+
return __builtin_spirv_smoothstep(Min, Max, X);
98+
#else
99+
vector<T, N> S = saturate((X - Min) / (Max - Min));
100+
return (3 - 2 * S) * S * S;
101+
#endif
102+
}
103+
84104
} // namespace __detail
85105
} // namespace hlsl
86106

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,5 +322,53 @@ reflect(__detail::HLSL_FIXED_VECTOR<float, L> I,
322322
__detail::HLSL_FIXED_VECTOR<float, L> N) {
323323
return __detail::reflect_vec_impl(I, N);
324324
}
325+
326+
//===----------------------------------------------------------------------===//
327+
// smoothstep builtin
328+
//===----------------------------------------------------------------------===//
329+
330+
/// \fn T smoothstep(T Min, T Max, T X)
331+
/// \brief Returns a smooth Hermite interpolation between 0 and 1, if \a X is in
332+
/// the range [\a Min, \a Max].
333+
/// \param Min The minimum range of the x parameter.
334+
/// \param Max The maximum range of the x parameter.
335+
/// \param X The specified value to be interpolated.
336+
///
337+
/// The return value is 0.0 if \a X ≤ \a Min and 1.0 if \a X ≥ \a Max. When \a
338+
/// Min < \a X < \a Max, the function performs smooth Hermite interpolation
339+
/// between 0 and 1.
340+
341+
template <typename T>
342+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
343+
const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
344+
__detail::is_same<half, T>::value,
345+
T> smoothstep(T Min, T Max, T X) {
346+
return __detail::smoothstep_impl(Min, Max, X);
347+
}
348+
349+
template <typename T>
350+
const inline __detail::enable_if_t<
351+
__detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
352+
smoothstep(T Min, T Max, T X) {
353+
return __detail::smoothstep_impl(Min, Max, X);
354+
}
355+
356+
template <int N>
357+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
358+
const inline __detail::HLSL_FIXED_VECTOR<half, N> smoothstep(
359+
__detail::HLSL_FIXED_VECTOR<half, N> Min,
360+
__detail::HLSL_FIXED_VECTOR<half, N> Max,
361+
__detail::HLSL_FIXED_VECTOR<half, N> X) {
362+
return __detail::smoothstep_vec_impl(Min, Max, X);
363+
}
364+
365+
template <int N>
366+
const inline __detail::HLSL_FIXED_VECTOR<float, N>
367+
smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min,
368+
__detail::HLSL_FIXED_VECTOR<float, N> Max,
369+
__detail::HLSL_FIXED_VECTOR<float, N> X) {
370+
return __detail::smoothstep_vec_impl(Min, Max, X);
371+
}
372+
325373
} // namespace hlsl
326374
#endif //_HLSL_HLSL_INTRINSICS_H_

clang/lib/Sema/SemaSPIRV.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,42 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
101101
TheCall->setType(RetTy);
102102
break;
103103
}
104+
case SPIRV::BI__builtin_spirv_smoothstep: {
105+
if (SemaRef.checkArgCount(TheCall, 3))
106+
return true;
107+
108+
// check if the all arguments have floating representation
109+
for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) {
110+
ExprResult Arg = TheCall->getArg(i);
111+
QualType ArgTy = Arg.get()->getType();
112+
if (!ArgTy->hasFloatingRepresentation()) {
113+
SemaRef.Diag(Arg.get()->getBeginLoc(),
114+
diag::err_builtin_invalid_arg_type)
115+
<< i + 1 << /* scalar or vector */ 5 << /* no int */ 0 << /* fp */ 1
116+
<< ArgTy;
117+
return true;
118+
}
119+
}
120+
121+
// check if all arguments are of the same type
122+
ExprResult A = TheCall->getArg(0);
123+
ExprResult B = TheCall->getArg(1);
124+
ExprResult C = TheCall->getArg(2);
125+
if (!(SemaRef.getASTContext().hasSameUnqualifiedType(A.get()->getType(),
126+
B.get()->getType()) &&
127+
SemaRef.getASTContext().hasSameUnqualifiedType(A.get()->getType(),
128+
C.get()->getType()))) {
129+
SemaRef.Diag(TheCall->getBeginLoc(),
130+
diag::err_vec_builtin_incompatible_vector)
131+
<< TheCall->getDirectCallee() << /*useAllTerminology*/ true
132+
<< SourceRange(A.get()->getBeginLoc(), C.get()->getEndLoc());
133+
return true;
134+
}
135+
136+
QualType RetTy = A.get()->getType();
137+
TheCall->setType(RetTy);
138+
break;
139+
}
104140
}
105141
return false;
106142
}

0 commit comments

Comments
 (0)