https://github.com/joaosaffran updated 
https://github.com/llvm/llvm-project/pull/211446

>From 8753620ffc54e1bce01546dcad51f120ea85045f Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Wed, 22 Jul 2026 18:53:34 -0700
Subject: [PATCH 1/6] moving cross implementation to header files

---
 clang/include/clang/Basic/Builtins.td         |  12 --
 clang/include/clang/Basic/HLSLIntrinsics.td   |  24 ---
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          |  16 --
 clang/lib/CodeGen/CGHLSLRuntime.h             |   1 -
 .../lib/Headers/hlsl/hlsl_intrinsic_helpers.h |   8 +
 clang/lib/Headers/hlsl/hlsl_intrinsics.h      |  22 +++
 clang/test/CodeGenHLSL/builtins/cross.hlsl    | 122 ++++++++++---
 .../test/SemaHLSL/BuiltIns/cross-errors.hlsl  |  50 ++---
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |   1 -
 .../Target/DirectX/DXILIntrinsicExpansion.cpp |  39 ----
 llvm/lib/Target/SPIRV/SPIRVCombine.td         |   9 +-
 llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp | 172 ++++++++++++++++++
 llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h   |   3 +
 llvm/test/CodeGen/DirectX/cross.ll            |  56 ------
 .../CodeGen/SPIRV/hlsl-intrinsics/cross.ll    |  57 ++++++
 15 files changed, 378 insertions(+), 214 deletions(-)
 delete mode 100644 llvm/test/CodeGen/DirectX/cross.ll

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..28f64a130ffc5 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5647,18 +5647,6 @@ def HLSLClamp : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void(...)";
 }
 
-def HLSLCrossFloat: LangBuiltin<"HLSL_LANG"> {
-  let Spellings = ["__builtin_hlsl_crossf32"];
-  let Attributes = [NoThrow, Const];
-  let Prototype = "_ExtVector<3, float>(_ExtVector<3, float>, _ExtVector<3, 
float>)";
-}
-
-def HLSLCrossHalf: LangBuiltin<"HLSL_LANG"> {
-  let Spellings = ["__builtin_hlsl_crossf16"];
-  let Attributes = [NoThrow, Const];
-  let Prototype = "_ExtVector<3, __fp16>(_ExtVector<3, __fp16>, _ExtVector<3, 
__fp16>)";
-}
-
 def HLSLDegrees : LangBuiltin<"HLSL_LANG"> {
   let Spellings = ["__builtin_hlsl_elementwise_degrees"];
   let Attributes = [NoThrow, Const, CustomTypeChecking];
diff --git a/clang/include/clang/Basic/HLSLIntrinsics.td 
b/clang/include/clang/Basic/HLSLIntrinsics.td
index 99259046940f1..ee1e4ca44db00 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -554,30 +554,6 @@ def hlsl_countbits : HLSLBuiltin<"countbits"> {
   let VaryingVecSizes = [2, 3, 4];
 }
 
-// Returns the cross product of two floating-point, 3D vectors.
-def hlsl_cross_float : HLSLBuiltin<"cross", "__builtin_hlsl_crossf32"> {
-  let Args = [VectorType<FloatTy, 3>, VectorType<FloatTy, 3>];
-  let ReturnType = VectorType<FloatTy, 3>;
-}
-
-def hlsl_cross_half : HLSLBuiltin<"cross", "__builtin_hlsl_crossf16"> {
-  let Doc = [{
-\fn T cross(T x, T y)
-\brief Returns the cross product of two floating-point, 3D vectors.
-\param x [in] The first floating-point, 3D vector.
-\param y [in] The second floating-point, 3D vector.
-
-Result is the cross product of x and y, i.e., the resulting
-components are, in order :
-x[1] * y[2] - y[1] * x[2]
-x[2] * y[0] - y[2] * x[0]
-x[0] * y[1] - y[0] * x[1]
-}];
-  let Args = [VectorType<HalfTy, 3>, VectorType<HalfTy, 3>];
-  let ReturnType = VectorType<HalfTy, 3>;
-  let Availability = SM6_2;
-}
-
 // Converts a floating-point, 4D vector set by a D3DCOLOR to a UBYTE4.
 def hlsl_d3d_color_to_ubyte4 : HLSLBuiltin<"D3DCOLORtoUBYTE4"> {
   let Doc = [{
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp 
b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 1bda113143e07..ae2b9b8d77b42 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1055,22 +1055,6 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned 
BuiltinID,
         /*ReturnType=*/OpX->getType(), Intr,
         ArrayRef<Value *>{OpX, OpMin, OpMax}, nullptr, "hlsl.clamp");
   }
-  case Builtin::BI__builtin_hlsl_crossf16:
-  case Builtin::BI__builtin_hlsl_crossf32: {
-    Value *Op0 = EmitScalarExpr(E->getArg(0));
-    Value *Op1 = EmitScalarExpr(E->getArg(1));
-    assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
-           E->getArg(1)->getType()->hasFloatingRepresentation() &&
-           "cross operands must have a float representation");
-    // make sure each vector has exactly 3 elements
-    assert(
-        E->getArg(0)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
-        E->getArg(1)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
-        "input vectors must have 3 elements each");
-    return Builder.CreateIntrinsic(
-        /*ReturnType=*/Op0->getType(), 
CGM.getHLSLRuntime().getCrossIntrinsic(),
-        ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.cross");
-  }
   case Builtin::BI__builtin_hlsl_dot: {
     Value *Op0 = EmitScalarExpr(E->getArg(0));
     Value *Op1 = EmitScalarExpr(E->getArg(1));
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 0664eef464f98..76a78764240a1 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -123,7 +123,6 @@ class CGHLSLRuntime {
 
   GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
-  GENERATE_HLSL_INTRINSIC_FUNCTION(Cross, cross)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Degrees, degrees)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
   GENERATE_HLSL_INTRINSIC_FUNCTION(FlattenedThreadIdInGroup,
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 45a066833afa2..70ed581fab5a1 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -51,6 +51,14 @@ enable_if_t<is_same<double, T>::value, T> 
mul_vec_impl(vector<T, N> x,
   return sum;
 }
 
+template <typename T>
+constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value,
+                      vector<T, 3>>
+cross_impl(vector<T, 3> x, vector<T, 3> y) {
+  return vector<T, 3>(x[1] * y[2] - y[1] * x[2], x[2] * y[0] - y[2] * x[0],
+                      x[0] * y[1] - y[0] * x[1]);
+}
+
 template <typename T>
 constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
 reflect_impl(T I, T N) {
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index a86fc7dc0988b..270cf4e1ff7eb 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -327,5 +327,27 @@ constexpr matrix<T, R, C> mul(matrix<T, R, C> x, T y) {
   return x * y;
 }
 
+//===----------------------------------------------------------------------===//
+// cross builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn T cross(T x, T y)
+/// \brief Returns the cross product of two floating-point, 3D vectors.
+/// \param x [in] The first floating-point, 3D vector.
+/// \param y [in] The second floating-point, 3D vector.
+///
+/// Result is the cross product of x and y, i.e., the resulting
+/// components are, in order :
+/// x[1] * y[2] - y[1] * x[2]
+/// x[2] * y[0] - y[2] * x[0]
+/// x[0] * y[1] - y[0] * x[1]
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+constexpr half3 cross(half3 x, half3 y) { return __detail::cross_impl(x, y); }
+
+constexpr float3 cross(float3 x, float3 y) {
+  return __detail::cross_impl(x, y);
+}
+
 } // namespace hlsl
 #endif //_HLSL_HLSL_INTRINSICS_H_
diff --git a/clang/test/CodeGenHLSL/builtins/cross.hlsl 
b/clang/test/CodeGenHLSL/builtins/cross.hlsl
index e3c65f5cf884c..a8aa43b634d91 100644
--- a/clang/test/CodeGenHLSL/builtins/cross.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/cross.hlsl
@@ -1,36 +1,108 @@
-// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// 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 
-fnative-int16-type \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
-// RUN:   --check-prefixes=CHECK,NATIVE_HALF \
-// RUN:   -DFNATTRS="hidden noundef nofpclass(nan inf)" -DTARGET=dx
-// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
-// RUN:   dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF \
-// RUN:   -DFNATTRS="hidden noundef nofpclass(nan inf)" -DTARGET=dx
-// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -triple \
 // RUN:   spirv-unknown-vulkan-library %s -fnative-half-type 
-fnative-int16-type \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
-// RUN:   --check-prefixes=CHECK,NATIVE_HALF \
-// RUN:   -DFNATTRS="hidden spir_func noundef nofpclass(nan inf)" -DTARGET=spv
-// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
-// RUN:   spirv-unknown-vulkan-library %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF \
-// RUN:   -DFNATTRS="hidden spir_func noundef nofpclass(nan inf)" -DTARGET=spv
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
 
-// NATIVE_HALF: define [[FNATTRS]] <3 x half> @
-// NATIVE_HALF: call reassoc nnan ninf nsz arcp afn <3 x half> 
@llvm.[[TARGET]].cross.v3f16(<3 x half>
-// NATIVE_HALF: ret <3 x half> %hlsl.cross
-// NO_HALF: define [[FNATTRS]] <3 x float> @
-// NO_HALF: call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.[[TARGET]].cross.v3f32(<3 x float>
-// NO_HALF: ret <3 x float> %hlsl.cross
+// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <3 x half> 
@_Z16test_cross_half3Dv3_DhS_(
+// CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]], <3 x half> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x half> [[P0]], i64 1
+// CHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x half> [[P1]], i64 2
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT1_I]], [[VECEXT_I]]
+// CHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x half> [[P1]], i64 1
+// CHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x half> [[P0]], i64 2
+// CHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT2_I]], [[VECEXT3_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half 
[[MUL_I]], [[MUL4_I]]
+// CHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x half> poison, half 
[[SUB_I]], i64 0
+// CHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x half> [[P1]], i64 0
+// CHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT6_I]], [[VECEXT3_I]]
+// CHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x half> [[P0]], i64 0
+// CHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT1_I]], [[VECEXT9_I]]
+// CHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half 
[[MUL7_I]], [[MUL10_I]]
+// CHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x half> 
[[VECINIT_I]], half [[SUB11_I]], i64 1
+// CHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT2_I]], [[VECEXT9_I]]
+// CHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT6_I]], [[VECEXT_I]]
+// CHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half 
[[MUL15_I]], [[MUL18_I]]
+// CHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x half> 
[[VECINIT12_I]], half [[SUB19_I]], i64 2
+// CHECK-NEXT:    ret <3 x half> [[VECINIT20_I]]
+//
+// SPVCHECK-LABEL: define hidden spir_func noundef nofpclass(nan inf) <3 x 
half> @_Z16test_cross_half3Dv3_DhS_(
+// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]], <3 x half> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x half> [[P0]], i64 1
+// SPVCHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x half> [[P1]], i64 
2
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT1_I]], [[VECEXT_I]]
+// SPVCHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x half> [[P1]], i64 
1
+// SPVCHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x half> [[P0]], i64 
2
+// SPVCHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT2_I]], [[VECEXT3_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half 
[[MUL_I]], [[MUL4_I]]
+// SPVCHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x half> poison, half 
[[SUB_I]], i64 0
+// SPVCHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x half> [[P1]], i64 0
+// SPVCHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT6_I]], [[VECEXT3_I]]
+// SPVCHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x half> [[P0]], i64 0
+// SPVCHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT1_I]], [[VECEXT9_I]]
+// SPVCHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
half [[MUL7_I]], [[MUL10_I]]
+// SPVCHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x half> 
[[VECINIT_I]], half [[SUB11_I]], i64 1
+// SPVCHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT2_I]], [[VECEXT9_I]]
+// SPVCHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT6_I]], [[VECEXT_I]]
+// SPVCHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
half [[MUL15_I]], [[MUL18_I]]
+// SPVCHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x half> 
[[VECINIT12_I]], half [[SUB19_I]], i64 2
+// SPVCHECK-NEXT:    ret <3 x half> [[VECINIT20_I]]
+//
 half3 test_cross_half3(half3 p0, half3 p1)
 {
     return cross(p0, p1);
 }
 
-// CHECK: define [[FNATTRS]] <3 x float> @
-// CHECK: %hlsl.cross = call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.[[TARGET]].cross.v3f32(
-// CHECK: ret <3 x float> %hlsl.cross
+// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <3 x float> 
@_Z17test_cross_float3Dv3_fS_(
+// CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]], <3 x float> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x float> [[P0]], i64 1
+// CHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x float> [[P1]], i64 2
+// CHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT1_I]], [[VECEXT_I]]
+// CHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x float> [[P1]], i64 1
+// CHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x float> [[P0]], i64 2
+// CHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT2_I]], [[VECEXT3_I]]
+// CHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float 
[[MUL_I]], [[MUL4_I]]
+// CHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x float> poison, float 
[[SUB_I]], i64 0
+// CHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x float> [[P1]], i64 0
+// CHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT6_I]], [[VECEXT3_I]]
+// CHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x float> [[P0]], i64 0
+// CHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT1_I]], [[VECEXT9_I]]
+// CHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float 
[[MUL7_I]], [[MUL10_I]]
+// CHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x float> 
[[VECINIT_I]], float [[SUB11_I]], i64 1
+// CHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT2_I]], [[VECEXT9_I]]
+// CHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT6_I]], [[VECEXT_I]]
+// CHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float 
[[MUL15_I]], [[MUL18_I]]
+// CHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x float> 
[[VECINIT12_I]], float [[SUB19_I]], i64 2
+// CHECK-NEXT:    ret <3 x float> [[VECINIT20_I]]
+//
+// SPVCHECK-LABEL: define hidden spir_func noundef nofpclass(nan inf) <3 x 
float> @_Z17test_cross_float3Dv3_fS_(
+// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]], <3 x 
float> noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// SPVCHECK-NEXT:  [[ENTRY:.*:]]
+// SPVCHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x float> [[P0]], i64 
1
+// SPVCHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 2
+// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT1_I]], [[VECEXT_I]]
+// SPVCHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 1
+// SPVCHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x float> [[P0]], 
i64 2
+// SPVCHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT2_I]], [[VECEXT3_I]]
+// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float 
[[MUL_I]], [[MUL4_I]]
+// SPVCHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x float> poison, 
float [[SUB_I]], i64 0
+// SPVCHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 0
+// SPVCHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT6_I]], [[VECEXT3_I]]
+// SPVCHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x float> [[P0]], 
i64 0
+// SPVCHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT1_I]], [[VECEXT9_I]]
+// SPVCHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
float [[MUL7_I]], [[MUL10_I]]
+// SPVCHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x float> 
[[VECINIT_I]], float [[SUB11_I]], i64 1
+// SPVCHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT2_I]], [[VECEXT9_I]]
+// SPVCHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT6_I]], [[VECEXT_I]]
+// SPVCHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
float [[MUL15_I]], [[MUL18_I]]
+// SPVCHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x float> 
[[VECINIT12_I]], float [[SUB19_I]], i64 2
+// SPVCHECK-NEXT:    ret <3 x float> [[VECINIT20_I]]
+//
 float3 test_cross_float3(float3 p0, float3 p1)
 {
     return cross(p0, p1);
diff --git a/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
index c9bd1bdd3cb8e..af811e2c2320d 100644
--- a/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
@@ -4,58 +4,30 @@ void test_too_few_arg()
 {
   return cross();
   // expected-error@-1 {{no matching function for call to 'cross'}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function 
not viable: requires 2 arguments, but 0 were provided}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function 
not viable: requires 2 arguments, but 0 were provided}}
-}
-
-void test_too_few_arg_f32()
-{
-  return __builtin_hlsl_crossf32();
-  // expected-error@-1 {{too few arguments to function call, expected 2, have 
0}}
-}
-
-void test_too_few_arg_f16()
-{
-  return __builtin_hlsl_crossf16();
-  // expected-error@-1 {{too few arguments to function call, expected 2, have 
0}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 0 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 0 were provided}}
 }
 
 void test_too_many_arg(float3 p0)
 {
   return cross(p0, p0, p0);
   // expected-error@-1 {{no matching function for call to 'cross'}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function 
not viable: requires 2 arguments, but 3 were provided}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function 
not viable: requires 2 arguments, but 3 were provided}}
-}
-
-void test_too_many_arg_f32(float3 p0)
-{
-  return __builtin_hlsl_crossf32(p0, p0, p0);
-  // expected-error@-1 {{too many arguments to function call, expected 2, have 
3}}
-}
-
-void test_too_many_arg_f16(half3 p0)
-{
-  return __builtin_hlsl_crossf16(p0, p0, p0);
-  // expected-error@-1 {{too many arguments to function call, expected 2, have 
3}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 3 were provided}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 3 were provided}}
 }
 
-bool2 builtin_cross_int2_to_float2_promotion(int2 p1)
+float2 test_cross_float2(float2 p1, float2 p2)
 {
-  return __builtin_hlsl_crossf32(p1, p1);
-  // expected-error@-1 {{cannot initialize a parameter of type 'vector<float, 
3>' (vector of 3 'float' values) with an lvalue of type 'int2' (aka 
'vector<int, 2>')}}
-}
-
-float2 builtin_cross_float2(float2 p1, float2 p2)
-{
-  return __builtin_hlsl_crossf32(p1, p2);
-  // expected-error@-1 {{cannot initialize a parameter of type 'vector<float, 
3>' (vector of 3 'float' values) with an lvalue of type 'float2' (aka 
'vector<float, 2>')}}
+  return cross(p1, p2);
+  // expected-error@-1 {{no matching function for call to 'cross'}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
 }
 
 void test_ambiguous(int p0)
 {
   return cross(p0,p0);
   // expected-error@-1 {{call to 'cross' is ambiguous}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function}}
-  // expected-note@hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
+  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
 }
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 4dd86270f0d01..14faf73508b46 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -183,7 +183,6 @@ def int_dx_asdouble : 
DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm
 def int_dx_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, 
LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
 def int_dx_sclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, 
LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
 def int_dx_nclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
-def int_dx_cross : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
 def int_dx_saturate : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem, IntrTriviallyScalarizable]>;
 
 def int_dx_dot2 : DefaultAttrsIntrinsic<[LLVMMatchType<0>],
diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp 
b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
index 1025015c09a1f..0b502152866b4 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -213,7 +213,6 @@ static bool isIntrinsicExpansion(Function &F) {
   case Intrinsic::powi:
   case Intrinsic::dx_all:
   case Intrinsic::dx_any:
-  case Intrinsic::dx_cross:
   case Intrinsic::dx_uclamp:
   case Intrinsic::dx_sclamp:
   case Intrinsic::dx_nclamp:
@@ -409,41 +408,6 @@ static Value *expandAbs(CallInst *Orig) {
                                  "dx.max");
 }
 
-static Value *expandCrossIntrinsic(CallInst *Orig) {
-
-  VectorType *VT = cast<VectorType>(Orig->getType());
-  if (cast<FixedVectorType>(VT)->getNumElements() != 3)
-    reportFatalUsageError("return vector must have exactly 3 elements");
-
-  Value *op0 = Orig->getOperand(0);
-  Value *op1 = Orig->getOperand(1);
-  IRBuilder<> Builder(Orig);
-
-  Value *op0_x = Builder.CreateExtractElement(op0, (uint64_t)0, "x0");
-  Value *op0_y = Builder.CreateExtractElement(op0, 1, "x1");
-  Value *op0_z = Builder.CreateExtractElement(op0, 2, "x2");
-
-  Value *op1_x = Builder.CreateExtractElement(op1, (uint64_t)0, "y0");
-  Value *op1_y = Builder.CreateExtractElement(op1, 1, "y1");
-  Value *op1_z = Builder.CreateExtractElement(op1, 2, "y2");
-
-  auto MulSub = [&](Value *x0, Value *y0, Value *x1, Value *y1) -> Value * {
-    Value *xy = Builder.CreateFMul(x0, y1);
-    Value *yx = Builder.CreateFMul(y0, x1);
-    return Builder.CreateFSub(xy, yx, Orig->getName());
-  };
-
-  Value *yz_zy = MulSub(op0_y, op0_z, op1_y, op1_z);
-  Value *zx_xz = MulSub(op0_z, op0_x, op1_z, op1_x);
-  Value *xy_yx = MulSub(op0_x, op0_y, op1_x, op1_y);
-
-  Value *cross = PoisonValue::get(VT);
-  cross = Builder.CreateInsertElement(cross, yz_zy, (uint64_t)0);
-  cross = Builder.CreateInsertElement(cross, zx_xz, 1);
-  cross = Builder.CreateInsertElement(cross, xy_yx, 2);
-  return cross;
-}
-
 // Create appropriate DXIL float dot intrinsic for the given A and B operands
 // The appropriate opcode will be determined by the size of the operands
 // The dot product is placed in the position indicated by Orig
@@ -1302,9 +1266,6 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
   case Intrinsic::dx_any:
     Result = expandAnyOrAllIntrinsic(Orig, IntrinsicId);
     break;
-  case Intrinsic::dx_cross:
-    Result = expandCrossIntrinsic(Orig);
-    break;
   case Intrinsic::dx_uclamp:
   case Intrinsic::dx_sclamp:
   case Intrinsic::dx_nclamp:
diff --git a/llvm/lib/Target/SPIRV/SPIRVCombine.td 
b/llvm/lib/Target/SPIRV/SPIRVCombine.td
index 7d69465de4ffb..fe40bdd607a71 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombine.td
+++ b/llvm/lib/Target/SPIRV/SPIRVCombine.td
@@ -34,10 +34,17 @@ def matrix_multiply_lowering
                         [{ return Helper.matchMatrixMultiply(*${root}); }]),
                     (apply [{ Helper.applyMatrixMultiply(*${root}); }])>;
 
+def vector_cross_lowering
+    : GICombineRule<(defs root:$root),
+                    (match (wip_match_opcode G_INTRINSIC_W_SIDE_EFFECTS):$root,
+                        [{ return Helper.matchCross(*${root}); }]),
+                    (apply [{ Helper.applySPIRVCross(*${root}); }])>;
+
 def SPIRVPreLegalizerCombiner
     : GICombiner<"SPIRVPreLegalizerCombinerImpl",
                  [vector_length_sub_to_distance_lowering,
                   vector_select_to_faceforward_lowering,
-                  matrix_transpose_lowering, matrix_multiply_lowering]> {
+                  matrix_transpose_lowering, matrix_multiply_lowering,
+                  vector_cross_lowering]> {
   let CombineAllMethodName = "tryCombineAllImpl";
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp 
b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
index 660bee0f7a2cd..dbd9d1fcfc8af 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
@@ -402,3 +402,175 @@ void 
SPIRVCombinerHelper::applyMatrixMultiply(MachineInstr &MI) const {
     Builder.buildBuildVector(ResReg, ResultScalars);
   MI.eraseFromParent();
 }
+
+/// Decodes \p Reg as an `spv_extractelt` intrinsic with a constant index. On
+/// success sets \p Vec to the source vector and \p Idx to the extracted 
element
+/// index.
+static bool matchExtractElt(Register Reg, MachineRegisterInfo &MRI,
+                            Register &Vec, int64_t &Idx) {
+  MachineInstr *Def = MRI.getVRegDef(Reg);
+  auto *GI = Def ? dyn_cast<GIntrinsic>(Def) : nullptr;
+  if (!GI || GI->getIntrinsicID() != Intrinsic::spv_extractelt)
+    return false;
+  Vec = Def->getOperand(2).getReg();
+  return mi_match(Def->getOperand(3).getReg(), MRI, m_ICst(Idx));
+}
+
+bool SPIRVCombinerHelper::getCrossOperands(MachineInstr &MI, Register &X,
+                                           Register &Y) const {
+  auto *TopInsert = dyn_cast<GIntrinsic>(&MI);
+  if (!TopInsert || TopInsert->getIntrinsicID() != Intrinsic::spv_insertelt)
+    return false;
+
+  // Walk the insertelt chain, collecting the scalar component inserted at each
+  // of the three indices. The chain must build all of indices 0, 1 and 2 on 
top
+  // of an undef/poison base vector.
+  Register Comp[3];
+  bool Seen[3] = {false, false, false};
+  MachineInstr *Cur = &MI;
+  for (unsigned Depth = 0; Depth < 3; ++Depth) {
+    auto *Insert = dyn_cast<GIntrinsic>(Cur);
+    if (!Insert || Insert->getIntrinsicID() != Intrinsic::spv_insertelt)
+      return false;
+    int64_t Idx;
+    if (!mi_match(Cur->getOperand(4).getReg(), MRI, m_ICst(Idx)))
+      return false;
+    if (Idx < 0 || Idx > 2 || Seen[Idx])
+      return false;
+    Seen[Idx] = true;
+    Comp[Idx] = Cur->getOperand(3).getReg();
+    Cur = MRI.getVRegDef(Cur->getOperand(2).getReg());
+  }
+  if (!Seen[0] || !Seen[1] || !Seen[2])
+    return false;
+  // The base of the chain must be an undef/poison vector.
+  if (!Cur || Cur->getOpcode() != TargetOpcode::G_IMPLICIT_DEF)
+    return false;
+
+  // Decodes a `G_FMUL` product of two extracted elements. On success sets \p 
Xr
+  // and \p Yr such that the product is `Xr[IdxX] * Yr[IdxY]`.
+  auto decodeProduct = [&](Register Prod, int64_t IdxX, int64_t IdxY,
+                           Register &Xr, Register &Yr) -> bool {
+    Register O0, O1;
+    if (!mi_match(Prod, MRI, m_GFMul(m_Reg(O0), m_Reg(O1))))
+      return false;
+    Register V0, V1;
+    int64_t I0, I1;
+    if (!matchExtractElt(O0, MRI, V0, I0) || !matchExtractElt(O1, MRI, V1, I1))
+      return false;
+    // `G_FMUL` is commutative, so accept either operand ordering.
+    if (I0 == IdxX && I1 == IdxY) {
+      Xr = V0;
+      Yr = V1;
+      return true;
+    }
+    if (I1 == IdxX && I0 == IdxY) {
+      Xr = V1;
+      Yr = V0;
+      return true;
+    }
+    return false;
+  };
+
+  Register XVec, YVec;
+  for (unsigned I = 0; I < 3; ++I) {
+    unsigned J = (I + 1) % 3;
+    unsigned K = (I + 2) % 3;
+
+    // result[I] = X[J] * Y[K] - Y[J] * X[K]
+    Register PosProd, NegProd;
+    if (!mi_match(Comp[I], MRI, m_GFSub(m_Reg(PosProd), m_Reg(NegProd))))
+      return false;
+
+    // Positive product is X[J] * Y[K], negative product is X[K] * Y[J].
+    Register XPos, YPos, XNeg, YNeg;
+    if (!decodeProduct(PosProd, J, K, XPos, YPos) ||
+        !decodeProduct(NegProd, K, J, XNeg, YNeg))
+      return false;
+
+    // The two products must reference the same X and Y vectors.
+    if (XPos != XNeg || YPos != YNeg)
+      return false;
+
+    // All components must agree on the same X and Y source vectors.
+    if (I == 0) {
+      XVec = XPos;
+      YVec = YPos;
+    } else if (XVec != XPos || YVec != YPos) {
+      return false;
+    }
+  }
+
+  // X and Y must be 3-element vectors of the same type as the result.
+  LLT ResTy = MRI.getType(MI.getOperand(0).getReg());
+  if (!ResTy.isVector() || ResTy.getNumElements() != 3)
+    return false;
+  if (MRI.getType(XVec) != ResTy || MRI.getType(YVec) != ResTy)
+    return false;
+
+  X = XVec;
+  Y = YVec;
+  return true;
+}
+
+bool SPIRVCombinerHelper::matchCross(MachineInstr &MI) const {
+  Register X, Y;
+  return getCrossOperands(MI, X, Y);
+}
+
+void SPIRVCombinerHelper::applySPIRVCross(MachineInstr &MI) const {
+  Register X, Y;
+  [[maybe_unused]] bool Matched = getCrossOperands(MI, X, Y);
+  assert(Matched && "applySPIRVCross called on a non-cross pattern");
+
+  // Collect the expanded pattern (insertelt chain, fsubs, fmuls and
+  // extractelts) so it can be removed once it has been replaced by the cross
+  // intrinsic. The `spv_insertelt`/`spv_extractelt` intrinsics are modeled 
with
+  // side effects, so they are not removed by dead code elimination and must be
+  // erased here.
+  SmallVector<MachineInstr *, 4> Inserts, Subs;
+  SmallVector<MachineInstr *, 8> Muls, Extracts;
+  MachineInstr *Cur = &MI;
+  for (unsigned Depth = 0; Depth < 3; ++Depth) {
+    Inserts.push_back(Cur);
+    MachineInstr *Sub = MRI.getVRegDef(Cur->getOperand(3).getReg());
+    Subs.push_back(Sub);
+    for (unsigned OpIdx : {1u, 2u}) {
+      MachineInstr *Mul = MRI.getVRegDef(Sub->getOperand(OpIdx).getReg());
+      Muls.push_back(Mul);
+      Extracts.push_back(MRI.getVRegDef(Mul->getOperand(1).getReg()));
+      Extracts.push_back(MRI.getVRegDef(Mul->getOperand(2).getReg()));
+    }
+    Cur = MRI.getVRegDef(Cur->getOperand(2).getReg());
+  }
+
+  Register ResultReg = MI.getOperand(0).getReg();
+  Builder.setInstrAndDebugLoc(MI);
+  Builder.buildIntrinsic(Intrinsic::spv_cross, ResultReg).addUse(X).addUse(Y);
+
+  SPIRVGlobalRegistry *GR =
+      MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
+  SmallPtrSet<MachineInstr *, 16> Erased;
+  auto EraseInstr = [&](MachineInstr *DeadMI, bool Force) {
+    if (!DeadMI || Erased.contains(DeadMI))
+      return;
+    // Only remove an instruction once it is truly dead, unless it shares its
+    // result register with the newly built intrinsic (the outermost insert).
+    if (!Force && !MRI.use_nodbg_empty(DeadMI->getOperand(0).getReg()))
+      return;
+    Erased.insert(DeadMI);
+    GR->invalidateMachineInstr(DeadMI);
+    DeadMI->eraseFromParent();
+  };
+
+  // Erase defs before their operands so that uses drop to zero along the way.
+  EraseInstr(&MI, /*Force=*/true);
+  for (MachineInstr *Insert : Inserts)
+    EraseInstr(Insert, /*Force=*/false);
+  for (MachineInstr *Sub : Subs)
+    EraseInstr(Sub, /*Force=*/false);
+  for (MachineInstr *Mul : Muls)
+    EraseInstr(Mul, /*Force=*/false);
+  for (MachineInstr *Extract : Extracts)
+    EraseInstr(Extract, /*Force=*/false);
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h 
b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
index 19e2a6901b8f0..9f6a50532ea1c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
@@ -37,8 +37,11 @@ class SPIRVCombinerHelper : public CombinerHelper {
   void applyMatrixTranspose(MachineInstr &MI) const;
   bool matchMatrixMultiply(MachineInstr &MI) const;
   void applyMatrixMultiply(MachineInstr &MI) const;
+  bool matchCross(MachineInstr &MI) const;
+  void applySPIRVCross(MachineInstr &MI) const;
 
 private:
+  bool getCrossOperands(MachineInstr &MI, Register &X, Register &Y) const;
   SPIRVTypeInst getDotProductVectorType(Register ResReg, uint32_t K,
                                         SPIRVGlobalRegistry *GR) const;
   SmallVector<Register, 4> extractColumns(Register BReg, uint32_t N,
diff --git a/llvm/test/CodeGen/DirectX/cross.ll 
b/llvm/test/CodeGen/DirectX/cross.ll
deleted file mode 100644
index 262f1983c449b..0000000000000
--- a/llvm/test/CodeGen/DirectX/cross.ll
+++ /dev/null
@@ -1,56 +0,0 @@
-; RUN: opt -S  -dxil-intrinsic-expansion  < %s | FileCheck %s
-
-; Make sure dxil operation function calls for cross are generated for 
half/float.
-
-declare <3 x half> @llvm.dx.cross.v3f16(<3 x half>, <3 x half>)
-declare <3 x float> @llvm.dx.cross.v3f32(<3 x float>, <3 x float>)
-
-define noundef <3 x half> @test_cross_half3(<3 x half> noundef %p0, <3 x half> 
noundef %p1) {
-entry:
-  ; CHECK: %x0 = extractelement <3 x half> %p0, i64 0
-  ; CHECK: %x1 = extractelement <3 x half> %p0, i64 1
-  ; CHECK: %x2 = extractelement <3 x half> %p0, i64 2
-  ; CHECK: %y0 = extractelement <3 x half> %p1, i64 0
-  ; CHECK: %y1 = extractelement <3 x half> %p1, i64 1
-  ; CHECK: %y2 = extractelement <3 x half> %p1, i64 2
-  ; CHECK: %0 = fmul half %x1, %y2
-  ; CHECK: %1 = fmul half %x2, %y1
-  ; CHECK: %hlsl.cross1 = fsub half %0, %1
-  ; CHECK: %2 = fmul half %x2, %y0
-  ; CHECK: %3 = fmul half %x0, %y2
-  ; CHECK: %hlsl.cross2 = fsub half %2, %3
-  ; CHECK: %4 = fmul half %x0, %y1
-  ; CHECK: %5 = fmul half %x1, %y0
-  ; CHECK: %hlsl.cross3 = fsub half %4, %5
-  ; CHECK: %6 = insertelement <3 x half> poison, half %hlsl.cross1, i64 0
-  ; CHECK: %7 = insertelement <3 x half> %6, half %hlsl.cross2, i64 1
-  ; CHECK: %8 = insertelement <3 x half> %7, half %hlsl.cross3, i64 2
-  ; CHECK: ret <3 x half> %8
-  %hlsl.cross = call <3 x half> @llvm.dx.cross.v3f16(<3 x half> %p0, <3 x 
half> %p1)
-  ret <3 x half> %hlsl.cross
-}
-
-define noundef <3 x float> @test_cross_float3(<3 x float> noundef %p0, <3 x 
float> noundef %p1) {
-entry:
-  ; CHECK: %x0 = extractelement <3 x float> %p0, i64 0
-  ; CHECK: %x1 = extractelement <3 x float> %p0, i64 1
-  ; CHECK: %x2 = extractelement <3 x float> %p0, i64 2
-  ; CHECK: %y0 = extractelement <3 x float> %p1, i64 0
-  ; CHECK: %y1 = extractelement <3 x float> %p1, i64 1
-  ; CHECK: %y2 = extractelement <3 x float> %p1, i64 2
-  ; CHECK: %0 = fmul float %x1, %y2
-  ; CHECK: %1 = fmul float %x2, %y1
-  ; CHECK: %hlsl.cross1 = fsub float %0, %1
-  ; CHECK: %2 = fmul float %x2, %y0
-  ; CHECK: %3 = fmul float %x0, %y2
-  ; CHECK: %hlsl.cross2 = fsub float %2, %3
-  ; CHECK: %4 = fmul float %x0, %y1
-  ; CHECK: %5 = fmul float %x1, %y0
-  ; CHECK: %hlsl.cross3 = fsub float %4, %5
-  ; CHECK: %6 = insertelement <3 x float> poison, float %hlsl.cross1, i64 0
-  ; CHECK: %7 = insertelement <3 x float> %6, float %hlsl.cross2, i64 1
-  ; CHECK: %8 = insertelement <3 x float> %7, float %hlsl.cross3, i64 2
-  ; CHECK: ret <3 x float> %8
-  %hlsl.cross = call <3 x float> @llvm.dx.cross.v3f32(<3 x float> %p0, <3 x 
float> %p1)
-  ret <3 x float> %hlsl.cross
-}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
index 6b7e209227dd0..901d3de346ea3 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
@@ -29,5 +29,62 @@ entry:
   ret <3 x float> %hlsl.cross
 }
 
+; Make sure the manually expanded cross product (as emitted by the header-only
+; HLSL implementation) is combined back into the Cross extended instruction.
+
+define noundef <3 x half> @cross_instcombine_half3(<3 x half> noundef %a, <3 x 
half> noundef %b) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_16]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_16]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_16]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_16]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
+  %a0 = extractelement <3 x half> %a, i64 0
+  %a1 = extractelement <3 x half> %a, i64 1
+  %a2 = extractelement <3 x half> %a, i64 2
+  %b0 = extractelement <3 x half> %b, i64 0
+  %b1 = extractelement <3 x half> %b, i64 1
+  %b2 = extractelement <3 x half> %b, i64 2
+  %mul0 = fmul half %a1, %b2
+  %mul1 = fmul half %b1, %a2
+  %sub0 = fsub half %mul0, %mul1
+  %vec0 = insertelement <3 x half> poison, half %sub0, i64 0
+  %mul2 = fmul half %a2, %b0
+  %mul3 = fmul half %b2, %a0
+  %sub1 = fsub half %mul2, %mul3
+  %vec1 = insertelement <3 x half> %vec0, half %sub1, i64 1
+  %mul4 = fmul half %a0, %b1
+  %mul5 = fmul half %b0, %a1
+  %sub2 = fsub half %mul4, %mul5
+  %vec2 = insertelement <3 x half> %vec1, half %sub2, i64 2
+  ret <3 x half> %vec2
+}
+
+define noundef <3 x float> @cross_instcombine_float3(<3 x float> noundef %a, 
<3 x float> noundef %b) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_32]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
+  %a0 = extractelement <3 x float> %a, i64 0
+  %a1 = extractelement <3 x float> %a, i64 1
+  %a2 = extractelement <3 x float> %a, i64 2
+  %b0 = extractelement <3 x float> %b, i64 0
+  %b1 = extractelement <3 x float> %b, i64 1
+  %b2 = extractelement <3 x float> %b, i64 2
+  %mul0 = fmul float %a1, %b2
+  %mul1 = fmul float %b1, %a2
+  %sub0 = fsub float %mul0, %mul1
+  %vec0 = insertelement <3 x float> poison, float %sub0, i64 0
+  %mul2 = fmul float %a2, %b0
+  %mul3 = fmul float %b2, %a0
+  %sub1 = fsub float %mul2, %mul3
+  %vec1 = insertelement <3 x float> %vec0, float %sub1, i64 1
+  %mul4 = fmul float %a0, %b1
+  %mul5 = fmul float %b0, %a1
+  %sub2 = fsub float %mul4, %mul5
+  %vec2 = insertelement <3 x float> %vec1, float %sub2, i64 2
+  ret <3 x float> %vec2
+}
+
 declare <3 x half> @llvm.spv.cross.v3f16(<3 x half>, <3 x half>)
 declare <3 x float> @llvm.spv.cross.v3f32(<3 x float>, <3 x float>)

>From 8eb16dd153dd69b932c2bb3604272ec333d0adb4 Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Thu, 23 Jul 2026 12:18:29 -0700
Subject: [PATCH 2/6] remove spirv inst combine

---
 llvm/lib/Target/SPIRV/SPIRVCombine.td         |   9 +-
 llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp | 172 ------------------
 llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h   |   1 -
 .../CodeGen/SPIRV/hlsl-intrinsics/cross.ll    |  57 ------
 4 files changed, 1 insertion(+), 238 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVCombine.td 
b/llvm/lib/Target/SPIRV/SPIRVCombine.td
index fe40bdd607a71..7d69465de4ffb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombine.td
+++ b/llvm/lib/Target/SPIRV/SPIRVCombine.td
@@ -34,17 +34,10 @@ def matrix_multiply_lowering
                         [{ return Helper.matchMatrixMultiply(*${root}); }]),
                     (apply [{ Helper.applyMatrixMultiply(*${root}); }])>;
 
-def vector_cross_lowering
-    : GICombineRule<(defs root:$root),
-                    (match (wip_match_opcode G_INTRINSIC_W_SIDE_EFFECTS):$root,
-                        [{ return Helper.matchCross(*${root}); }]),
-                    (apply [{ Helper.applySPIRVCross(*${root}); }])>;
-
 def SPIRVPreLegalizerCombiner
     : GICombiner<"SPIRVPreLegalizerCombinerImpl",
                  [vector_length_sub_to_distance_lowering,
                   vector_select_to_faceforward_lowering,
-                  matrix_transpose_lowering, matrix_multiply_lowering,
-                  vector_cross_lowering]> {
+                  matrix_transpose_lowering, matrix_multiply_lowering]> {
   let CombineAllMethodName = "tryCombineAllImpl";
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp 
b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
index dbd9d1fcfc8af..660bee0f7a2cd 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
@@ -402,175 +402,3 @@ void 
SPIRVCombinerHelper::applyMatrixMultiply(MachineInstr &MI) const {
     Builder.buildBuildVector(ResReg, ResultScalars);
   MI.eraseFromParent();
 }
-
-/// Decodes \p Reg as an `spv_extractelt` intrinsic with a constant index. On
-/// success sets \p Vec to the source vector and \p Idx to the extracted 
element
-/// index.
-static bool matchExtractElt(Register Reg, MachineRegisterInfo &MRI,
-                            Register &Vec, int64_t &Idx) {
-  MachineInstr *Def = MRI.getVRegDef(Reg);
-  auto *GI = Def ? dyn_cast<GIntrinsic>(Def) : nullptr;
-  if (!GI || GI->getIntrinsicID() != Intrinsic::spv_extractelt)
-    return false;
-  Vec = Def->getOperand(2).getReg();
-  return mi_match(Def->getOperand(3).getReg(), MRI, m_ICst(Idx));
-}
-
-bool SPIRVCombinerHelper::getCrossOperands(MachineInstr &MI, Register &X,
-                                           Register &Y) const {
-  auto *TopInsert = dyn_cast<GIntrinsic>(&MI);
-  if (!TopInsert || TopInsert->getIntrinsicID() != Intrinsic::spv_insertelt)
-    return false;
-
-  // Walk the insertelt chain, collecting the scalar component inserted at each
-  // of the three indices. The chain must build all of indices 0, 1 and 2 on 
top
-  // of an undef/poison base vector.
-  Register Comp[3];
-  bool Seen[3] = {false, false, false};
-  MachineInstr *Cur = &MI;
-  for (unsigned Depth = 0; Depth < 3; ++Depth) {
-    auto *Insert = dyn_cast<GIntrinsic>(Cur);
-    if (!Insert || Insert->getIntrinsicID() != Intrinsic::spv_insertelt)
-      return false;
-    int64_t Idx;
-    if (!mi_match(Cur->getOperand(4).getReg(), MRI, m_ICst(Idx)))
-      return false;
-    if (Idx < 0 || Idx > 2 || Seen[Idx])
-      return false;
-    Seen[Idx] = true;
-    Comp[Idx] = Cur->getOperand(3).getReg();
-    Cur = MRI.getVRegDef(Cur->getOperand(2).getReg());
-  }
-  if (!Seen[0] || !Seen[1] || !Seen[2])
-    return false;
-  // The base of the chain must be an undef/poison vector.
-  if (!Cur || Cur->getOpcode() != TargetOpcode::G_IMPLICIT_DEF)
-    return false;
-
-  // Decodes a `G_FMUL` product of two extracted elements. On success sets \p 
Xr
-  // and \p Yr such that the product is `Xr[IdxX] * Yr[IdxY]`.
-  auto decodeProduct = [&](Register Prod, int64_t IdxX, int64_t IdxY,
-                           Register &Xr, Register &Yr) -> bool {
-    Register O0, O1;
-    if (!mi_match(Prod, MRI, m_GFMul(m_Reg(O0), m_Reg(O1))))
-      return false;
-    Register V0, V1;
-    int64_t I0, I1;
-    if (!matchExtractElt(O0, MRI, V0, I0) || !matchExtractElt(O1, MRI, V1, I1))
-      return false;
-    // `G_FMUL` is commutative, so accept either operand ordering.
-    if (I0 == IdxX && I1 == IdxY) {
-      Xr = V0;
-      Yr = V1;
-      return true;
-    }
-    if (I1 == IdxX && I0 == IdxY) {
-      Xr = V1;
-      Yr = V0;
-      return true;
-    }
-    return false;
-  };
-
-  Register XVec, YVec;
-  for (unsigned I = 0; I < 3; ++I) {
-    unsigned J = (I + 1) % 3;
-    unsigned K = (I + 2) % 3;
-
-    // result[I] = X[J] * Y[K] - Y[J] * X[K]
-    Register PosProd, NegProd;
-    if (!mi_match(Comp[I], MRI, m_GFSub(m_Reg(PosProd), m_Reg(NegProd))))
-      return false;
-
-    // Positive product is X[J] * Y[K], negative product is X[K] * Y[J].
-    Register XPos, YPos, XNeg, YNeg;
-    if (!decodeProduct(PosProd, J, K, XPos, YPos) ||
-        !decodeProduct(NegProd, K, J, XNeg, YNeg))
-      return false;
-
-    // The two products must reference the same X and Y vectors.
-    if (XPos != XNeg || YPos != YNeg)
-      return false;
-
-    // All components must agree on the same X and Y source vectors.
-    if (I == 0) {
-      XVec = XPos;
-      YVec = YPos;
-    } else if (XVec != XPos || YVec != YPos) {
-      return false;
-    }
-  }
-
-  // X and Y must be 3-element vectors of the same type as the result.
-  LLT ResTy = MRI.getType(MI.getOperand(0).getReg());
-  if (!ResTy.isVector() || ResTy.getNumElements() != 3)
-    return false;
-  if (MRI.getType(XVec) != ResTy || MRI.getType(YVec) != ResTy)
-    return false;
-
-  X = XVec;
-  Y = YVec;
-  return true;
-}
-
-bool SPIRVCombinerHelper::matchCross(MachineInstr &MI) const {
-  Register X, Y;
-  return getCrossOperands(MI, X, Y);
-}
-
-void SPIRVCombinerHelper::applySPIRVCross(MachineInstr &MI) const {
-  Register X, Y;
-  [[maybe_unused]] bool Matched = getCrossOperands(MI, X, Y);
-  assert(Matched && "applySPIRVCross called on a non-cross pattern");
-
-  // Collect the expanded pattern (insertelt chain, fsubs, fmuls and
-  // extractelts) so it can be removed once it has been replaced by the cross
-  // intrinsic. The `spv_insertelt`/`spv_extractelt` intrinsics are modeled 
with
-  // side effects, so they are not removed by dead code elimination and must be
-  // erased here.
-  SmallVector<MachineInstr *, 4> Inserts, Subs;
-  SmallVector<MachineInstr *, 8> Muls, Extracts;
-  MachineInstr *Cur = &MI;
-  for (unsigned Depth = 0; Depth < 3; ++Depth) {
-    Inserts.push_back(Cur);
-    MachineInstr *Sub = MRI.getVRegDef(Cur->getOperand(3).getReg());
-    Subs.push_back(Sub);
-    for (unsigned OpIdx : {1u, 2u}) {
-      MachineInstr *Mul = MRI.getVRegDef(Sub->getOperand(OpIdx).getReg());
-      Muls.push_back(Mul);
-      Extracts.push_back(MRI.getVRegDef(Mul->getOperand(1).getReg()));
-      Extracts.push_back(MRI.getVRegDef(Mul->getOperand(2).getReg()));
-    }
-    Cur = MRI.getVRegDef(Cur->getOperand(2).getReg());
-  }
-
-  Register ResultReg = MI.getOperand(0).getReg();
-  Builder.setInstrAndDebugLoc(MI);
-  Builder.buildIntrinsic(Intrinsic::spv_cross, ResultReg).addUse(X).addUse(Y);
-
-  SPIRVGlobalRegistry *GR =
-      MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
-  SmallPtrSet<MachineInstr *, 16> Erased;
-  auto EraseInstr = [&](MachineInstr *DeadMI, bool Force) {
-    if (!DeadMI || Erased.contains(DeadMI))
-      return;
-    // Only remove an instruction once it is truly dead, unless it shares its
-    // result register with the newly built intrinsic (the outermost insert).
-    if (!Force && !MRI.use_nodbg_empty(DeadMI->getOperand(0).getReg()))
-      return;
-    Erased.insert(DeadMI);
-    GR->invalidateMachineInstr(DeadMI);
-    DeadMI->eraseFromParent();
-  };
-
-  // Erase defs before their operands so that uses drop to zero along the way.
-  EraseInstr(&MI, /*Force=*/true);
-  for (MachineInstr *Insert : Inserts)
-    EraseInstr(Insert, /*Force=*/false);
-  for (MachineInstr *Sub : Subs)
-    EraseInstr(Sub, /*Force=*/false);
-  for (MachineInstr *Mul : Muls)
-    EraseInstr(Mul, /*Force=*/false);
-  for (MachineInstr *Extract : Extracts)
-    EraseInstr(Extract, /*Force=*/false);
-}
diff --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h 
b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
index 9f6a50532ea1c..afddb220a5403 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
@@ -41,7 +41,6 @@ class SPIRVCombinerHelper : public CombinerHelper {
   void applySPIRVCross(MachineInstr &MI) const;
 
 private:
-  bool getCrossOperands(MachineInstr &MI, Register &X, Register &Y) const;
   SPIRVTypeInst getDotProductVectorType(Register ResReg, uint32_t K,
                                         SPIRVGlobalRegistry *GR) const;
   SmallVector<Register, 4> extractColumns(Register BReg, uint32_t N,
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
index 901d3de346ea3..6b7e209227dd0 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
@@ -29,62 +29,5 @@ entry:
   ret <3 x float> %hlsl.cross
 }
 
-; Make sure the manually expanded cross product (as emitted by the header-only
-; HLSL implementation) is combined back into the Cross extended instruction.
-
-define noundef <3 x half> @cross_instcombine_half3(<3 x half> noundef %a, <3 x 
half> noundef %b) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_16]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_16]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_16]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_16]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
-  %a0 = extractelement <3 x half> %a, i64 0
-  %a1 = extractelement <3 x half> %a, i64 1
-  %a2 = extractelement <3 x half> %a, i64 2
-  %b0 = extractelement <3 x half> %b, i64 0
-  %b1 = extractelement <3 x half> %b, i64 1
-  %b2 = extractelement <3 x half> %b, i64 2
-  %mul0 = fmul half %a1, %b2
-  %mul1 = fmul half %b1, %a2
-  %sub0 = fsub half %mul0, %mul1
-  %vec0 = insertelement <3 x half> poison, half %sub0, i64 0
-  %mul2 = fmul half %a2, %b0
-  %mul3 = fmul half %b2, %a0
-  %sub1 = fsub half %mul2, %mul3
-  %vec1 = insertelement <3 x half> %vec0, half %sub1, i64 1
-  %mul4 = fmul half %a0, %b1
-  %mul5 = fmul half %b0, %a1
-  %sub2 = fsub half %mul4, %mul5
-  %vec2 = insertelement <3 x half> %vec1, half %sub2, i64 2
-  ret <3 x half> %vec2
-}
-
-define noundef <3 x float> @cross_instcombine_float3(<3 x float> noundef %a, 
<3 x float> noundef %b) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_32]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_32]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_32]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_32]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
-  %a0 = extractelement <3 x float> %a, i64 0
-  %a1 = extractelement <3 x float> %a, i64 1
-  %a2 = extractelement <3 x float> %a, i64 2
-  %b0 = extractelement <3 x float> %b, i64 0
-  %b1 = extractelement <3 x float> %b, i64 1
-  %b2 = extractelement <3 x float> %b, i64 2
-  %mul0 = fmul float %a1, %b2
-  %mul1 = fmul float %b1, %a2
-  %sub0 = fsub float %mul0, %mul1
-  %vec0 = insertelement <3 x float> poison, float %sub0, i64 0
-  %mul2 = fmul float %a2, %b0
-  %mul3 = fmul float %b2, %a0
-  %sub1 = fsub float %mul2, %mul3
-  %vec1 = insertelement <3 x float> %vec0, float %sub1, i64 1
-  %mul4 = fmul float %a0, %b1
-  %mul5 = fmul float %b0, %a1
-  %sub2 = fsub float %mul4, %mul5
-  %vec2 = insertelement <3 x float> %vec1, float %sub2, i64 2
-  ret <3 x float> %vec2
-}
-
 declare <3 x half> @llvm.spv.cross.v3f16(<3 x half>, <3 x half>)
 declare <3 x float> @llvm.spv.cross.v3f32(<3 x float>, <3 x float>)

>From fe16ea89e09067e0bafaafb390e2d98e7c3c43e1 Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Thu, 23 Jul 2026 13:08:05 -0700
Subject: [PATCH 3/6] clean up

---
 llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h 
b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
index afddb220a5403..19e2a6901b8f0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
@@ -37,8 +37,6 @@ class SPIRVCombinerHelper : public CombinerHelper {
   void applyMatrixTranspose(MachineInstr &MI) const;
   bool matchMatrixMultiply(MachineInstr &MI) const;
   void applyMatrixMultiply(MachineInstr &MI) const;
-  bool matchCross(MachineInstr &MI) const;
-  void applySPIRVCross(MachineInstr &MI) const;
 
 private:
   SPIRVTypeInst getDotProductVectorType(Register ResReg, uint32_t K,

>From 58e07ad4fb5b0a3265f360bc57bb7a37f1471f3d Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Thu, 23 Jul 2026 14:18:48 -0700
Subject: [PATCH 4/6] remove spirv intrinsic

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |  1 -
 .../CodeGen/SPIRV/hlsl-intrinsics/cross.ll    | 33 -------------------
 2 files changed, 34 deletions(-)
 delete mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index d8289dc786f45..14bf3c76c47ce 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -691,7 +691,6 @@ static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
   case Intrinsic::spv_any:
   case Intrinsic::spv_bitcast:
   case Intrinsic::spv_const_composite:
-  case Intrinsic::spv_cross:
   case Intrinsic::spv_degrees:
   case Intrinsic::spv_distance:
   case Intrinsic::spv_extractelt:
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
deleted file mode 100644
index 6b7e209227dd0..0000000000000
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/cross.ll
+++ /dev/null
@@ -1,33 +0,0 @@
-; RUN: llc -O0 -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val %}
-
-; Make sure SPIRV operation function calls for cross are lowered correctly.
-
-; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
-; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
-; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
-; CHECK-DAG: %[[#vec3_float_16:]] = OpTypeVector %[[#float_16]] 3
-; CHECK-DAG: %[[#vec3_float_32:]] = OpTypeVector %[[#float_32]] 3
-
-define noundef <3 x half> @cross_half4(<3 x half> noundef %a, <3 x half> 
noundef %b) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_16]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_16]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_16]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_16]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
-  %hlsl.cross = call <3 x half> @llvm.spv.cross.v3f16(<3 x half> %a, <3 x 
half> %b)
-  ret <3 x half> %hlsl.cross
-}
-
-define noundef <3 x float> @cross_float4(<3 x float> noundef %a, <3 x float> 
noundef %b) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec3_float_32]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec3_float_32]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec3_float_32]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec3_float_32]] %[[#op_ext_glsl]] Cross 
%[[#arg0]] %[[#arg1]]
-  %hlsl.cross = call <3 x float> @llvm.spv.cross.v3f32(<3 x float> %a, <3 x 
float> %b)
-  ret <3 x float> %hlsl.cross
-}
-
-declare <3 x half> @llvm.spv.cross.v3f16(<3 x half>, <3 x half>)
-declare <3 x float> @llvm.spv.cross.v3f32(<3 x float>, <3 x float>)

>From ddcb489b682d24b211c63d7211494e29674c3c84 Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Mon, 27 Jul 2026 12:40:51 -0700
Subject: [PATCH 5/6] finish removing spirv cross

---
 clang/test/CodeGenHLSL/builtins/cross.hlsl    | 54 ++-----------------
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  1 -
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |  2 -
 3 files changed, 4 insertions(+), 53 deletions(-)

diff --git a/clang/test/CodeGenHLSL/builtins/cross.hlsl 
b/clang/test/CodeGenHLSL/builtins/cross.hlsl
index a8aa43b634d91..db7db1ad54afb 100644
--- a/clang/test/CodeGenHLSL/builtins/cross.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/cross.hlsl
@@ -1,12 +1,12 @@
 // 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 
-fnative-int16-type \
-// RUN:   -emit-llvm -O1 -o - | FileCheck %s
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s -DSPIR_FUNC=""
 // RUN: %clang_cc1 -finclude-default-header -triple \
 // RUN:   spirv-unknown-vulkan-library %s -fnative-half-type 
-fnative-int16-type \
-// RUN:   -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
+// RUN:   -emit-llvm -O1 -o - | FileCheck %s -DSPIR_FUNC="spir_func "
 
-// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <3 x half> 
@_Z16test_cross_half3Dv3_DhS_(
+// CHECK: define hidden [[SPIR_FUNC]]noundef nofpclass(nan inf) <3 x half> 
@_Z16test_cross_half3Dv3_DhS_(
 // CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]], <3 x half> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x half> [[P0]], i64 1
@@ -29,35 +29,12 @@
 // CHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x half> 
[[VECINIT12_I]], half [[SUB19_I]], i64 2
 // CHECK-NEXT:    ret <3 x half> [[VECINIT20_I]]
 //
-// SPVCHECK-LABEL: define hidden spir_func noundef nofpclass(nan inf) <3 x 
half> @_Z16test_cross_half3Dv3_DhS_(
-// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]], <3 x half> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
-// SPVCHECK-NEXT:  [[ENTRY:.*:]]
-// SPVCHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x half> [[P0]], i64 1
-// SPVCHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x half> [[P1]], i64 
2
-// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT1_I]], [[VECEXT_I]]
-// SPVCHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x half> [[P1]], i64 
1
-// SPVCHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x half> [[P0]], i64 
2
-// SPVCHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT2_I]], [[VECEXT3_I]]
-// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn half 
[[MUL_I]], [[MUL4_I]]
-// SPVCHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x half> poison, half 
[[SUB_I]], i64 0
-// SPVCHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x half> [[P1]], i64 0
-// SPVCHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn half 
[[VECEXT6_I]], [[VECEXT3_I]]
-// SPVCHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x half> [[P0]], i64 0
-// SPVCHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT1_I]], [[VECEXT9_I]]
-// SPVCHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
half [[MUL7_I]], [[MUL10_I]]
-// SPVCHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x half> 
[[VECINIT_I]], half [[SUB11_I]], i64 1
-// SPVCHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT2_I]], [[VECEXT9_I]]
-// SPVCHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
half [[VECEXT6_I]], [[VECEXT_I]]
-// SPVCHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
half [[MUL15_I]], [[MUL18_I]]
-// SPVCHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x half> 
[[VECINIT12_I]], half [[SUB19_I]], i64 2
-// SPVCHECK-NEXT:    ret <3 x half> [[VECINIT20_I]]
-//
 half3 test_cross_half3(half3 p0, half3 p1)
 {
     return cross(p0, p1);
 }
 
-// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <3 x float> 
@_Z17test_cross_float3Dv3_fS_(
+// CHECK: define hidden [[SPIR_FUNC]]noundef nofpclass(nan inf) <3 x float> 
@_Z17test_cross_float3Dv3_fS_(
 // CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]], <3 x float> 
noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0]] {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x float> [[P0]], i64 1
@@ -80,29 +57,6 @@ half3 test_cross_half3(half3 p0, half3 p1)
 // CHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x float> 
[[VECINIT12_I]], float [[SUB19_I]], i64 2
 // CHECK-NEXT:    ret <3 x float> [[VECINIT20_I]]
 //
-// SPVCHECK-LABEL: define hidden spir_func noundef nofpclass(nan inf) <3 x 
float> @_Z17test_cross_float3Dv3_fS_(
-// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]], <3 x 
float> noundef nofpclass(nan inf) [[P1:%.*]]) local_unnamed_addr #[[ATTR0]] {
-// SPVCHECK-NEXT:  [[ENTRY:.*:]]
-// SPVCHECK-NEXT:    [[VECEXT_I:%.*]] = extractelement <3 x float> [[P0]], i64 
1
-// SPVCHECK-NEXT:    [[VECEXT1_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 2
-// SPVCHECK-NEXT:    [[MUL_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn float 
[[VECEXT1_I]], [[VECEXT_I]]
-// SPVCHECK-NEXT:    [[VECEXT2_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 1
-// SPVCHECK-NEXT:    [[VECEXT3_I:%.*]] = extractelement <3 x float> [[P0]], 
i64 2
-// SPVCHECK-NEXT:    [[MUL4_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT2_I]], [[VECEXT3_I]]
-// SPVCHECK-NEXT:    [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn float 
[[MUL_I]], [[MUL4_I]]
-// SPVCHECK-NEXT:    [[VECINIT_I:%.*]] = insertelement <3 x float> poison, 
float [[SUB_I]], i64 0
-// SPVCHECK-NEXT:    [[VECEXT6_I:%.*]] = extractelement <3 x float> [[P1]], 
i64 0
-// SPVCHECK-NEXT:    [[MUL7_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT6_I]], [[VECEXT3_I]]
-// SPVCHECK-NEXT:    [[VECEXT9_I:%.*]] = extractelement <3 x float> [[P0]], 
i64 0
-// SPVCHECK-NEXT:    [[MUL10_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT1_I]], [[VECEXT9_I]]
-// SPVCHECK-NEXT:    [[SUB11_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
float [[MUL7_I]], [[MUL10_I]]
-// SPVCHECK-NEXT:    [[VECINIT12_I:%.*]] = insertelement <3 x float> 
[[VECINIT_I]], float [[SUB11_I]], i64 1
-// SPVCHECK-NEXT:    [[MUL15_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT2_I]], [[VECEXT9_I]]
-// SPVCHECK-NEXT:    [[MUL18_I:%.*]] = fmul reassoc nnan ninf nsz arcp afn 
float [[VECEXT6_I]], [[VECEXT_I]]
-// SPVCHECK-NEXT:    [[SUB19_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn 
float [[MUL15_I]], [[MUL18_I]]
-// SPVCHECK-NEXT:    [[VECINIT20_I:%.*]] = insertelement <3 x float> 
[[VECINIT12_I]], float [[SUB19_I]], i64 2
-// SPVCHECK-NEXT:    ret <3 x float> [[VECINIT20_I]]
-//
 float3 test_cross_float3(float3 p0, float3 p1)
 {
     return cross(p0, p1);
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td 
b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index d948ef78b9584..066598bd777a7 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -99,7 +99,6 @@ let TargetPrefix = "spv" in {
   def int_spv_flattened_thread_id_in_group : Intrinsic<[llvm_i32_ty], [], 
[IntrNoMem, IntrWillReturn]>;
   def int_spv_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], 
[IntrNoMem]>;
   def int_spv_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], 
[IntrNoMem]>;
-  def int_spv_cross : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_degrees : DefaultAttrsIntrinsic<[LLVMMatchType<0>], 
[llvm_anyfloat_ty], [IntrNoMem]>;
   def int_spv_distance : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], 
[llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_faceforward : DefaultAttrsIntrinsic<[LLVMMatchType<0>], 
[llvm_anyfloat_ty, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 14bf3c76c47ce..5f1e2f96b8bf0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5330,8 +5330,6 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register 
ResVReg,
     return selectAll(ResVReg, ResType, I);
   case Intrinsic::spv_any:
     return selectAny(ResVReg, ResType, I);
-  case Intrinsic::spv_cross:
-    return selectExtInst(ResVReg, ResType, I, CL::cross, GL::Cross);
   case Intrinsic::spv_distance:
     return selectExtInst(ResVReg, ResType, I, CL::distance, GL::Distance);
   case Intrinsic::spv_lerp:

>From 1bad9e16d1071a7835611924098b8785544c3fb7 Mon Sep 17 00:00:00 2001
From: Joao Saffran <[email protected]>
Date: Mon, 27 Jul 2026 17:48:05 -0700
Subject: [PATCH 6/6] address comments

---
 clang/include/clang/Basic/HLSLIntrinsics.td   | 21 ++++++++++++++++++
 clang/lib/Headers/hlsl/hlsl_intrinsics.h      | 22 -------------------
 .../test/SemaHLSL/BuiltIns/cross-errors.hlsl  | 12 ++++------
 3 files changed, 25 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/Basic/HLSLIntrinsics.td 
b/clang/include/clang/Basic/HLSLIntrinsics.td
index ee1e4ca44db00..6636f05f2a1c4 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -684,6 +684,27 @@ def hlsl_degrees : HLSLOneArgBuiltin<"degrees", 
"__builtin_hlsl_elementwise_degr
   let VaryingTypes = [HalfTy, FloatTy];
   let VaryingMatDims = [];
 }
+// Returns the cross product of two floating-point, 3D vectors.
+def hlsl_cross : HLSLTwoArgDetail<"cross", "cross_impl"> {
+  let Doc = [{
+\fn T cross(T x, T y)
+\brief Returns the cross product of two floating-point, 3D vectors.
+\param x [in] The first floating-point, 3D vector.
+\param y [in] The second floating-point, 3D vector.
+
+Result is the cross product of x and y, i.e., the resulting
+components are, in order :
+x[1] * y[2] - y[1] * x[2]
+x[2] * y[0] - y[2] * x[0]
+x[0] * y[1] - y[0] * x[1]
+}];
+  let ParamNames = ["x", "y"];
+  let VaryingTypes = [HalfTy, FloatTy];
+  let VaryingScalar = 0;
+  let VaryingVecSizes = [3];
+  let VaryingMatDims = [];
+  let IsConstexpr = 1;
+}
 
 // Returns a distance scalar between X and Y.
 // The distance between X and Y is length(X - Y).
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 270cf4e1ff7eb..a86fc7dc0988b 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -327,27 +327,5 @@ constexpr matrix<T, R, C> mul(matrix<T, R, C> x, T y) {
   return x * y;
 }
 
-//===----------------------------------------------------------------------===//
-// cross builtins
-//===----------------------------------------------------------------------===//
-
-/// \fn T cross(T x, T y)
-/// \brief Returns the cross product of two floating-point, 3D vectors.
-/// \param x [in] The first floating-point, 3D vector.
-/// \param y [in] The second floating-point, 3D vector.
-///
-/// Result is the cross product of x and y, i.e., the resulting
-/// components are, in order :
-/// x[1] * y[2] - y[1] * x[2]
-/// x[2] * y[0] - y[2] * x[0]
-/// x[0] * y[1] - y[0] * x[1]
-
-_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
-constexpr half3 cross(half3 x, half3 y) { return __detail::cross_impl(x, y); }
-
-constexpr float3 cross(float3 x, float3 y) {
-  return __detail::cross_impl(x, y);
-}
-
 } // namespace hlsl
 #endif //_HLSL_HLSL_INTRINSICS_H_
diff --git a/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
index af811e2c2320d..04300fd95063d 100644
--- a/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/cross-errors.hlsl
@@ -4,30 +4,26 @@ void test_too_few_arg()
 {
   return cross();
   // expected-error@-1 {{no matching function for call to 'cross'}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 0 were provided}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 0 were provided}}
+  // expected-note@hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate 
function not viable: requires 2 arguments, but 0 were provided}}
 }
 
 void test_too_many_arg(float3 p0)
 {
   return cross(p0, p0, p0);
   // expected-error@-1 {{no matching function for call to 'cross'}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 3 were provided}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function not viable: 
requires 2 arguments, but 3 were provided}}
+  // expected-note@hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate 
function not viable: requires 2 arguments, but 3 were provided}}
 }
 
 float2 test_cross_float2(float2 p1, float2 p2)
 {
   return cross(p1, p2);
   // expected-error@-1 {{no matching function for call to 'cross'}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
+  // expected-note@hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate 
function}}
 }
 
 void test_ambiguous(int p0)
 {
   return cross(p0,p0);
   // expected-error@-1 {{call to 'cross' is ambiguous}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
-  // expected-note@hlsl/hlsl_intrinsics.h:* {{candidate function}}
+  // expected-note@hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate 
function}}
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to