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
