diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 241f741eb1bd01..45cc3583fe04ac 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7233,6 +7233,34 @@ bool GenTree::OperMayThrow(Compiler* comp) return OperExceptions(comp) != ExceptionSetFlags::None; } +//------------------------------------------------------------------------------ +// NodeOrContainedOperandsMayThrow : Check whether the operation or any contained +// children will throw +// +// Arguments: +// comp - Compiler instance +// +// Return Value: +// True if the given operator or contained children may cause an exception +// +bool GenTree::NodeOrContainedOperandsMayThrow(Compiler* comp) +{ + if (OperMayThrow(comp)) + { + return true; + } + + // Check all contained children + for (GenTree* operand : Operands()) + { + if (operand->isContained() && operand->NodeOrContainedOperandsMayThrow(comp)) + { + return true; + } + } + return false; +} + //------------------------------------------------------------------------------ // OperRequiresGlobRefFlag : Check whether the operation requires GTF_GLOB_REF // flag regardless of the children's flags. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c6efe050d7e743..88c1f6a8f4ca1f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1910,6 +1910,7 @@ struct GenTree ExceptionSetFlags OperExceptions(Compiler* comp); bool OperMayThrow(Compiler* comp); + bool NodeOrContainedOperandsMayThrow(Compiler* comp); bool OperRequiresGlobRefFlag(Compiler* comp) const; diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 837a15faa21b54..18873239e739a6 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -591,6 +591,11 @@ const bool dspGCtbls = true; #define DISPTREERANGE(range, t) \ if (JitTls::GetCompiler()->verbose) \ JitTls::GetCompiler()->gtDispTreeRange(range, t); +#define LABELEDDISPTREERANGE(label, range, t) \ + JITDUMP(label ":\n"); \ + if (JitTls::GetCompiler()->verbose) \ + JitTls::GetCompiler()->gtDispTreeRange(range, t); \ + JITDUMP("\n"); #define DISPBLOCK(b) \ if (JitTls::GetCompiler()->verbose) \ JitTls::GetCompiler()->fgTableDispBasicBlock(b); @@ -609,6 +614,7 @@ const bool dspGCtbls = true; #define DISPSTMT(t) #define DISPRANGE(range) #define DISPTREERANGE(range, t) +#define LABELEDDISPTREERANGE(title, range, t) #define DISPBLOCK(b) #define VERBOSE 0 #endif // !DEBUG diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 54a20e9e7a4f87..228656a8b6ea1e 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1624,7 +1624,7 @@ bool Compiler::fgTryRemoveNonLocal(GenTree* node, LIR::Range* blockRange) // (as opposed to side effects of their children). // This default case should never include calls or stores. assert(!node->OperRequiresAsgFlag() && !node->OperIs(GT_CALL)); - if (!node->gtSetFlags() && !node->OperMayThrow(this)) + if (!node->gtSetFlags() && !node->NodeOrContainedOperandsMayThrow(this)) { JITDUMP("Removing dead node:\n"); DISPNODE(node); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 38d6cb6cf6ba41..bad90f7214e6c6 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1948,9 +1948,7 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) if (HWIntrinsicInfo::IsEmbeddedMaskedOperation(intrinsicId)) { LIR::Use use; - JITDUMP("lowering EmbeddedMasked HWIntrinisic (before):\n"); - DISPTREERANGE(BlockRange(), node); - JITDUMP("\n"); + LABELEDDISPTREERANGE("lowering EmbeddedMasked HWIntrinisic (before)", BlockRange(), node); // Use lastOp to verify if it's a ConditionlSelectNode. size_t lastOpNum = node->GetOperandCount(); @@ -1959,12 +1957,12 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) node->Op(lastOpNum)->AsHWIntrinsic()->GetHWIntrinsicId() == NI_Sve_ConditionalSelect && TryContainingCselOp(node, node->Op(lastOpNum)->AsHWIntrinsic())) { - JITDUMP("lowering EmbeddedMasked HWIntrinisic (after):\n"); - DISPTREERANGE(BlockRange(), node); - JITDUMP("\n"); + LABELEDDISPTREERANGE("Contained conditional select", BlockRange(), node); return node->gtNext; } + // Wrap a conditional select around the embedded mask operation + CorInfoType simdBaseJitType = node->GetSimdBaseJitType(); unsigned simdSize = node->GetSimdSize(); var_types simdType = Compiler::getSIMDTypeForSize(simdSize); @@ -1996,9 +1994,7 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) condSelNode->SetUnusedValue(); } - JITDUMP("lowering EmbeddedMasked HWIntrinisic (after):\n"); - DISPTREERANGE(BlockRange(), condSelNode); - JITDUMP("\n"); + LABELEDDISPTREERANGE("Embedded HWIntrinisic inside conditional select", BlockRange(), condSelNode); } ContainCheckHWIntrinsic(node); @@ -4116,8 +4112,7 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* cndSelNode) if (op2->OperIsHWIntrinsic(NI_Sve_ConditionalSelect)) { - // Handle cases where there is a nested ConditionalSelect for - // `trueValue` + // Handle cases where there is a nested ConditionalSelect for `trueValue` GenTreeHWIntrinsic* nestedCndSel = op2->AsHWIntrinsic(); GenTree* nestedOp1 = nestedCndSel->Op(1); GenTree* nestedOp2 = nestedCndSel->Op(2); @@ -4137,14 +4132,12 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* cndSelNode) GenTree* nestedOp2 = nestedCndSel->Op(2); GenTree* nestedOp3 = nestedCndSel->Op(3); - JITDUMP("lowering nested ConditionalSelect HWIntrinisic (before):\n"); - DISPTREERANGE(BlockRange(), cndSelNode); - JITDUMP("\n"); + LABELEDDISPTREERANGE("Removed nested conditionalselect (before):", BlockRange(), cndSelNode); // Transform: // - // CndSel(mask, CndSel(AllTrue, embeddedMask(trueValOp2), trueValOp3), op3) to - // CndSel(mask, embedded(trueValOp2), op3) + // CndSel1(mask, CndSel2(AllTrue, embedded(), trueValOp3), op3) to + // CndSel1(mask, embedded(), op3) // cndSelNode->Op(2) = nestedCndSel->Op(2); nestedOp3->SetUnusedValue(); @@ -4152,10 +4145,7 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* cndSelNode) BlockRange().Remove(nestedOp1); BlockRange().Remove(nestedCndSel); - JITDUMP("lowering nested ConditionalSelect HWIntrinisic (after):\n"); - DISPTREERANGE(BlockRange(), cndSelNode); - JITDUMP("\n"); - + LABELEDDISPTREERANGE("Removed nested conditionalselect (after)", BlockRange(), cndSelNode); return cndSelNode; } } @@ -4166,9 +4156,7 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* cndSelNode) if (!op2->OperIsHWIntrinsic() || !HWIntrinsicInfo::IsEmbeddedMaskedOperation(op2->AsHWIntrinsic()->GetHWIntrinsicId())) { - JITDUMP("lowering ConditionalSelect HWIntrinisic (before):\n"); - DISPTREERANGE(BlockRange(), cndSelNode); - JITDUMP("\n"); + LABELEDDISPTREERANGE("Lowered ConditionalSelect(True, op2, op3) to op2 (before)", BlockRange(), cndSelNode); // Transform // CndSel(AllTrue, op2, op3) to @@ -4190,10 +4178,7 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* cndSelNode) GenTree* next = cndSelNode->gtNext; BlockRange().Remove(cndSelNode); - JITDUMP("lowering ConditionalSelect HWIntrinisic (after):\n"); - DISPTREERANGE(BlockRange(), op2); - JITDUMP("\n"); - + LABELEDDISPTREERANGE("Lowered ConditionalSelect(True, op2, op3) to op2 (after)", BlockRange(), op2); return next; } } diff --git a/src/tests/JIT/opt/MaskConversions/ChangeMaskUse.cs b/src/tests/JIT/opt/SVE/ChangeMaskUse.cs similarity index 100% rename from src/tests/JIT/opt/MaskConversions/ChangeMaskUse.cs rename to src/tests/JIT/opt/SVE/ChangeMaskUse.cs diff --git a/src/tests/JIT/opt/MaskConversions/ChangeMaskUse.csproj b/src/tests/JIT/opt/SVE/ChangeMaskUse.csproj similarity index 100% rename from src/tests/JIT/opt/MaskConversions/ChangeMaskUse.csproj rename to src/tests/JIT/opt/SVE/ChangeMaskUse.csproj diff --git a/src/tests/JIT/opt/SVE/EmbeddedLoads.cs b/src/tests/JIT/opt/SVE/EmbeddedLoads.cs new file mode 100644 index 00000000000000..45de9c21b8ab90 --- /dev/null +++ b/src/tests/JIT/opt/SVE/EmbeddedLoads.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Unit tests for the masks conversion optimization +// Uses vectors as masks and vice versa. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using System.Threading; +using Xunit; + +public class EmbeddedLoads +{ + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Consume(T value) { } + + [Fact] + public static void TestEntryPoint() + { + + if (Sve.IsSupported) + { + int[] array = new int[10]; + + Vector op1 = Vector.Create(11); + Vector op2 = Vector.Create(22); + Vector op3 = Vector.Create(33); + Vector opl1 = Vector.Create(44); + Vector opl2 = Vector.Create(55); + + CndSelectEmbeddedOp3LoadTrueMask(array, op1); + CndSelectEmbeddedOp3LoadAllBits(array, op1); + CndSelectEmbeddedOp3LoadFalseMask(array, op1); + CndSelectEmbeddedOp3LoadZero(array, op1); + } + } + + // SVE load operation with embedded mask inside a conditional select + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void CndSelectEmbeddedOp3LoadTrueMask(int[] array, Vector op1) { + //ARM6-FULL-LINE: ldnf1w { {{z[0-9]+}}.s }, {{p[0-9]+}}/m, [{{x[0-9]+}}] + fixed (int* arr_ptr = array) + { + var result = Sve.ConditionalSelect(Sve.CreateTrueMaskInt32(), op1, Sve.LoadVectorNonFaulting(arr_ptr)); + Consume(result); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void CndSelectEmbeddedOp3LoadAllBits(int[] array, Vector op1) { + //ARM6-FULL-LINE: ldnf1w { {{z[0-9]+}}.s }, {{p[0-9]+}}/m, [{{x[0-9]+}}] + fixed (int* arr_ptr = array) + { + var result = Sve.ConditionalSelect(Vector.AllBitsSet, op1, Sve.LoadVectorNonFaulting(arr_ptr)); + Consume(result); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void CndSelectEmbeddedOp3LoadFalseMask(int[] array, Vector op1) { + //ARM6-FULL-LINE: ldnf1w { {{z[0-9]+}}.s }, {{p[0-9]+}}/m, [{{x[0-9]+}}] + fixed (int* arr_ptr = array) + { + var result = Sve.ConditionalSelect(Sve.CreateFalseMaskInt32(), op1, Sve.LoadVectorNonFaulting(arr_ptr)); + Consume(result); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void CndSelectEmbeddedOp3LoadZero(int[] array, Vector op1) { + //ARM6-FULL-LINE: ldnf1w { {{z[0-9]+}}.s }, {{p[0-9]+}}/m, [{{x[0-9]+}}] + fixed (int* arr_ptr = array) + { + var result = Sve.ConditionalSelect(Vector.Zero, op1, Sve.LoadVectorNonFaulting(arr_ptr)); + Consume(result); + } + } + +} diff --git a/src/tests/JIT/opt/SVE/EmbeddedLoads.csproj b/src/tests/JIT/opt/SVE/EmbeddedLoads.csproj new file mode 100644 index 00000000000000..f6885895f8d22a --- /dev/null +++ b/src/tests/JIT/opt/SVE/EmbeddedLoads.csproj @@ -0,0 +1,20 @@ + + + + true + + + None + True + $(NoWarn),SYSLIB5003 + + + + true + + + + + + + diff --git a/src/tests/JIT/opt/MaskConversions/MaskUse.cs b/src/tests/JIT/opt/SVE/MaskUse.cs similarity index 100% rename from src/tests/JIT/opt/MaskConversions/MaskUse.cs rename to src/tests/JIT/opt/SVE/MaskUse.cs diff --git a/src/tests/JIT/opt/MaskConversions/MaskUse.csproj b/src/tests/JIT/opt/SVE/MaskUse.csproj similarity index 100% rename from src/tests/JIT/opt/MaskConversions/MaskUse.csproj rename to src/tests/JIT/opt/SVE/MaskUse.csproj