summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2024-02-21 12:13:01 +0100
committer VladimĂ­r Marko <vmarko@google.com> 2024-02-29 12:44:34 +0000
commitaa02410a2de17b638a1eb5c664b390c4b1c36aed (patch)
tree9eb6c1da551ba729c413a49482acdea22e814439
parenta24ffcd41360ed6f6d2fb984041237495e39f7f2 (diff)
Inliner: Always try code pattern recognition.
Try code pattern recognition whenever we're allowed to inline. The pattern recognition is fast and when we find a match, we avoid costly processing, such as building the callee graph. The pattern replacement is always better than the invoke, so do it even when inlining is not "encouraged", for example in blocks that end with a `throw`. Reorder the code so that the pattern recognition respects the @NeverInline annotation. Enable run-test 569 for target and add a test for the @NeverInline annotation. Also remove some duplicated helper functions and reduce the number of arguments we pass around. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: testrunner.py --jvm Test: run-gtests.sh Test: testrunner.py --target --optimizing Bug: 181943478 Change-Id: I863fe1190eb38c7decf0c5e34a00c103e8e559f1
-rw-r--r--compiler/dex/inline_method_analyser.cc178
-rw-r--r--compiler/dex/inline_method_analyser.h41
-rw-r--r--compiler/optimizing/inliner.cc32
-rw-r--r--compiler/optimizing/inliner.h1
-rw-r--r--test/2243-checker-not-inline-into-throw/src/Main.java10
-rw-r--r--test/476-checker-ctor-fence-redun-elim/src/Main.java9
-rw-r--r--test/476-checker-ctor-memory-barrier/src/Main.java4
-rw-r--r--test/569-checker-pattern-replacement/build.py20
-rw-r--r--test/569-checker-pattern-replacement/src-multidex/Second.java5
-rw-r--r--test/569-checker-pattern-replacement/src/Main.java33
-rw-r--r--test/639-checker-code-sinking/src/Main.java6
-rw-r--r--test/knownfailures.json6
12 files changed, 188 insertions, 157 deletions
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 381db3d21d..43e984b5cb 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -152,8 +152,7 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir
if (kIsDebugBuild && target_method != nullptr) {
CHECK(!target_method->IsStatic());
CHECK(target_method->IsConstructor());
- CHECK(target_method->GetDeclaringClass() == method->GetDeclaringClass() ||
- target_method->GetDeclaringClass() == method->GetDeclaringClass()->GetSuperClass());
+ CHECK(method->GetDeclaringClass()->IsSubClass(target_method->GetDeclaringClass()));
}
return target_method;
}
@@ -256,11 +255,11 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item,
/*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
REQUIRES_SHARED(Locks::mutator_lock_) {
// On entry we should not have any IPUTs yet.
- DCHECK_EQ(0, std::count_if(
+ DCHECK(std::all_of(
iputs,
iputs + arraysize(iputs),
[](const ConstructorIPutData& iput_data) {
- return iput_data.field_index != DexFile::kDexNoIndex16;
+ return iput_data.field_index == DexFile::kDexNoIndex16;
}));
// Limit the maximum number of code units we're willing to match.
@@ -396,56 +395,36 @@ bool AnalyseConstructor(const CodeItemDataAccessor* code_item,
return true;
}
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
- "iget_object type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
- "iget_boolean type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE), "iget_byte type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR), "iget_char type");
-static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT), "iget_short type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT), "iput type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE), "iput_wide type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
- "iput_object type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
- "iput_boolean type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE), "iput_byte type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR), "iput_char type");
-static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT), "iput_short type");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT), "iget/iput variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), "iget/iput_wide variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), "iget/iput_object variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), "iget/iput_boolean variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), "iget/iput_byte variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), "iget/iput_char variant");
-static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
- InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
-
-bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
- CodeItemDataAccessor code_item(method->DexInstructionData());
- if (!code_item.HasCodeItem()) {
- // Native or abstract.
- return false;
- }
- return AnalyseMethodCode(&code_item,
- MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
- method->IsStatic(),
- method,
- result);
-}
-
-bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
+static_assert(IsInstructionIGet(Instruction::IGET));
+static_assert(IsInstructionIGet(Instruction::IGET_WIDE));
+static_assert(IsInstructionIGet(Instruction::IGET_OBJECT));
+static_assert(IsInstructionIGet(Instruction::IGET_BOOLEAN));
+static_assert(IsInstructionIGet(Instruction::IGET_BYTE));
+static_assert(IsInstructionIGet(Instruction::IGET_CHAR));
+static_assert(IsInstructionIGet(Instruction::IGET_SHORT));
+static_assert(IsInstructionIPut(Instruction::IPUT));
+static_assert(IsInstructionIPut(Instruction::IPUT_WIDE));
+static_assert(IsInstructionIPut(Instruction::IPUT_OBJECT));
+static_assert(IsInstructionIPut(Instruction::IPUT_BOOLEAN));
+static_assert(IsInstructionIPut(Instruction::IPUT_BYTE));
+static_assert(IsInstructionIPut(Instruction::IPUT_CHAR));
+static_assert(IsInstructionIPut(Instruction::IPUT_SHORT));
+static_assert(IGetMemAccessType(Instruction::IGET) == IPutMemAccessType(Instruction::IPUT));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_WIDE) == IPutMemAccessType(Instruction::IPUT_WIDE));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_OBJECT) == IPutMemAccessType(Instruction::IPUT_OBJECT));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_BOOLEAN) == IPutMemAccessType(Instruction::IPUT_BOOLEAN));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_BYTE) == IPutMemAccessType(Instruction::IPUT_BYTE));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_CHAR) == IPutMemAccessType(Instruction::IPUT_CHAR));
+static_assert(
+ IGetMemAccessType(Instruction::IGET_SHORT) == IPutMemAccessType(Instruction::IPUT_SHORT));
+
+bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
@@ -492,7 +471,7 @@ bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_it
// case Instruction::IGET_QUICK:
// case Instruction::IGET_WIDE_QUICK:
// case Instruction::IGET_OBJECT_QUICK:
- return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
+ return AnalyseIGetMethod(method, code_item, result);
case Instruction::IPUT:
case Instruction::IPUT_OBJECT:
case Instruction::IPUT_BOOLEAN:
@@ -504,15 +483,16 @@ bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_it
// case Instruction::IPUT_QUICK:
// case Instruction::IPUT_WIDE_QUICK:
// case Instruction::IPUT_OBJECT_QUICK:
- return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
+ return AnalyseIPutMethod(method, code_item, result);
default:
return false;
}
}
-bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) {
- const dex::MethodId& method_id = ref.dex_file->GetMethodId(ref.index);
- const char* method_name = ref.dex_file->GetMethodName(method_id);
+bool InlineMethodAnalyser::IsSyntheticAccessor(ArtMethod* method) {
+ const DexFile* dex_file = method->GetDexFile();
+ const dex::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
+ const char* method_name = dex_file->GetMethodName(method_id);
// javac names synthetic accessors "access$nnn",
// jack names them "-getN", "-putN", "-wrapN".
return strncmp(method_name, "access$", strlen("access$")) == 0 ||
@@ -572,10 +552,8 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_i
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
+bool InlineMethodAnalyser::AnalyseIGetMethod(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
InlineMethod* result) {
DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
@@ -607,39 +585,37 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_it
return false; // Not returning the value retrieved by IGET?
}
- if (is_static || object_arg != 0u) {
- // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
- // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(method_ref)) {
- return false;
- }
- }
-
// InlineIGetIPutData::object_arg is only 4 bits wide.
static constexpr uint16_t kMaxObjectArg = 15u;
if (object_arg > kMaxObjectArg) {
return false;
}
- if (result != nullptr) {
- InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
+ bool is_static = method->IsStatic();
+ if (is_static || object_arg != 0u) {
+ // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
+ // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
+ if (!IsSyntheticAccessor(method)) {
return false;
}
- result->opcode = kInlineOpIGet;
- data->op_variant = IGetVariant(opcode);
- data->method_is_static = is_static ? 1u : 0u;
- data->object_arg = object_arg; // Allow IGET on any register, not just "this".
- data->src_arg = 0u;
- data->return_arg_plus1 = 0u;
}
+
+ DCHECK(result != nullptr);
+ InlineIGetIPutData* data = &result->d.ifield_data;
+ if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
+ return false;
+ }
+ result->opcode = kInlineOpIGet;
+ data->op_variant = enum_cast<uint16_t>(IGetMemAccessType(opcode));
+ data->method_is_static = is_static ? 1u : 0u;
+ data->object_arg = object_arg; // Allow IGET on any register, not just "this".
+ data->src_arg = 0u;
+ data->return_arg_plus1 = 0u;
return true;
}
-bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
+bool InlineMethodAnalyser::AnalyseIPutMethod(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
InlineMethod* result) {
DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
@@ -673,14 +649,6 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_it
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
- if (is_static || object_arg != 0u) {
- // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
- // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(method_ref)) {
- return false;
- }
- }
-
// InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide.
static constexpr uint16_t kMaxObjectArg = 15u;
static constexpr uint16_t kMaxSrcArg = 15u;
@@ -689,18 +657,26 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_it
return false;
}
- if (result != nullptr) {
- InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
+ bool is_static = method->IsStatic();
+ if (is_static || object_arg != 0u) {
+ // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
+ // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
+ if (!IsSyntheticAccessor(method)) {
return false;
}
- result->opcode = kInlineOpIPut;
- data->op_variant = IPutVariant(opcode);
- data->method_is_static = is_static ? 1u : 0u;
- data->object_arg = object_arg; // Allow IPUT on any register, not just "this".
- data->src_arg = src_arg;
- data->return_arg_plus1 = return_arg_plus1;
}
+
+ DCHECK(result != nullptr);
+ InlineIGetIPutData* data = &result->d.ifield_data;
+ if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
+ return false;
+ }
+ result->opcode = kInlineOpIPut;
+ data->op_variant = enum_cast<uint16_t>(IPutMemAccessType(opcode));
+ data->method_is_static = is_static ? 1u : 0u;
+ data->object_arg = object_arg; // Allow IPUT on any register, not just "this".
+ data->src_arg = src_arg;
+ data->return_arg_plus1 = return_arg_plus1;
return true;
}
diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h
index 99d07c6152..4cd5b824f1 100644
--- a/compiler/dex/inline_method_analyser.h
+++ b/compiler/dex/inline_method_analyser.h
@@ -21,7 +21,6 @@
#include "base/mutex.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
-#include "dex/method_reference.h"
/*
* NOTE: This code is part of the quick compiler. It lives in the runtime
@@ -100,47 +99,23 @@ class InlineMethodAnalyser {
*
* @return true if the method is a candidate for inlining, false otherwise.
*/
- static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
+ static bool AnalyseMethodCode(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
+ InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
- return Instruction::IGET <= opcode && opcode <= Instruction::IGET_SHORT;
- }
-
- static constexpr bool IsInstructionIPut(Instruction::Code opcode) {
- return Instruction::IPUT <= opcode && opcode <= Instruction::IPUT_SHORT;
- }
-
- static constexpr uint16_t IGetVariant(Instruction::Code opcode) {
- return opcode - Instruction::IGET;
- }
-
- static constexpr uint16_t IPutVariant(Instruction::Code opcode) {
- return opcode - Instruction::IPUT;
- }
-
// Determines whether the method is a synthetic accessor (method name starts with "access$").
- static bool IsSyntheticAccessor(MethodReference ref);
+ static bool IsSyntheticAccessor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- static bool AnalyseMethodCode(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
- InlineMethod* result)
- REQUIRES_SHARED(Locks::mutator_lock_);
static bool AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
static bool AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
+ static bool AnalyseIGetMethod(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
- const MethodReference& method_ref,
- bool is_static,
- ArtMethod* method,
+ static bool AnalyseIPutMethod(ArtMethod* method,
+ const CodeItemDataAccessor* code_item,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index d7ca17b646..f1e2733f3e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1619,17 +1619,28 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
return true;
}
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+
+ if (!IsInliningAllowed(method, accessor)) {
+ return false;
+ }
+
+ // We have checked above that inlining is "allowed" to make sure that the method has bytecode
+ // (is not native), is compilable and verified and to enforce the @NeverInline annotation.
+ // However, the pattern substitution is always preferable, so we do it before the check if
+ // inlining is "encouraged". It also has an exception to the `MayInline()` restriction.
+ if (TryPatternSubstitution(invoke_instruction, method, accessor, return_replacement)) {
+ LOG_SUCCESS() << "Successfully replaced pattern of invoke "
+ << method->PrettyMethod();
+ MaybeRecordStat(stats_, MethodCompilationStat::kReplacedInvokeWithSimplePattern);
+ return true;
+ }
+
// Check whether we're allowed to inline. The outermost compilation unit is the relevant
// dex file here (though the transitivity of an inline chain would allow checking the caller).
if (!MayInline(codegen_->GetCompilerOptions(),
*method->GetDexFile(),
*outer_compilation_unit_.GetDexFile())) {
- if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) {
- LOG_SUCCESS() << "Successfully replaced pattern of invoke "
- << method->PrettyMethod();
- MaybeRecordStat(stats_, MethodCompilationStat::kReplacedInvokeWithSimplePattern);
- return true;
- }
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedWont)
<< "Won't inline " << method->PrettyMethod() << " in "
<< outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
@@ -1638,12 +1649,6 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
return false;
}
- CodeItemDataAccessor accessor(method->DexInstructionData());
-
- if (!IsInliningAllowed(method, accessor)) {
- return false;
- }
-
if (!IsInliningSupported(invoke_instruction, method, accessor)) {
return false;
}
@@ -1683,9 +1688,10 @@ static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
ArtMethod* method,
+ const CodeItemDataAccessor& accessor,
HInstruction** return_replacement) {
InlineMethod inline_method;
- if (!InlineMethodAnalyser::AnalyseMethodCode(method, &inline_method)) {
+ if (!InlineMethodAnalyser::AnalyseMethodCode(method, &accessor, &inline_method)) {
return false;
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 48600543c6..57d3364051 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -126,6 +126,7 @@ class HInliner : public HOptimization {
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool TryPatternSubstitution(HInvoke* invoke_instruction,
ArtMethod* method,
+ const CodeItemDataAccessor& accessor,
HInstruction** return_replacement)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/2243-checker-not-inline-into-throw/src/Main.java b/test/2243-checker-not-inline-into-throw/src/Main.java
index 6f1280c026..f1d60a129d 100644
--- a/test/2243-checker-not-inline-into-throw/src/Main.java
+++ b/test/2243-checker-not-inline-into-throw/src/Main.java
@@ -32,14 +32,18 @@ public class Main {
// Empty methods are easy to inline anywhere.
private static void easyToInline() {}
private static void $inline$easyToInline() {}
+ private static void twoLevelEasyToInline() { easyToInline(); }
/// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (before)
- /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+ /// CHECK: InvokeStaticOrDirect method_name:Main.twoLevelEasyToInline
/// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (after)
- /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+ /// CHECK: InvokeStaticOrDirect method_name:Main.twoLevelEasyToInline
static int $noinline$testEndsWithThrow() {
- easyToInline();
+ // Use two level inlining to avoid a pattern match in the inliner.
+ // The pattern matching is deliberately done before we check if inlining is "encouraged"
+ // which includes checking if the block ends with a `throw`.
+ twoLevelEasyToInline();
throw new Error("");
}
diff --git a/test/476-checker-ctor-fence-redun-elim/src/Main.java b/test/476-checker-ctor-fence-redun-elim/src/Main.java
index 05f2f7c5cf..b065b1315e 100644
--- a/test/476-checker-ctor-fence-redun-elim/src/Main.java
+++ b/test/476-checker-ctor-fence-redun-elim/src/Main.java
@@ -32,6 +32,13 @@ class Base {
int w2;
int w3;
+ Base() {
+ // Prevent inliner from matching the code pattern when calling this constructor
+ // to test the normal inlining that builds and inserts the callee graph.
+ // (Pattern matching can merge or eliminate constructor barriers.)
+ $inline$nop();
+ }
+
@Override
public String toString() {
return getClass().getName() + "(" + baseString() + ")";
@@ -40,6 +47,8 @@ class Base {
protected String baseString() {
return String.format("w0: %d, w1: %d, w2: %d, w3: %d", w0, w1, w2, w3);
}
+
+ private void $inline$nop() {}
}
// This has a final field in its constructor, so there must be a field freeze
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index e887cd32a0..f4ae3a9182 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -61,7 +61,9 @@ class ClassWithFinals {
/// CHECK-NOT: {{[slm]}}fence
public ClassWithFinals() {
// Exactly one constructor barrier.
- x = 0;
+ // Note: Do not store 0 as that can be eliminated together with the constructor
+ // barrier by the code pattern substitution in the inliner.
+ x = 1;
}
/// CHECK-START: void ClassWithFinals.<init>(int) inliner (after)
diff --git a/test/569-checker-pattern-replacement/build.py b/test/569-checker-pattern-replacement/build.py
new file mode 100644
index 0000000000..abe80d8bbb
--- /dev/null
+++ b/test/569-checker-pattern-replacement/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/569-checker-pattern-replacement/src-multidex/Second.java b/test/569-checker-pattern-replacement/src-multidex/Second.java
index 89835c6f22..6528806a0c 100644
--- a/test/569-checker-pattern-replacement/src-multidex/Second.java
+++ b/test/569-checker-pattern-replacement/src-multidex/Second.java
@@ -14,9 +14,14 @@
* limitations under the License.
*/
+import dalvik.annotation.optimization.NeverInline;
+
public final class Second {
public static void staticNop(int unused) { }
+ @NeverInline
+ public static void staticNopNeverInline(int unused) { }
+
public void nop() { }
public static Object staticReturnArg2(int unused1, String arg2) {
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
index 8951d3058b..9d0f772383 100644
--- a/test/569-checker-pattern-replacement/src/Main.java
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -15,6 +15,32 @@
*/
public class Main {
+ static class ExpectedError extends Error {}
+
+ /// CHECK-START: void Main.localStaticNopAndThrow() inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.localStaticNop
+
+ /// CHECK-START: void Main.localStaticNopAndThrow() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect method_name:Main.localStaticNop
+
+ public static void localStaticNopAndThrow() {
+ // Pattern matching replaces the invoke even in a block that ends with a `throw`.
+ localStaticNop();
+ throw new ExpectedError();
+ }
+
+ public static void localStaticNop() {}
+
+ /// CHECK-START: void Main.staticNopNeverInline() inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.staticNopNeverInline() inliner (after)
+ /// CHECK: InvokeStaticOrDirect
+
+ public static void staticNopNeverInline() {
+ Second.staticNopNeverInline(11);
+ }
+
/// CHECK-START: void Main.staticNop() inliner (before)
/// CHECK: InvokeStaticOrDirect
@@ -1177,6 +1203,8 @@ public class Main {
// Replaced NOP pattern.
staticNop();
nop(s);
+ // Not replaced NOP pattern.
+ staticNopNeverInline();
// Replaced "return arg" pattern.
assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
assertEquals(4321L, returnArg1(s, 4321L));
@@ -1259,6 +1287,11 @@ public class Main {
assertEquals(123, constructDerivedInSecondDex(123));
assertEquals(0, constructDerivedInSecondDexWith0());
assertEquals(0, constructDerivedInSecondDex(7L));
+
+ try {
+ localStaticNopAndThrow();
+ throw new Error("Unreachable");
+ } catch (ExpectedError expected) {}
}
private static void assertEquals(int expected, int actual) {
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index f8c1d9d4e7..6d1cded67f 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -17,8 +17,14 @@
public class Main {
static class ValueHolder {
int getValue() {
+ // Prevent inliner from matching the code pattern when calling this method to test
+ // the normal inlining path that does not inline in blocks that end with a `throw`.
+ $inline$nop();
+
return 1;
}
+
+ private void $inline$nop() {}
}
public static void main(String[] args) throws Exception {
diff --git a/test/knownfailures.json b/test/knownfailures.json
index b47a1a1b35..5323fd5fc5 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -58,12 +58,6 @@
"loaded systems."]
},
{
- "tests": "569-checker-pattern-replacement",
- "variant": "target",
- "description": ["569-checker-pattern-replacement tests behaviour",
- "present only on host."]
- },
- {
"tests": ["116-nodex2oat",
"118-noimage-dex2oat"],
"variant": "prebuild",