Early inlining of simple methods.
Inlining "special" methods: empty methods, methods returning
constants or their arguments, simple getters and setters.
Bug: 8164439
Change-Id: I8c7fa9c14351fbb2470000b378a22974daaef236
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index e50ba24..53e26c7 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -21,6 +21,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "base/mutex-inl.h"
+#include "dex/frontend.h"
#include "thread.h"
#include "thread-inl.h"
#include "dex/mir_graph.h"
@@ -31,6 +32,23 @@
namespace art {
+namespace { // anonymous namespace
+
+MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) {
+ ArenaAllocator* arena = mir_graph->GetArena();
+ MIR* insn = static_cast<MIR*>(arena->Alloc(sizeof(MIR), kArenaAllocMIR));
+ insn->offset = invoke->offset;
+ insn->width = invoke->width;
+ insn->optimization_flags = MIR_CALLEE;
+ if (move_return != nullptr) {
+ DCHECK_EQ(move_return->offset, invoke->offset + invoke->width);
+ insn->width += move_return->width;
+ }
+ return insn;
+}
+
+} // anonymous namespace
+
const uint32_t DexFileMethodInliner::kIndexUnresolved;
const char* const DexFileMethodInliner::kClassCacheNames[] = {
"Z", // kClassCacheBoolean
@@ -348,6 +366,51 @@
return backend->SpecialMIR2LIR(special);
}
+bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ uint32_t method_idx) {
+ InlineMethod method;
+ {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_idx);
+ if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
+ return false;
+ }
+ method = it->second;
+ }
+
+ MIR* move_result = nullptr;
+ bool result = true;
+ switch (method.opcode) {
+ case kInlineOpNop:
+ break;
+ case kInlineOpNonWideConst:
+ move_result = mir_graph->FindMoveResult(bb, invoke);
+ result = GenInlineConst(mir_graph, bb, invoke, move_result, method);
+ break;
+ case kInlineOpReturnArg:
+ move_result = mir_graph->FindMoveResult(bb, invoke);
+ result = GenInlineReturnArg(mir_graph, bb, invoke, move_result, method);
+ break;
+ case kInlineOpIGet:
+ move_result = mir_graph->FindMoveResult(bb, invoke);
+ result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx);
+ break;
+ case kInlineOpIPut:
+ result = GenInlineIPut(mir_graph, bb, invoke, method, method_idx);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected inline op: " << method.opcode;
+ }
+ if (result) {
+ invoke->optimization_flags |= MIR_INLINED;
+ if (move_result != nullptr) {
+ move_result->optimization_flags |= MIR_INLINED;
+ move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ }
+ }
+ return result;
+}
+
uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
ClassCacheIndex index) {
uint32_t* class_index = &cache->class_indexes[index];
@@ -484,4 +547,149 @@
}
}
+bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method) {
+ if (move_result == nullptr) {
+ // Result is unused.
+ return true;
+ }
+
+ // Check the opcode and for MOVE_RESULT_OBJECT check also that the constant is null.
+ DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT ||
+ (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT &&
+ method.d.data == 0u));
+
+ // Insert the CONST instruction.
+ MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+ insn->dalvikInsn.opcode = Instruction::CONST;
+ insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
+ insn->dalvikInsn.vB = method.d.data;
+ mir_graph->InsertMIRAfter(bb, move_result, insn);
+ return true;
+}
+
+bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method) {
+ if (move_result == nullptr) {
+ // Result is unused.
+ return true;
+ }
+
+ // Select opcode and argument.
+ const InlineReturnArgData& data = method.d.return_data;
+ Instruction::Code opcode = Instruction::MOVE_FROM16;
+ if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
+ DCHECK_EQ(data.is_object, 1u);
+ opcode = Instruction::MOVE_OBJECT_FROM16;
+ } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) {
+ DCHECK_EQ(data.is_wide, 1u);
+ opcode = Instruction::MOVE_WIDE_FROM16;
+ } else {
+ DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT);
+ DCHECK_EQ(data.is_wide, 0u);
+ DCHECK_EQ(data.is_object, 0u);
+ }
+ DCHECK_LT(data.is_wide ? data.arg + 1u : data.arg, invoke->dalvikInsn.vA);
+ int arg;
+ if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k35c) {
+ arg = invoke->dalvikInsn.arg[data.arg]; // Non-range invoke.
+ } else {
+ DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k3rc);
+ arg = invoke->dalvikInsn.vC + data.arg; // Range invoke.
+ }
+
+ // Insert the move instruction
+ MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+ insn->dalvikInsn.opcode = opcode;
+ insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
+ insn->dalvikInsn.vB = arg;
+ mir_graph->InsertMIRAfter(bb, move_result, insn);
+ return true;
+}
+
+bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method,
+ uint32_t method_idx) {
+ CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
+ if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
+ return false;
+ }
+
+ const InlineIGetIPutData& data = method.d.ifield_data;
+ if (invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+ invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE ||
+ data.object_arg != 0) {
+ // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
+ return false;
+ }
+
+ if (move_result == nullptr) {
+ // Result is unused. If volatile, we still need to emit the IGET but we have no destination.
+ return !data.is_volatile;
+ }
+
+ Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant);
+ DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant);
+
+ MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result);
+ insn->width += insn->offset - invoke->offset;
+ insn->offset = invoke->offset;
+ insn->dalvikInsn.opcode = opcode;
+ insn->dalvikInsn.vA = move_result->dalvikInsn.vA;
+ DCHECK_LT(data.object_arg, invoke->dalvikInsn.vA);
+ if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) {
+ insn->dalvikInsn.vB = invoke->dalvikInsn.vC + data.object_arg;
+ } else {
+ DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c);
+ insn->dalvikInsn.vB = invoke->dalvikInsn.arg[data.object_arg];
+ }
+ mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
+
+ DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
+ DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastGet());
+ DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
+ DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
+
+ mir_graph->InsertMIRAfter(bb, move_result, insn);
+ return true;
+}
+
+bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ const InlineMethod& method, uint32_t method_idx) {
+ CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit();
+ if (cu->enable_debug & (1 << kDebugSlowFieldPath)) {
+ return false;
+ }
+
+ const InlineIGetIPutData& data = method.d.ifield_data;
+ if (invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+ invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE ||
+ data.object_arg != 0) {
+ // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
+ return false;
+ }
+
+ Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant);
+ DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant);
+
+ MIR* insn = AllocReplacementMIR(mir_graph, invoke, nullptr);
+ insn->dalvikInsn.opcode = opcode;
+ if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) {
+ insn->dalvikInsn.vA = invoke->dalvikInsn.vC + data.src_arg;
+ insn->dalvikInsn.vB = invoke->dalvikInsn.vC + data.object_arg;
+ } else {
+ insn->dalvikInsn.vA = invoke->dalvikInsn.arg[data.src_arg];
+ insn->dalvikInsn.vB = invoke->dalvikInsn.arg[data.object_arg];
+ }
+ mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn);
+
+ DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved());
+ DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastPut());
+ DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value());
+ DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u);
+
+ mir_graph->InsertMIRAfter(bb, invoke, insn);
+ return true;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index a6d4cab..b4e190a 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -31,7 +31,10 @@
class MethodVerifier;
} // namespace verifier
+struct BasicBlock;
struct CallInfo;
+struct MIR;
+class MIRGraph;
class Mir2Lir;
/**
@@ -79,7 +82,13 @@
/**
* Generate code for a special function.
*/
- bool GenSpecial(Mir2Lir* backend, uint32_t method_idx);
+ bool GenSpecial(Mir2Lir* backend, uint32_t method_idx) LOCKS_EXCLUDED(lock_);
+
+ /**
+ * Try to inline an invoke.
+ */
+ bool GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, uint32_t method_idx)
+ LOCKS_EXCLUDED(lock_);
/**
* To avoid multiple lookups of a class by its descriptor, we cache its
@@ -286,6 +295,15 @@
bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) LOCKS_EXCLUDED(lock_);
+ static bool GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method);
+ static bool GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method);
+ static bool GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ MIR* move_result, const InlineMethod& method, uint32_t method_idx);
+ static bool GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
+ const InlineMethod& method, uint32_t method_idx);
+
ReaderWriterMutex lock_;
/*
* Maps method indexes (for the particular DexFile) to Intrinsic defintions.
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 92c13ce..4f02fd7 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1423,6 +1423,16 @@
}
void Mir2Lir::GenInvoke(CallInfo* info) {
+ if ((info->opt_flags & MIR_INLINED) != 0) {
+ // Already inlined but we may still need the null check.
+ if (info->type != kStatic &&
+ ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 ||
+ (info->opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) {
+ RegLocation rl_obj = LoadValue(info->args[0], kCoreReg);
+ GenImmedCheck(kCondEq, rl_obj.reg.GetReg(), 0, kThrowNullPointer);
+ }
+ return;
+ }
DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
->GenIntrinsic(this, info)) {
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 538c292..39994e9 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -346,15 +346,17 @@
break;
case Instruction::MOVE_RESULT_WIDE:
- if (opt_flags & MIR_INLINED)
+ if ((opt_flags & MIR_INLINED) != 0) {
break; // Nop - combined w/ previous invoke.
+ }
StoreValueWide(rl_dest, GetReturnWide(rl_dest.fp));
break;
case Instruction::MOVE_RESULT:
case Instruction::MOVE_RESULT_OBJECT:
- if (opt_flags & MIR_INLINED)
+ if ((opt_flags & MIR_INLINED) != 0) {
break; // Nop - combined w/ previous invoke.
+ }
StoreValue(rl_dest, GetReturn(rl_dest.fp));
break;