[clang] [llvm] [HLSL] Move cross implementation to header files (PR #211446)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 27 17:48:21 PDT 2026
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 <jderezende at microsoft.com>
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 at -1 {{no matching function for call to 'cross'}}
- // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function not viable: requires 2 arguments, but 0 were provided}}
- // expected-note at 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 at -1 {{too few arguments to function call, expected 2, have 0}}
-}
-
-void test_too_few_arg_f16()
-{
- return __builtin_hlsl_crossf16();
- // expected-error at -1 {{too few arguments to function call, expected 2, have 0}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 0 were provided}}
+ // expected-note at 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 at -1 {{no matching function for call to 'cross'}}
- // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
- // expected-note at 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 at -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 at -1 {{too many arguments to function call, expected 2, have 3}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
+ // expected-note at 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 at -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 at -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 at -1 {{no matching function for call to 'cross'}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
}
void test_ambiguous(int p0)
{
return cross(p0,p0);
// expected-error at -1 {{call to 'cross' is ambiguous}}
- // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function}}
- // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{candidate function}}
+ // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
+ // expected-note at 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 <jderezende at microsoft.com>
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 <jderezende at microsoft.com>
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 <jderezende at microsoft.com>
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 <jderezende at microsoft.com>
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 <jderezende at microsoft.com>
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 at -1 {{no matching function for call to 'cross'}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 0 were provided}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 0 were provided}}
+ // expected-note at 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 at -1 {{no matching function for call to 'cross'}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
+ // expected-note at 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 at -1 {{no matching function for call to 'cross'}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
+ // expected-note at hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate function}}
}
void test_ambiguous(int p0)
{
return cross(p0,p0);
// expected-error at -1 {{call to 'cross' is ambiguous}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
- // expected-note at hlsl/hlsl_intrinsics.h:* {{candidate function}}
+ // expected-note at hlsl/hlsl_inline_intrinsics_gen.inc:* 2 {{candidate function}}
}
More information about the cfe-commits
mailing list