diff options
author | 2016-01-19 14:08:02 +0000 | |
---|---|---|
committer | 2016-01-20 16:28:39 +0000 | |
commit | 82fc9bb45dbf8ff728122fb7ab72d1eb7b2f4869 (patch) | |
tree | 3a22cf5bb2e0012723ecdc7640e4db0dd5be2f0a | |
parent | 17ccfff2de292fd2b4a78aef87d79b662381f920 (diff) |
Inline methods with loops.
Compiling Gms/Fb/Framework/Docs:
- Overall compilation-time increase: 2.2%
- Overall code size increase: 1.1%
Performance improvements:
- Richards with jit: +6%
- Takl: +11%
Change-Id: I0a6fcf2a360e5ad193cd95b5c4fe92227ac6bd96
-rw-r--r-- | compiler/optimizing/inliner.cc | 12 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 27 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 4 | ||||
-rw-r--r-- | test/004-StackWalk/src/Main.java | 8 | ||||
-rw-r--r-- | test/482-checker-loop-back-edge-use/src/Main.java | 6 | ||||
-rw-r--r-- | test/510-checker-try-catch/smali/SsaBuilder.smali | 7 | ||||
-rw-r--r-- | test/564-checker-inline-loop/expected.txt | 0 | ||||
-rw-r--r-- | test/564-checker-inline-loop/info.txt | 1 | ||||
-rw-r--r-- | test/564-checker-inline-loop/src/Main.java | 64 |
9 files changed, 113 insertions, 16 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 20c4f1f698..2e79df1b84 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -419,7 +419,10 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); if (code_item->insns_size_in_code_units_ > inline_max_code_units) { VLOG(compiler) << "Method " << PrettyMethod(method) - << " is too big to inline"; + << " is too big to inline: " + << code_item->insns_size_in_code_units_ + << " > " + << inline_max_code_units; return false; } @@ -639,9 +642,12 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, for (; !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - if (block->IsLoopHeader()) { + + if (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible()) { + // Don't inline methods with irreducible loops, they could prevent some + // optimizations to run. VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file) - << " could not be inlined because it contains a loop"; + << " could not be inlined because it contains an irreducible loop"; return false; } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 2eabadf861..adf8734214 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -288,9 +288,10 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { // Make sure the loop has only one pre header. This simplifies SSA building by having // to just look at the pre header to know which locals are initialized at entry of the - // loop. + // loop. Also, don't allow the entry block to be a pre header: this simplifies inlining + // this graph. size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges(); - if (number_of_incomings != 1) { + if (number_of_incomings != 1 || (GetEntryBlock()->GetSingleSuccessor() == header)) { HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc()); AddBlock(pre_header); pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc())); @@ -1837,6 +1838,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { DCHECK(GetBlocks()[0]->IsEntryBlock()); DCHECK(GetBlocks()[2]->IsExitBlock()); DCHECK(!body->IsExitBlock()); + DCHECK(!body->IsInLoop()); HInstruction* last = body->GetLastInstruction(); invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions()); @@ -1895,7 +1897,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // Update the meta information surrounding blocks: // (1) the graph they are now in, // (2) the reverse post order of that graph, - // (3) the potential loop information they are now in, + // (3) their potential loop information, inner and outer, // (4) try block membership. // Note that we do not need to update catch phi inputs because they // correspond to the register file of the outer method which the inlinee @@ -1924,15 +1926,24 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); if (current != exit_block_ && current != entry_block_ && current != first) { - DCHECK(!current->IsInLoop()); DCHECK(current->GetTryCatchInformation() == nullptr); DCHECK(current->GetGraph() == this); current->SetGraph(outer_graph); outer_graph->AddBlock(current); outer_graph->reverse_post_order_[++index_of_at] = current; - if (loop_info != nullptr) { + if (!current->IsInLoop()) { current->SetLoopInformation(loop_info); - for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + } else if (current->IsLoopHeader()) { + // Clear the information of which blocks are contained in that loop. Since the + // information is stored as a bit vector based on block ids, we have to update + // it, as those block ids were specific to the callee graph and we are now adding + // these blocks to the caller graph. + current->GetLoopInformation()->ClearAllBlocks(); + } + if (current->IsInLoop()) { + for (HLoopInformationOutwardIterator loop_it(*current); + !loop_it.Done(); + loop_it.Advance()) { loop_it.Current()->Add(current); } } @@ -1945,7 +1956,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(to); outer_graph->reverse_post_order_[++index_of_at] = to; if (loop_info != nullptr) { - to->SetLoopInformation(loop_info); + if (!to->IsInLoop()) { + to->SetLoopInformation(loop_info); + } for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { loop_it.Current()->Add(to); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e222ef7260..48201e3d23 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -689,6 +689,10 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { void Add(HBasicBlock* block); void Remove(HBasicBlock* block); + void ClearAllBlocks() { + blocks_.ClearAllBits(); + } + private: // Internal recursive implementation of `Populate`. void PopulateRecursive(HBasicBlock* block); diff --git a/test/004-StackWalk/src/Main.java b/test/004-StackWalk/src/Main.java index 883ce2c9fe..072b1d0c4d 100644 --- a/test/004-StackWalk/src/Main.java +++ b/test/004-StackWalk/src/Main.java @@ -2,14 +2,14 @@ public class Main { public Main() { } + boolean doThrow = false; + int $noinline$f() throws Exception { g(1); g(2); - // This loop currently defeats inlining of `f`. - for (int i = 0; i < 10; i++) { - Thread.sleep(0); - } + // This currently defeats inlining of `f`. + if (doThrow) { throw new Error(); } return 0; } diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index d0b33b9282..f8f0aa3f0a 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -40,6 +40,9 @@ public class Main { /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> public static void loop2(boolean incoming) { + // Add some code at entry to avoid having the entry block be a pre header. + // This avoids having to create a synthesized block. + System.out.println("Enter"); while (true) { System.out.println("foo"); while (incoming) {} @@ -170,6 +173,9 @@ public class Main { /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> public static void loop9() { + // Add some code at entry to avoid having the entry block be a pre header. + // This avoids having to create a synthesized block. + System.out.println("Enter"); while (Runtime.getRuntime() != null) { // 'incoming' must only have a use in the inner loop. boolean incoming = field; diff --git a/test/510-checker-try-catch/smali/SsaBuilder.smali b/test/510-checker-try-catch/smali/SsaBuilder.smali index 710e849c45..a6a5bfebee 100644 --- a/test/510-checker-try-catch/smali/SsaBuilder.smali +++ b/test/510-checker-try-catch/smali/SsaBuilder.smali @@ -21,7 +21,7 @@ ## CHECK-START: int SsaBuilder.testSimplifyCatchBlock(int, int, int) ssa_builder (after) -## CHECK: name "B0" +## CHECK: name "B1" ## CHECK-NEXT: from_bci ## CHECK-NEXT: to_bci ## CHECK-NEXT: predecessors @@ -39,12 +39,15 @@ ## CHECK: name "<<BExtracted>>" ## CHECK-NEXT: from_bci ## CHECK-NEXT: to_bci -## CHECK-NEXT: predecessors "B0" "<<BCatch>>" +## CHECK-NEXT: predecessors "B1" "<<BCatch>>" ## CHECK-NOT: flags "catch_block" ## CHECK: Add .method public static testSimplifyCatchBlock(III)I .registers 4 + # Avoid entry block be a pre header, which leads to + # the cfg simplifier to add a synthesized block. + goto :catch_all :catch_all add-int/2addr p0, p1 diff --git a/test/564-checker-inline-loop/expected.txt b/test/564-checker-inline-loop/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/564-checker-inline-loop/expected.txt diff --git a/test/564-checker-inline-loop/info.txt b/test/564-checker-inline-loop/info.txt new file mode 100644 index 0000000000..a590bc6f23 --- /dev/null +++ b/test/564-checker-inline-loop/info.txt @@ -0,0 +1 @@ +Tests inlining of loops in the optimizing compiler. diff --git a/test/564-checker-inline-loop/src/Main.java b/test/564-checker-inline-loop/src/Main.java new file mode 100644 index 0000000000..6929913864 --- /dev/null +++ b/test/564-checker-inline-loop/src/Main.java @@ -0,0 +1,64 @@ +/* + * 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: int Main.inlineLoop() inliner (before) + /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Invoke>>] + + /// CHECK-START: int Main.inlineLoop() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START: int Main.inlineLoop() inliner (after) + /// CHECK-DAG: <<Constant:i\d+>> IntConstant 42 + /// CHECK-DAG: Return [<<Constant>>] + + /// CHECK-START: int Main.inlineLoop() licm (after) + /// CHECK: Goto loop:{{B\d+}} + + public static int inlineLoop() { + return loopMethod(); + } + + /// CHECK-START: void Main.inlineWithinLoop() inliner (before) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: void Main.inlineWithinLoop() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START: void Main.inlineWithinLoop() licm (after) + /// CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none + /// CHECK-DAG: Goto outer_loop:<<OuterLoop>> + + public static void inlineWithinLoop() { + while (doLoop) { + loopMethod(); + } + } + + public static int loopMethod() { + while (doLoop) {} + return 42; + } + + public static boolean doLoop = false; + + public static void main(String[] args) { + inlineLoop(); + inlineWithinLoop(); + } +} |