Inliner: Always try code pattern recognition. am: aa02410a2d
Original change: https://android-review.googlesource.com/c/platform/art/+/2971991
Change-Id: I4cba83d09b74edb0024eba9e47f5d63a03f74a61
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 381db3d..43e984b 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -152,8 +152,7 @@
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 @@
/*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 @@
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");
+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, 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,
+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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 99d07c6..4cd5b82 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 @@
*
* @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 d7ca17b..f1e2733 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1619,17 +1619,28 @@
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 @@
return false;
}
- CodeItemDataAccessor accessor(method->DexInstructionData());
-
- if (!IsInliningAllowed(method, accessor)) {
- return false;
- }
-
if (!IsInliningSupported(invoke_instruction, method, accessor)) {
return false;
}
@@ -1683,9 +1688,10 @@
// 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 4860054..57d3364 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -126,6 +126,7 @@
// 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 6f1280c..f1d60a1 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 @@
// 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 05f2f7c..b065b13 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 @@
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 @@
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 e887cd3..f4ae3a9 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 @@
/// 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 0000000..abe80d8
--- /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 89835c6..6528806 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 8951d30..9d0f772 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 @@
// 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 @@
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 f8c1d9d..6d1cded 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 b47a1a1..5323fd5 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",