Skip to content

[HLSL] Implement the lit intrinsic #134171

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

Merged
merged 13 commits into from
Apr 9, 2025
Merged

[HLSL] Implement the lit intrinsic #134171

merged 13 commits into from
Apr 9, 2025

Conversation

kmpeng
Copy link
Contributor

@kmpeng kmpeng commented Apr 2, 2025

Closes #99135.

Tasks completed:

  • Wrote implementation in hlsl_intrinsics.h/hlsl_intrinsic_helpers.h
  • Added codegen tests to clang/test/CodeGenHLSL/builtins/lit.hlsl

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:headers Headers provided by Clang, e.g. for intrinsics HLSL HLSL Language Support labels Apr 2, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 2, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-hlsl

Author: Kaitlin Peng (kmpeng)

Changes

Closes #99135.

Tasks completed:

  • Wrote implementation in hlsl_intrinsics.h/hlsl_intrinsic_helpers.h that matches DXC
    • Created overloads in hlsl_compat_overloads.h that take doubles/ints and explicitly converts them to floats, like DXC does
  • Added codegen tests to clang/test/CodeGenHLSL/builtins/lit.hlsl and lit-overloads.hlsl
  • Added sema tests to clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl and lit-errors-16bit.hlsl

Full diff: https://github.com/llvm/llvm-project/pull/134171.diff

8 Files Affected:

  • (modified) clang/lib/Headers/hlsl.h (+1-1)
  • (modified) clang/lib/Headers/hlsl/hlsl_compat_overloads.h (+16)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h (+10)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsics.h (+31)
  • (added) clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl (+93)
  • (added) clang/test/CodeGenHLSL/builtins/lit.hlsl (+36)
  • (added) clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl (+9)
  • (added) clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl (+41)
diff --git a/clang/lib/Headers/hlsl.h b/clang/lib/Headers/hlsl.h
index b494b4d0f78bb..2bc1973f6eb2b 100644
--- a/clang/lib/Headers/hlsl.h
+++ b/clang/lib/Headers/hlsl.h
@@ -22,10 +22,10 @@
 
 // HLSL standard library function declarations/definitions.
 #include "hlsl/hlsl_alias_intrinsics.h"
+#include "hlsl/hlsl_intrinsics.h"
 #if __HLSL_VERSION <= __HLSL_202x
 #include "hlsl/hlsl_compat_overloads.h"
 #endif
-#include "hlsl/hlsl_intrinsics.h"
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
diff --git a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
index 47ae34adfe541..cbf5364c0b29c 100644
--- a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
+++ b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
@@ -280,6 +280,22 @@ constexpr bool4 isinf(double4 V) { return isinf((float4)V); }
 _DXC_COMPAT_TERNARY_DOUBLE_OVERLOADS(lerp)
 _DXC_COMPAT_TERNARY_INTEGER_OVERLOADS(lerp)
 
+//===----------------------------------------------------------------------===//
+// lit builtins overloads
+//===----------------------------------------------------------------------===//
+
+template <typename T>
+constexpr __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                    (__detail::is_same<double, T>::value ||
+                                     __detail::is_same<int, T>::value ||
+                                     __detail::is_same<uint, T>::value ||
+                                     __detail::is_same<int64_t, T>::value ||
+                                     __detail::is_same<uint64_t, T>::value),
+                                vector<T, 4>>
+lit(T NDotL, T NDotH, T M) {
+  return lit((float)NDotL, (float)NDotH, (float)M);
+}
+
 //===----------------------------------------------------------------------===//
 // log builtins overloads
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 8cdd63d7e07bb..3a41635a89aa4 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -101,6 +101,16 @@ constexpr vector<T, N> smoothstep_vec_impl(vector<T, N> Min, vector<T, N> Max,
 #endif
 }
 
+template <typename T> constexpr vector<T, 4> lit_impl(T NDotL, T NDotH, T M) {
+  bool DiffuseCond = NDotL < 0;
+  T Diffuse = select<T>(DiffuseCond, 0, NDotL);
+  vector<T, 4> Result = {1, Diffuse, 0, 1};
+  bool SpecularCond = or (DiffuseCond, (NDotH < 0));
+  T SpecularExp = exp(log(NDotH) * M);
+  Result[2] = select<T>(SpecularCond, 0, SpecularExp);
+  return Result;
+}
+
 } // namespace __detail
 } // namespace hlsl
 
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index fd799b8d874ae..5d23eb89b889b 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -253,6 +253,37 @@ const inline float length(__detail::HLSL_FIXED_VECTOR<float, N> X) {
   return __detail::length_vec_impl(X);
 }
 
+//===----------------------------------------------------------------------===//
+// lit builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn vector<T, 4> lit(T NDotL, T NDotH, T M)
+/// \brief Returns a lighting coefficient vector.
+/// \param NDotL The dot product of the normalized surface normal and the
+/// light vector.
+/// \param NDotH The dot product of the half-angle vector and the surface
+/// normal.
+/// \param M A specular exponent.
+///
+/// This function returns a lighting coefficient vector (ambient, diffuse,
+/// specular, 1).
+
+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,
+                                   vector<T, 4>> lit(T NDotL, T NDotH, T M) {
+  return __detail::lit_impl(NDotL, NDotH, M);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                       __detail::is_same<float, T>::value,
+                                   vector<T, 4>>
+lit(T NDotL, T NDotH, T M) {
+  return __detail::lit_impl(NDotL, NDotH, M);
+}
+
 //===----------------------------------------------------------------------===//
 // D3DCOLORtoUBYTE4 builtin
 //===----------------------------------------------------------------------===//
diff --git a/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl b/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl
new file mode 100644
index 0000000000000..676889ecf16b0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+// CHECK-LABEL: test_lit_double
+// CHECK: %conv.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %conv1.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %conv2.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fpext reassoc nnan ninf nsz arcp afn <4 x float> %{{.*}} to <4 x double>
+// CHECK: ret <4 x double> %conv3.i
+double4 test_lit_double(double NDotL, double NDotH, double M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_int
+// CHECK: %conv.i = sitofp i32 %{{.*}} to float
+// CHECK: %conv1.i = sitofp i32 %{{.*}} to float
+// CHECK: %conv2.i = sitofp i32 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptosi <4 x float> %{{.*}} to <4 x i32>
+// CHECK: ret <4 x i32> %conv3.i
+int4 test_lit_int(int NDotL, int NDotH, int M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_uint
+// CHECK: %conv.i = uitofp i32 %{{.*}} to float
+// CHECK: %conv1.i = uitofp i32 %{{.*}} to float
+// CHECK: %conv2.i = uitofp i32 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptoui <4 x float> %{{.*}} to <4 x i32>
+// CHECK: ret <4 x i32> %conv3.i
+uint4 test_lit_uint(uint NDotL, uint NDotH, uint M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_int64_t
+// CHECK: %conv.i = sitofp i64 %{{.*}} to float
+// CHECK: %conv1.i = sitofp i64 %{{.*}} to float
+// CHECK: %conv2.i = sitofp i64 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptosi <4 x float> %{{.*}} to <4 x i64>
+// CHECK: ret <4 x i64> %conv3.i
+int64_t4 test_lit_int64_t(int64_t NDotL, int64_t NDotH, int64_t M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_uint64_t
+// CHECK: %conv.i = uitofp i64 %{{.*}} to float
+// CHECK: %conv1.i = uitofp i64 %{{.*}} to float
+// CHECK: %conv2.i = uitofp i64 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptoui <4 x float> %{{.*}} to <4 x i64>
+// CHECK: ret <4 x i64> %conv3.i
+uint64_t4 test_lit_uint64_t(uint64_t NDotL, uint64_t NDotH, uint64_t M) { return lit(NDotL, NDotH, M); }
diff --git a/clang/test/CodeGenHLSL/builtins/lit.hlsl b/clang/test/CodeGenHLSL/builtins/lit.hlsl
new file mode 100644
index 0000000000000..1737a460e020f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/lit.hlsl
@@ -0,0 +1,36 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -O1 -o - | FileCheck %s
+
+// CHECK-LABEL: test_lit_half
+// CHECK-SAME: half noundef nofpclass(nan inf) [[NDOTL:%.*]], half noundef nofpclass(nan inf) [[NDOTH:%.*]], half noundef nofpclass(nan inf) [[M:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CMP_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt half [[NDOTL]], 0xH0000
+// CHECK-NEXT:    [[HLSL_SELECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.maxnum.f16(half [[NDOTL]], half 0xH0000)
+// CHECK-NEXT:    [[VECINIT2_I:%.*]] = insertelement <4 x half> <half 0xH3C00, half poison, half poison, half 0xH3C00>, half [[HLSL_SELECT_I]], i64 1
+// CHECK-NEXT:    [[CMP4_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt half [[NDOTH]], 0xH0000
+// CHECK-NEXT:    [[HLSL_OR_I:%.*]] = or i1 [[CMP_I]], [[CMP4_I]]
+// CHECK-NEXT:    [[ELT_LOG_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.log.f16(half [[NDOTH]])
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[ELT_LOG_I]], [[M]]
+// CHECK-NEXT:    [[ELT_EXP_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.exp.f16(half [[MUL_I]])
+// CHECK-NEXT:    [[HLSL_SELECT7_I:%.*]] = select reassoc nnan ninf nsz arcp afn i1 [[HLSL_OR_I]], half 0xH0000, half [[ELT_EXP_I]]
+// CHECK-NEXT:    [[VECINS_I:%.*]] = insertelement <4 x half> [[VECINIT2_I]], half [[HLSL_SELECT7_I]], i64 2
+// CHECK-NEXT:    ret <4 x half> [[VECINS_I]]
+//
+half4 test_lit_half(half NDotL, half NDotH, half M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_float
+// CHECK-SAME: float noundef nofpclass(nan inf) [[NDOTL:%.*]], float noundef nofpclass(nan inf) [[NDOTH:%.*]], float noundef nofpclass(nan inf) [[M:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CMP_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt float [[NDOTL]], 0.000000e+00
+// CHECK-NEXT:    [[HLSL_SELECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.maxnum.f32(float [[NDOTL]], float 0.000000e+00)
+// CHECK-NEXT:    [[VECINIT2_I:%.*]] = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float 1.000000e+00>, float [[HLSL_SELECT_I]], i64 1
+// CHECK-NEXT:    [[CMP4_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt float [[NDOTH]], 0.000000e+00
+// CHECK-NEXT:    [[HLSL_OR_I:%.*]] = or i1 [[CMP_I]], [[CMP4_I]]
+// CHECK-NEXT:    [[ELT_LOG_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float [[NDOTH]])
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[ELT_LOG_I]], [[M]]
+// CHECK-NEXT:    [[ELT_EXP_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float [[MUL_I]])
+// CHECK-NEXT:    [[HLSL_SELECT7_I:%.*]] = select reassoc nnan ninf nsz arcp afn i1 [[HLSL_OR_I]], float 0.000000e+00, float [[ELT_EXP_I]]
+// CHECK-NEXT:    [[VECINS_I:%.*]] = insertelement <4 x float> [[VECINIT2_I]], float [[HLSL_SELECT7_I]], i64 2
+// CHECK-NEXT:    ret <4 x float> [[VECINS_I]]
+//
+float4 test_lit_float(float NDotL, float NDotH, float M) { return lit(NDotL, NDotH, M); }
diff --git a/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl b/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl
new file mode 100644
index 0000000000000..311bad9a0ef79
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl
@@ -0,0 +1,9 @@
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=half
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=int16_t
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=uint16_t
+
+// check we error on 16 bit type if shader model is too old
+// CHECK: '-enable-16bit-types' option requires target HLSL Version >= 2018 and shader model >= 6.2, but HLSL Version is 'hlsl202x' and shader model is '6.0'
+vector<TEST_TYPE,4> test_error(TEST_TYPE p0) {
+  return lit(p0, p0, p0);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl
new file mode 100644
index 0000000000000..799fb62a9560d
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify
+
+float4 test_no_second_arg(float p0) {
+  return lit(p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+}
+
+float4 test_no_third_arg(float p0) {
+  return lit(p0, p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+}
+
+float4 test_too_many_arg(float p0) {
+  return lit(p0, p0, p0, p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+}
+
+float4 test_vec_inputs(float2 p0, float2 p1, float2 p2) {
+  return lit(p0, p1, p2);
+  // expected-error@-1  {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+}
+
+float4 test_vec1_inputs(float1 p0, float1 p1, float1 p2) {
+  return lit(p0, p1, p2);
+  // expected-error@-1  {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+}

@llvmbot
Copy link
Member

llvmbot commented Apr 2, 2025

@llvm/pr-subscribers-backend-x86

Author: Kaitlin Peng (kmpeng)

Changes

Closes #99135.

Tasks completed:

  • Wrote implementation in hlsl_intrinsics.h/hlsl_intrinsic_helpers.h that matches DXC
    • Created overloads in hlsl_compat_overloads.h that take doubles/ints and explicitly converts them to floats, like DXC does
  • Added codegen tests to clang/test/CodeGenHLSL/builtins/lit.hlsl and lit-overloads.hlsl
  • Added sema tests to clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl and lit-errors-16bit.hlsl

Full diff: https://github.com/llvm/llvm-project/pull/134171.diff

8 Files Affected:

  • (modified) clang/lib/Headers/hlsl.h (+1-1)
  • (modified) clang/lib/Headers/hlsl/hlsl_compat_overloads.h (+16)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h (+10)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsics.h (+31)
  • (added) clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl (+93)
  • (added) clang/test/CodeGenHLSL/builtins/lit.hlsl (+36)
  • (added) clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl (+9)
  • (added) clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl (+41)
diff --git a/clang/lib/Headers/hlsl.h b/clang/lib/Headers/hlsl.h
index b494b4d0f78bb..2bc1973f6eb2b 100644
--- a/clang/lib/Headers/hlsl.h
+++ b/clang/lib/Headers/hlsl.h
@@ -22,10 +22,10 @@
 
 // HLSL standard library function declarations/definitions.
 #include "hlsl/hlsl_alias_intrinsics.h"
+#include "hlsl/hlsl_intrinsics.h"
 #if __HLSL_VERSION <= __HLSL_202x
 #include "hlsl/hlsl_compat_overloads.h"
 #endif
-#include "hlsl/hlsl_intrinsics.h"
 
 #if defined(__clang__)
 #pragma clang diagnostic pop
diff --git a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
index 47ae34adfe541..cbf5364c0b29c 100644
--- a/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
+++ b/clang/lib/Headers/hlsl/hlsl_compat_overloads.h
@@ -280,6 +280,22 @@ constexpr bool4 isinf(double4 V) { return isinf((float4)V); }
 _DXC_COMPAT_TERNARY_DOUBLE_OVERLOADS(lerp)
 _DXC_COMPAT_TERNARY_INTEGER_OVERLOADS(lerp)
 
+//===----------------------------------------------------------------------===//
+// lit builtins overloads
+//===----------------------------------------------------------------------===//
+
+template <typename T>
+constexpr __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                    (__detail::is_same<double, T>::value ||
+                                     __detail::is_same<int, T>::value ||
+                                     __detail::is_same<uint, T>::value ||
+                                     __detail::is_same<int64_t, T>::value ||
+                                     __detail::is_same<uint64_t, T>::value),
+                                vector<T, 4>>
+lit(T NDotL, T NDotH, T M) {
+  return lit((float)NDotL, (float)NDotH, (float)M);
+}
+
 //===----------------------------------------------------------------------===//
 // log builtins overloads
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 8cdd63d7e07bb..3a41635a89aa4 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -101,6 +101,16 @@ constexpr vector<T, N> smoothstep_vec_impl(vector<T, N> Min, vector<T, N> Max,
 #endif
 }
 
+template <typename T> constexpr vector<T, 4> lit_impl(T NDotL, T NDotH, T M) {
+  bool DiffuseCond = NDotL < 0;
+  T Diffuse = select<T>(DiffuseCond, 0, NDotL);
+  vector<T, 4> Result = {1, Diffuse, 0, 1};
+  bool SpecularCond = or (DiffuseCond, (NDotH < 0));
+  T SpecularExp = exp(log(NDotH) * M);
+  Result[2] = select<T>(SpecularCond, 0, SpecularExp);
+  return Result;
+}
+
 } // namespace __detail
 } // namespace hlsl
 
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index fd799b8d874ae..5d23eb89b889b 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -253,6 +253,37 @@ const inline float length(__detail::HLSL_FIXED_VECTOR<float, N> X) {
   return __detail::length_vec_impl(X);
 }
 
+//===----------------------------------------------------------------------===//
+// lit builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn vector<T, 4> lit(T NDotL, T NDotH, T M)
+/// \brief Returns a lighting coefficient vector.
+/// \param NDotL The dot product of the normalized surface normal and the
+/// light vector.
+/// \param NDotH The dot product of the half-angle vector and the surface
+/// normal.
+/// \param M A specular exponent.
+///
+/// This function returns a lighting coefficient vector (ambient, diffuse,
+/// specular, 1).
+
+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,
+                                   vector<T, 4>> lit(T NDotL, T NDotH, T M) {
+  return __detail::lit_impl(NDotL, NDotH, M);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
+                                       __detail::is_same<float, T>::value,
+                                   vector<T, 4>>
+lit(T NDotL, T NDotH, T M) {
+  return __detail::lit_impl(NDotL, NDotH, M);
+}
+
 //===----------------------------------------------------------------------===//
 // D3DCOLORtoUBYTE4 builtin
 //===----------------------------------------------------------------------===//
diff --git a/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl b/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl
new file mode 100644
index 0000000000000..676889ecf16b0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/lit-overloads.hlsl
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+// CHECK-LABEL: test_lit_double
+// CHECK: %conv.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %conv1.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %conv2.i = fptrunc reassoc nnan ninf nsz arcp afn double %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fpext reassoc nnan ninf nsz arcp afn <4 x float> %{{.*}} to <4 x double>
+// CHECK: ret <4 x double> %conv3.i
+double4 test_lit_double(double NDotL, double NDotH, double M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_int
+// CHECK: %conv.i = sitofp i32 %{{.*}} to float
+// CHECK: %conv1.i = sitofp i32 %{{.*}} to float
+// CHECK: %conv2.i = sitofp i32 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptosi <4 x float> %{{.*}} to <4 x i32>
+// CHECK: ret <4 x i32> %conv3.i
+int4 test_lit_int(int NDotL, int NDotH, int M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_uint
+// CHECK: %conv.i = uitofp i32 %{{.*}} to float
+// CHECK: %conv1.i = uitofp i32 %{{.*}} to float
+// CHECK: %conv2.i = uitofp i32 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptoui <4 x float> %{{.*}} to <4 x i32>
+// CHECK: ret <4 x i32> %conv3.i
+uint4 test_lit_uint(uint NDotL, uint NDotH, uint M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_int64_t
+// CHECK: %conv.i = sitofp i64 %{{.*}} to float
+// CHECK: %conv1.i = sitofp i64 %{{.*}} to float
+// CHECK: %conv2.i = sitofp i64 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptosi <4 x float> %{{.*}} to <4 x i64>
+// CHECK: ret <4 x i64> %conv3.i
+int64_t4 test_lit_int64_t(int64_t NDotL, int64_t NDotH, int64_t M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_uint64_t
+// CHECK: %conv.i = uitofp i64 %{{.*}} to float
+// CHECK: %conv1.i = uitofp i64 %{{.*}} to float
+// CHECK: %conv2.i = uitofp i64 %{{.*}} to float
+// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecinit.i = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float poison>, float %{{.*}}, i32 1
+// CHECK: %cmp4.i = fcmp reassoc nnan ninf nsz arcp afn olt float %{{.*}}, 0.000000e+00
+// CHECK: %hlsl.or.i = or i1 %{{.*}}, %cmp4.i
+// CHECK: %elt.log.i = call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float %{{.*}})
+// CHECK: %mul.i = fmul reassoc nnan ninf nsz arcp afn float %elt.log.i, %{{.*}}
+// CHECK: %elt.exp.i = call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float %mul.i)
+// CHECK: %hlsl.select7.i = select reassoc nnan ninf nsz arcp afn i1 %{{.*}}, float 0.000000e+00, float %{{.*}}
+// CHECK: %vecins.i = insertelement <4 x float> %{{.*}}, float %hlsl.select7.i, i32 2
+// CHECK: %conv3.i = fptoui <4 x float> %{{.*}} to <4 x i64>
+// CHECK: ret <4 x i64> %conv3.i
+uint64_t4 test_lit_uint64_t(uint64_t NDotL, uint64_t NDotH, uint64_t M) { return lit(NDotL, NDotH, M); }
diff --git a/clang/test/CodeGenHLSL/builtins/lit.hlsl b/clang/test/CodeGenHLSL/builtins/lit.hlsl
new file mode 100644
index 0000000000000..1737a460e020f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/lit.hlsl
@@ -0,0 +1,36 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -O1 -o - | FileCheck %s
+
+// CHECK-LABEL: test_lit_half
+// CHECK-SAME: half noundef nofpclass(nan inf) [[NDOTL:%.*]], half noundef nofpclass(nan inf) [[NDOTH:%.*]], half noundef nofpclass(nan inf) [[M:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CMP_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt half [[NDOTL]], 0xH0000
+// CHECK-NEXT:    [[HLSL_SELECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.maxnum.f16(half [[NDOTL]], half 0xH0000)
+// CHECK-NEXT:    [[VECINIT2_I:%.*]] = insertelement <4 x half> <half 0xH3C00, half poison, half poison, half 0xH3C00>, half [[HLSL_SELECT_I]], i64 1
+// CHECK-NEXT:    [[CMP4_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt half [[NDOTH]], 0xH0000
+// CHECK-NEXT:    [[HLSL_OR_I:%.*]] = or i1 [[CMP_I]], [[CMP4_I]]
+// CHECK-NEXT:    [[ELT_LOG_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.log.f16(half [[NDOTH]])
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[ELT_LOG_I]], [[M]]
+// CHECK-NEXT:    [[ELT_EXP_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.exp.f16(half [[MUL_I]])
+// CHECK-NEXT:    [[HLSL_SELECT7_I:%.*]] = select reassoc nnan ninf nsz arcp afn i1 [[HLSL_OR_I]], half 0xH0000, half [[ELT_EXP_I]]
+// CHECK-NEXT:    [[VECINS_I:%.*]] = insertelement <4 x half> [[VECINIT2_I]], half [[HLSL_SELECT7_I]], i64 2
+// CHECK-NEXT:    ret <4 x half> [[VECINS_I]]
+//
+half4 test_lit_half(half NDotL, half NDotH, half M) { return lit(NDotL, NDotH, M); }
+
+// CHECK-LABEL: test_lit_float
+// CHECK-SAME: float noundef nofpclass(nan inf) [[NDOTL:%.*]], float noundef nofpclass(nan inf) [[NDOTH:%.*]], float noundef nofpclass(nan inf) [[M:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CMP_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt float [[NDOTL]], 0.000000e+00
+// CHECK-NEXT:    [[HLSL_SELECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.maxnum.f32(float [[NDOTL]], float 0.000000e+00)
+// CHECK-NEXT:    [[VECINIT2_I:%.*]] = insertelement <4 x float> <float 1.000000e+00, float poison, float poison, float 1.000000e+00>, float [[HLSL_SELECT_I]], i64 1
+// CHECK-NEXT:    [[CMP4_I:%.*]] = fcmp reassoc nnan ninf nsz arcp afn olt float [[NDOTH]], 0.000000e+00
+// CHECK-NEXT:    [[HLSL_OR_I:%.*]] = or i1 [[CMP_I]], [[CMP4_I]]
+// CHECK-NEXT:    [[ELT_LOG_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.log.f32(float [[NDOTH]])
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[ELT_LOG_I]], [[M]]
+// CHECK-NEXT:    [[ELT_EXP_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.exp.f32(float [[MUL_I]])
+// CHECK-NEXT:    [[HLSL_SELECT7_I:%.*]] = select reassoc nnan ninf nsz arcp afn i1 [[HLSL_OR_I]], float 0.000000e+00, float [[ELT_EXP_I]]
+// CHECK-NEXT:    [[VECINS_I:%.*]] = insertelement <4 x float> [[VECINIT2_I]], float [[HLSL_SELECT7_I]], i64 2
+// CHECK-NEXT:    ret <4 x float> [[VECINS_I]]
+//
+float4 test_lit_float(float NDotL, float NDotH, float M) { return lit(NDotL, NDotH, M); }
diff --git a/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl b/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl
new file mode 100644
index 0000000000000..311bad9a0ef79
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/lit-errors-16bit.hlsl
@@ -0,0 +1,9 @@
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=half
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=int16_t
+// RUN: not %clang_dxc -enable-16bit-types -T cs_6_0 -HV 202x %s 2>&1  | FileCheck %s -DTEST_TYPE=uint16_t
+
+// check we error on 16 bit type if shader model is too old
+// CHECK: '-enable-16bit-types' option requires target HLSL Version >= 2018 and shader model >= 6.2, but HLSL Version is 'hlsl202x' and shader model is '6.0'
+vector<TEST_TYPE,4> test_error(TEST_TYPE p0) {
+  return lit(p0, p0, p0);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl
new file mode 100644
index 0000000000000..799fb62a9560d
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/lit-errors.hlsl
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify
+
+float4 test_no_second_arg(float p0) {
+  return lit(p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 1 was provided}}
+}
+
+float4 test_no_third_arg(float p0) {
+  return lit(p0, p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 2 were provided}}
+}
+
+float4 test_too_many_arg(float p0) {
+  return lit(p0, p0, p0, p0);
+  // expected-error@-1 {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate function template not viable: requires 3 arguments, but 4 were provided}}
+}
+
+float4 test_vec_inputs(float2 p0, float2 p1, float2 p2) {
+  return lit(p0, p1, p2);
+  // expected-error@-1  {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate template ignored: substitution failure [with T = float2]: invalid vector element type 'vector<float, 2>' (vector of 2 'float' values)}}
+}
+
+float4 test_vec1_inputs(float1 p0, float1 p1, float1 p2) {
+  return lit(p0, p1, p2);
+  // expected-error@-1  {{no matching function for call to 'lit'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+  // expected-note@hlsl/hlsl_compat_overloads.h:* {{candidate template ignored: substitution failure [with T = float1]: invalid vector element type 'vector<float, 1>' (vector of 1 'float' value)}}
+}

Copy link

github-actions bot commented Apr 3, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@farzonl
Copy link
Member

farzonl commented Apr 7, 2025

I'm concerned that you had to remove your change in clang/lib/Headers/hlsl.h. I think you did it because you needed a template definition before you could specialize it. My concern is that now the generic template is gated by #if __HLSL_VERSION <= __HLSL_202x which mean you should see an error at shader compile time if the HLSL version were 202y.

@Icohedron
Copy link
Contributor

In my opinion, we should just define lit without templates, thereby following Clang's overload resolution rules and allowing implicit vector truncation. Overload resolution and implicit vector truncation are behaviors that should be consistent across the language. To me, it feels wrong to have special-case behavior on language-defining features implemented/overridden on a per-function basis.

Implicit vector truncation is also an area that needs addressing as a problem with the language, rather than being a special-case behavior handled by each function's implementation. Again, a compiler warning would go a long way to addressing problems where implicit truncation may not be intended by the programmer.

warning: implicit truncation of vector type [-Wconversion]

Or maybe implicit truncation should be removed from the language entirely (for 202y perhaps)?

If you can make the behavior consistent with DXC under all cases with some special implementation in hlsl_compat_overloads for 202x, then you can do that, but I don't think we should be writing an implementation that matches DXC now and then rewrite it entirely again when we swap to 202y that makes the language more consistent (which is one of our goals for HLSL's future, right?)

@llvm-beanz
Copy link
Collaborator

We should have half and float overloads for lit and let the rest be ambiguous.

Because lit is used primarily for graphics, it is extremely unlikely anyone is intentionally using it for double or integer values.

@farzonl
Copy link
Member

farzonl commented Apr 7, 2025

We should have half and float overloads for lit and let the rest be ambiguous.

Because lit is used primarily for graphics, it is extremely unlikely anyone is intentionally using it for double or integer values.

I think we are all in agreement about not resolving lit cases outside of half and float. The question is how to do it.

Deric and I were of the opinion that we shouldn't do templates and instead just define a function overload for half and float.
#134171 (comment)
#134171 (comment)

Kaitlin astutely pointed out that lit in dxc doesn't support implicit vector truncation. So we have been trying to find a way to get the same restrictions to just half and float using templates. Thats how we landed on the is_arithmetic approach for HLSL202X and deleting the generic template for 202Y.

Given your feedback here it sounds like you want us to drop the template and allow for implicit vector truncation so that the error will be ambiguous instead of call to deleted function 'lit'?

@llvm-beanz
Copy link
Collaborator

Given your feedback here it sounds like you want us to drop the template and allow for implicit vector truncation so that the error will be ambiguous instead of call to deleted function 'lit'?

Yes, let's not add compatibility overloads unless we have driving reasons for them.

@@ -0,0 +1,34 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -O1 -o - | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do -01 here. Drop the check-next and just check for the instructions you are expecting like
select, or, exp, log, and select again

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for future reference, when do we want to use -O1 and/or -disable-llvm-passes in tests? I haven't been able to figure out a consistent rule looking through the other codegen tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally you should prefer -disable-llvm-passes since it makes the test run faster

@kmpeng kmpeng merged commit 2ab2276 into llvm:main Apr 9, 2025
11 checks passed
AllinLeeYL pushed a commit to AllinLeeYL/llvm-project that referenced this pull request Apr 10, 2025
Closes llvm#99135.

Tasks completed:
- Wrote implementation in `hlsl_intrinsics.h`/`hlsl_intrinsic_helpers.h`
- Added codegen tests to `clang/test/CodeGenHLSL/builtins/lit.hlsl`
var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
Closes llvm#99135.

Tasks completed:
- Wrote implementation in `hlsl_intrinsics.h`/`hlsl_intrinsic_helpers.h`
- Added codegen tests to `clang/test/CodeGenHLSL/builtins/lit.hlsl`
@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
Status: Closed
Development

Successfully merging this pull request may close these issues.

Implement the lit HLSL Function
6 participants