Inlining support in optimizing.
Currently only inlines simple things that don't require an
environment, such as:
- Returning a constant.
- Returning a parameter.
- Returning an arithmetic operation.
Change-Id: Ie844950cb44f69e104774a3cf7a8dea66bc85661
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 18d6cf7..8bcc2f9 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -96,6 +96,7 @@
optimizing/graph_checker.cc \
optimizing/graph_visualizer.cc \
optimizing/gvn.cc \
+ optimizing/inliner.cc \
optimizing/instruction_simplifier.cc \
optimizing/locations.cc \
optimizing/nodes.cc \
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cd9280c..051b310 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1373,8 +1373,7 @@
if (resolved_method != nullptr) {
*vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type);
- if (enable_devirtualization) {
- DCHECK(mUnit->GetVerifiedMethod() != nullptr);
+ if (enable_devirtualization && mUnit->GetVerifiedMethod() != nullptr) {
const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc);
stats_flags = IsFastInvoke(
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index d39f1c7..f9054e0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -259,13 +259,13 @@
return false;
}
-HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item, int start_instruction_id) {
const uint16_t* code_ptr = code_item.insns_;
const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
code_start_ = code_ptr;
// Setup the graph with the entry block and exit block.
- graph_ = new (arena_) HGraph(arena_);
+ graph_ = new (arena_) HGraph(arena_, start_instruction_id);
entry_block_ = new (arena_) HBasicBlock(graph_, 0);
graph_->AddBlock(entry_block_);
exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
@@ -273,7 +273,7 @@
graph_->SetExitBlock(exit_block_);
InitializeLocals(code_item.registers_size_);
- graph_->UpdateMaximumNumberOfOutVRegs(code_item.outs_size_);
+ graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
// Compute the number of dex instructions, blocks, and branches. We will
// check these values against limits given to the compiler.
@@ -613,9 +613,9 @@
// Sharpening to kDirect only works if we compile PIC.
DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
|| compiler_driver_->GetCompilerOptions().GetCompilePic());
- // Treat invoke-direct like static calls for now.
- invoke = new (arena_) HInvokeStatic(
- arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index);
+ invoke = new (arena_) HInvokeStaticOrDirect(
+ arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
+ optimized_invoke_type);
}
size_t start_index = 0;
@@ -709,42 +709,53 @@
uint32_t source_or_dest_reg = instruction.VRegA_21c();
uint16_t field_index = instruction.VRegB_21c();
- uint32_t storage_index;
- bool is_referrers_class;
- bool is_initialized;
- bool is_volatile;
- MemberOffset field_offset(0u);
- Primitive::Type field_type;
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ArtField> resolved_field(hs.NewHandle(compiler_driver_->ResolveField(
+ soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true)));
- bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
- dex_compilation_unit_,
- is_put,
- &field_offset,
- &storage_index,
- &is_referrers_class,
- &is_volatile,
- &is_initialized,
- &field_type);
- if (!fast_path) {
+ if (resolved_field.Get() == nullptr) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
return false;
}
- if (is_volatile) {
+ if (resolved_field->IsVolatile()) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledVolatile);
return false;
}
- HLoadClass* constant = new (arena_) HLoadClass(
- storage_index, is_referrers_class, dex_pc);
+ Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
+ soa, dex_cache, class_loader, outer_compilation_unit_)));
+
+ // The index at which the field's class is stored in the DexCache's type array.
+ uint32_t storage_index;
+ std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+ dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index);
+ bool can_easily_access = is_put ? pair.second : pair.first;
+ if (!can_easily_access) {
+ return false;
+ }
+
+ // TODO: find out why this check is needed.
+ bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+ *outer_compilation_unit_->GetDexFile(), storage_index);
+ bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
+ bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
+
+ HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
current_block_->AddInstruction(constant);
HInstruction* cls = constant;
- if (!is_initialized) {
+ if (!is_initialized && !is_referrer_class) {
cls = new (arena_) HClinitCheck(constant, dex_pc);
current_block_->AddInstruction(cls);
}
+ Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
if (is_put) {
// We need to keep the class alive before loading the value.
Temporaries temps(graph_);
@@ -752,9 +763,10 @@
HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
DCHECK_EQ(value->GetType(), field_type);
current_block_->AddInstruction(
- new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+ new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset()));
} else {
- current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+ current_block_->AddInstruction(
+ new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset()));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
return true;
@@ -949,16 +961,20 @@
uint32_t dex_pc) {
bool type_known_final;
bool type_known_abstract;
- bool is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsCompilingClass` instead.
+ bool dont_use_is_referrers_class;
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &is_referrers_class);
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
if (!can_access) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
- HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc);
+ HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
current_block_->AddInstruction(cls);
// The class needs a temporary before being used by the type check.
Temporaries temps(graph_);
@@ -1929,16 +1945,20 @@
uint16_t type_index = instruction.VRegB_21c();
bool type_known_final;
bool type_known_abstract;
- bool is_referrers_class;
+ bool dont_use_is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsCompilingClass` instead.
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &is_referrers_class);
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
if (!can_access) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
current_block_->AddInstruction(
- new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc));
+ new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 75c8634..cc5f6a0 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -36,6 +36,7 @@
public:
HGraphBuilder(ArenaAllocator* arena,
DexCompilationUnit* dex_compilation_unit,
+ const DexCompilationUnit* const outer_compilation_unit,
const DexFile* dex_file,
CompilerDriver* driver,
OptimizingCompilerStats* compiler_stats)
@@ -51,6 +52,7 @@
dex_file_(dex_file),
dex_compilation_unit_(dex_compilation_unit),
compiler_driver_(driver),
+ outer_compilation_unit_(outer_compilation_unit),
return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
code_start_(nullptr),
latest_result_(nullptr),
@@ -70,12 +72,13 @@
dex_file_(nullptr),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
+ outer_compilation_unit_(nullptr),
return_type_(return_type),
code_start_(nullptr),
latest_result_(nullptr),
compilation_stats_(nullptr) {}
- HGraph* BuildGraph(const DexFile::CodeItem& code);
+ HGraph* BuildGraph(const DexFile::CodeItem& code, int start_instruction_id = 0);
private:
// Analyzes the dex instruction and adds HInstruction to the graph
@@ -225,6 +228,14 @@
void MaybeRecordStat(MethodCompilationStat compilation_stat);
+ // Returns whether `type_index` points to the outer-most compiling method's class.
+ bool IsCompilingClass(uint16_t type_index) const {
+ uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex();
+ const DexFile::MethodId& method_id =
+ outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index);
+ return method_id.class_idx_ == type_index;
+ }
+
ArenaAllocator* const arena_;
// A list of the size of the dex code holding block information for
@@ -242,9 +253,21 @@
HIntConstant* constant0_;
HIntConstant* constant1_;
+ // The dex file where the method being compiled is.
const DexFile* const dex_file_;
+
+ // The compilation unit of the current method being compiled. Note that
+ // it can be an inlined method.
DexCompilationUnit* const dex_compilation_unit_;
+
CompilerDriver* const compiler_driver_;
+
+ // The compilation unit of the outermost method being compiled. That is the
+ // method being compiled (and not inlined), and potentially inlining other
+ // methods.
+ const DexCompilationUnit* const outer_compilation_unit_;
+
+ // The return type of the method being compiled.
const Primitive::Type return_type_;
// The pointer in the dex file where the instructions of the code item
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 461409d..6f424ce 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -565,10 +565,19 @@
stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
++i;
DCHECK_LT(i, environment_size);
- } else {
- DCHECK(current->IsIntConstant());
+ } else if (current->IsDoubleConstant()) {
+ int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ } else if (current->IsIntConstant()) {
int32_t value = current->AsIntConstant()->GetValue();
stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+ } else {
+ DCHECK(current->IsFloatConstant());
+ int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
}
break;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cbe5f0c..002d9d4 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1171,7 +1171,7 @@
codegen_->GenerateFrameExit();
}
-void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
HandleInvoke(invoke);
}
@@ -1179,7 +1179,7 @@
__ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
}
-void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
// TODO: Implement all kinds of calls:
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b048c07..c7517d3 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1796,11 +1796,11 @@
HandleInvoke(invoke);
}
-void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
// Make sure that ArtMethod* is passed in W0 as per the calling convention
DCHECK(temp.Is(w0));
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4757235..e7edd8a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1115,11 +1115,11 @@
__ ret();
}
-void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
// TODO: Implement all kinds of calls:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index f8651f6..ff7fcdc 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1122,11 +1122,11 @@
return Location();
}
-void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
// TODO: Implement all kinds of calls:
// 1) boot -> boot
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a56b9d9..cad6683 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -38,8 +38,7 @@
HGraph* graph = CreateCFG(&allocator, data, return_type);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 5d4b9cb..3dbd04e 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -32,8 +32,7 @@
HGraph* graph = CreateCFG(&allocator, data);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 39def82..923468f 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -62,7 +62,7 @@
ASSERT_NE(graph, nullptr);
graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TransformToSsa();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4ed2156..5d1703e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -17,7 +17,6 @@
#include "graph_visualizer.h"
#include "code_generator.h"
-#include "driver/dex_compilation_unit.h"
#include "nodes.h"
#include "ssa_liveness_analysis.h"
@@ -270,39 +269,20 @@
HGraph* graph,
const char* string_filter,
const CodeGenerator& codegen,
- const DexCompilationUnit& cu)
+ const char* method_name)
: output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
if (output == nullptr) {
return;
}
- std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile());
- if (pretty_name.find(string_filter) == std::string::npos) {
+ if (strstr(method_name, string_filter) == nullptr) {
return;
}
is_enabled_ = true;
HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
printer.StartTag("compilation");
- printer.PrintProperty("name", pretty_name.c_str());
- printer.PrintProperty("method", pretty_name.c_str());
- printer.PrintTime("date");
- printer.EndTag("compilation");
-}
-
-HGraphVisualizer::HGraphVisualizer(std::ostream* output,
- HGraph* graph,
- const CodeGenerator& codegen,
- const char* name)
- : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
- if (output == nullptr) {
- return;
- }
-
- is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
- printer.StartTag("compilation");
- printer.PrintProperty("name", name);
- printer.PrintProperty("method", name);
+ printer.PrintProperty("name", method_name);
+ printer.PrintProperty("method", method_name);
printer.PrintTime("date");
printer.EndTag("compilation");
}
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 60d996b..b5baed9 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -47,16 +47,7 @@
HGraph* graph,
const char* string_filter,
const CodeGenerator& codegen,
- const DexCompilationUnit& cu);
-
- /**
- * Version of `HGraphVisualizer` for unit testing, that is when a
- * `DexCompilationUnit` is not available.
- */
- HGraphVisualizer(std::ostream* output,
- HGraph* graph,
- const CodeGenerator& codegen,
- const char* name);
+ const char* method_name);
/**
* If this visualizer is enabled, emit the compilation information
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index a6a68ca..94ff192 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -59,8 +59,7 @@
ASSERT_EQ(different_offset->GetBlock(), block);
ASSERT_EQ(use_after_kill->GetBlock(), block);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
GlobalValueNumberer(&allocator, graph).Run();
ASSERT_TRUE(to_remove->GetBlock() == nullptr);
@@ -108,8 +107,7 @@
new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
join->AddInstruction(new (&allocator) HExit());
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
GlobalValueNumberer(&allocator, graph).Run();
// Check that all field get instructions have been GVN'ed.
@@ -173,9 +171,7 @@
ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
GlobalValueNumberer(&allocator, graph).Run();
// Check that all field get instructions are still there.
@@ -237,9 +233,7 @@
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
outer_loop_exit->AddInstruction(new (&allocator) HExit());
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
*outer_loop_header->GetLoopInformation()));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
new file mode 100644
index 0000000..1de5b78
--- /dev/null
+++ b/compiler/optimizing/inliner.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "inliner.h"
+
+#include "builder.h"
+#include "class_linker.h"
+#include "constant_folding.h"
+#include "dead_code_elimination.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/dex_compilation_unit.h"
+#include "instruction_simplifier.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+#include "nodes.h"
+#include "ssa_phi_elimination.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr int kMaxInlineCodeUnits = 100;
+static constexpr int kMaxInlineNumberOfBlocks = 3;
+
+void HInliner::Run() {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ for (HInstructionIterator instr_it(it.Current()->GetInstructions());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ HInvokeStaticOrDirect* current = instr_it.Current()->AsInvokeStaticOrDirect();
+ if (current != nullptr) {
+ if (!TryInline(current, current->GetIndexInDexCache(), current->GetInvokeType())) {
+ if (kIsDebugBuild) {
+ std::string callee_name =
+ PrettyMethod(current->GetIndexInDexCache(), *outer_compilation_unit_.GetDexFile());
+ bool should_inline = callee_name.find("$inline$") != std::string::npos;
+ CHECK(!should_inline) << "Could not inline " << callee_name;
+ }
+ }
+ }
+ }
+ }
+}
+
+bool HInliner::TryInline(HInvoke* invoke_instruction,
+ uint32_t method_index,
+ InvokeType invoke_type) const {
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
+ VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file);
+
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader())));
+ Handle<mirror::ArtMethod> resolved_method(hs.NewHandle(
+ compiler_driver_->ResolveMethod(
+ soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type)));
+
+ if (resolved_method.Get() == nullptr) {
+ VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+ return false;
+ }
+
+ if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
+ VLOG(compiler) << "Did not inline "
+ << PrettyMethod(method_index, outer_dex_file)
+ << " because it is in a different dex file";
+ return false;
+ }
+
+ const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
+
+ if (code_item == nullptr) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because it is native";
+ return false;
+ }
+
+ if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is too big to inline";
+ return false;
+ }
+
+ if (code_item->tries_size_ != 0) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because of try block";
+ return false;
+ }
+
+ if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because its class could not be verified";
+ return false;
+ }
+
+ DexCompilationUnit dex_compilation_unit(
+ nullptr,
+ outer_compilation_unit_.GetClassLoader(),
+ outer_compilation_unit_.GetClassLinker(),
+ outer_dex_file,
+ code_item,
+ resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+ method_index,
+ resolved_method->GetAccessFlags(),
+ nullptr);
+
+ OptimizingCompilerStats inline_stats;
+ HGraphBuilder builder(graph_->GetArena(),
+ &dex_compilation_unit,
+ &outer_compilation_unit_,
+ &outer_dex_file,
+ compiler_driver_,
+ &inline_stats);
+ HGraph* callee_graph = builder.BuildGraph(*code_item, graph_->GetCurrentInstructionId());
+
+ if (callee_graph == nullptr) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be built, so cannot be inlined";
+ return false;
+ }
+
+ if (callee_graph->GetBlocks().Size() > kMaxInlineNumberOfBlocks) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " has too many blocks to be inlined: "
+ << callee_graph->GetBlocks().Size();
+ return false;
+ }
+
+ if (!callee_graph->TryBuildingSsa()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be transformed to SSA";
+ return false;
+ }
+
+ HReversePostOrderIterator it(*callee_graph);
+ it.Advance(); // Past the entry block to avoid seeing the suspend check.
+ for (; !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (block->IsLoopHeader()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because it contains a loop";
+ return false;
+ }
+
+ for (HInstructionIterator instr_it(block->GetInstructions());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ HInstruction* current = instr_it.Current();
+ if (current->CanThrow()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " can throw";
+ return false;
+ }
+
+ if (current->NeedsEnvironment()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " needs an environment";
+ return false;
+ }
+ }
+ }
+
+ // Run simple optimizations on the graph.
+ SsaRedundantPhiElimination redundant_phi(callee_graph);
+ SsaDeadPhiElimination dead_phi(callee_graph);
+ HDeadCodeElimination dce(callee_graph);
+ HConstantFolding fold(callee_graph);
+ InstructionSimplifier simplify(callee_graph);
+
+ HOptimization* optimizations[] = {
+ &redundant_phi,
+ &dead_phi,
+ &dce,
+ &fold,
+ &simplify,
+ };
+
+ for (size_t i = 0; i < arraysize(optimizations); ++i) {
+ HOptimization* optimization = optimizations[i];
+ optimization->Run();
+ }
+
+ callee_graph->InlineInto(graph_, invoke_instruction);
+ VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+ outer_stats_->RecordStat(kInlinedInvoke);
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
new file mode 100644
index 0000000..370e33c
--- /dev/null
+++ b/compiler/optimizing/inliner.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
+#define ART_COMPILER_OPTIMIZING_INLINER_H_
+
+#include "invoke_type.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexCompilationUnit;
+class HGraph;
+class HInvoke;
+class OptimizingCompilerStats;
+
+class HInliner : public HOptimization {
+ public:
+ HInliner(HGraph* outer_graph,
+ const DexCompilationUnit& outer_compilation_unit,
+ CompilerDriver* compiler_driver,
+ OptimizingCompilerStats* stats)
+ : HOptimization(outer_graph, true, "inliner"),
+ outer_compilation_unit_(outer_compilation_unit),
+ compiler_driver_(compiler_driver),
+ outer_stats_(stats) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
+
+ const DexCompilationUnit& outer_compilation_unit_;
+ CompilerDriver* const compiler_driver_;
+ OptimizingCompilerStats* const outer_stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInliner);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INLINER_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 28ca5e8..59404dc 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -42,9 +42,7 @@
HGraph* graph = builder.BuildGraph(*item);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
x86::CodeGeneratorX86 codegen(graph);
SsaLivenessAnalysis liveness(*graph, &codegen);
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 5c7e6f0..007c43e 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -36,9 +36,7 @@
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
return graph;
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 4b69e57..6f706c3 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -48,9 +48,7 @@
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
x86::CodeGeneratorX86 codegen(graph);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ba4dccf..fb941b5 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -167,7 +167,7 @@
}
}
-void HGraph::TransformToSSA() {
+void HGraph::TransformToSsa() {
DCHECK(!reverse_post_order_.IsEmpty());
SsaBuilder ssa_builder(this);
ssa_builder.BuildSsa();
@@ -682,4 +682,81 @@
return os;
}
+void HInstruction::InsertBefore(HInstruction* cursor) {
+ next_->previous_ = previous_;
+ if (previous_ != nullptr) {
+ previous_->next_ = next_;
+ }
+ if (block_->instructions_.first_instruction_ == this) {
+ block_->instructions_.first_instruction_ = next_;
+ }
+
+ previous_ = cursor->previous_;
+ if (previous_ != nullptr) {
+ previous_->next_ = this;
+ }
+ next_ = cursor;
+ cursor->previous_ = this;
+ block_ = cursor->block_;
+}
+
+void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+ // We currently only support graphs with one entry block, one body block, and one exit block.
+ DCHECK_EQ(GetBlocks().Size(), 3u);
+
+ // Walk over the entry block and:
+ // - Move constants from the entry block to the outer_graph's entry block,
+ // - Replace HParameterValue instructions with their real value.
+ // - Remove suspend checks, that hold an environment.
+ int parameter_index = 0;
+ for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsConstant()) {
+ current->InsertBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+ } else if (current->IsParameterValue()) {
+ current->ReplaceWith(invoke->InputAt(parameter_index++));
+ } else {
+ DCHECK(current->IsGoto() || current->IsSuspendCheck());
+ entry_block_->RemoveInstruction(current);
+ }
+ }
+
+ // Insert the body's instructions except the last, just after the `invoke`
+ // instruction.
+ HBasicBlock* body = GetBlocks().Get(1);
+ DCHECK(!body->IsExitBlock());
+ HInstruction* last = body->GetLastInstruction();
+ HInstruction* first = body->GetFirstInstruction();
+
+ if (first != last) {
+ HInstruction* antelast = last->GetPrevious();
+
+ // Update the instruction list of the body to only contain the last
+ // instruction.
+ last->previous_ = nullptr;
+ body->instructions_.first_instruction_ = last;
+ body->instructions_.last_instruction_ = last;
+
+ // Update the instruction list of the `invoke`'s block to now contain the
+ // body's instructions.
+ antelast->next_ = invoke->GetNext();
+ antelast->next_->previous_ = antelast;
+ first->previous_ = invoke;
+ invoke->next_ = first;
+
+ // Update the block pointer of all instructions.
+ for (HInstruction* current = antelast; current != invoke; current = current->GetPrevious()) {
+ current->SetBlock(invoke->GetBlock());
+ }
+ }
+
+ // Finally, replace the invoke with the return value of the inlined graph.
+ if (last->IsReturn()) {
+ invoke->ReplaceWith(last->InputAt(0));
+ body->RemoveInstruction(last);
+ } else {
+ DCHECK(last->IsReturnVoid());
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8a25de1..c963b70 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_H_
#define ART_COMPILER_OPTIMIZING_NODES_H_
+#include "invoke_type.h"
#include "locations.h"
#include "offsets.h"
#include "primitive.h"
@@ -30,6 +31,7 @@
class HEnvironment;
class HInstruction;
class HIntConstant;
+class HInvoke;
class HGraphVisitor;
class HPhi;
class HSuspendCheck;
@@ -75,6 +77,8 @@
HInstruction* last_instruction_;
friend class HBasicBlock;
+ friend class HGraph;
+ friend class HInstruction;
friend class HInstructionIterator;
friend class HBackwardInstructionIterator;
@@ -84,7 +88,7 @@
// Control-flow graph of a method. Contains a list of basic blocks.
class HGraph : public ArenaObject<kArenaAllocMisc> {
public:
- explicit HGraph(ArenaAllocator* arena)
+ HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
: arena_(arena),
blocks_(arena, kDefaultNumberOfBlocks),
reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -94,7 +98,7 @@
number_of_vregs_(0),
number_of_in_vregs_(0),
temporaries_vreg_slots_(0),
- current_instruction_id_(0) {}
+ current_instruction_id_(start_instruction_id) {}
ArenaAllocator* GetArena() const { return arena_; }
const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -108,8 +112,16 @@
void AddBlock(HBasicBlock* block);
+ // Try building the SSA form of this graph, with dominance computation and loop
+ // recognition. Returns whether it was successful in doing all these steps.
+ bool TryBuildingSsa() {
+ BuildDominatorTree();
+ TransformToSsa();
+ return AnalyzeNaturalLoops();
+ }
+
void BuildDominatorTree();
- void TransformToSSA();
+ void TransformToSsa();
void SimplifyCFG();
// Analyze all natural loops in this graph. Returns false if one
@@ -117,19 +129,31 @@
// back edge.
bool AnalyzeNaturalLoops() const;
+ // Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
+ void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
void SimplifyLoop(HBasicBlock* header);
- int GetNextInstructionId() {
+ int32_t GetNextInstructionId() {
+ DCHECK_NE(current_instruction_id_, INT32_MAX);
return current_instruction_id_++;
}
+ int32_t GetCurrentInstructionId() const {
+ return current_instruction_id_;
+ }
+
+ void SetCurrentInstructionId(int32_t id) {
+ current_instruction_id_ = id;
+ }
+
uint16_t GetMaximumNumberOfOutVRegs() const {
return maximum_number_of_out_vregs_;
}
- void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) {
- maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
+ void SetMaximumNumberOfOutVRegs(uint16_t new_value) {
+ maximum_number_of_out_vregs_ = new_value;
}
void UpdateTemporariesVRegSlots(size_t slots) {
@@ -152,10 +176,6 @@
number_of_in_vregs_ = value;
}
- uint16_t GetNumberOfInVRegs() const {
- return number_of_in_vregs_;
- }
-
uint16_t GetNumberOfLocalVRegs() const {
return number_of_vregs_ - number_of_in_vregs_;
}
@@ -200,8 +220,9 @@
size_t temporaries_vreg_slots_;
// The current id to assign to a newly added instruction. See HInstruction.id_.
- int current_instruction_id_;
+ int32_t current_instruction_id_;
+ ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -474,6 +495,9 @@
size_t lifetime_end_;
bool is_catch_block_;
+ friend class HGraph;
+ friend class HInstruction;
+
DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
};
@@ -503,7 +527,7 @@
M(InstanceOf, Instruction) \
M(IntConstant, Constant) \
M(InvokeInterface, Invoke) \
- M(InvokeStatic, Invoke) \
+ M(InvokeStaticOrDirect, Invoke) \
M(InvokeVirtual, Invoke) \
M(LessThan, Condition) \
M(LessThanOrEqual, Condition) \
@@ -748,6 +772,9 @@
void ReplaceWith(HInstruction* instruction);
void ReplaceInput(HInstruction* replacement, size_t index);
+ // Insert `this` instruction in `cursor`'s graph, just before `cursor`.
+ void InsertBefore(HInstruction* cursor);
+
bool HasOnlyOneUse() const {
return uses_ != nullptr && uses_->GetTail() == nullptr;
}
@@ -836,6 +863,7 @@
const SideEffects side_effects_;
friend class HBasicBlock;
+ friend class HGraph;
friend class HInstructionList;
DISALLOW_COPY_AND_ASSIGN(HInstruction);
@@ -1595,24 +1623,28 @@
DISALLOW_COPY_AND_ASSIGN(HInvoke);
};
-class HInvokeStatic : public HInvoke {
+class HInvokeStaticOrDirect : public HInvoke {
public:
- HInvokeStatic(ArenaAllocator* arena,
- uint32_t number_of_arguments,
- Primitive::Type return_type,
- uint32_t dex_pc,
- uint32_t index_in_dex_cache)
+ HInvokeStaticOrDirect(ArenaAllocator* arena,
+ uint32_t number_of_arguments,
+ Primitive::Type return_type,
+ uint32_t dex_pc,
+ uint32_t index_in_dex_cache,
+ InvokeType invoke_type)
: HInvoke(arena, number_of_arguments, return_type, dex_pc),
- index_in_dex_cache_(index_in_dex_cache) {}
+ index_in_dex_cache_(index_in_dex_cache),
+ invoke_type_(invoke_type) {}
uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; }
+ InvokeType GetInvokeType() const { return invoke_type_; }
- DECLARE_INSTRUCTION(InvokeStatic);
+ DECLARE_INSTRUCTION(InvokeStaticOrDirect);
private:
const uint32_t index_in_dex_cache_;
+ const InvokeType invoke_type_;
- DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
+ DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
};
class HInvokeVirtual : public HInvoke {
@@ -2425,7 +2457,7 @@
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
-// TODO: Pass this check to HInvokeStatic nodes.
+// TODO: Pass this check to HInvokeStaticOrDirect nodes.
/**
* Performs an initialization check on its Class object input.
*/
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d47217f..deebaf7 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -30,6 +30,7 @@
#include "elf_writer_quick.h"
#include "graph_visualizer.h"
#include "gvn.h"
+#include "inliner.h"
#include "instruction_simplifier.h"
#include "jni/quick/jni_compiler.h"
#include "mirror/art_method-inl.h"
@@ -181,25 +182,33 @@
return code_item.tries_size_ == 0;
}
-static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) {
- HDeadCodeElimination opt1(graph);
- HConstantFolding opt2(graph);
- SsaRedundantPhiElimination opt3(graph);
- SsaDeadPhiElimination opt4(graph);
- InstructionSimplifier opt5(graph);
- GVNOptimization opt6(graph);
+static void RunOptimizations(HGraph* graph,
+ CompilerDriver* driver,
+ OptimizingCompilerStats* stats,
+ const DexCompilationUnit& dex_compilation_unit,
+ const HGraphVisualizer& visualizer) {
+ SsaRedundantPhiElimination redundant_phi(graph);
+ SsaDeadPhiElimination dead_phi(graph);
+ HDeadCodeElimination dce(graph);
+ HConstantFolding fold(graph);
+ InstructionSimplifier simplify1(graph);
+
+ HInliner inliner(graph, dex_compilation_unit, driver, stats);
+
+ GVNOptimization gvn(graph);
BoundsCheckElimination bce(graph);
- InstructionSimplifier opt8(graph);
+ InstructionSimplifier simplify2(graph);
HOptimization* optimizations[] = {
- &opt1,
- &opt2,
- &opt3,
- &opt4,
- &opt5,
- &opt6,
+ &redundant_phi,
+ &dead_phi,
+ &dce,
+ &fold,
+ &simplify1,
+ &inliner,
+ &gvn,
&bce,
- &opt8
+ &simplify2
};
for (size_t i = 0; i < arraysize(optimizations); ++i) {
@@ -210,23 +219,6 @@
}
}
-static bool TryBuildingSsa(HGraph* graph,
- const DexCompilationUnit& dex_compilation_unit,
- const HGraphVisualizer& visualizer) {
- graph->BuildDominatorTree();
- graph->TransformToSSA();
-
- if (!graph->AnalyzeNaturalLoops()) {
- LOG(INFO) << "Skipping compilation of "
- << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
- *dex_compilation_unit.GetDexFile())
- << ": it contains a non natural loop";
- return false;
- }
- visualizer.DumpGraph("ssa transform");
- return true;
-}
-
// The stack map we generate must be 4-byte aligned on ARM. Since existing
// maps are generated alongside these stack maps, we must also align them.
static std::vector<uint8_t>& AlignVectorSize(std::vector<uint8_t>& vector) {
@@ -270,19 +262,23 @@
class_def_idx, method_idx, access_flags,
GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
+ std::string method_name = PrettyMethod(method_idx, dex_file);
+
// For testing purposes, we put a special marker on method names that should be compiled
// with this compiler. This makes sure we're not regressing.
- bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
- bool shouldOptimize =
- dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
+ bool shouldCompile = method_name.find("$opt$") != std::string::npos;
+ bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos;
ArenaPool pool;
ArenaAllocator arena(&pool);
HGraphBuilder builder(&arena,
&dex_compilation_unit,
- &dex_file, GetCompilerDriver(),
+ &dex_compilation_unit,
+ &dex_file,
+ GetCompilerDriver(),
&compilation_stats_);
+ VLOG(compiler) << "Building " << PrettyMethod(method_idx, dex_file);
HGraph* graph = builder.BuildGraph(*code_item);
if (graph == nullptr) {
CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
@@ -297,7 +293,7 @@
}
HGraphVisualizer visualizer(
- visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit);
+ visualizer_output_.get(), graph, kStringFilter, *codegen, method_name.c_str());
visualizer.DumpGraph("builder");
CodeVectorAllocator allocator;
@@ -306,12 +302,16 @@
bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set);
if (run_optimizations_ && can_optimize && can_allocate_registers) {
VLOG(compiler) << "Optimizing " << PrettyMethod(method_idx, dex_file);
- if (!TryBuildingSsa(graph, dex_compilation_unit, visualizer)) {
+ if (!graph->TryBuildingSsa()) {
+ LOG(INFO) << "Skipping compilation of "
+ << PrettyMethod(method_idx, dex_file)
+ << ": it contains a non natural loop";
// We could not transform the graph to SSA, bailout.
compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA);
return nullptr;
}
- RunOptimizations(graph, visualizer);
+ RunOptimizations(
+ graph, GetCompilerDriver(), &compilation_stats_, dex_compilation_unit, visualizer);
PrepareForRegisterAllocation(graph).Run();
SsaLivenessAnalysis liveness(*graph, codegen);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 829982e..7993b19 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -28,6 +28,7 @@
kAttemptCompilation = 0,
kCompiledBaseline,
kCompiledOptimized,
+ kInlinedInvoke,
kNotCompiledUnsupportedIsa,
kNotCompiledPathological,
kNotCompiledHugeMethod,
@@ -65,10 +66,10 @@
std::ostringstream oss;
oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "
<< unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "
- << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.\n";
+ << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.";
for (int i = 0; i < kLastStat; i++) {
if (compile_stats_[i] != 0) {
- oss << PrintMethodCompilationStat(i) << ": " << compile_stats_[i] << "\n";
+ oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
}
}
LOG(INFO) << oss.str();
@@ -81,6 +82,7 @@
case kAttemptCompilation : return "kAttemptCompilation";
case kCompiledBaseline : return "kCompiledBaseline";
case kCompiledOptimized : return "kCompiledOptimized";
+ case kInlinedInvoke : return "kInlinedInvoke";
case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
case kNotCompiledPathological : return "kNotCompiledPathological";
case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 8d75db9..f677e84 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -39,9 +39,7 @@
HGraphBuilder builder(&allocator);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
x86::CodeGeneratorX86 codegen(graph);
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -253,9 +251,7 @@
HGraphBuilder builder(allocator);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
return graph;
}
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 6174dd4..6b6bf05 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -87,7 +87,7 @@
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- graph->TransformToSSA();
+ graph->TransformToSsa();
ReNumberInstructions(graph);
// Test that phis had their type set.
diff --git a/test/437-inline/expected.txt b/test/437-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/437-inline/expected.txt
diff --git a/test/437-inline/info.txt b/test/437-inline/info.txt
new file mode 100644
index 0000000..6487a21
--- /dev/null
+++ b/test/437-inline/info.txt
@@ -0,0 +1 @@
+Tests inlining in the compiler.
diff --git a/test/437-inline/src/Main.java b/test/437-inline/src/Main.java
new file mode 100644
index 0000000..ccddab7
--- /dev/null
+++ b/test/437-inline/src/Main.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 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 {
+ public static void main(String[] args) {
+ if ($opt$inline$returnInt() != 4) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnParameter(42) != 42) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnWide() != 12L) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnWideParameter(0x100000001L) != 0x100000001L) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnReferenceParameter(Main.class) != Main.class) {
+ throw new Error();
+ }
+
+ $opt$inline$returnVoid();
+ $opt$inline$returnVoidWithOneParameter(32);
+
+ if ($opt$inline$returnAdd(42, 1) != 43) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnSub(42, 1) != 41) {
+ throw new Error();
+ }
+ }
+
+ public static int $opt$inline$returnParameter(int a) {
+ return a;
+ }
+
+ public static int $opt$inline$returnAdd(int a, int b) {
+ return a + b;
+ }
+
+ public static int $opt$inline$returnSub(int a, int b) {
+ return a - b;
+ }
+
+ public static int $opt$inline$returnInt() {
+ return 4;
+ }
+
+ public static long $opt$inline$returnWideParameter(long a) {
+ return a;
+ }
+
+ public static long $opt$inline$returnWide() {
+ return 12L;
+ }
+
+ public static Object $opt$inline$returnReferenceParameter(Object o) {
+ return o;
+ }
+
+ public static void $opt$inline$returnVoid() {
+ return;
+ }
+
+ public static void $opt$inline$returnVoidWithOneParameter(int a) {
+ return;
+ }
+}