blob: a88422fa0d4ae04e72e35073bb4dc3136cc60763 [file] [log] [blame]
# Copyright (C) 2015 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.
.class public LIrreducibleLoop;
.super Ljava/lang/Object;
# Back-edges in the ascii-art graphs are represented with dash '-'.
# Test that we support a simple irreducible loop.
#
# entry
# / \
# / \
# loop_entry \
# / \- \
# exit \- \
# other_loop_entry
#
## CHECK-START: int IrreducibleLoop.simpleLoop(int) dead_code_elimination$initial (before)
## CHECK: irreducible:true
.method public static simpleLoop(I)I
.registers 2
const/16 v0, 42
if-eq v1, v0, :other_loop_entry
:loop_entry
if-ne v1, v0, :exit
add-int v0, v0, v0
:other_loop_entry
add-int v0, v0, v0
goto :loop_entry
:exit
return v0
.end method
# Test that lse does not wrongly optimize loads in irreducible loops. At the
# SSA level, since we create redundant phis for irreducible loop headers, lse
# does not see the relation between the dex register and the phi.
#
# entry
# p1
# / \
# / \
# / \
# / \
# loop_pre_entry \
# set 42 in p1:myField \
# / \
# loop_entry \
# get p1.myField \
# / \- \
# exit \- \
# \- \
# other_loop_entry
# set 30 in p1:myField
#
## CHECK-START: int IrreducibleLoop.lse(int, Main) dead_code_elimination$initial (after)
## CHECK: irreducible:true
#
## CHECK-START: int IrreducibleLoop.lse(int, Main) load_store_elimination (after)
## CHECK: InstanceFieldGet
.method public static lse(ILMain;)I
.registers 4
const/16 v0, 42
const/16 v1, 30
if-eq p0, v0, :other_loop_pre_entry
goto: loop_pre_entry
:loop_pre_entry
iput v0, p1, LMain;->myField:I
:loop_entry
if-ne v1, v0, :exit
:other_loop_entry
iget v0, p1, LMain;->myField:I
if-eq v1, v0, :exit
goto :loop_entry
:exit
return v0
:other_loop_pre_entry
iput v1, p1, LMain;->myField:I
goto :other_loop_entry
.end method
# Check that dce does not apply for irreducible loops.
#
# entry
# / \
# / \
# loop_entry \
# / \- \
# exit \- \
# other_loop_entry
#
## CHECK-START: int IrreducibleLoop.dce(int) dead_code_elimination$initial (before)
## CHECK: irreducible:true
## CHECK-START: int IrreducibleLoop.dce(int) dead_code_elimination$initial (after)
## CHECK: irreducible:true
.method public static dce(I)I
.registers 3
const/16 v0, 42
const/16 v1, 168
if-ne v0, v0, :other_loop_pre_entry
:loop_entry
if-ne v0, v0, :exit
add-int v0, v0, v0
:other_loop_entry
add-int v0, v0, v0
if-eq v0, v1, :exit
goto :loop_entry
:exit
return v0
:other_loop_pre_entry
add-int v0, v0, v0
goto :other_loop_entry
.end method
# Check that a dex register only used in the loop header remains live thanks
# to the (redundant) Phi created at the loop header for it.
#
# entry
# p0
# / \
# / \
# / \
# loop_entry \
# i0 = phi(p0,i1) \
# / \- \
# exit \- \
# other_loop_entry
# i1 = phi(p0, i0)
#
## CHECK-START: int IrreducibleLoop.liveness(int, int) liveness (after)
## CHECK-DAG: <<Arg:i\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopPhiUse:\d+>>)}
## CHECK-DAG: <<LoopPhi:i\d+>> Phi [<<Arg>>,<<PhiInLoop:i\d+>>] liveness:<<ArgLoopPhiUse>> ranges:{[<<ArgLoopPhiUse>>,<<PhiInLoopUse:\d+>>)}
## CHECK-DAG: <<PhiInLoop>> Phi [<<Arg>>,<<LoopPhi>>] liveness:<<PhiInLoopUse>> ranges:{[<<PhiInLoopUse>>,<<BackEdgeLifetimeEnd:\d+>>)}
## CHECK: Return liveness:<<ReturnLiveness:\d+>>
## CHECK-EVAL: <<ReturnLiveness>> == <<BackEdgeLifetimeEnd>> + 2
.method public static liveness(II)I
.registers 2
if-eq p0, p1, :other_loop_entry
:loop_entry
add-int p1, p1, p0
if-ne v0, p1, :exit
:other_loop_entry
add-int p1, p1, p1
goto :loop_entry
:exit
return p1
.end method
# Check that we don't GVN across irreducible loops:
# "const-class 1" in loop_entry should not be GVN with
# "const-class 1" in entry.
#
# entry
# const-class 1
# / \
# / \
# loop_entry \
# const-class 1 \
# / \- \
# exit \- \
# other_loop_entry
# const-class 2
#
## CHECK-START: java.lang.Class IrreducibleLoop.gvn() GVN (before)
## CHECK: LoadClass
## CHECK: LoadClass
## CHECK: LoadClass
## CHECK-NOT: LoadClass
## CHECK-START: java.lang.Class IrreducibleLoop.gvn() GVN (after)
## CHECK: LoadClass
## CHECK: LoadClass
## CHECK: LoadClass
## CHECK-NOT: LoadClass
.method public static gvn()Ljava/lang/Class;
.registers 3
const/4 v2, 0
const-class v0, LMain;
if-ne v0, v2, :other_loop_entry
:loop_entry
const-class v0, LMain;
if-ne v0, v2, :exit
:other_loop_entry
const-class v1, LOther; # LoadClass that can throw
goto :loop_entry
:exit
return-object v0
.end method
# Check that we don't LICM across irreducible loops:
# "add" in loop_entry should not be LICMed.
#
# entry
# / \
# / \
# loop_entry \
# add \
# / \- \
# exit \- \
# other_loop_entry
#
## CHECK-START: int IrreducibleLoop.licm1(int) licm (after)
## CHECK: Add irreducible:true
.method public static licm1(I)I
.registers 3
const/4 v0, 0
if-ne p0, v0, :other_loop_entry
:loop_entry
add-int v0, p0, p0
if-ne v0, p0, :exit
:other_loop_entry
sub-int v1, p0, p0
goto :loop_entry
:exit
sub-int v0, v0, p0
return v0
.end method
# Check that we don't LICM across irreducible loops:
# "const-class" in loop_entry should not be LICMed.
#
# entry
# / \
# / \
# loop_entry \
# const-class \
# / \- \
# exit \- \
# other_loop_entry
#
## CHECK-START: int IrreducibleLoop.licm2(int) licm (after)
## CHECK: LoadClass irreducible:true
.method public static licm2(I)I
.registers 3
const/4 v0, 0
if-ne p0, v0, :other_loop_entry
:loop_entry
const-class v1, LOther; # LoadClass that can throw
if-ne v0, p0, :exit
:other_loop_entry
sub-int v1, p0, p0
goto :loop_entry
:exit
sub-int v0, v0, p0
return v0
.end method
# Check that we don't LICM in a natural loop that contains an irreducible loop:
# "const-class" should not be LICMed.
#
# entry
# |
# loop_entry
# const-class -------------------
# / \ -
# / \ -
# exit loop_body -
# / \ -
# / \ -
# irreducible_loop_entry \ -
# - \ \ -
# - \ \ -
# - irreducible_loop_other_entry
# - |
# - |
# ------ irreducible_loop_back_edge
#
## CHECK-START: int IrreducibleLoop.licm3(int, int, int) licm (after)
## CHECK: LoadClass loop:<<OuterLoop:B\d+>> irreducible:false
## CHECK: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static licm3(III)I
.registers 4
:loop_entry
const-class v0, LOther; # LoadClass that can throw
if-ne p1, p2, :exit
goto :loop_body
:loop_body
if-eq p0, p1, :irreducible_loop_entry
goto :irreducible_loop_other_entry
:irreducible_loop_entry
goto :irreducible_loop_other_entry
:irreducible_loop_other_entry
if-eq p0, p2, :loop_entry
goto :irreducible_loop_back_edge
:irreducible_loop_back_edge
goto :irreducible_loop_entry
:exit
return p0
.end method
# Check a loop within an irreducible loop
#
# entry
# / \
# / \
# irreducible_loop_entry \
# / - \ irreducible_loop_pre_other_entry
# exit - \ /
# - irreducible_loop_body
# - |
# - |
# - loop_within_header
# - / \-
# - / \-
# irreducible_loop_back_edge loop_within_back_edge
#
## CHECK-START: void IrreducibleLoop.analyze1(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:false
.method public static analyze1(I)V
.registers 1
if-eq p0, p0, :irreducible_loop_entry
goto :irreducible_loop_pre_other_entry
:irreducible_loop_entry
if-eq p0, p0, :exit
goto :irreducible_loop_body
:irreducible_loop_body
:loop_within_header
if-eq p0, p0, :irreducible_loop_back_edge
goto :loop_within_back_edge
:loop_within_back_edge
goto :loop_within_header
:irreducible_loop_back_edge
goto :irreducible_loop_entry
:irreducible_loop_pre_other_entry
goto :irreducible_loop_body
:exit
return-void
.end method
# Check than a loop before an irreducible loop is not part of the
# irreducible loop.
#
# entry
# |
# |
# loop_header
# / \-
# / \-
# irreducible_loop_pre_entry loop_body
# / \
# / \
# irreducible_loop_entry \
# / \- irreducible_loop_other_pre_entry
# / \- /
# exit \- /
# irreducible_loop_body
#
## CHECK-START: void IrreducibleLoop.analyze2(int) builder (after)
## CHECK-DAG: Goto outer_loop:none irreducible:false
## CHECK-DAG: Goto outer_loop:none irreducible:true
.method public static analyze2(I)V
.registers 1
:loop_header
if-eq p0, p0, :irreducible_loop_pre_entry
goto :loop_body
:loop_body
goto :loop_header
:irreducible_loop_pre_entry
if-eq p0, p0, :irreducible_loop_other_pre_entry
goto :irreducible_loop_entry
:irreducible_loop_entry
if-eq p0, p0, :exit
goto :irreducible_loop_body
:irreducible_loop_body
goto :irreducible_loop_entry
:irreducible_loop_other_pre_entry
goto :irreducible_loop_body
:exit
return-void
.end method
# Check two irreducible loops, one within another.
#
# entry
# / \
# / \
# loop1_header loop2_header
# - | / -
# - | / -
# - | / -
# - | / -
# - loop2_body -
# - / \ -
# - / \ -
# loop1_body loop2_back_edge
# |
# |
# exit
#
## CHECK-START: void IrreducibleLoop.analyze3(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze3(I)V
.registers 1
if-eq p0, p0, :loop2_header
goto :loop1_header
:loop1_header
goto :loop2_body
:loop2_header
goto :loop2_body
:loop2_body
if-eq p0, p0, :loop2_back_edge
goto :loop1_body
:loop2_back_edge
goto :loop2_header
:loop1_body
if-eq p0, p0, :exit
goto :loop1_header
:exit
return-void
.end method
# Check two irreducible loops, one within another. Almost identical
# to analyze3 except the branches of the first 'if' are swapped, to
# ensure the order at which we find the back edges does not matter.
#
# entry
# / \
# / \
# loop1_header loop2_header
# - | / -
# - | / -
# - | / -
# - | / -
# - loop2_body -
# - / \ -
# - / \ -
# loop1_body loop2_back_edge
# |
# |
# exit
#
## CHECK-START: void IrreducibleLoop.analyze4(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze4(I)V
.registers 1
if-eq p0, p0, :loop1_header
goto :loop2_header
:loop1_header
goto :loop2_body
:loop2_header
goto :loop2_body
:loop2_body
if-eq p0, p0, :loop2_back_edge
goto :loop1_body
:loop2_back_edge
goto :loop2_header
:loop1_body
if-eq p0, p0, :exit
goto :loop1_header
:exit
return-void
.end method
# Check two irreducible loops, one within another. Almost identical
# to analyze3 and analyze4, except that the inner loop exits from the
# back edge, and not the body.
#
# entry
# / \
# / \
# loop1_header loop2_header
# - \ / -
# - \ / -
# - \ / -
# - \ / -
# - loop2_body -
# - | -
# - | -
# - loop2_back_edge ------
# - |
# - |
# ----- loop1_body
# |
# |
# exit
#
## CHECK-START: void IrreducibleLoop.analyze5(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze5(I)V
.registers 1
if-eq p0, p0, :loop1_header
goto :loop2_header
:loop1_header
goto :loop2_body
:loop2_header
goto :loop2_body
:loop2_body
goto :loop2_back_edge
:loop2_back_edge
if-eq p0, p0, :loop2_header
goto :loop1_body
:loop1_body
if-eq p0, p0, :exit
goto :loop1_header
:exit
return-void
.end method
## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (before)
## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
#
## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (after)
## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
.method public static testDoNotInlineIrreducible(I)I
.registers 2
invoke-static {p0}, LIrreducibleLoop;->doNotInlineIrreducible(I)I
move-result v0
return v0
.end method
# Check that doNotInlineIrreducible has a simple irreducible loop
#
# entry
# / \
# / \
# loop_entry \
# / \- \
# try_start\- \
# other_loop_entry
#
## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
## CHECK: irreducible:true
#
# Check that we didn't optimized away the try.
## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
## CHECK: TryBoundary kind:exit
.method public static doNotInlineIrreducible(I)I
.registers 3
const/16 v0, 42
const/16 v1, 21
# Irreducible loop
if-eq v1, v0, :other_loop_entry
:loop_entry
if-ne v1, v0, :try_start
add-int v0, v0, v0
:other_loop_entry
add-int v0, v0, v0
goto :loop_entry
:try_start
# We make this division to make sure the try doesn't get optimized out
div-int v0, v0, p0
return v0
:try_end
.catchall {:try_start .. :try_end} :catch_all
:catch_all
# This is only reachable if the parameter is 0
const/4 v0, -0x1
return v0
.end method