diff options
| -rw-r--r-- | compiler/dex/inline_method_analyser.cc | 178 | ||||
| -rw-r--r-- | compiler/dex/inline_method_analyser.h | 41 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 32 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.h | 1 | ||||
| -rw-r--r-- | test/2243-checker-not-inline-into-throw/src/Main.java | 10 | ||||
| -rw-r--r-- | test/476-checker-ctor-fence-redun-elim/src/Main.java | 9 | ||||
| -rw-r--r-- | test/476-checker-ctor-memory-barrier/src/Main.java | 4 | ||||
| -rw-r--r-- | test/569-checker-pattern-replacement/build.py | 20 | ||||
| -rw-r--r-- | test/569-checker-pattern-replacement/src-multidex/Second.java | 5 | ||||
| -rw-r--r-- | test/569-checker-pattern-replacement/src/Main.java | 33 | ||||
| -rw-r--r-- | test/639-checker-code-sinking/src/Main.java | 6 | ||||
| -rw-r--r-- | test/knownfailures.json | 6 |
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", |