#include "class_linker.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "quick/inline_method_analyser.h"
#include "sharpening.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
-#include "dex/verified_method.h"
-#include "dex/verification_results.h"
namespace art {
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
+ if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
+ MaybeRecordStat(kReplacedInvokeWithSimplePattern);
+ return true;
+ }
VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
<< outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
<< caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
return true;
}
+static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
+ size_t arg_vreg_index)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ size_t input_index = 0;
+ for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ if (Primitive::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
+ ++i;
+ DCHECK_NE(i, arg_vreg_index);
+ }
+ }
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ return invoke_instruction->InputAt(input_index);
+}
+
+// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool do_rtp) {
+ InlineMethod inline_method;
+ if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
+ return false;
+ }
+
+ HInstruction* return_replacement = nullptr;
+ switch (inline_method.opcode) {
+ case kInlineOpNop:
+ DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ break;
+ case kInlineOpReturnArg:
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
+ break;
+ case kInlineOpNonWideConst:
+ if (resolved_method->GetShorty()[0] == 'L') {
+ DCHECK_EQ(inline_method.d.data, 0u);
+ return_replacement = graph_->GetNullConstant();
+ } else {
+ return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ }
+ break;
+ case kInlineOpIGet: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+ DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
+ return_replacement = iget;
+ break;
+ }
+ case kInlineOpIPut: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+ DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+ if (data.return_arg_plus1 != 0u) {
+ size_t return_arg = data.return_arg_plus1 - 1u;
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
+ }
+
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
+ obj,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ if (iget->GetType() == Primitive::kPrimNot) {
+ ReferenceTypePropagation rtp(graph_, handles_);
+ rtp.Visit(iget);
+ }
+ return iget;
+}
+
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
+ obj,
+ value,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ return iput;
+}
bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
bool same_dex_file,
if (return_replacement != nullptr) {
DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
}
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == Primitive::kPrimNot) {
}
}
}
-
- return true;
}
} // namespace art
bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+ bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Create a new HInstanceFieldGet.
+ HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj);
+ // Create a new HInstanceFieldSet.
+ HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value);
+
// Try to inline the target of a monomorphic call. If successful, the code
// in the graph will look like:
// if (receiver.getClass() != ic.GetMonomorphicType()) deopt
uint32_t dex_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ void FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
kAttemptCompilation = 0,
kCompiled,
kInlinedInvoke,
+ kReplacedInvokeWithSimplePattern,
kInstructionSimplifications,
kInstructionSimplificationsArch,
kUnresolvedMethod,
case kAttemptCompilation : name = "AttemptCompilation"; break;
case kCompiled : name = "Compiled"; break;
case kInlinedInvoke : name = "InlinedInvoke"; break;
+ case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break;
case kInstructionSimplifications: name = "InstructionSimplifications"; break;
case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break;
case kUnresolvedMethod : name = "UnresolvedMethod"; break;
: HOptimization(graph, name),
handle_cache_(handles),
worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) {
- worklist_.reserve(kDefaultWorklistSize);
}
void ReferenceTypePropagation::ValidateTypes() {
}
}
+void ReferenceTypePropagation::Visit(HInstruction* instruction) {
+ RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+ instruction->Accept(&visitor);
+}
+
void ReferenceTypePropagation::Run() {
+ worklist_.reserve(kDefaultWorklistSize);
+
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
// We take advantage of this order in `VisitBasicBlock`.
StackHandleScopeCollection* handles,
const char* name = kReferenceTypePropagationPassName);
+ // Visit a single instruction.
+ void Visit(HInstruction* instruction);
+
void Run() OVERRIDE;
static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
// we need to be able to detect possibly inlined method, we pass a null inline method to indicate
// we don't want to take unresolved methods and fields into account during analysis.
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
- InlineMethod* method) {
+ InlineMethod* result) {
DCHECK(verifier != nullptr);
if (!Runtime::Current()->UseJit()) {
- DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+ DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
+ }
+
+ // Note: verifier->GetMethod() may be null.
+ return AnalyseMethodCode(verifier->CodeItem(),
+ verifier->GetMethodReference(),
+ (verifier->GetAccessFlags() & kAccStatic) != 0u,
+ verifier->GetMethod(),
+ result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (code_item == nullptr) {
+ // Native or abstract.
+ return false;
}
+ return AnalyseMethodCode(
+ code_item, method->ToMethodReference(), method->IsStatic(), method, result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
- const DexFile::CodeItem* code_item = verifier->CodeItem();
DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
switch (opcode) {
case Instruction::RETURN_VOID:
if (method != nullptr) {
- method->opcode = kInlineOpNop;
- method->flags = kInlineSpecial;
- method->d.data = 0u;
+ result->opcode = kInlineOpNop;
+ result->flags = kInlineSpecial;
+ result->d.data = 0u;
}
return true;
case Instruction::RETURN:
case Instruction::RETURN_OBJECT:
case Instruction::RETURN_WIDE:
- return AnalyseReturnMethod(code_item, method);
+ return AnalyseReturnMethod(code_item, result);
case Instruction::CONST:
case Instruction::CONST_4:
case Instruction::CONST_16:
case Instruction::CONST_HIGH16:
// TODO: Support wide constants (RETURN_WIDE).
- return AnalyseConstMethod(code_item, method);
+ return AnalyseConstMethod(code_item, result);
case Instruction::IGET:
case Instruction::IGET_OBJECT:
case Instruction::IGET_BOOLEAN:
// case Instruction::IGET_QUICK:
// case Instruction::IGET_WIDE_QUICK:
// case Instruction::IGET_OBJECT_QUICK:
- return AnalyseIGetMethod(verifier, method);
+ return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
case Instruction::IPUT:
case Instruction::IPUT_OBJECT:
case Instruction::IPUT_BOOLEAN:
// case Instruction::IPUT_QUICK:
// case Instruction::IPUT_WIDE_QUICK:
// case Instruction::IPUT_OBJECT_QUICK:
- return AnalyseIPutMethod(verifier, method);
+ return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
default:
return false;
}
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIGet(opcode));
return false; // Not returning the value retrieved by IGET?
}
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ 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(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
return false;
}
result->opcode = kInlineOpIGet;
result->flags = kInlineSpecial;
data->op_variant = IGetVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ 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(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIPut(opcode));
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ 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(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
return false;
}
result->opcode = kInlineOpIPut;
result->flags = kInlineSpecial;
data->op_variant = IPutVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ 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;
}
-bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result) {
- mirror::DexCache* dex_cache = verifier->GetDexCache();
- uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
- auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, cl->GetImagePointerSize());
- ArtField* field = cl->GetResolvedField(field_idx, dex_cache);
- if (method == nullptr || field == nullptr || field->IsStatic()) {
+ if (method == nullptr) {
+ return false;
+ }
+ mirror::DexCache* dex_cache = method->GetDexCache();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size);
+ if (field == nullptr || field->IsStatic()) {
return false;
}
mirror::Class* method_class = method->GetDeclaringClass();
* @param method placeholder for the inline method data.
* @return true if the method is a candidate for inlining, false otherwise.
*/
- static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
+ static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
static bool IsSyntheticAccessor(MethodReference ref);
private:
+ static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast path instance field access in a verified accessor?
// If yes, computes field's offset and volatility and whether the method is static or not.
- static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+ static bool ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result)
SHARED_REQUIRES(Locks::mutator_lock_);
};
return dex_cache_.Get();
}
+inline ArtMethod* MethodVerifier::GetMethod() const {
+ return mirror_method_;
+}
+
inline MethodReference MethodVerifier::GetMethodReference() const {
return MethodReference(dex_file_, dex_method_idx_);
}
ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index);
mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
MethodReference GetMethodReference() const;
uint32_t GetAccessFlags() const;
bool HasCheckCasts() const;
--- /dev/null
+Test pattern substitution used when we cannot inline.
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+exec ${RUN} "$@" \
+ -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex
--- /dev/null
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public final class Second {
+ public static void staticNop(int unused) { }
+
+ public void nop() { }
+
+ public static Object staticReturnArg2(int unused1, String arg2) {
+ return arg2;
+ }
+
+ public long returnArg1(long arg1) {
+ return arg1;
+ }
+
+ public static int staticReturn9() {
+ return 9;
+ }
+
+ public int return7(Object unused) {
+ return 7;
+ }
+
+ public static String staticReturnNull() {
+ return null;
+ }
+
+ public Object returnNull() {
+ return null;
+ }
+
+ public int getInstanceIntField() {
+ return instanceIntField;
+ }
+
+ public double getInstanceDoubleField(int unused1) {
+ return instanceDoubleField;
+ }
+
+ public Object getInstanceObjectField(long unused1) {
+ return instanceObjectField;
+ }
+
+ public String getInstanceStringField(Object unused1, String unused2, long unused3) {
+ return instanceStringField;
+ }
+
+ public static int staticGetInstanceIntField(Second s) {
+ return s.instanceIntField;
+ }
+
+ public double getInstanceDoubleFieldFromParam(Second s) {
+ return s.instanceDoubleField;
+ }
+
+ public int getStaticIntField() {
+ return staticIntField;
+ }
+
+ public void setInstanceLongField(int ignored, long value) {
+ instanceLongField = value;
+ }
+
+ public int setInstanceLongFieldReturnArg2(long value, int arg2) {
+ instanceLongField = value;
+ return arg2;
+ }
+
+ public static void staticSetInstanceLongField(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setInstanceLongFieldThroughParam(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setStaticFloatField(float value) {
+ staticFloatField = value;
+ }
+
+ public int instanceIntField = 42;
+ public double instanceDoubleField = -42.0;
+ public Object instanceObjectField = null;
+ public String instanceStringField = "dummy";
+ public long instanceLongField = 0; // Overwritten by setters.
+
+ public static int staticIntField = 4242;
+ public static float staticFloatField = 0.0f; // Overwritten by setters.
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+ /// CHECK-START: void Main.staticNop() inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.staticNop() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static void staticNop() {
+ Second.staticNop(11);
+ }
+
+ /// CHECK-START: void Main.nop(Second) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: void Main.nop(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static void nop(Second s) {
+ s.nop();
+ }
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Ignored:i\d+>> IntConstant 77
+ /// CHECK-DAG: <<ClinitCk:l\d+>> ClinitCheck
+ /// CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect [<<Ignored>>,<<Value>>,<<ClinitCk>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object staticReturnArg2(String value) {
+ return Second.staticReturnArg2(77, value);
+ }
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<Invoke:j\d+>> InvokeVirtual [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long returnArg1(Second s, long value) {
+ return s.returnArg1(value);
+ }
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK-NOT: IntConstant 9
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
+ /// CHECK-DAG: Return [<<Const9>>]
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int staticReturn9() {
+ return Second.staticReturn9();
+ }
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK-NOT: IntConstant 7
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ /// CHECK-DAG: Return [<<Const7>>]
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int return7(Second s) {
+ return s.return7(null);
+ }
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK: {{l\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static String staticReturnNull() {
+ return Second.staticReturnNull();
+ }
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object returnNull(Second s) {
+ return s.returnNull();
+ }
+
+ /// CHECK-START: int Main.getInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InstanceFieldGet
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int getInt(Second s) {
+ return s.getInstanceIntField();
+ }
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK: {{d\d+}} InstanceFieldGet
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static double getDouble(Second s) {
+ return s.getInstanceDoubleField(22);
+ }
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object getObject(Second s) {
+ return s.getInstanceObjectField(-1L);
+ }
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static String getString(Second s) {
+ return s.getInstanceStringField(null, "whatever", 1234L);
+ }
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static int staticGetInt(Second s) {
+ return Second.staticGetInstanceIntField(s);
+ }
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static double getDoubleFromParam(Second s) {
+ return s.getInstanceDoubleFieldFromParam(s);
+ }
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: StaticFieldGet
+
+ public static int getStaticInt(Second s) {
+ return s.getStaticIntField();
+ }
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLong(Second s, long value) {
+ s.setInstanceLongField(-1, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: InstanceFieldSet [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: <<NullCk2:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<IGet:j\d+>> InstanceFieldGet [<<NullCk2>>]
+ /// CHECK-DAG: <<Conv:j\d+>> TypeConversion [<<Arg2>>]
+ /// CHECK-DAG: <<Add:j\d+>> Add [<<IGet>>,<<Conv>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLongReturnArg2(Second s, long value, int arg2) {
+ int result = s.setInstanceLongFieldReturnArg2(value, arg2);
+ return s.instanceLongField + result;
+ }
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long staticSetLong(Second s, long value) {
+ Second.staticSetInstanceLongField(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long setLongThroughParam(Second s, long value) {
+ s.setInstanceLongFieldThroughParam(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: StaticFieldSet
+
+ public static float setStaticFloat(Second s, float value) {
+ s.setStaticFloatField(value);
+ return s.staticFloatField;
+ }
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>] method_name:java.lang.Object.<init>
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object newObject() {
+ return new Object();
+ }
+
+ public static void main(String[] args) throws Exception {
+ Second s = new Second();
+
+ // Replaced NOP pattern.
+ staticNop();
+ nop(s);
+ // Replaced "return arg" pattern.
+ assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
+ assertEquals(4321L, returnArg1(s, 4321L));
+ // Replaced "return const" pattern.
+ assertEquals(9, staticReturn9());
+ assertEquals(7, return7(s));
+ assertEquals(null, staticReturnNull());
+ assertEquals(null, returnNull(s));
+ // Replaced IGET pattern.
+ assertEquals(42, getInt(s));
+ assertEquals(-42.0, getDouble(s));
+ assertEquals(null, getObject(s));
+ assertEquals("dummy", getString(s));
+ // Not replaced IGET pattern.
+ assertEquals(42, staticGetInt(s));
+ assertEquals(-42.0, getDoubleFromParam(s));
+ // SGET.
+ assertEquals(4242, getStaticInt(s));
+ // Replaced IPUT pattern.
+ assertEquals(111L, setLong(s, 111L));
+ assertEquals(345L, setLongReturnArg2(s, 222L, 123));
+ // Not replaced IPUT pattern.
+ assertEquals(222L, staticSetLong(s, 222L));
+ assertEquals(333L, setLongThroughParam(s, 333L));
+ // SPUT.
+ assertEquals(-11.5f, setStaticFloat(s, -11.5f));
+
+ if (newObject() == null) {
+ throw new AssertionError("new Object() cannot be null.");
+ }
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && (expected == null || !expected.equals(actual))) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+}