Skip to content

[HLSL] Implement the reflect HLSL function #122992

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 3 commits into from
Jan 21, 2025
Merged

Conversation

Icohedron
Copy link
Contributor

@Icohedron Icohedron commented Jan 15, 2025

Fixes #99152

Tasks completed:

  • Implement reflect in clang/lib/Headers/hlsl/hlsl_intrinsics.h
  • Implement the reflect SPIR-V target built-in in clang/include/clang/Basic/BuiltinsSPIRV.td
  • Add a SPIR-V fast path in clang/lib/Headers/hlsl/hlsl_detail.h in the form
#if (__has_builtin(__builtin_spirv_reflect))
 return __builtin_spirv_reflect(...);
#else
  return ...; // regular behavior
#endif
  • Add codegen for the SPIR-V reflect built-in to EmitSPIRVBuiltinExpr in clang/lib/CodeGen/CGBuiltin.cpp
  • Add HLSL codegen tests to clang/test/CodeGenHLSL/builtins/reflect.hlsl
  • Add SPIR-V built-in codegen tests to clang/test/CodeGenSPIRV/Builtins/reflect.c
  • Add sema tests to clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl
  • Add SPIR-V sema tests to clang/test/CodeGenSPIRV/Builtins/reflect-errors.c
  • Create the int_spv_reflect intrinsic in llvm/include/llvm/IR/IntrinsicsSPIRV.td
  • In llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp create the reflect lowering and map it to int_spv_reflect in SPIRVInstructionSelector::selectIntrinsic
  • Create a SPIR-V backend test case in llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll

Additional tasks completed:

  • Implement sema check for the reflect SPIR-V built-in in clang/lib/Sema/SemaSPIRV.cpp
    • Required for HLSL codegen to work via the SPIR-V fast path, because the types defined in clang/include/clang/Basic/BuiltinsSPIRV.td are being overridden
  • Create SPIR-V backend error test case in llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
    • Since reflect is only available in the GLSL extended instruction set, using it in OpenCL should result in an error

Incomplete tasks:

  • Create SPIR-V backend test case in llvm/test/CodeGen/SPIRV/opencl/reflect.ll

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:codegen IR generation bugs: mangling, exceptions, etc. HLSL HLSL Language Support backend:SPIR-V llvm:ir labels Jan 15, 2025
@llvmbot
Copy link
Member

llvmbot commented Jan 15, 2025

@llvm/pr-subscribers-backend-spir-v
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-hlsl

Author: Deric Cheung (Icohedron)

Changes

Fixes #99152

Tasks completed:

  • Implement reflect in clang/lib/Headers/hlsl/hlsl_intrinsics.h
  • Implement the reflect SPIR-V target built-in in clang/include/clang/Basic/BuiltinsSPIRV.td
  • Add a SPIR-V fast path in clang/lib/Headers/hlsl/hlsl_detail.h in the form
#if (__has_builtin(__builtin_spirv_reflect))
 return __builtin_spirv_reflect(...);
#else
  return ...; // regular behavior
#endif
  • Add codegen for the SPIR-V reflect built-in to EmitSPIRVBuiltinExpr in clang/lib/CodeGen/CGBuiltin.cpp
  • Add HLSL codegen tests to clang/test/CodeGenHLSL/builtins/reflect.hlsl
  • Add SPIR-V built-in codegen tests to clang/test/CodeGenSPIRV/Builtins/reflect.c
  • Add sema tests to clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl
  • Add SPIR-V sema tests to clang/test/CodeGenSPIRV/Builtins/reflect-errors.c
  • Create the int_spv_reflect intrinsic in llvm/include/llvm/IR/IntrinsicsSPIRV.td
  • In llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp create the reflect lowering and map it to int_spv_reflect in SPIRVInstructionSelector::selectIntrinsic
  • Create a SPIR-V backend test case in llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll

Additional tasks completed:

  • Implement sema check for the reflect SPIR-V built-in in clang/lib/Sema/SemaSPIRV.cpp
    • Required for HLSL codegen to work via the SPIR-V fast path, otherwise the test clang/test/CodeGenHLSL/builtins/reflect.hlsl outright fails to compile

Incomplete tasks:

  • Create SPIR-V backend test case in llvm/test/CodeGen/SPIRV/opencl/reflect.ll

Patch is 29.45 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122992.diff

12 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+6)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+13)
  • (modified) clang/lib/Headers/hlsl/hlsl_detail.h (+17)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsics.h (+43)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+32)
  • (added) clang/test/CodeGenHLSL/builtins/reflect.hlsl (+195)
  • (added) clang/test/CodeGenSPIRV/Builtins/reflect.c (+32)
  • (added) clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl (+33)
  • (added) clang/test/SemaSPIRV/BuiltIns/reflect-errors.c (+23)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+2)
  • (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll (+33)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index 1e66939b822ef8..ce00459fc8ede7 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -13,3 +13,9 @@ def SPIRVDistance : Builtin {
   let Attributes = [NoThrow, Const];
   let Prototype = "void(...)";
 }
+
+def SPIRVReflect : Builtin {
+  let Spellings = ["__builtin_spirv_reflect"];
+  let Attributes = [NoThrow, Const];
+  let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1b25d365932c30..b9e680685b05e0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -20487,6 +20487,19 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/X->getType()->getScalarType(), Intrinsic::spv_distance,
         ArrayRef<Value *>{X, Y}, nullptr, "spv.distance");
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    Value *I = EmitScalarExpr(E->getArg(0));
+    Value *N = EmitScalarExpr(E->getArg(1));
+    assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
+           E->getArg(1)->getType()->hasFloatingRepresentation() &&
+           "Reflect operands must have a float representation");
+    assert(E->getArg(0)->getType()->isVectorType() &&
+           E->getArg(1)->getType()->isVectorType() &&
+           "Reflect operands must be a vector");
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/I->getType(), Intrinsic::spv_reflect,
+        ArrayRef<Value *>{I, N}, nullptr, "spv.reflect");
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/hlsl/hlsl_detail.h b/clang/lib/Headers/hlsl/hlsl_detail.h
index 19d83ea5471c7c..624a7b05b56ad0 100644
--- a/clang/lib/Headers/hlsl/hlsl_detail.h
+++ b/clang/lib/Headers/hlsl/hlsl_detail.h
@@ -68,6 +68,23 @@ distance_vec_impl(vector<T, N> X, vector<T, N> Y) {
   return length_vec_impl(X - Y);
 #endif
 }
+
+template <typename T>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_impl(T I, T N) {
+  return I - 2 * N * I * N;
+}
+
+template <typename T, int L>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_vec_impl(vector<T, L> I, vector<T, L> N) {
+#if (__has_builtin(__builtin_spirv_reflect))
+  return __builtin_spirv_reflect(I, N);
+#else
+  return I - 2 * N * __builtin_hlsl_dot(I, N);
+#endif
+}
+
 } // namespace __detail
 } // namespace hlsl
 #endif //_HLSL_HLSL_DETAILS_H_
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index d2d3abd92ea6a8..484ae708342a59 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -1919,6 +1919,49 @@ double3 rcp(double3);
 _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_rcp)
 double4 rcp(double4);
 
+//===----------------------------------------------------------------------===//
+// reflect builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T reflect(T I, T N)
+/// \brief Returns a reflection using an incident ray, \a I, and a surface
+/// normal, \a N.
+/// \param I The incident ray.
+/// \param N The surface normal.
+///
+/// The return value is a floating-point vector that represents the reflection
+/// of the incident ray, \a I, off a surface with the normal \a N.
+///
+/// This function calculates the reflection vector using the following formula:
+/// V = I - 2 * N * dot(I N) .
+///
+/// N must already be normalized in order to achieve the desired result.
+///
+/// The operands must all be a scalar or vector whose component type is
+/// floating-point.
+///
+/// Result type and the type of all operands must be the same type.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(half I, half N) {
+  return __detail::reflect_impl(I, N);
+}
+
+const inline float reflect(float I, float N) {
+  return __detail::reflect_impl(I, N);
+}
+
+template <int L>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(vector<half, L> I, vector<half, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
+template <int L>
+const inline float reflect(vector<float, L> I, vector<float, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
 //===----------------------------------------------------------------------===//
 // rsqrt builtins
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index d2de64826c6eb3..5d8da023c0c55d 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -51,6 +51,38 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
     TheCall->setType(RetTy);
     break;
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    if (SemaRef.checkArgCount(TheCall, 2))
+      return true;
+
+    ExprResult A = TheCall->getArg(0);
+    QualType ArgTyA = A.get()->getType();
+    auto *VTyA = ArgTyA->getAs<VectorType>();
+    if (VTyA == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyA
+          << SemaRef.Context.getVectorType(ArgTyA, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    ExprResult B = TheCall->getArg(1);
+    QualType ArgTyB = B.get()->getType();
+    auto *VTyB = ArgTyB->getAs<VectorType>();
+    if (VTyB == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyB
+          << SemaRef.Context.getVectorType(ArgTyB, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    QualType RetTy = ArgTyA;
+    TheCall->setType(RetTy);
+    break;
+  }
   }
   return false;
 }
diff --git a/clang/test/CodeGenHLSL/builtins/reflect.hlsl b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
new file mode 100644
index 00000000000000..66d0a4b6f6a24a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
@@ -0,0 +1,195 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// CHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret half [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// SPVCHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret half [[SUB_I]]
+//
+half test_reflect_half(half I, half N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// CHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPLAT_SPLATINSERT]], <2 x half> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// SPVCHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x half> @llvm.spv.reflect.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPV_REFLECT_I]], <2 x half> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+half2 test_reflect_half2(half2 I, half2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPLAT_SPLATINSERT]], <3 x half> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x half> @llvm.spv.reflect.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPV_REFLECT_I]], <3 x half> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+half3 test_reflect_half3(half3 I, half3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// CHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPLAT_SPLATINSERT]], <4 x half> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// SPVCHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x half> @llvm.spv.reflect.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPV_REFLECT_I]], <4 x half> poison, <4 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+half4 test_reflect_half4(half4 I, half4 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// CHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret float [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// SPVCHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret float [[SUB_I]]
+//
+float test_reflect_float(float I, float N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// CHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPLAT_SPLATINSERT]], <2 x float> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// SPVCHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.spv.reflect.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPV_REFLECT_I]], <2 x float> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+float2 test_reflect_float2(float2 I, float2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPLAT_SPLATINSERT]], <3 x float> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.spv.reflect.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPV_REFLECT_I]], <3 x float> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+float3 test_reflect_float3(float3 I, float3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v4f32(<4 x float> [[I]], <4 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x float> [[SPLAT_SPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// SPVCHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x float> @...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jan 15, 2025

@llvm/pr-subscribers-clang-codegen

Author: Deric Cheung (Icohedron)

Changes

Fixes #99152

Tasks completed:

  • Implement reflect in clang/lib/Headers/hlsl/hlsl_intrinsics.h
  • Implement the reflect SPIR-V target built-in in clang/include/clang/Basic/BuiltinsSPIRV.td
  • Add a SPIR-V fast path in clang/lib/Headers/hlsl/hlsl_detail.h in the form
#if (__has_builtin(__builtin_spirv_reflect))
 return __builtin_spirv_reflect(...);
#else
  return ...; // regular behavior
#endif
  • Add codegen for the SPIR-V reflect built-in to EmitSPIRVBuiltinExpr in clang/lib/CodeGen/CGBuiltin.cpp
  • Add HLSL codegen tests to clang/test/CodeGenHLSL/builtins/reflect.hlsl
  • Add SPIR-V built-in codegen tests to clang/test/CodeGenSPIRV/Builtins/reflect.c
  • Add sema tests to clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl
  • Add SPIR-V sema tests to clang/test/CodeGenSPIRV/Builtins/reflect-errors.c
  • Create the int_spv_reflect intrinsic in llvm/include/llvm/IR/IntrinsicsSPIRV.td
  • In llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp create the reflect lowering and map it to int_spv_reflect in SPIRVInstructionSelector::selectIntrinsic
  • Create a SPIR-V backend test case in llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll

Additional tasks completed:

  • Implement sema check for the reflect SPIR-V built-in in clang/lib/Sema/SemaSPIRV.cpp
    • Required for HLSL codegen to work via the SPIR-V fast path, otherwise the test clang/test/CodeGenHLSL/builtins/reflect.hlsl outright fails to compile

Incomplete tasks:

  • Create SPIR-V backend test case in llvm/test/CodeGen/SPIRV/opencl/reflect.ll

Patch is 29.45 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122992.diff

12 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+6)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+13)
  • (modified) clang/lib/Headers/hlsl/hlsl_detail.h (+17)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsics.h (+43)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+32)
  • (added) clang/test/CodeGenHLSL/builtins/reflect.hlsl (+195)
  • (added) clang/test/CodeGenSPIRV/Builtins/reflect.c (+32)
  • (added) clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl (+33)
  • (added) clang/test/SemaSPIRV/BuiltIns/reflect-errors.c (+23)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+2)
  • (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll (+33)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index 1e66939b822ef8..ce00459fc8ede7 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -13,3 +13,9 @@ def SPIRVDistance : Builtin {
   let Attributes = [NoThrow, Const];
   let Prototype = "void(...)";
 }
+
+def SPIRVReflect : Builtin {
+  let Spellings = ["__builtin_spirv_reflect"];
+  let Attributes = [NoThrow, Const];
+  let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1b25d365932c30..b9e680685b05e0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -20487,6 +20487,19 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/X->getType()->getScalarType(), Intrinsic::spv_distance,
         ArrayRef<Value *>{X, Y}, nullptr, "spv.distance");
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    Value *I = EmitScalarExpr(E->getArg(0));
+    Value *N = EmitScalarExpr(E->getArg(1));
+    assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
+           E->getArg(1)->getType()->hasFloatingRepresentation() &&
+           "Reflect operands must have a float representation");
+    assert(E->getArg(0)->getType()->isVectorType() &&
+           E->getArg(1)->getType()->isVectorType() &&
+           "Reflect operands must be a vector");
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/I->getType(), Intrinsic::spv_reflect,
+        ArrayRef<Value *>{I, N}, nullptr, "spv.reflect");
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/hlsl/hlsl_detail.h b/clang/lib/Headers/hlsl/hlsl_detail.h
index 19d83ea5471c7c..624a7b05b56ad0 100644
--- a/clang/lib/Headers/hlsl/hlsl_detail.h
+++ b/clang/lib/Headers/hlsl/hlsl_detail.h
@@ -68,6 +68,23 @@ distance_vec_impl(vector<T, N> X, vector<T, N> Y) {
   return length_vec_impl(X - Y);
 #endif
 }
+
+template <typename T>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_impl(T I, T N) {
+  return I - 2 * N * I * N;
+}
+
+template <typename T, int L>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_vec_impl(vector<T, L> I, vector<T, L> N) {
+#if (__has_builtin(__builtin_spirv_reflect))
+  return __builtin_spirv_reflect(I, N);
+#else
+  return I - 2 * N * __builtin_hlsl_dot(I, N);
+#endif
+}
+
 } // namespace __detail
 } // namespace hlsl
 #endif //_HLSL_HLSL_DETAILS_H_
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index d2d3abd92ea6a8..484ae708342a59 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -1919,6 +1919,49 @@ double3 rcp(double3);
 _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_rcp)
 double4 rcp(double4);
 
+//===----------------------------------------------------------------------===//
+// reflect builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T reflect(T I, T N)
+/// \brief Returns a reflection using an incident ray, \a I, and a surface
+/// normal, \a N.
+/// \param I The incident ray.
+/// \param N The surface normal.
+///
+/// The return value is a floating-point vector that represents the reflection
+/// of the incident ray, \a I, off a surface with the normal \a N.
+///
+/// This function calculates the reflection vector using the following formula:
+/// V = I - 2 * N * dot(I N) .
+///
+/// N must already be normalized in order to achieve the desired result.
+///
+/// The operands must all be a scalar or vector whose component type is
+/// floating-point.
+///
+/// Result type and the type of all operands must be the same type.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(half I, half N) {
+  return __detail::reflect_impl(I, N);
+}
+
+const inline float reflect(float I, float N) {
+  return __detail::reflect_impl(I, N);
+}
+
+template <int L>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(vector<half, L> I, vector<half, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
+template <int L>
+const inline float reflect(vector<float, L> I, vector<float, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
 //===----------------------------------------------------------------------===//
 // rsqrt builtins
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index d2de64826c6eb3..5d8da023c0c55d 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -51,6 +51,38 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
     TheCall->setType(RetTy);
     break;
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    if (SemaRef.checkArgCount(TheCall, 2))
+      return true;
+
+    ExprResult A = TheCall->getArg(0);
+    QualType ArgTyA = A.get()->getType();
+    auto *VTyA = ArgTyA->getAs<VectorType>();
+    if (VTyA == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyA
+          << SemaRef.Context.getVectorType(ArgTyA, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    ExprResult B = TheCall->getArg(1);
+    QualType ArgTyB = B.get()->getType();
+    auto *VTyB = ArgTyB->getAs<VectorType>();
+    if (VTyB == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyB
+          << SemaRef.Context.getVectorType(ArgTyB, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    QualType RetTy = ArgTyA;
+    TheCall->setType(RetTy);
+    break;
+  }
   }
   return false;
 }
diff --git a/clang/test/CodeGenHLSL/builtins/reflect.hlsl b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
new file mode 100644
index 00000000000000..66d0a4b6f6a24a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
@@ -0,0 +1,195 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// CHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret half [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// SPVCHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret half [[SUB_I]]
+//
+half test_reflect_half(half I, half N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// CHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPLAT_SPLATINSERT]], <2 x half> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// SPVCHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x half> @llvm.spv.reflect.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPV_REFLECT_I]], <2 x half> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+half2 test_reflect_half2(half2 I, half2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPLAT_SPLATINSERT]], <3 x half> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x half> @llvm.spv.reflect.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPV_REFLECT_I]], <3 x half> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+half3 test_reflect_half3(half3 I, half3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// CHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPLAT_SPLATINSERT]], <4 x half> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// SPVCHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x half> @llvm.spv.reflect.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPV_REFLECT_I]], <4 x half> poison, <4 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+half4 test_reflect_half4(half4 I, half4 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// CHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret float [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// SPVCHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret float [[SUB_I]]
+//
+float test_reflect_float(float I, float N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// CHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPLAT_SPLATINSERT]], <2 x float> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// SPVCHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.spv.reflect.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPV_REFLECT_I]], <2 x float> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+float2 test_reflect_float2(float2 I, float2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPLAT_SPLATINSERT]], <3 x float> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.spv.reflect.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPV_REFLECT_I]], <3 x float> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+float3 test_reflect_float3(float3 I, float3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v4f32(<4 x float> [[I]], <4 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x float> [[SPLAT_SPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// SPVCHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x float> @...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jan 15, 2025

@llvm/pr-subscribers-clang

Author: Deric Cheung (Icohedron)

Changes

Fixes #99152

Tasks completed:

  • Implement reflect in clang/lib/Headers/hlsl/hlsl_intrinsics.h
  • Implement the reflect SPIR-V target built-in in clang/include/clang/Basic/BuiltinsSPIRV.td
  • Add a SPIR-V fast path in clang/lib/Headers/hlsl/hlsl_detail.h in the form
#if (__has_builtin(__builtin_spirv_reflect))
 return __builtin_spirv_reflect(...);
#else
  return ...; // regular behavior
#endif
  • Add codegen for the SPIR-V reflect built-in to EmitSPIRVBuiltinExpr in clang/lib/CodeGen/CGBuiltin.cpp
  • Add HLSL codegen tests to clang/test/CodeGenHLSL/builtins/reflect.hlsl
  • Add SPIR-V built-in codegen tests to clang/test/CodeGenSPIRV/Builtins/reflect.c
  • Add sema tests to clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl
  • Add SPIR-V sema tests to clang/test/CodeGenSPIRV/Builtins/reflect-errors.c
  • Create the int_spv_reflect intrinsic in llvm/include/llvm/IR/IntrinsicsSPIRV.td
  • In llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp create the reflect lowering and map it to int_spv_reflect in SPIRVInstructionSelector::selectIntrinsic
  • Create a SPIR-V backend test case in llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll

Additional tasks completed:

  • Implement sema check for the reflect SPIR-V built-in in clang/lib/Sema/SemaSPIRV.cpp
    • Required for HLSL codegen to work via the SPIR-V fast path, otherwise the test clang/test/CodeGenHLSL/builtins/reflect.hlsl outright fails to compile

Incomplete tasks:

  • Create SPIR-V backend test case in llvm/test/CodeGen/SPIRV/opencl/reflect.ll

Patch is 29.45 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122992.diff

12 Files Affected:

  • (modified) clang/include/clang/Basic/BuiltinsSPIRV.td (+6)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+13)
  • (modified) clang/lib/Headers/hlsl/hlsl_detail.h (+17)
  • (modified) clang/lib/Headers/hlsl/hlsl_intrinsics.h (+43)
  • (modified) clang/lib/Sema/SemaSPIRV.cpp (+32)
  • (added) clang/test/CodeGenHLSL/builtins/reflect.hlsl (+195)
  • (added) clang/test/CodeGenSPIRV/Builtins/reflect.c (+32)
  • (added) clang/test/SemaHLSL/BuiltIns/reflect-errors.hlsl (+33)
  • (added) clang/test/SemaSPIRV/BuiltIns/reflect-errors.c (+23)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+2)
  • (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reflect.ll (+33)
diff --git a/clang/include/clang/Basic/BuiltinsSPIRV.td b/clang/include/clang/Basic/BuiltinsSPIRV.td
index 1e66939b822ef8..ce00459fc8ede7 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRV.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRV.td
@@ -13,3 +13,9 @@ def SPIRVDistance : Builtin {
   let Attributes = [NoThrow, Const];
   let Prototype = "void(...)";
 }
+
+def SPIRVReflect : Builtin {
+  let Spellings = ["__builtin_spirv_reflect"];
+  let Attributes = [NoThrow, Const];
+  let Prototype = "void(...)";
+}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1b25d365932c30..b9e680685b05e0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -20487,6 +20487,19 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
         /*ReturnType=*/X->getType()->getScalarType(), Intrinsic::spv_distance,
         ArrayRef<Value *>{X, Y}, nullptr, "spv.distance");
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    Value *I = EmitScalarExpr(E->getArg(0));
+    Value *N = EmitScalarExpr(E->getArg(1));
+    assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
+           E->getArg(1)->getType()->hasFloatingRepresentation() &&
+           "Reflect operands must have a float representation");
+    assert(E->getArg(0)->getType()->isVectorType() &&
+           E->getArg(1)->getType()->isVectorType() &&
+           "Reflect operands must be a vector");
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/I->getType(), Intrinsic::spv_reflect,
+        ArrayRef<Value *>{I, N}, nullptr, "spv.reflect");
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/hlsl/hlsl_detail.h b/clang/lib/Headers/hlsl/hlsl_detail.h
index 19d83ea5471c7c..624a7b05b56ad0 100644
--- a/clang/lib/Headers/hlsl/hlsl_detail.h
+++ b/clang/lib/Headers/hlsl/hlsl_detail.h
@@ -68,6 +68,23 @@ distance_vec_impl(vector<T, N> X, vector<T, N> Y) {
   return length_vec_impl(X - Y);
 #endif
 }
+
+template <typename T>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_impl(T I, T N) {
+  return I - 2 * N * I * N;
+}
+
+template <typename T, int L>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
+reflect_vec_impl(vector<T, L> I, vector<T, L> N) {
+#if (__has_builtin(__builtin_spirv_reflect))
+  return __builtin_spirv_reflect(I, N);
+#else
+  return I - 2 * N * __builtin_hlsl_dot(I, N);
+#endif
+}
+
 } // namespace __detail
 } // namespace hlsl
 #endif //_HLSL_HLSL_DETAILS_H_
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index d2d3abd92ea6a8..484ae708342a59 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -1919,6 +1919,49 @@ double3 rcp(double3);
 _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_rcp)
 double4 rcp(double4);
 
+//===----------------------------------------------------------------------===//
+// reflect builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T reflect(T I, T N)
+/// \brief Returns a reflection using an incident ray, \a I, and a surface
+/// normal, \a N.
+/// \param I The incident ray.
+/// \param N The surface normal.
+///
+/// The return value is a floating-point vector that represents the reflection
+/// of the incident ray, \a I, off a surface with the normal \a N.
+///
+/// This function calculates the reflection vector using the following formula:
+/// V = I - 2 * N * dot(I N) .
+///
+/// N must already be normalized in order to achieve the desired result.
+///
+/// The operands must all be a scalar or vector whose component type is
+/// floating-point.
+///
+/// Result type and the type of all operands must be the same type.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(half I, half N) {
+  return __detail::reflect_impl(I, N);
+}
+
+const inline float reflect(float I, float N) {
+  return __detail::reflect_impl(I, N);
+}
+
+template <int L>
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+const inline half reflect(vector<half, L> I, vector<half, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
+template <int L>
+const inline float reflect(vector<float, L> I, vector<float, L> N) {
+  return __detail::reflect_vec_impl(I, N);
+}
+
 //===----------------------------------------------------------------------===//
 // rsqrt builtins
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index d2de64826c6eb3..5d8da023c0c55d 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -51,6 +51,38 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
     TheCall->setType(RetTy);
     break;
   }
+  case SPIRV::BI__builtin_spirv_reflect: {
+    if (SemaRef.checkArgCount(TheCall, 2))
+      return true;
+
+    ExprResult A = TheCall->getArg(0);
+    QualType ArgTyA = A.get()->getType();
+    auto *VTyA = ArgTyA->getAs<VectorType>();
+    if (VTyA == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyA
+          << SemaRef.Context.getVectorType(ArgTyA, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    ExprResult B = TheCall->getArg(1);
+    QualType ArgTyB = B.get()->getType();
+    auto *VTyB = ArgTyB->getAs<VectorType>();
+    if (VTyB == nullptr) {
+      SemaRef.Diag(A.get()->getBeginLoc(),
+                   diag::err_typecheck_convert_incompatible)
+          << ArgTyB
+          << SemaRef.Context.getVectorType(ArgTyB, 2, VectorKind::Generic) << 1
+          << 0 << 0;
+      return true;
+    }
+
+    QualType RetTy = ArgTyA;
+    TheCall->setType(RetTy);
+    break;
+  }
   }
   return false;
 }
diff --git a/clang/test/CodeGenHLSL/builtins/reflect.hlsl b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
new file mode 100644
index 00000000000000..66d0a4b6f6a24a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/reflect.hlsl
@@ -0,0 +1,195 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -fnative-half-type \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// CHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret half [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_reflect_halfDhDh(
+// SPVCHECK-SAME: half noundef nofpclass(nan inf) [[I:%.*]], half noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[I]], 0xH4000
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret half [[SUB_I]]
+//
+half test_reflect_half(half I, half N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// CHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPLAT_SPLATINSERT]], <2 x half> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x half> @_Z18test_reflect_half2Dv2_DhS_(
+// SPVCHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[I:%.*]], <2 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x half> @llvm.spv.reflect.v2f16(<2 x half> [[I]], <2 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x half> [[SPV_REFLECT_I]], <2 x half> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x half> [[SPLAT_SPLAT]]
+//
+half2 test_reflect_half2(half2 I, half2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPLAT_SPLATINSERT]], <3 x half> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x half> @_Z18test_reflect_half3Dv3_DhS_(
+// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[I:%.*]], <3 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x half> @llvm.spv.reflect.v3f16(<3 x half> [[I]], <3 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x half> [[SPV_REFLECT_I]], <3 x half> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x half> [[SPLAT_SPLAT]]
+//
+half3 test_reflect_half3(half3 I, half3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// CHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x half> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x half> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP1]], 0xH4000
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn half [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x half> poison, half [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPLAT_SPLATINSERT]], <4 x half> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x half> @_Z18test_reflect_half4Dv4_DhS_(
+// SPVCHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[I:%.*]], <4 x half> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x half> @llvm.spv.reflect.v4f16(<4 x half> [[I]], <4 x half> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x half> [[SPV_REFLECT_I]], <4 x half> poison, <4 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <4 x half> [[SPLAT_SPLAT]]
+//
+half4 test_reflect_half4(half4 I, half4 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// CHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// CHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// CHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// CHECK-NEXT:    ret float [[SUB_I]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_reflect_floatff(
+// SPVCHECK-SAME: float noundef nofpclass(nan inf) [[I:%.*]], float noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[I]], 2.000000e+00
+// SPVCHECK-NEXT:    [[TMP0:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[N]], [[N]]
+// SPVCHECK-NEXT:    [[MUL2_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP0]], [[MUL_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[I]], [[MUL2_I]]
+// SPVCHECK-NEXT:    ret float [[SUB_I]]
+//
+float test_reflect_float(float I, float N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// CHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <2 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <2 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPLAT_SPLATINSERT]], <2 x float> poison, <2 x i32> zeroinitializer
+// CHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <2 x float> @_Z19test_reflect_float2Dv2_fS_(
+// SPVCHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[I:%.*]], <2 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.spv.reflect.v2f32(<2 x float> [[I]], <2 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <2 x float> [[SPV_REFLECT_I]], <2 x float> poison, <2 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <2 x float> [[SPLAT_SPLAT]]
+//
+float2 test_reflect_float2(float2 I, float2 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <3 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <3 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <3 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPLAT_SPLATINSERT]], <3 x float> poison, <3 x i32> zeroinitializer
+// CHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <3 x float> @_Z19test_reflect_float3Dv3_fS_(
+// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[I:%.*]], <3 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.spv.reflect.v3f32(<3 x float> [[I]], <3 x float> [[N]])
+// SPVCHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <3 x float> [[SPV_REFLECT_I]], <3 x float> poison, <3 x i32> zeroinitializer
+// SPVCHECK-NEXT:    ret <3 x float> [[SPLAT_SPLAT]]
+//
+float3 test_reflect_float3(float3 I, float3 N) {
+    return reflect(I, N);
+}
+
+// CHECK-LABEL: define noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v4f32(<4 x float> [[I]], <4 x float> [[N]])
+// CHECK-NEXT:    [[TMP0:%.*]] = extractelement <4 x float> [[I]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractelement <4 x float> [[N]], i64 0
+// CHECK-NEXT:    [[TMP2:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP1]], 2.000000e+00
+// CHECK-NEXT:    [[TMP3:%.*]] = fmul reassoc nnan ninf nsz arcp afn float [[TMP2]], [[HLSL_DOT_I]]
+// CHECK-NEXT:    [[CAST_VTRUNC_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float [[TMP0]], [[TMP3]]
+// CHECK-NEXT:    [[SPLAT_SPLATINSERT:%.*]] = insertelement <4 x float> poison, float [[CAST_VTRUNC_I]], i64 0
+// CHECK-NEXT:    [[SPLAT_SPLAT:%.*]] = shufflevector <4 x float> [[SPLAT_SPLATINSERT]], <4 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    ret <4 x float> [[SPLAT_SPLAT]]
+//
+// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) <4 x float> @_Z19test_reflect_float4Dv4_fS_(
+// SPVCHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[I:%.*]], <4 x float> noundef nofpclass(nan inf) [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[SPV_REFLECT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn <4 x float> @...
[truncated]

@Icohedron Icohedron changed the title [HLSL] Implement reflect HLSL function [HLSL] Implement the reflect HLSL function Jan 15, 2025
@Icohedron Icohedron marked this pull request as draft January 15, 2025 15:52
@Icohedron Icohedron marked this pull request as ready for review January 16, 2025 20:59
Copy link
Contributor

@inbelic inbelic left a comment

Choose a reason for hiding this comment

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

This pr LGTM once the graceful exit comment is handled.

With respect to this comment. Is the final solution to just gracefully exit or is this an intermediate step and an issue will be used to track an update using inst combine later?

Will a follow-up issue be created to move the useful Sema helpers into a common file?

@farzonl
Copy link
Member

farzonl commented Jan 17, 2025

This pr LGTM once the graceful exit comment is handled.

With respect to this comment. Is the final solution to just gracefully exit or is this an intermediate step and an issue will be used to track an update using inst combine later?

The graceful exit should be sufficient. We don't expose the clang builtin when targeting spirv32 or spirv64 and for now we are the only consumers of this intrinsic. Like I said in the last comment the only way to trigger this crash is with hand spun llvmir. To me making this not crash if you aren't in a supported environment is good enough.

@farzonl
Copy link
Member

farzonl commented Jan 17, 2025

Will a follow-up issue be created to move the useful Sema helpers into a common file?

I'm fine if you want to do this as a follow up.

Copy link

github-actions bot commented Jan 18, 2025

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

@@ -3030,6 +3031,15 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
return selectExtInst(ResVReg, ResType, I, CL::fract, GL::Fract);
case Intrinsic::spv_normalize:
return selectExtInst(ResVReg, ResType, I, CL::normalize, GL::Normalize);
case Intrinsic::spv_reflect:
if (!STI.canUseExtInstSet(SPIRV::InstructionSet::InstructionSet::GLSL_std_450)) {
Copy link
Member

Choose a reason for hiding this comment

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

I was hoping to put this conditional in the GL overloaded version of selectExtInst so that other GL only extention instructions could benefit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I add a similar conditional to the OpenCL overloaded version of selectExtInst? Just to keep the OpenCL and GLSL functions similar.

Copy link
Member

Choose a reason for hiding this comment

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

Not a bad idea, but in another pr. so that the changes here are more focused to the reflect feature. Feel free to file an issue and identify some tests to add.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made an issue for this here #123847

; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}
; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}

; CHECK: LLVM ERROR: Intrinsic selection not supported for this instruction set: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.reflect), %{{.*}}, %{{.*}}
Copy link
Member

@farzonl farzonl Jan 18, 2025

Choose a reason for hiding this comment

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

This is my bad now that I'm seeing it the lannguage is a bit obtuse and it doesn't look like instruction is encoded. We should simplify this error message.

Maybe:

Suggested change
; CHECK: LLVM ERROR: Intrinsic selection not supported for this instruction set: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.reflect), %{{.*}}, %{{.*}}
; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.reflect), %{{.*}}, %{{.*}} is only supported for GL instruction targets.

Or if you have something better go with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have changed the error message to read LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.reflect), %{{.*}}, %{{.*}} is only supported with the GLSL extended instruction set.

Certain SPIR-V instructions are only available in the GLSL extended
instruction set, such as "Reflect".

This commit adds a check to ensure the current SPIR-V target can use
the GLSL extended instruction set before selecting an instruction that
is only available from the GLSL extended instruction set.
@@ -34,6 +34,7 @@
#include "llvm/IR/IntrinsicsSPIRV.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why you need an include your changes to selectExtInst are the same string stream as the default intrinsic error reporting case: https://github.com/llvm/llvm-project/blob/6518b121f037717fd211c36659f7b25266424719/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp#L3124C5-L3128C48

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't recall adding that. I think my IDE did that for me :P
I will remove it

@Icohedron
Copy link
Contributor Author

Will a follow-up issue be created to move the useful Sema helpers into a common file?

I just created issue #123831 for this.

@inbelic inbelic merged commit dd860bc into llvm:main Jan 21, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-gcc-ubuntu running on sie-linux-worker3 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/174/builds/11773

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder ml-opt-rel-x86-64 running on ml-opt-rel-x86-64-b1 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/185/builds/11836

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/ml-opt-rel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-rel-x86-64-b1/build/bin/FileCheck /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-rel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/ml-opt-rel-x86-64-b1/build/bin/FileCheck /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/ml-opt-rel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-rel-x86-64-b1/build/bin/FileCheck /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-rel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/ml-opt-rel-x86-64-b1/build/bin/FileCheck /b/ml-opt-rel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder ml-opt-dev-x86-64 running on ml-opt-dev-x86-64-b2 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/137/builds/11988

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/ml-opt-dev-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-dev-x86-64-b1/build/bin/FileCheck /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-dev-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/ml-opt-dev-x86-64-b1/build/bin/FileCheck /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/ml-opt-dev-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-dev-x86-64-b1/build/bin/FileCheck /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-dev-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/ml-opt-dev-x86-64-b1/build/bin/FileCheck /b/ml-opt-dev-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder ml-opt-devrel-x86-64 running on ml-opt-devrel-x86-64-b2 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/175/builds/11843

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/ml-opt-devrel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-devrel-x86-64-b1/build/bin/FileCheck /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-devrel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/ml-opt-devrel-x86-64-b1/build/bin/FileCheck /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/ml-opt-devrel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/ml-opt-devrel-x86-64-b1/build/bin/FileCheck /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ /b/ml-opt-devrel-x86-64-b1/build/bin/FileCheck /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/ml-opt-devrel-x86-64-b1/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/ml-opt-devrel-x86-64-b1/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
RUN: at line 3: not
+ not

--

********************


@inbelic
Copy link
Contributor

inbelic commented Jan 21, 2025

The build failures are because of this commit and the reflect-error.ll test. It seems that we wanted to delete this testcase as mentioned here? But since I am not sure on what the quick resolution is I will revert the change.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-sve2-vla running on linaro-g4-02 while building clang,llvm at step 7 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/198/builds/1364

Here is the relevant piece of the build log for the reference
Step 7 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 21, 2025

LLVM Buildbot has detected a new failure on builder clang-debian-cpp20 running on clang-debian-cpp20 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/108/builds/8397

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
RUN: at line 3: not
+ not

--

********************


github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 21, 2025
…3846)

Reverts llvm/llvm-project#122992

Due to an included failing test-case the commit causes build failures.
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-expensive-checks-debian running on gribozavr4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/16/builds/12395

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder llvm-x86_64-debian-dylib running on gribozavr4 while building clang,llvm at step 7 "test-build-unified-tree-check-llvm".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/60/builds/17567

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-llvm) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/1/llvm-x86_64-debian-dylib/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/llvm-x86_64-debian-dylib/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/1/llvm-x86_64-debian-dylib/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/llvm-x86_64-debian-dylib/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder clang-x86_64-debian-fast running on gribozavr4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/56/builds/16774

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder lld-x86_64-ubuntu-fast running on as-builder-4 while building clang,llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/33/builds/9977

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/as-builder-4/ramdisk/lld-x86_64/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder premerge-monolithic-linux running on premerge-linux-1 while building clang,llvm at step 7 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/153/builds/20420

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SPIRV/opencl/reflect-error.ll' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 1: not /build/buildbot/premerge-monolithic-linux/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /build/buildbot/premerge-monolithic-linux/build/bin/FileCheck /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /build/buildbot/premerge-monolithic-linux/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /build/buildbot/premerge-monolithic-linux/build/bin/FileCheck /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 2: not /build/buildbot/premerge-monolithic-linux/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null 2>&1 | /build/buildbot/premerge-monolithic-linux/build/bin/FileCheck /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
+ not /build/buildbot/premerge-monolithic-linux/build/bin/llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll -o /dev/null
+ /build/buildbot/premerge-monolithic-linux/build/bin/FileCheck /build/buildbot/premerge-monolithic-linux/llvm-project/llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll
RUN: at line 3: not
+ not

--

********************


inbelic pushed a commit that referenced this pull request Jan 22, 2025
This PR relands
[#122992](#122992).

Some machines were failing to run the `reflect-error.ll` test due to the
RUN lines
```llvm
; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}
; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}
```
which failed when `spirv-tools` was not present on the machine due to
running the command `not` without any arguments.

These RUN lines have been removed since they don't actually test
anything new compared to the other two RUN lines due to the expected
error during instruction selection.
```llvm
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
```
github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 22, 2025
…3853)

This PR relands
[#122992](llvm/llvm-project#122992).

Some machines were failing to run the `reflect-error.ll` test due to the
RUN lines
```llvm
; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}
; RUN: not %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 -filetype=obj %}
```
which failed when `spirv-tools` was not present on the machine due to
running the command `not` without any arguments.

These RUN lines have been removed since they don't actually test
anything new compared to the other two RUN lines due to the expected
error during instruction selection.
```llvm
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
```
Icohedron added a commit that referenced this pull request Feb 25, 2025
This PR relands #122992.

A reland was attempted before (#123853), but it [failed to pass the
`sanitizer-aarch64-linux-bootstrap-hwasan`
buildbot](#123853 (comment))
due to the test `llvm/test/CodeGen/SPIRV/opencl/reflect-error.ll`

The issue has since been patched thanks to @vitalybuka, so the PR is
safe to reland without any changes.
See
#125599 (comment)
and
#125599 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:SPIR-V backend:X86 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang Clang issues not falling into any other category HLSL HLSL Language Support llvm:ir
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Implement the reflect HLSL Function
5 participants