diff options
author | 2024-11-14 15:09:41 +0000 | |
---|---|---|
committer | 2024-11-21 12:39:22 +0000 | |
commit | a21eb118c8c1e9d362b7465327f1fae8db48a572 (patch) | |
tree | e5542170e0de695c9dd491fa7b77a7deb73e28ae | |
parent | 53d8174b0003eb22192aba21d54ca96947002151 (diff) |
Allow the inliner to devirtualize intrinsics
To do so update:
* TryReplaceStringBuilderAppend
* Code paths relevant to previously InvokeVirtual that are now
InvokeStaticOrDirect
* checker tests.
Bug: 369206455
Test: art/test/testrunner/testrunner.py --host --64 -b --optimizing
Change-Id: I4d40980e416f3130d3c344c5f07b7b331deb5c97
-rw-r--r-- | compiler/optimizing/inliner.cc | 9 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 53 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 26 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 24 | ||||
-rw-r--r-- | test/624-checker-stringops/src/Main.java | 40 | ||||
-rw-r--r-- | test/672-checker-throw-method/src/Main.java | 8 | ||||
-rw-r--r-- | test/673-checker-throw-vmethod/src/Main.java | 8 |
7 files changed, 87 insertions, 81 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 44af0d6b66..d679261d42 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1342,13 +1342,6 @@ bool HInliner::TryDevirtualize(HInvoke* invoke_instruction, return false; } - // Don't try to devirtualize intrinsics as it breaks pattern matching from later phases. - // TODO(solanes): This `if` could be removed if we update optimizations like - // TryReplaceStringBuilderAppend. - if (invoke_instruction->IsIntrinsic()) { - return false; - } - // Don't devirtualize to an intrinsic invalid after the builder phase. The ArtMethod might be an // intrinsic even when the HInvoke isn't e.g. java.lang.CharSequence.isEmpty (not an intrinsic) // can get devirtualized into java.lang.String.isEmpty (which is an intrinsic). @@ -2452,7 +2445,7 @@ bool HInliner::ReturnTypeMoreSpecific(HInstruction* return_replacement, ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); if (IsReferenceTypeRefinement(invoke_rti.GetTypeHandle().Get(), invoke_rti.IsExact(), - /*declared_can_be_null=*/ true, + invoke_instruction->CanBeNull(), return_replacement)) { return true; } else if (return_replacement->IsInstanceFieldGet()) { diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 931050855c..703f6c77e8 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2976,13 +2976,8 @@ void InstructionSimplifierVisitor::SimplifyReturnThis(HInvoke* invoke) { // Helper method for StringBuffer escape analysis. static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) { - if (user->IsInvokeStaticOrDirect()) { - // Any constructor on StringBuffer is okay. - return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && - user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && - user->InputAt(0) == reference; - } else if (user->IsInvokeVirtual()) { - switch (user->AsInvokeVirtual()->GetIntrinsic()) { + if (user->IsInvoke()) { + switch (user->AsInvoke()->GetIntrinsic()) { case Intrinsics::kStringBufferLength: case Intrinsics::kStringBufferToString: DCHECK_EQ(user->InputAt(0), reference); @@ -2996,6 +2991,14 @@ static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstructi break; } } + + if (user->IsInvokeStaticOrDirect()) { + // Any constructor on StringBuffer is okay. + return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && + user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && + user->InputAt(0) == reference; + } + return false; } @@ -3051,13 +3054,24 @@ static bool TryReplaceStringBuilderAppend(CodeGenerator* codegen, HInvoke* invok return false; } } - // Then we should see the arguments. - if (user->IsInvokeVirtual()) { - HInvokeVirtual* as_invoke_virtual = user->AsInvokeVirtual(); + + // Pattern match seeing arguments, then constructor, then constructor fence. + if (user->IsInvokeStaticOrDirect() && + user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && + user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && + user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) { + // After arguments, we should see the constructor. + // We accept only the constructor with no extra arguments. + DCHECK(!seen_constructor); + DCHECK(!seen_constructor_fence); + seen_constructor = true; + } else if (user->IsInvoke()) { + // The arguments. + HInvoke* as_invoke = user->AsInvoke(); DCHECK(!seen_constructor); DCHECK(!seen_constructor_fence); StringBuilderAppend::Argument arg; - switch (as_invoke_virtual->GetIntrinsic()) { + switch (as_invoke->GetIntrinsic()) { case Intrinsics::kStringBuilderAppendObject: // TODO: Unimplemented, needs to call String.valueOf(). return false; @@ -3089,7 +3103,7 @@ static bool TryReplaceStringBuilderAppend(CodeGenerator* codegen, HInvoke* invok has_fp_args = true; break; case Intrinsics::kStringBuilderAppendCharSequence: { - ReferenceTypeInfo rti = user->AsInvokeVirtual()->InputAt(1)->GetReferenceTypeInfo(); + ReferenceTypeInfo rti = as_invoke->InputAt(1)->GetReferenceTypeInfo(); if (!rti.IsValid()) { return false; } @@ -3112,23 +3126,14 @@ static bool TryReplaceStringBuilderAppend(CodeGenerator* codegen, HInvoke* invok } } // Uses of the append return value should have been replaced with the first input. - DCHECK(!as_invoke_virtual->HasUses()); - DCHECK(!as_invoke_virtual->HasEnvironmentUses()); + DCHECK(!as_invoke->HasUses()); + DCHECK(!as_invoke->HasEnvironmentUses()); if (num_args == StringBuilderAppend::kMaxArgs) { return false; } format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg); - args[num_args] = as_invoke_virtual->InputAt(1u); + args[num_args] = as_invoke->InputAt(1u); ++num_args; - } else if (user->IsInvokeStaticOrDirect() && - user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && - user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && - user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) { - // After arguments, we should see the constructor. - // We accept only the constructor with no extra arguments. - DCHECK(!seen_constructor); - DCHECK(!seen_constructor_fence); - seen_constructor = true; } else if (user->IsConstructorFence()) { // The last use we see is the constructor fence. DCHECK(seen_constructor); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index e7aabb223a..31e999a282 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -3263,17 +3263,35 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } bool HInvokeStaticOrDirect::CanBeNull() const { - if (GetType() != DataType::Type::kReference || IsStringInit()) { + if (IsStringInit()) { return false; } + return HInvoke::CanBeNull(); +} + +bool HInvoke::CanBeNull() const { switch (GetIntrinsic()) { + case Intrinsics::kThreadCurrentThread: + case Intrinsics::kStringBufferAppend: + case Intrinsics::kStringBufferToString: + case Intrinsics::kStringBuilderAppendObject: + case Intrinsics::kStringBuilderAppendString: + case Intrinsics::kStringBuilderAppendCharSequence: + case Intrinsics::kStringBuilderAppendCharArray: + case Intrinsics::kStringBuilderAppendBoolean: + case Intrinsics::kStringBuilderAppendChar: + case Intrinsics::kStringBuilderAppendInt: + case Intrinsics::kStringBuilderAppendLong: + case Intrinsics::kStringBuilderAppendFloat: + case Intrinsics::kStringBuilderAppendDouble: + case Intrinsics::kStringBuilderToString: #define DEFINE_BOXED_CASE(name, unused1, unused2, unused3, unused4) \ - case Intrinsics::k##name##ValueOf: \ - return false; + case Intrinsics::k##name##ValueOf: BOXED_TYPES(DEFINE_BOXED_CASE) #undef DEFINE_BOXED_CASE + return false; default: - return true; + return GetType() == DataType::Type::kReference; } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 676a9b9dd2..e0913c821c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4632,6 +4632,8 @@ class HInvoke : public HVariableInputSizeInstruction { bool CanBeMoved() const override { return IsIntrinsic() && !DoesAnyWrite(); } + bool CanBeNull() const override; + bool InstructionDataEquals(const HInstruction* other) const override { return intrinsic_ != Intrinsics::kNone && intrinsic_ == other->AsInvoke()->intrinsic_; } @@ -5110,28 +5112,6 @@ class HInvokeVirtual final : public HInvoke { bool IsClonable() const override { return true; } - bool CanBeNull() const override { - switch (GetIntrinsic()) { - case Intrinsics::kThreadCurrentThread: - case Intrinsics::kStringBufferAppend: - case Intrinsics::kStringBufferToString: - case Intrinsics::kStringBuilderAppendObject: - case Intrinsics::kStringBuilderAppendString: - case Intrinsics::kStringBuilderAppendCharSequence: - case Intrinsics::kStringBuilderAppendCharArray: - case Intrinsics::kStringBuilderAppendBoolean: - case Intrinsics::kStringBuilderAppendChar: - case Intrinsics::kStringBuilderAppendInt: - case Intrinsics::kStringBuilderAppendLong: - case Intrinsics::kStringBuilderAppendFloat: - case Intrinsics::kStringBuilderAppendDouble: - case Intrinsics::kStringBuilderToString: - return false; - default: - return HInvoke::CanBeNull(); - } - } - bool CanDoImplicitNullCheckOn(HInstruction* obj) const override; uint32_t GetVTableIndex() const { return vtable_index_; } diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java index 055a4d7ec9..06b1af36df 100644 --- a/test/624-checker-stringops/src/Main.java +++ b/test/624-checker-stringops/src/Main.java @@ -27,16 +27,16 @@ public class Main { // Variant intrinsics remain in the loop, but invariant references are hoisted out of the loop. // /// CHECK-START: int Main.liveIndexOf() licm (before) - /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOf loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeStaticOrDirect intrinsic:StringStringIndexOf loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeStaticOrDirect intrinsic:StringStringIndexOfAfter loop:{{B\d+}} outer_loop:none // /// CHECK-START: int Main.liveIndexOf() licm (after) - /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOf loop:none - /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:none + /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: InvokeStaticOrDirect intrinsic:StringStringIndexOf loop:none + /// CHECK-DAG: InvokeStaticOrDirect intrinsic:StringStringIndexOfAfter loop:none static int liveIndexOf() { int k = ABC.length() + XYZ.length(); // does LoadString before loops for (char c = 'A'; c <= 'Z'; c++) { @@ -89,8 +89,8 @@ public class Main { // Explicit null check on receiver, implicit null check on argument prevents hoisting. // /// CHECK-START: int Main.indexOfExceptions(java.lang.String, java.lang.String) licm (after) - /// CHECK-DAG: <<String:l\d+>> NullCheck loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: InvokeVirtual [<<String>>,{{l\d+}}] intrinsic:StringStringIndexOf loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<String:l\d+>> NullCheck loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: InvokeStaticOrDirect [<<String>>,{{l\d+}}] intrinsic:StringStringIndexOf loop:<<Loop>> outer_loop:none static int indexOfExceptions(String s, String t) { int k = 0; for (char c = 'A'; c <= 'Z'; c++) { @@ -234,17 +234,27 @@ public class Main { } // - // All calls in the loop-body and thus loop can be eliminated. + // All calls in the loop-body are dead and thus loop can be eliminated. // - /// CHECK-START: int Main.bufferDeadLoop() instruction_simplifier (before) + /// CHECK-START: int Main.bufferDeadLoop() dead_code_elimination$initial (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString loop:<<Loop>> /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:<<Loop>> // + /// CHECK-START: int Main.bufferDeadLoop() dead_code_elimination$initial (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter + // + /// CHECK-START: int Main.bufferDeadLoop() instruction_simplifier$after_inlining (before) + /// CHECK: InvokeStaticOrDirect intrinsic:StringBufferToString + // + /// CHECK-START: int Main.bufferDeadLoop() instruction_simplifier$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect intrinsic:StringBufferToString + // + /// CHECK-START: int Main.bufferDeadLoop() loop_optimization (before) + /// CHECK: Phi + // /// CHECK-START: int Main.bufferDeadLoop() loop_optimization (after) /// CHECK-NOT: Phi - /// CHECK-NOT: InvokeVirtual intrinsic:StringBufferToString - /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter static int bufferDeadLoop() { StringBuffer b = new StringBuffer(); String x = "x"; @@ -255,7 +265,7 @@ public class Main { } // - // All calls in the loop-body and thus loop can be eliminated. + // All calls in the loop-body are dead and thus loop can be eliminated. // /// CHECK-START: int Main.builderDeadLoop() instruction_simplifier (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> diff --git a/test/672-checker-throw-method/src/Main.java b/test/672-checker-throw-method/src/Main.java index c2344b23e8..f149536e1d 100644 --- a/test/672-checker-throw-method/src/Main.java +++ b/test/672-checker-throw-method/src/Main.java @@ -55,7 +55,7 @@ public class Main { /// CHECK: If [<<Tst>>] /// CHECK: end_block /// CHECK: begin_block - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block // @@ -66,7 +66,7 @@ public class Main { /// CHECK: end_block /// CHECK: begin_block /// CHECK: <<Str:l\d+>> LoadString - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block static public void doit1(int[] a) { @@ -117,7 +117,7 @@ public class Main { /// CHECK: If [<<Tst>>] /// CHECK: end_block /// CHECK: begin_block - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block // @@ -128,7 +128,7 @@ public class Main { /// CHECK: end_block /// CHECK: begin_block /// CHECK: <<Str:l\d+>> LoadString - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block static public void doit3(int[] a) { diff --git a/test/673-checker-throw-vmethod/src/Main.java b/test/673-checker-throw-vmethod/src/Main.java index 39793001f0..dcffcabf51 100644 --- a/test/673-checker-throw-vmethod/src/Main.java +++ b/test/673-checker-throw-vmethod/src/Main.java @@ -49,7 +49,7 @@ public class Main { /// CHECK: If [<<Tst>>] /// CHECK: end_block /// CHECK: begin_block - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block // @@ -60,7 +60,7 @@ public class Main { /// CHECK: end_block /// CHECK: begin_block /// CHECK: <<Str:l\d+>> LoadString - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block public void doit1(int[] a) { @@ -111,7 +111,7 @@ public class Main { /// CHECK: If [<<Tst>>] /// CHECK: end_block /// CHECK: begin_block - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block // @@ -122,7 +122,7 @@ public class Main { /// CHECK: end_block /// CHECK: begin_block /// CHECK: <<Str:l\d+>> LoadString - /// CHECK: InvokeVirtual [{{l\d+}},<<Str>>] + /// CHECK: InvokeStaticOrDirect [{{l\d+}},<<Str>>] method_name:java.lang.StringBuilder.append /// CHECK: Throw /// CHECK: end_block public void doit3(int[] a) { |