[SPIRV] add pre legalization instruction combine (#122839)
- Add the boilerplate to support instcombine in SPIRV - instcombine length(X-Y) to distance(X,Y) - switch HLSL's distance intrinsic to not special case for SPIRV. - fixes #122766 - This RFC we were requested to add in the infra for pattern matching: https://discourse.llvm.org/t/rfc-add-targetbuiltins-for-spirv-to-support-hlsl/83329/13
This commit is contained in:
@@ -13,3 +13,9 @@ def SPIRVDistance : Builtin {
|
||||
let Attributes = [NoThrow, Const];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
def SPIRVLength : Builtin {
|
||||
let Spellings = ["__builtin_spirv_length"];
|
||||
let Attributes = [NoThrow, Const];
|
||||
let Prototype = "void(...)";
|
||||
}
|
||||
|
||||
@@ -20528,6 +20528,16 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
|
||||
/*ReturnType=*/X->getType()->getScalarType(), Intrinsic::spv_distance,
|
||||
ArrayRef<Value *>{X, Y}, nullptr, "spv.distance");
|
||||
}
|
||||
case SPIRV::BI__builtin_spirv_length: {
|
||||
Value *X = EmitScalarExpr(E->getArg(0));
|
||||
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
|
||||
"length operand must have a float representation");
|
||||
assert(E->getArg(0)->getType()->isVectorType() &&
|
||||
"length operand must be a vector");
|
||||
return Builder.CreateIntrinsic(
|
||||
/*ReturnType=*/X->getType()->getScalarType(), Intrinsic::spv_length,
|
||||
ArrayRef<Value *>{X}, nullptr, "spv.length");
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,11 @@ length_impl(T X) {
|
||||
template <typename T, int N>
|
||||
constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
|
||||
length_vec_impl(vector<T, N> X) {
|
||||
#if (__has_builtin(__builtin_spirv_length))
|
||||
return __builtin_spirv_length(X);
|
||||
#else
|
||||
return __builtin_elementwise_sqrt(__builtin_hlsl_dot(X, X));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -73,11 +77,7 @@ distance_impl(T X, T Y) {
|
||||
template <typename T, int N>
|
||||
constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
|
||||
distance_vec_impl(vector<T, N> X, vector<T, N> Y) {
|
||||
#if (__has_builtin(__builtin_spirv_distance))
|
||||
return __builtin_spirv_distance(X, Y);
|
||||
#else
|
||||
return length_vec_impl(X - Y);
|
||||
#endif
|
||||
}
|
||||
} // namespace __detail
|
||||
} // namespace hlsl
|
||||
|
||||
@@ -51,6 +51,24 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
|
||||
TheCall->setType(RetTy);
|
||||
break;
|
||||
}
|
||||
case SPIRV::BI__builtin_spirv_length: {
|
||||
if (SemaRef.checkArgCount(TheCall, 1))
|
||||
return true;
|
||||
ExprResult A = TheCall->getArg(0);
|
||||
QualType ArgTyA = A.get()->getType();
|
||||
auto *VTy = ArgTyA->getAs<VectorType>();
|
||||
if (VTy == nullptr) {
|
||||
SemaRef.Diag(A.get()->getBeginLoc(),
|
||||
diag::err_typecheck_convert_incompatible)
|
||||
<< ArgTyA
|
||||
<< SemaRef.Context.getVectorType(ArgTyA, 2, VectorKind::Generic) << 1
|
||||
<< 0 << 0;
|
||||
return true;
|
||||
}
|
||||
QualType RetTy = VTy->getElementType();
|
||||
TheCall->setType(RetTy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,9 @@ half test_distance_half(half X, half Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z19test_distance_half2Dv2_DhS_(
|
||||
// SPVCHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[X:%.*]], <2 x half> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.distance.v2f16(<2 x half> [[X]], <2 x half> [[Y]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <2 x half> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v2f16(<2 x half> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_distance_half2(half2 X, half2 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -49,8 +50,9 @@ half test_distance_half2(half2 X, half2 Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z19test_distance_half3Dv3_DhS_(
|
||||
// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[X:%.*]], <3 x half> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.distance.v3f16(<3 x half> [[X]], <3 x half> [[Y]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <3 x half> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v3f16(<3 x half> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_distance_half3(half3 X, half3 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -65,8 +67,9 @@ half test_distance_half3(half3 X, half3 Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z19test_distance_half4Dv4_DhS_(
|
||||
// SPVCHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[X:%.*]], <4 x half> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.distance.v4f16(<4 x half> [[X]], <4 x half> [[Y]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <4 x half> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v4f16(<4 x half> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_distance_half4(half4 X, half4 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -97,8 +100,9 @@ float test_distance_float(float X, float Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z20test_distance_float2Dv2_fS_(
|
||||
// SPVCHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[X:%.*]], <2 x float> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.distance.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <2 x float> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v2f32(<2 x float> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_distance_float2(float2 X, float2 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -113,8 +117,9 @@ float test_distance_float2(float2 X, float2 Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z20test_distance_float3Dv3_fS_(
|
||||
// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[X:%.*]], <3 x float> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.distance.v3f32(<3 x float> [[X]], <3 x float> [[Y]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <3 x float> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v3f32(<3 x float> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_distance_float3(float3 X, float3 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -129,7 +134,8 @@ float test_distance_float3(float3 X, float3 Y) { return distance(X, Y); }
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z20test_distance_float4Dv4_fS_(
|
||||
// SPVCHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[X:%.*]], <4 x float> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_DISTANCE_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.distance.v4f32(<4 x float> [[X]], <4 x float> [[Y]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_DISTANCE_I]]
|
||||
// SPVCHECK-NEXT: [[SUB_I:%.*]] = fsub reassoc nnan ninf nsz arcp afn <4 x float> [[X]], [[Y]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v4f32(<4 x float> [[SUB_I]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_distance_float4(float4 X, float4 Y) { return distance(X, Y); }
|
||||
|
||||
@@ -1,114 +1,163 @@
|
||||
// RUN: %clang_cc1 -finclude-default-header -triple \
|
||||
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
|
||||
// RUN: -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,DXCHECK \
|
||||
// RUN: -DTARGET=dx
|
||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
|
||||
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
|
||||
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
|
||||
// RUN: -emit-llvm -O1 -o - | FileCheck %s
|
||||
|
||||
// RUN: %clang_cc1 -finclude-default-header -triple \
|
||||
// RUN: spirv-unknown-vulkan-compute %s -fnative-half-type \
|
||||
// RUN: -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,SPVCHECK \
|
||||
// RUN: -DTARGET=spv
|
||||
// RUN: -emit-llvm -O1 -o - | FileCheck %s --check-prefix=SPVCHECK
|
||||
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z16test_length_halfDh(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) half @_Z16test_length_halfDh(
|
||||
//
|
||||
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z16test_length_halfDh(
|
||||
// CHECK-SAME: half noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ELT_ABS_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.fabs.f16(half [[P0]])
|
||||
// CHECK-NEXT: ret half [[ELT_ABS_I]]
|
||||
//
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z16test_length_halfDh(
|
||||
// SPVCHECK-SAME: half noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[ELT_ABS_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.fabs.f16(half [[P0]])
|
||||
// SPVCHECK-NEXT: ret half [[ELT_ABS_I]]
|
||||
//
|
||||
half test_length_half(half p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half2Dv2_Dh(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half2Dv2_Dh(
|
||||
// CHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.[[TARGET]].fdot.v2f16(<2 x half> [[P0]], <2 x half> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.sqrt.f16(half [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret half [[TMP0]]
|
||||
//
|
||||
|
||||
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half2Dv2_Dh(
|
||||
// CHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v2f16(<2 x half> [[P0]], <2 x half> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.sqrt.f16(half [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret half [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half2Dv2_Dh(
|
||||
// SPVCHECK-SAME: <2 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v2f16(<2 x half> [[P0]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_length_half2(half2 p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half3Dv3_Dh(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half3Dv3_Dh(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half3Dv3_Dh(
|
||||
// CHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.[[TARGET]].fdot.v3f16(<3 x half> [[P0]], <3 x half> [[P0]])
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v3f16(<3 x half> [[P0]], <3 x half> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.sqrt.f16(half [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret half [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half3Dv3_Dh(
|
||||
// SPVCHECK-SAME: <3 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v3f16(<3 x half> [[P0]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_length_half3(half3 p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half4Dv4_Dh(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half4Dv4_Dh(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) half @_Z17test_length_half4Dv4_Dh(
|
||||
// CHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.[[TARGET]].fdot.v4f16(<4 x half> [[P0]], <4 x half> [[P0]])
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v4f16(<4 x half> [[P0]], <4 x half> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.sqrt.f16(half [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret half [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) half @_Z17test_length_half4Dv4_Dh(
|
||||
// SPVCHECK-SAME: <4 x half> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.length.v4f16(<4 x half> [[P0]])
|
||||
// SPVCHECK-NEXT: ret half [[SPV_LENGTH_I]]
|
||||
//
|
||||
half test_length_half4(half4 p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z17test_length_floatf(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) float @_Z17test_length_floatf(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z17test_length_floatf(
|
||||
// CHECK-SAME: float noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[ELT_ABS_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.fabs.f32(float [[P0]])
|
||||
// CHECK-NEXT: ret float [[ELT_ABS_I]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z17test_length_floatf(
|
||||
// SPVCHECK-SAME: float noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[ELT_ABS_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.fabs.f32(float [[P0]])
|
||||
// SPVCHECK-NEXT: ret float [[ELT_ABS_I]]
|
||||
//
|
||||
float test_length_float(float p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float2Dv2_f(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float2Dv2_f(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float2Dv2_f(
|
||||
// CHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].fdot.v2f32(<2 x float> [[P0]], <2 x float> [[P0]])
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v2f32(<2 x float> [[P0]], <2 x float> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.sqrt.f32(float [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret float [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float2Dv2_f(
|
||||
// SPVCHECK-SAME: <2 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v2f32(<2 x float> [[P0]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_length_float2(float2 p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float3Dv3_f(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float3Dv3_f(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float3Dv3_f(
|
||||
// CHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].fdot.v3f32(<3 x float> [[P0]], <3 x float> [[P0]])
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v3f32(<3 x float> [[P0]], <3 x float> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.sqrt.f32(float [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret float [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float3Dv3_f(
|
||||
// SPVCHECK-SAME: <3 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v3f32(<3 x float> [[P0]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_length_float3(float3 p0)
|
||||
{
|
||||
return length(p0);
|
||||
}
|
||||
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float4Dv4_f(
|
||||
// DXCHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float4Dv4_f(
|
||||
// CHECK-LABEL: define noundef nofpclass(nan inf) float @_Z18test_length_float4Dv4_f(
|
||||
// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].fdot.v4f32(<4 x float> [[P0]], <4 x float> [[P0]])
|
||||
// CHECK-NEXT: [[HLSL_DOT_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v4f32(<4 x float> [[P0]], <4 x float> [[P0]])
|
||||
// CHECK-NEXT: [[TMP0:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.sqrt.f32(float [[HLSL_DOT_I]])
|
||||
// CHECK-NEXT: ret float [[TMP0]]
|
||||
//
|
||||
// SPVCHECK-LABEL: define spir_func noundef nofpclass(nan inf) float @_Z18test_length_float4Dv4_f(
|
||||
// SPVCHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[P0:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SPVCHECK-NEXT: [[ENTRY:.*:]]
|
||||
// SPVCHECK-NEXT: [[SPV_LENGTH_I:%.*]] = tail call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.length.v4f32(<4 x float> [[P0]])
|
||||
// SPVCHECK-NEXT: ret float [[SPV_LENGTH_I]]
|
||||
//
|
||||
float test_length_float4(float4 p0)
|
||||
{
|
||||
return length(p0);
|
||||
|
||||
31
clang/test/CodeGenSPIRV/Builtins/length.c
Normal file
31
clang/test/CodeGenSPIRV/Builtins/length.c
Normal file
@@ -0,0 +1,31 @@
|
||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
|
||||
|
||||
// RUN: %clang_cc1 -O1 -triple spirv-pc-vulkan-compute %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
typedef float float2 __attribute__((ext_vector_type(2)));
|
||||
typedef float float3 __attribute__((ext_vector_type(3)));
|
||||
typedef float float4 __attribute__((ext_vector_type(4)));
|
||||
|
||||
// CHECK-LABEL: define spir_func float @test_length_float2(
|
||||
// CHECK-SAME: <2 x float> noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[SPV_LENGTH:%.*]] = tail call float @llvm.spv.length.v2f32(<2 x float> [[X]])
|
||||
// CHECK-NEXT: ret float [[SPV_LENGTH]]
|
||||
//
|
||||
float test_length_float2(float2 X) { return __builtin_spirv_length(X); }
|
||||
|
||||
// CHECK-LABEL: define spir_func float @test_length_float3(
|
||||
// CHECK-SAME: <3 x float> noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[SPV_LENGTH:%.*]] = tail call float @llvm.spv.length.v3f32(<3 x float> [[X]])
|
||||
// CHECK-NEXT: ret float [[SPV_LENGTH]]
|
||||
//
|
||||
float test_length_float3(float3 X) { return __builtin_spirv_length(X); }
|
||||
|
||||
// CHECK-LABEL: define spir_func float @test_length_float4(
|
||||
// CHECK-SAME: <4 x float> noundef [[X:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// CHECK-NEXT: [[ENTRY:.*:]]
|
||||
// CHECK-NEXT: [[SPV_LENGTH:%.*]] = tail call float @llvm.spv.length.v4f32(<4 x float> [[X]])
|
||||
// CHECK-NEXT: ret float [[SPV_LENGTH]]
|
||||
//
|
||||
float test_length_float4(float4 X) { return __builtin_spirv_length(X); }
|
||||
25
clang/test/SemaSPIRV/BuiltIns/length-errors.c
Normal file
25
clang/test/SemaSPIRV/BuiltIns/length-errors.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify
|
||||
|
||||
typedef float float2 __attribute__((ext_vector_type(2)));
|
||||
|
||||
void test_too_few_arg()
|
||||
{
|
||||
return __builtin_spirv_length();
|
||||
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
|
||||
}
|
||||
|
||||
void test_too_many_arg(float2 p0)
|
||||
{
|
||||
return __builtin_spirv_length(p0, p0);
|
||||
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
|
||||
}
|
||||
|
||||
float test_double_scalar_inputs(double p0) {
|
||||
return __builtin_spirv_length(p0);
|
||||
// expected-error@-1 {{passing 'double' to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(double)))) double' (vector of 2 'double' values)}}
|
||||
}
|
||||
|
||||
float test_int_scalar_inputs(int p0) {
|
||||
return __builtin_spirv_length(p0);
|
||||
// expected-error@-1 {{passing 'int' to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(int)))) int' (vector of 2 'int' values)}}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ tablegen(LLVM SPIRVGenRegisterBank.inc -gen-register-bank)
|
||||
tablegen(LLVM SPIRVGenRegisterInfo.inc -gen-register-info)
|
||||
tablegen(LLVM SPIRVGenSubtargetInfo.inc -gen-subtarget)
|
||||
tablegen(LLVM SPIRVGenTables.inc -gen-searchable-tables)
|
||||
tablegen(LLVM SPIRVGenPreLegalizeGICombiner.inc -gen-global-isel-combiner
|
||||
-combiners="SPIRVPreLegalizerCombiner")
|
||||
|
||||
add_public_tablegen_target(SPIRVCommonTableGen)
|
||||
|
||||
@@ -33,6 +35,7 @@ add_llvm_target(SPIRVCodeGen
|
||||
SPIRVModuleAnalysis.cpp
|
||||
SPIRVStructurizer.cpp
|
||||
SPIRVPreLegalizer.cpp
|
||||
SPIRVPreLegalizerCombiner.cpp
|
||||
SPIRVPostLegalizer.cpp
|
||||
SPIRVPrepareFunctions.cpp
|
||||
SPIRVRegisterBankInfo.cpp
|
||||
|
||||
@@ -24,6 +24,7 @@ FunctionPass *createSPIRVStructurizerPass();
|
||||
FunctionPass *createSPIRVMergeRegionExitTargetsPass();
|
||||
FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
|
||||
FunctionPass *createSPIRVRegularizerPass();
|
||||
FunctionPass *createSPIRVPreLegalizerCombiner();
|
||||
FunctionPass *createSPIRVPreLegalizerPass();
|
||||
FunctionPass *createSPIRVPostLegalizerPass();
|
||||
ModulePass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
|
||||
@@ -36,6 +37,7 @@ createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
|
||||
void initializeSPIRVModuleAnalysisPass(PassRegistry &);
|
||||
void initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PassRegistry &);
|
||||
void initializeSPIRVPreLegalizerPass(PassRegistry &);
|
||||
void initializeSPIRVPreLegalizerCombinerPass(PassRegistry &);
|
||||
void initializeSPIRVPostLegalizerPass(PassRegistry &);
|
||||
void initializeSPIRVStructurizerPass(PassRegistry &);
|
||||
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
|
||||
|
||||
@@ -11,6 +11,7 @@ include "llvm/Target/Target.td"
|
||||
include "SPIRVRegisterInfo.td"
|
||||
include "SPIRVRegisterBanks.td"
|
||||
include "SPIRVInstrInfo.td"
|
||||
include "SPIRVCombine.td"
|
||||
include "SPIRVBuiltins.td"
|
||||
|
||||
def SPIRVInstrInfo : InstrInfo;
|
||||
|
||||
22
llvm/lib/Target/SPIRV/SPIRVCombine.td
Normal file
22
llvm/lib/Target/SPIRV/SPIRVCombine.td
Normal file
@@ -0,0 +1,22 @@
|
||||
//=- SPIRVCombine.td - Define SPIRV Combine Rules -------------*-tablegen -*-=//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
|
||||
include "llvm/Target/GlobalISel/Combine.td"
|
||||
|
||||
|
||||
def vector_length_sub_to_distance_lowering : GICombineRule <
|
||||
(defs root:$root),
|
||||
(match (wip_match_opcode G_INTRINSIC):$root,
|
||||
[{ return matchLengthToDistance(*${root}, MRI); }]),
|
||||
(apply [{ applySPIRVDistance(*${root}, MRI, B); }])
|
||||
>;
|
||||
|
||||
def SPIRVPreLegalizerCombiner
|
||||
: GICombiner<"SPIRVPreLegalizerCombinerImpl",
|
||||
[vector_length_sub_to_distance_lowering]> {
|
||||
let CombineAllMethodName = "tryCombineAllImpl";
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "SPIRVUtils.h"
|
||||
#include "llvm/ADT/PostOrderIterator.h"
|
||||
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
||||
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
|
||||
#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DebugInfoMetadata.h"
|
||||
@@ -35,9 +37,15 @@ public:
|
||||
initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void SPIRVPreLegalizer::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addPreserved<GISelKnownBitsAnalysis>();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
static void
|
||||
addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR,
|
||||
const SPIRVSubtarget &STI,
|
||||
|
||||
244
llvm/lib/Target/SPIRV/SPIRVPreLegalizerCombiner.cpp
Normal file
244
llvm/lib/Target/SPIRV/SPIRVPreLegalizerCombiner.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
|
||||
//===-- SPIRVPreLegalizerCombiner.cpp - combine legalization ----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This pass does combining of machine instructions at the generic MI level,
|
||||
// before the legalizer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SPIRV.h"
|
||||
#include "SPIRVTargetMachine.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
|
||||
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
|
||||
#include "llvm/CodeGen/GlobalISel/Combiner.h"
|
||||
#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
|
||||
#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
|
||||
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
|
||||
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
|
||||
#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"
|
||||
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
|
||||
#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
|
||||
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
|
||||
#include "llvm/CodeGen/GlobalISel/Utils.h"
|
||||
#include "llvm/CodeGen/MachineDominators.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/CodeGen/TargetOpcodes.h"
|
||||
#include "llvm/CodeGen/TargetPassConfig.h"
|
||||
#include "llvm/IR/IntrinsicsSPIRV.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
||||
#define GET_GICOMBINER_DEPS
|
||||
#include "SPIRVGenPreLegalizeGICombiner.inc"
|
||||
#undef GET_GICOMBINER_DEPS
|
||||
|
||||
#define DEBUG_TYPE "spirv-prelegalizer-combiner"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace MIPatternMatch;
|
||||
|
||||
namespace {
|
||||
|
||||
#define GET_GICOMBINER_TYPES
|
||||
#include "SPIRVGenPreLegalizeGICombiner.inc"
|
||||
#undef GET_GICOMBINER_TYPES
|
||||
|
||||
/// This match is part of a combine that
|
||||
/// rewrites length(X - Y) to distance(X, Y)
|
||||
/// (f32 (g_intrinsic length
|
||||
/// (g_fsub (vXf32 X) (vXf32 Y))))
|
||||
/// ->
|
||||
/// (f32 (g_intrinsic distance
|
||||
/// (vXf32 X) (vXf32 Y)))
|
||||
///
|
||||
bool matchLengthToDistance(MachineInstr &MI, MachineRegisterInfo &MRI) {
|
||||
if (MI.getOpcode() != TargetOpcode::G_INTRINSIC ||
|
||||
cast<GIntrinsic>(MI).getIntrinsicID() != Intrinsic::spv_length)
|
||||
return false;
|
||||
|
||||
// First operand of MI is `G_INTRINSIC` so start at operand 2.
|
||||
Register SubReg = MI.getOperand(2).getReg();
|
||||
MachineInstr *SubInstr = MRI.getVRegDef(SubReg);
|
||||
if (!SubInstr || SubInstr->getOpcode() != TargetOpcode::G_FSUB)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
void applySPIRVDistance(MachineInstr &MI, MachineRegisterInfo &MRI,
|
||||
MachineIRBuilder &B) {
|
||||
|
||||
// Extract the operands for X and Y from the match criteria.
|
||||
Register SubDestReg = MI.getOperand(2).getReg();
|
||||
MachineInstr *SubInstr = MRI.getVRegDef(SubDestReg);
|
||||
Register SubOperand1 = SubInstr->getOperand(1).getReg();
|
||||
Register SubOperand2 = SubInstr->getOperand(2).getReg();
|
||||
|
||||
// Remove the original `spv_length` instruction.
|
||||
|
||||
Register ResultReg = MI.getOperand(0).getReg();
|
||||
DebugLoc DL = MI.getDebugLoc();
|
||||
MachineBasicBlock &MBB = *MI.getParent();
|
||||
MachineBasicBlock::iterator InsertPt = MI.getIterator();
|
||||
|
||||
// Build the `spv_distance` intrinsic.
|
||||
MachineInstrBuilder NewInstr =
|
||||
BuildMI(MBB, InsertPt, DL, B.getTII().get(TargetOpcode::G_INTRINSIC));
|
||||
NewInstr
|
||||
.addDef(ResultReg) // Result register
|
||||
.addIntrinsicID(Intrinsic::spv_distance) // Intrinsic ID
|
||||
.addUse(SubOperand1) // Operand X
|
||||
.addUse(SubOperand2); // Operand Y
|
||||
|
||||
auto RemoveAllUses = [&](Register Reg) {
|
||||
SmallVector<MachineInstr *, 4> UsesToErase;
|
||||
for (auto &UseMI : MRI.use_instructions(Reg))
|
||||
UsesToErase.push_back(&UseMI);
|
||||
|
||||
// calling eraseFromParent to early invalidates the iterator.
|
||||
for (auto *MIToErase : UsesToErase)
|
||||
MIToErase->eraseFromParent();
|
||||
};
|
||||
RemoveAllUses(SubDestReg); // remove all uses of FSUB Result
|
||||
SubInstr->eraseFromParent(); // remove FSUB instruction
|
||||
}
|
||||
|
||||
class SPIRVPreLegalizerCombinerImpl : public Combiner {
|
||||
protected:
|
||||
const CombinerHelper Helper;
|
||||
const SPIRVPreLegalizerCombinerImplRuleConfig &RuleConfig;
|
||||
const SPIRVSubtarget &STI;
|
||||
|
||||
public:
|
||||
SPIRVPreLegalizerCombinerImpl(
|
||||
MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
|
||||
GISelKnownBits &KB, GISelCSEInfo *CSEInfo,
|
||||
const SPIRVPreLegalizerCombinerImplRuleConfig &RuleConfig,
|
||||
const SPIRVSubtarget &STI, MachineDominatorTree *MDT,
|
||||
const LegalizerInfo *LI);
|
||||
|
||||
static const char *getName() { return "SPIRVPreLegalizerCombiner"; }
|
||||
|
||||
bool tryCombineAll(MachineInstr &I) const override;
|
||||
|
||||
bool tryCombineAllImpl(MachineInstr &I) const;
|
||||
|
||||
private:
|
||||
#define GET_GICOMBINER_CLASS_MEMBERS
|
||||
#include "SPIRVGenPreLegalizeGICombiner.inc"
|
||||
#undef GET_GICOMBINER_CLASS_MEMBERS
|
||||
};
|
||||
|
||||
#define GET_GICOMBINER_IMPL
|
||||
#include "SPIRVGenPreLegalizeGICombiner.inc"
|
||||
#undef GET_GICOMBINER_IMPL
|
||||
|
||||
SPIRVPreLegalizerCombinerImpl::SPIRVPreLegalizerCombinerImpl(
|
||||
MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
|
||||
GISelKnownBits &KB, GISelCSEInfo *CSEInfo,
|
||||
const SPIRVPreLegalizerCombinerImplRuleConfig &RuleConfig,
|
||||
const SPIRVSubtarget &STI, MachineDominatorTree *MDT,
|
||||
const LegalizerInfo *LI)
|
||||
: Combiner(MF, CInfo, TPC, &KB, CSEInfo),
|
||||
Helper(Observer, B, /*IsPreLegalize*/ true, &KB, MDT, LI),
|
||||
RuleConfig(RuleConfig), STI(STI),
|
||||
#define GET_GICOMBINER_CONSTRUCTOR_INITS
|
||||
#include "SPIRVGenPreLegalizeGICombiner.inc"
|
||||
#undef GET_GICOMBINER_CONSTRUCTOR_INITS
|
||||
{
|
||||
}
|
||||
|
||||
bool SPIRVPreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
|
||||
return tryCombineAllImpl(MI);
|
||||
}
|
||||
|
||||
// Pass boilerplate
|
||||
// ================
|
||||
|
||||
class SPIRVPreLegalizerCombiner : public MachineFunctionPass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
SPIRVPreLegalizerCombiner();
|
||||
|
||||
StringRef getPassName() const override { return "SPIRVPreLegalizerCombiner"; }
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
|
||||
private:
|
||||
SPIRVPreLegalizerCombinerImplRuleConfig RuleConfig;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
void SPIRVPreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<TargetPassConfig>();
|
||||
AU.setPreservesCFG();
|
||||
getSelectionDAGFallbackAnalysisUsage(AU);
|
||||
AU.addRequired<GISelKnownBitsAnalysis>();
|
||||
AU.addPreserved<GISelKnownBitsAnalysis>();
|
||||
AU.addRequired<MachineDominatorTreeWrapperPass>();
|
||||
AU.addPreserved<MachineDominatorTreeWrapperPass>();
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
SPIRVPreLegalizerCombiner::SPIRVPreLegalizerCombiner()
|
||||
: MachineFunctionPass(ID) {
|
||||
initializeSPIRVPreLegalizerCombinerPass(*PassRegistry::getPassRegistry());
|
||||
|
||||
if (!RuleConfig.parseCommandLineOption())
|
||||
report_fatal_error("Invalid rule identifier");
|
||||
}
|
||||
|
||||
bool SPIRVPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
|
||||
if (MF.getProperties().hasProperty(
|
||||
MachineFunctionProperties::Property::FailedISel))
|
||||
return false;
|
||||
auto &TPC = getAnalysis<TargetPassConfig>();
|
||||
|
||||
const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
|
||||
const auto *LI = ST.getLegalizerInfo();
|
||||
|
||||
const Function &F = MF.getFunction();
|
||||
bool EnableOpt =
|
||||
MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
|
||||
GISelKnownBits *KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF);
|
||||
MachineDominatorTree *MDT =
|
||||
&getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
|
||||
CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
|
||||
/*LegalizerInfo*/ nullptr, EnableOpt, F.hasOptSize(),
|
||||
F.hasMinSize());
|
||||
// Disable fixed-point iteration to reduce compile-time
|
||||
CInfo.MaxIterations = 1;
|
||||
CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
|
||||
// This is the first Combiner, so the input IR might contain dead
|
||||
// instructions.
|
||||
CInfo.EnableFullDCE = false;
|
||||
SPIRVPreLegalizerCombinerImpl Impl(MF, CInfo, &TPC, *KB, /*CSEInfo*/ nullptr,
|
||||
RuleConfig, ST, MDT, LI);
|
||||
return Impl.combineMachineInstrs();
|
||||
}
|
||||
|
||||
char SPIRVPreLegalizerCombiner::ID = 0;
|
||||
INITIALIZE_PASS_BEGIN(SPIRVPreLegalizerCombiner, DEBUG_TYPE,
|
||||
"Combine SPIRV machine instrs before legalization", false,
|
||||
false)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
|
||||
INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis)
|
||||
INITIALIZE_PASS_END(SPIRVPreLegalizerCombiner, DEBUG_TYPE,
|
||||
"Combine SPIRV machine instrs before legalization", false,
|
||||
false)
|
||||
|
||||
namespace llvm {
|
||||
FunctionPass *createSPIRVPreLegalizerCombiner() {
|
||||
return new SPIRVPreLegalizerCombiner();
|
||||
}
|
||||
} // end namespace llvm
|
||||
@@ -48,6 +48,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
|
||||
initializeSPIRVModuleAnalysisPass(PR);
|
||||
initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR);
|
||||
initializeSPIRVStructurizerPass(PR);
|
||||
initializeSPIRVPreLegalizerCombinerPass(PR);
|
||||
}
|
||||
|
||||
static std::string computeDataLayout(const Triple &TT) {
|
||||
@@ -217,6 +218,7 @@ bool SPIRVPassConfig::addIRTranslator() {
|
||||
}
|
||||
|
||||
void SPIRVPassConfig::addPreLegalizeMachineIR() {
|
||||
addPass(createSPIRVPreLegalizerCombiner());
|
||||
addPass(createSPIRVPreLegalizerPass());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# RUN: llc -verify-machineinstrs -O0 -mtriple spirv-unknown-unknown -run-pass=spirv-prelegalizer-combiner %s -o - | FileCheck %s
|
||||
# REQUIRES: asserts
|
||||
---
|
||||
name: distance_instcombine_float4
|
||||
tracksRegLiveness: true
|
||||
legalized: true
|
||||
body: |
|
||||
bb.1.entry:
|
||||
; CHECK-LABEL: name: distance_instcombine_float4
|
||||
; CHECK-NOT: %6:_(<4 x s32>) = G_FSUB %2, %3
|
||||
; CHECK-NOT: %7:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.length), %6(<4 x s32>)
|
||||
; CHECK: %7:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.distance), %2(<4 x s32>), %3(<4 x s32>)
|
||||
%0:type(s64) = OpTypeFloat 32
|
||||
%1:type(s64) = OpTypeVector %0(s64), 4
|
||||
OpName %2(<4 x s32>), 97
|
||||
OpName %3(<4 x s32>), 98
|
||||
%4:type(s64) = OpTypeFunction %0(s64), %1(s64), %1(s64)
|
||||
%5:iid(s64) = OpFunction %0(s64), 0, %4(s64)
|
||||
%2:vfid(<4 x s32>) = OpFunctionParameter %1(s64)
|
||||
%3:vfid(<4 x s32>) = OpFunctionParameter %1(s64)
|
||||
OpName %5(s64), 1953720676, 1701015137, 1936615775, 1836016500, 1701734754, 1869375071, 3437665
|
||||
OpDecorate %5(s64), 41, 1953720676, 1701015137, 1936615775, 1836016500, 1701734754, 1869375071, 3437665, 0
|
||||
%6:_(<4 x s32>) = G_FSUB %2, %3
|
||||
%7:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.length), %6(<4 x s32>)
|
||||
OpReturnValue %7(s32)
|
||||
|
||||
@@ -1,33 +1,44 @@
|
||||
; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; Make sure SPIRV operation function calls for distance are lowered correctly.
|
||||
|
||||
; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
|
||||
; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
|
||||
; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
|
||||
; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
|
||||
; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
|
||||
|
||||
define noundef half @distance_half4(<4 x half> noundef %a, <4 x half> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_16]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_16]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] Distance %[[#arg0]] %[[#arg1]]
|
||||
%spv.distance = call half @llvm.spv.distance.f16(<4 x half> %a, <4 x half> %b)
|
||||
ret half %spv.distance
|
||||
}
|
||||
|
||||
define noundef float @distance_float4(<4 x float> noundef %a, <4 x float> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] Distance %[[#arg0]] %[[#arg1]]
|
||||
%spv.distance = call float @llvm.spv.distance.f32(<4 x float> %a, <4 x float> %b)
|
||||
ret float %spv.distance
|
||||
}
|
||||
|
||||
declare half @llvm.spv.distance.f16(<4 x half>, <4 x half>)
|
||||
declare float @llvm.spv.distance.f32(<4 x float>, <4 x float>)
|
||||
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
|
||||
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
|
||||
|
||||
; Make sure SPIRV operation function calls for distance are lowered correctly.
|
||||
|
||||
; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
|
||||
; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
|
||||
; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
|
||||
; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
|
||||
; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
|
||||
|
||||
define noundef half @distance_half4(<4 x half> noundef %a, <4 x half> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_16]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_16]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] Distance %[[#arg0]] %[[#arg1]]
|
||||
%spv.distance = call half @llvm.spv.distance.f16(<4 x half> %a, <4 x half> %b)
|
||||
ret half %spv.distance
|
||||
}
|
||||
|
||||
define noundef float @distance_float4(<4 x float> noundef %a, <4 x float> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] Distance %[[#arg0]] %[[#arg1]]
|
||||
%spv.distance = call float @llvm.spv.distance.f32(<4 x float> %a, <4 x float> %b)
|
||||
ret float %spv.distance
|
||||
}
|
||||
|
||||
define noundef float @distance_instcombine_float4(<4 x float> noundef %a, <4 x float> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] Distance %[[#arg0]] %[[#arg1]]
|
||||
%delta = fsub <4 x float> %a, %b
|
||||
%spv.length = call float @llvm.spv.length.f32(<4 x float> %delta)
|
||||
ret float %spv.length
|
||||
}
|
||||
|
||||
declare half @llvm.spv.distance.f16(<4 x half>, <4 x half>)
|
||||
declare float @llvm.spv.distance.f32(<4 x float>, <4 x float>)
|
||||
|
||||
@@ -30,5 +30,16 @@ entry:
|
||||
ret float %spv.distance
|
||||
}
|
||||
|
||||
define noundef float @distance_instcombine_float4(<4 x float> noundef %a, <4 x float> noundef %b) {
|
||||
entry:
|
||||
; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
|
||||
; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_cl]] distance %[[#arg0]] %[[#arg1]]
|
||||
%delta = fsub <4 x float> %a, %b
|
||||
%spv.length = call float @llvm.spv.length.f32(<4 x float> %delta)
|
||||
ret float %spv.length
|
||||
}
|
||||
|
||||
declare half @llvm.spv.distance.f16(<4 x half>, <4 x half>)
|
||||
declare float @llvm.spv.distance.f32(<4 x float>, <4 x float>)
|
||||
|
||||
Reference in New Issue
Block a user