[clang] [HLSL] Implement the `ldexp` intrinsic (PR #138182)
Kaitlin Peng via cfe-commits
cfe-commits at lists.llvm.org
Fri May 2 13:06:59 PDT 2025
https://github.com/kmpeng updated https://github.com/llvm/llvm-project/pull/138182
>From d5187a940fc0a16325eedb0c67bcf647deb95d8f Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Thu, 1 May 2025 11:48:47 -0700
Subject: [PATCH 1/4] ldexp implementation and tests
---
.../lib/Headers/hlsl/hlsl_intrinsic_helpers.h | 4 ++
clang/lib/Headers/hlsl/hlsl_intrinsics.h | 41 ++++++++++++++++
clang/test/CodeGenHLSL/builtins/ldexp.hlsl | 49 +++++++++++++++++++
.../test/SemaHLSL/BuiltIns/ldexp-errors.hlsl | 39 +++++++++++++++
4 files changed, 133 insertions(+)
create mode 100644 clang/test/CodeGenHLSL/builtins/ldexp.hlsl
create mode 100644 clang/test/SemaHLSL/BuiltIns/ldexp-errors.hlsl
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 at -1 {{no matching function for call to 'ldexp'}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+}
+
+float test_int_inputs(int p0, int p1, int p2) {
+ return ldexp(p0, p1);
+ // expected-error at -1 {{no matching function for call to 'ldexp'}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate template ignored}}
+}
+
+float1 test_vec1_inputs(float1 p0, float1 p1) {
+ return ldexp(p0, p1);
+ // expected-error at -1 {{no matching function for call to 'ldexp'}}
+ // expected-note at 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 at 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 at 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 at 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 at -1 {{no matching function for call to 'ldexp'}}
+ // expected-note at 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 at 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 at 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 at hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with N = 5]: no type named 'Type' in 'hlsl::__detail::enable_if<false, float>'}}
+}
>From c466fc692232e23ec65f9753726c1f96ef289e5b Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Thu, 1 May 2025 12:18:45 -0700
Subject: [PATCH 2/4] clang-format
---
clang/lib/Headers/hlsl/hlsl_intrinsics.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index ffeb09bb49bef..679f409864887 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -308,7 +308,8 @@ fmod(__detail::HLSL_FIXED_VECTOR<float, N> X,
//===----------------------------------------------------------------------===//
/// \fn T ldexp(T X, T Exp)
-/// \brief Returns the result of multiplying \a X by two, raised to the power of \a Exp.
+/// \brief Returns the result of multiplying the specified value by two, raised
+/// to the power of the specified exponent.
/// \param X [in] The specified value.
/// \param Exp [in] The specified exponent.
///
>From 247f3695ec63405f8d9c7f94f6516f32c8f480d6 Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Thu, 1 May 2025 16:14:48 -0700
Subject: [PATCH 3/4] add -disable-llvm-passes to codegen test run command
---
clang/test/CodeGenHLSL/builtins/ldexp.hlsl | 66 +++++++++++-----------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/clang/test/CodeGenHLSL/builtins/ldexp.hlsl b/clang/test/CodeGenHLSL/builtins/ldexp.hlsl
index 6a6c04318f805..ea0d1348c6e4e 100644
--- a/clang/test/CodeGenHLSL/builtins/ldexp.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ldexp.hlsl
@@ -1,49 +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
+// 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
-// 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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) half @_ZN4hlsl8__detail10ldexp_implIDhEET_S2_S2_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn half @llvm.exp2.f16(half %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn half %elt.exp2, %{{.*}}
+// CHECK: ret half %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <2 x half> @_ZN4hlsl8__detail10ldexp_implIDv2_DhEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <2 x half> @llvm.exp2.v2f16(<2 x half> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <2 x half> %elt.exp2, %{{.*}}
+// CHECK: ret <2 x half> %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <3 x half> @_ZN4hlsl8__detail10ldexp_implIDv3_DhEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <3 x half> @llvm.exp2.v3f16(<3 x half> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <3 x half> %elt.exp2, %{{.*}}
+// CHECK: ret <3 x half> %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <4 x half> @_ZN4hlsl8__detail10ldexp_implIDv4_DhEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <4 x half> @llvm.exp2.v4f16(<4 x half> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <4 x half> %elt.exp2, %{{.*}}
+// CHECK: ret <4 x half> %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) float @_ZN4hlsl8__detail10ldexp_implIfEET_S2_S2_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn float @llvm.exp2.f32(float %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn float %elt.exp2, %{{.*}}
+// CHECK: ret float %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <2 x float> @_ZN4hlsl8__detail10ldexp_implIDv2_fEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.exp2.v2f32(<2 x float> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <2 x float> %elt.exp2, %{{.*}}
+// CHECK: ret <2 x float> %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <3 x float> @_ZN4hlsl8__detail10ldexp_implIDv3_fEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.exp2.v3f32(<3 x float> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <3 x float> %elt.exp2, %{{.*}}
+// CHECK: ret <3 x float> %mul
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
+// CHECK-LABEL: define linkonce_odr noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl8__detail10ldexp_implIDv4_fEET_S3_S3_
+// CHECK: %elt.exp2 = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.exp2.v4f32(<4 x float> %{{.*}})
+// CHECK: %mul = fmul reassoc nnan ninf nsz arcp afn <4 x float> %elt.exp2, %{{.*}}
+// CHECK: ret <4 x float> %mul
float4 test_ldexp_float4(float4 X, float4 Exp) { return ldexp(X, Exp); }
>From 2c186c517d12963feb9c20787a6b48bc2ef6b7ac Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Fri, 2 May 2025 13:06:47 -0700
Subject: [PATCH 4/4] change ldexp description
---
clang/lib/Headers/hlsl/hlsl_intrinsics.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 679f409864887..ea880105fac3b 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -308,7 +308,7 @@ fmod(__detail::HLSL_FIXED_VECTOR<float, N> X,
//===----------------------------------------------------------------------===//
/// \fn T ldexp(T X, T Exp)
-/// \brief Returns the result of multiplying the specified value by two, raised
+/// \brief Returns the result of multiplying the specified value by two raised
/// to the power of the specified exponent.
/// \param X [in] The specified value.
/// \param Exp [in] The specified exponent.
More information about the cfe-commits
mailing list