-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[HLSL] Implement the ldexp
intrinsic
#138182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-hlsl @llvm/pr-subscribers-backend-x86 Author: Kaitlin Peng (kmpeng) ChangesCloses #99133. Implemented Full diff: https://github.com/llvm/llvm-project/pull/138182.diff 4 Files Affected:
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 3180492b7de36..4eb7b8f45c85a 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -134,6 +134,10 @@ template <typename T> constexpr T faceforward_impl(T N, T I, T Ng) {
#endif
}
+template <typename T> constexpr T ldexp_impl(T X, T Exp) {
+ return exp2(Exp) * X;
+}
+
} // namespace __detail
} // namespace hlsl
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 193e7e6e99498..ffeb09bb49bef 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -303,6 +303,47 @@ fmod(__detail::HLSL_FIXED_VECTOR<float, N> X,
return __detail::fmod_vec_impl(X, Y);
}
+//===----------------------------------------------------------------------===//
+// ldexp builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn T ldexp(T X, T Exp)
+/// \brief Returns the result of multiplying \a X by two, raised to the power of \a Exp.
+/// \param X [in] The specified value.
+/// \param Exp [in] The specified exponent.
+///
+/// This function uses the following formula: X * 2^Exp
+
+template <typename T>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+ __detail::is_same<half, T>::value,
+ T> ldexp(T X, T Exp) {
+ return __detail::ldexp_impl(X, Exp);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<
+ __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
+ldexp(T X, T Exp) {
+ return __detail::ldexp_impl(X, Exp);
+}
+
+template <int N>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline __detail::HLSL_FIXED_VECTOR<half, N> ldexp(
+ __detail::HLSL_FIXED_VECTOR<half, N> X,
+ __detail::HLSL_FIXED_VECTOR<half, N> Exp) {
+ return __detail::ldexp_impl(X, Exp);
+}
+
+template <int N>
+const inline __detail::HLSL_FIXED_VECTOR<float, N>
+ldexp(__detail::HLSL_FIXED_VECTOR<float, N> X,
+ __detail::HLSL_FIXED_VECTOR<float, N> Exp) {
+ return __detail::ldexp_impl(X, Exp);
+}
+
//===----------------------------------------------------------------------===//
// length builtins
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CodeGenHLSL/builtins/ldexp.hlsl b/clang/test/CodeGenHLSL/builtins/ldexp.hlsl
new file mode 100644
index 0000000000000..6a6c04318f805
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ldexp.hlsl
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -o - | FileCheck %s
+
+// CHECK-LABEL: test_ldexp_half
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn half @llvm.exp2.f16(half %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn half %elt.exp2.i, %{{.*}}
+// CHECK: ret half %mul.i
+half test_ldexp_half(half X, half Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_half2
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <2 x half> @llvm.exp2.v2f16(<2 x half> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <2 x half> %elt.exp2.i, %{{.*}}
+// CHECK: ret <2 x half> %mul.i
+half2 test_ldexp_half2(half2 X, half2 Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_half3
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <3 x half> @llvm.exp2.v3f16(<3 x half> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <3 x half> %elt.exp2.i, %{{.*}}
+// CHECK: ret <3 x half> %mul.i
+half3 test_ldexp_half3(half3 X, half3 Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_half4
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <4 x half> @llvm.exp2.v4f16(<4 x half> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <4 x half> %elt.exp2.i, %{{.*}}
+// CHECK: ret <4 x half> %mul.i
+half4 test_ldexp_half4(half4 X, half4 Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_float
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp2.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.exp2.i, %{{.*}}
+// CHECK: ret float %mul.i
+float test_ldexp_float(float X, float Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_float2
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.exp2.v2f32(<2 x float> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <2 x float> %elt.exp2.i, %{{.*}}
+// CHECK: ret <2 x float> %mul.i
+float2 test_ldexp_float2(float2 X, float2 Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_float3
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.exp2.v3f32(<3 x float> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <3 x float> %elt.exp2.i, %{{.*}}
+// CHECK: ret <3 x float> %mul.i
+float3 test_ldexp_float3(float3 X, float3 Exp) { return ldexp(X, Exp); }
+
+// CHECK-LABEL: test_ldexp_float4
+// CHECK: %elt.exp2.i = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.exp2.v4f32(<4 x float> %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn <4 x float> %elt.exp2.i, %{{.*}}
+// CHECK: ret <4 x float> %mul.i
+float4 test_ldexp_float4(float4 X, float4 Exp) { return ldexp(X, Exp); }
diff --git a/clang/test/SemaHLSL/BuiltIns/ldexp-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/ldexp-errors.hlsl
new file mode 100644
index 0000000000000..0bc7f7e40f5d3
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ldexp-errors.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify
+
+float test_double_inputs(double p0, double p1) {
+ return ldexp(p0, p1);
+ // expected-error@-1 {{no matching function for call to 'ldexp'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+}
+
+float test_int_inputs(int p0, int p1, int p2) {
+ return ldexp(p0, p1);
+ // expected-error@-1 {{no matching function for call to 'ldexp'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+}
+
+float1 test_vec1_inputs(float1 p0, float1 p1) {
+ return ldexp(p0, p1);
+ // expected-error@-1 {{no matching function for call to 'ldexp'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: no type named 'Type' in 'hlsl::__detail::enable_if<false, vector<float, 1>>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: no type named 'Type' in 'hlsl::__detail::enable_if<false, vector<float, 1>>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with N = 1]: no type named 'Type' in 'hlsl::__detail::enable_if<false, half>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with N = 1]: no type named 'Type' in 'hlsl::__detail::enable_if<false, float>'}}
+}
+
+typedef float float5 __attribute__((ext_vector_type(5)));
+
+float5 test_vec5_inputs(float5 p0, float5 p1) {
+ return ldexp(p0, p1);
+ // expected-error@-1 {{no matching function for call to 'ldexp'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float5]: no type named 'Type' in 'hlsl::__detail::enable_if<false, vector<float, 5>>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float5]: no type named 'Type' in 'hlsl::__detail::enable_if<false, vector<float, 5>>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with N = 5]: no type named 'Type' in 'hlsl::__detail::enable_if<false, half>'}}
+ // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with N = 5]: no type named 'Type' in 'hlsl::__detail::enable_if<false, float>'}}
+}
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
@@ -0,0 +1,49 @@ | |||
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of the builtins that support half and float have tests with native half and without native half.
See: https://github.com/llvm/llvm-project/blob/main/clang/test/CodeGenHLSL/builtins/cos.hlsl
Should we do that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, to be consistent if nothing else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hoping we would only do those for intrinsics that require clang builtins since we define most of them as void(...)
. for anything thats pure HLSL like this one we have extensive testing of native half.
float test_double_inputs(double p0, double p1) { | ||
return ldexp(p0, p1); | ||
// expected-error@-1 {{no matching function for call to 'ldexp'}} | ||
// expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it worth including these notes in these tests? They don't seem valuable to me, and can be excluded with -verify-ignore-unexpected=note
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea the notes don't seem worthwhile to include. They're more distracting than anything useful. Plus, I don't see any other tests checking for notes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignoring notes will also ignore the template substitution notes which explains the no matching function error for vec5 call to 'ldexp'. Thats something I'd like to explicitly test for to make sure the template is behaving the way we want it to.
Plus, I don't see any other tests checking for notes.
git grep for with T = float5
you will find the others that Kaitlin is basing this off of.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spall pointed out some good changes that should be made
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine.
I would not like to carry forward native half testing of intrinsics that are written purely in HLSL. I think the builtin ones aren't great either but I understand how they came to exist.
As for looking at notes. its easy to mess up the the vector size enforcements. and we have been using notes to confirm we did them right. I know for some notes we are going to have a bunch of repeated things we don't care about. But there is benefit to keeping them as there is no other way to confirm that the no matching function for call to 'ldexp'
is happeing because of vector size out of bounds.
Might be worth looking at usages of ldexp in https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/MiniEngine/Core/Shaders/PixelPacking_RGBE.hlsli specically lines 52 and 79 and make sure their is no ambiguous cases to answer @V-FEXrt question here: llvm/wg-hlsl#264 (comment) Then start looking at |
Closes #99133.
Implemented
ldexp
entirely inhlsl_intrinsics.h
andhlsl_intrinsic_helpers.h
, added coresponding tests inclang/test/CodeGenHLSL/builtins/ldexp.hlsl
andclang/test/SemaHLSL/BuiltIns/ldexp-errors.hlsl
.