| ; RUN: opt -S -passes=licm < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED |
| ; RUN: opt -S -passes=licm -licm-control-flow-hoisting=1 < %s | FileCheck %s -check-prefixes=CHECK,CHECK-ENABLED |
| ; RUN: opt -S -passes=licm -licm-control-flow-hoisting=0 < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED |
| ; RUN: opt -passes='require<opt-remark-emit>,loop-mssa(licm)' -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED |
| |
| ; RUN: opt -passes='require<opt-remark-emit>,loop-mssa(licm)' -licm-control-flow-hoisting=1 -verify-memoryssa -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-ENABLED |
| ; Enable run below when adding promotion. e.g. "store i32 %phi, ptr %p" is promoted to phi.lcssa. |
| ; opt -passes='require<opt-remark-emit>,loop-mssa(licm)' -licm-control-flow-hoisting=0 -verify-memoryssa -S < %s | FileCheck %s -check-prefixes=CHECK,CHECK-DISABLED |
| |
| |
| ; CHECK-LABEL: @triangle_phi |
| define void @triangle_phi(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK: %add = add i32 %x, 1 |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: phi i32 [ %add, %[[IF_LICM]] ], [ %x, %entry ] |
| ; CHECK-ENABLED: store i32 %phi, ptr %p |
| ; CHECK-ENABLED: %cmp2 = icmp ne i32 %phi, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %add = add i32 %x, 1 |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %x, %loop ] |
| ; CHECK-DISABLED: %cmp2 = icmp ne i32 %phi, 0 |
| then: |
| %phi = phi i32 [ %add, %if ], [ %x, %loop ] |
| store i32 %phi, ptr %p |
| %cmp2 = icmp ne i32 %phi, 0 |
| br i1 %cmp2, label %loop, label %end |
| |
| ; CHECK-LABEL: end: |
| ; CHECK-DISABLED: %[[PHI_LCSSA:.*]] = phi i32 [ %phi, %then ] |
| ; CHECK-DISABLED: store i32 %[[PHI_LCSSA]], ptr %p |
| end: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @diamond_phi |
| define void @diamond_phi(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-DAG: %sub = sub i32 %x, 1 |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]] |
| ; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ] |
| ; CHECK-ENABLED: store i32 %phi, ptr %p |
| ; CHECK-ENABLED: %cmp2 = icmp ne i32 %phi, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %add = add i32 %x, 1 |
| br label %then |
| |
| else: |
| %sub = sub i32 %x, 1 |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| ; CHECK-DISABLED: %cmp2 = icmp ne i32 %phi, 0 |
| then: |
| %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| store i32 %phi, ptr %p |
| %cmp2 = icmp ne i32 %phi, 0 |
| br i1 %cmp2, label %loop, label %end |
| |
| ; CHECK-LABEL: end: |
| ; CHECK-DISABLED: %[[PHI_LCSSA:.*]] = phi i32 [ %phi, %then ] |
| ; CHECK-DISABLED: store i32 %[[PHI_LCSSA]], ptr %p |
| end: |
| ret void |
| } |
| |
| ; TODO: This is currently too complicated for us to be able to hoist the phi. |
| ; CHECK-LABEL: @three_way_phi |
| define void @three_way_phi(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK: %sub = sub i32 %x, 1 |
| ; CHECK: br label %loop |
| |
| entry: |
| br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 %add, 0 |
| br i1 %cmp2, label %if.if, label %then |
| |
| if.if: |
| %sub = sub i32 %x, 1 |
| br label %then |
| |
| then: |
| %phi = phi i32 [ 0, %loop ], [ %add, %if ], [ %sub, %if.if ] |
| store i32 %phi, ptr %p |
| %cmp3 = icmp ne i32 %phi, 0 |
| br i1 %cmp3, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; TODO: This is currently too complicated for us to be able to hoist the phi. |
| ; CHECK-LABEL: @tree_phi |
| define void @tree_phi(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0 |
| ; CHECK-DAG: %sub = sub i32 %x, 1 |
| ; CHECK: br label %loop |
| |
| entry: |
| br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 %add, 0 |
| br i1 %cmp2, label %if.if, label %if.else |
| |
| if.if: |
| br label %then |
| |
| if.else: |
| br label %then |
| |
| else: |
| %sub = sub i32 %x, 1 |
| br label %then |
| |
| then: |
| %phi = phi i32 [ %add, %if.if ], [ 0, %if.else ], [ %sub, %else ] |
| store i32 %phi, ptr %p |
| %cmp3 = icmp ne i32 %phi, 0 |
| br i1 %cmp3, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; TODO: We can hoist the first phi, but not the second. |
| ; CHECK-LABEL: @phi_phi |
| define void @phi_phi(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-DAG: %cmp2 = icmp sgt i32 %add, 0 |
| ; CHECK-DAG: %sub = sub i32 %x, 1 |
| ; CHECK-ENABLED: br i1 %cmp2, label %[[IF_IF_LICM:.*]], label %[[IF_ELSE_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[IF_THEN_LICM]]: |
| ; CHECK-ENABLED: %phi1 = phi i32 [ %add, %[[IF_IF_LICM]] ], [ 0, %[[IF_ELSE_LICM]] ] |
| ; CHECK: br label %loop |
| |
| entry: |
| br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 %add, 0 |
| br i1 %cmp2, label %if.if, label %if.else |
| |
| if.if: |
| br label %if.then |
| |
| if.else: |
| br label %if.then |
| |
| ; CHECK-LABEL: if.then: |
| ; CHECK-DISABLED: %phi1 = phi i32 [ %add, %if.if ], [ 0, %if.else ] |
| if.then: |
| %phi1 = phi i32 [ %add, %if.if ], [ 0, %if.else ] |
| br label %then |
| |
| else: |
| %sub = sub i32 %x, 1 |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK: %phi2 = phi i32 [ %phi1, %if.then ], [ %sub, %else ] |
| then: |
| %phi2 = phi i32 [ %phi1, %if.then ], [ %sub, %else ] |
| store i32 %phi2, ptr %p |
| %cmp3 = icmp ne i32 %phi2, 0 |
| br i1 %cmp3, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; Check that we correctly duplicate empty control flow. |
| ; CHECK-LABEL: @empty_triangle_phi |
| define i8 @empty_triangle_phi(i32 %x, i32 %y) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp eq i32 %x, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ] |
| ; CHECK: %cmp2 = icmp eq i32 %y, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp eq i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %loop ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %loop ] |
| %cmp2 = icmp eq i32 %y, 0 |
| br i1 %cmp2, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; CHECK-LABEL: @empty_diamond_phi |
| define i8 @empty_diamond_phi(i32 %x, i32 %y) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp eq i32 %x, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ] |
| ; CHECK: %cmp2 = icmp eq i32 %y, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp eq i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| br label %then |
| |
| else: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| %cmp2 = icmp eq i32 %y, 0 |
| br i1 %cmp2, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; Check that we correctly handle the case that the first thing we try to hoist is a phi. |
| ; CHECK-LABEL: @empty_triangle_phi_first |
| define i8 @empty_triangle_phi_first(i32 %x, i1 %cond) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ] |
| ; CHECK: %cmp = icmp eq i32 %x, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 %cond, label %if, label %then |
| |
| if: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %loop ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %loop ] |
| %cmp = icmp eq i32 %x, 0 |
| br i1 %cmp, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; CHECK-LABEL: @empty_diamond_phi |
| define i8 @empty_diamond_phi_first(i32 %x, i1 %cond) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ] |
| ; CHECK: %cmp = icmp eq i32 %x, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 %cond, label %if, label %else |
| |
| if: |
| br label %then |
| |
| else: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| %cmp = icmp eq i32 %x, 0 |
| br i1 %cmp, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; CHECK-LABEL: @empty_triangle_phi_first |
| define i8 @empty_triangle_phi_first_empty_loop_head(i32 %x, i1 %cond) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %entry ] |
| ; CHECK: %cmp = icmp eq i32 %x, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| br label %test |
| |
| test: |
| br i1 %cond, label %if, label %then |
| |
| if: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %test ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %test ] |
| %cmp = icmp eq i32 %x, 0 |
| br i1 %cmp, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; CHECK-LABEL: @empty_diamond_phi_first_empty_loop_head |
| define i8 @empty_diamond_phi_first_empty_loop_head(i32 %x, i1 %cond) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-ENABLED: br i1 %cond, label %[[IF_LICM:.*]], label %[[ELSE_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i8 [ 0, %[[IF_LICM]] ], [ 1, %[[ELSE_LICM]] ] |
| ; CHECK: %cmp = icmp eq i32 %x, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| br label %test |
| |
| test: |
| br i1 %cond, label %if, label %else |
| |
| if: |
| br label %then |
| |
| else: |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| then: |
| %phi = phi i8 [ 0, %if ], [ 1, %else ] |
| %cmp = icmp eq i32 %x, 0 |
| br i1 %cmp, label %end, label %loop |
| |
| end: |
| ret i8 %phi |
| } |
| |
| ; The phi is on one branch of a diamond while simultaneously at the end of a |
| ; triangle. Check that we duplicate the triangle and not the diamond. |
| ; CHECK-LABEL: @triangle_diamond |
| define void @triangle_diamond(ptr %ptr, i32 %x, i32 %y) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp ne i32 %x, 0 |
| ; CHECK-DAG: %cmp2 = icmp ne i32 %y, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i32 [ 0, %[[IF_LICM]] ], [ 127, %entry ] |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp ne i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %cmp2 = icmp ne i32 %y, 0 |
| br i1 %cmp2, label %if.then, label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ 0, %if ], [ 127, %loop ] |
| then: |
| %phi = phi i32 [ 0, %if ], [ 127, %loop ] |
| store i32 %phi, ptr %ptr |
| br label %end |
| |
| if.then: |
| br label %end |
| |
| end: |
| br label %loop |
| } |
| |
| ; As the previous, but the end of the diamond is the head of the loop. |
| ; CHECK-LABEL: @triangle_diamond_backedge |
| define void @triangle_diamond_backedge(ptr %ptr, i32 %x, i32 %y) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp ne i32 %x, 0 |
| ; CHECK-DAG: %cmp2 = icmp ne i32 %y, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i32 [ 0, %[[IF_LICM]] ], [ 127, %entry ] |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp ne i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %cmp2 = icmp ne i32 %y, 0 |
| br i1 %cmp2, label %backedge, label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ 0, %if ], [ 127, %loop ] |
| then: |
| %phi = phi i32 [ 0, %if ], [ 127, %loop ] |
| store i32 %phi, ptr %ptr |
| br label %loop |
| |
| backedge: |
| br label %loop |
| } |
| |
| ; TODO: The inner diamonds can be hoisted, but not currently the outer diamond |
| ; CHECK-LABEL: @diamonds_inside_diamond |
| define void @diamonds_inside_diamond(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %cmp3 = icmp slt i32 %x, -10 |
| ; CHECK-ENABLED: br i1 %cmp3, label %[[ELSE_IF_LICM:.*]], label %[[ELSE_ELSE_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[ELSE_IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[ELSE_THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[ELSE_THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[ELSE_THEN_LICM]]: |
| ; CHECK-ENABLED: %phi2 = phi i32 [ 2, %[[ELSE_IF_LICM]] ], [ 3, %[[ELSE_ELSE_LICM]] ] |
| ; CHECK: %cmp2 = icmp sgt i32 %x, 10 |
| ; CHECK-ENABLED: br i1 %cmp2, label %[[IF_IF_LICM:.*]], label %[[IF_ELSE_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[IF_THEN_LICM]]: |
| ; CHECK-ENABLED: %phi1 = phi i32 [ 0, %[[IF_IF_LICM]] ], [ 1, %[[IF_ELSE_LICM]] ] |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %cmp2 = icmp sgt i32 %x, 10 |
| br i1 %cmp2, label %if.if, label %if.else |
| |
| if.if: |
| br label %if.then |
| |
| if.else: |
| br label %if.then |
| |
| ; CHECK-LABEL: if.then: |
| ; CHECK-DISABLED: %phi1 = phi i32 [ 0, %if.if ], [ 1, %if.else ] |
| if.then: |
| %phi1 = phi i32 [ 0, %if.if ], [ 1, %if.else ] |
| br label %then |
| |
| else: |
| %cmp3 = icmp slt i32 %x, -10 |
| br i1 %cmp3, label %else.if, label %else.else |
| |
| else.if: |
| br label %else.then |
| |
| else.else: |
| br label %else.then |
| |
| ; CHECK-LABEL: else.then: |
| ; CHECK-DISABLED: %phi2 = phi i32 [ 2, %else.if ], [ 3, %else.else ] |
| else.then: |
| %phi2 = phi i32 [ 2, %else.if ], [ 3, %else.else ] |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK: %phi3 = phi i32 [ %phi1, %if.then ], [ %phi2, %else.then ] |
| ; CHECK: %cmp4 = icmp ne i32 %phi3, 0 |
| then: |
| %phi3 = phi i32 [ %phi1, %if.then ], [ %phi2, %else.then ] |
| store i32 %phi3, ptr %p |
| %cmp4 = icmp ne i32 %phi3, 0 |
| br i1 %cmp4, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; We can hoist blocks that contain an edge that exits the loop by ignoring that |
| ; edge in the hoisted block. |
| define void @triangle_phi_loopexit(i32 %x, ptr %p) { |
| ; CHECK-DISABLED-LABEL: @triangle_phi_loopexit( |
| ; CHECK-DISABLED-NEXT: entry: |
| ; CHECK-DISABLED-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], 1 |
| ; CHECK-DISABLED-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0 |
| ; CHECK-DISABLED-NEXT: [[CMP2:%.*]] = icmp sgt i32 10, [[ADD]] |
| ; CHECK-DISABLED-NEXT: br label [[LOOP:%.*]] |
| ; CHECK-DISABLED: loop: |
| ; CHECK-DISABLED-NEXT: br i1 [[CMP1]], label [[IF:%.*]], label [[THEN:%.*]] |
| ; CHECK-DISABLED: if: |
| ; CHECK-DISABLED-NEXT: br i1 [[CMP2]], label [[THEN]], label [[END:%.*]] |
| ; CHECK-DISABLED: then: |
| ; CHECK-DISABLED-NEXT: [[PHI:%.*]] = phi i32 [ [[ADD]], [[IF]] ], [ [[X]], [[LOOP]] ] |
| ; CHECK-DISABLED-NEXT: store i32 [[PHI]], ptr [[P:%.*]], align 4 |
| ; CHECK-DISABLED-NEXT: [[CMP3:%.*]] = icmp ne i32 [[PHI]], 0 |
| ; CHECK-DISABLED-NEXT: br i1 [[CMP3]], label [[LOOP]], label [[END]] |
| ; CHECK-DISABLED: end: |
| ; CHECK-DISABLED-NEXT: ret void |
| ; |
| ; CHECK-ENABLED-LABEL: @triangle_phi_loopexit( |
| ; CHECK-ENABLED-NEXT: entry: |
| ; CHECK-ENABLED-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], 1 |
| ; CHECK-ENABLED-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0 |
| ; CHECK-ENABLED-NEXT: [[CMP2:%.*]] = icmp sgt i32 10, [[ADD]] |
| ; CHECK-ENABLED-NEXT: br i1 [[CMP1]], label [[IF_LICM:%.*]], label [[THEN_LICM:%.*]] |
| ; CHECK-ENABLED: if.licm: |
| ; CHECK-ENABLED-NEXT: br label [[THEN_LICM]] |
| ; CHECK-ENABLED: then.licm: |
| ; CHECK-ENABLED-NEXT: [[PHI:%.*]] = phi i32 [ [[ADD]], [[IF_LICM]] ], [ [[X]], [[ENTRY:%.*]] ] |
| ; CHECK-ENABLED-NEXT: [[CMP3:%.*]] = icmp ne i32 [[PHI]], 0 |
| ; CHECK-ENABLED-NEXT: br label [[LOOP:%.*]] |
| ; CHECK-ENABLED: loop: |
| ; CHECK-ENABLED-NEXT: br i1 [[CMP1]], label [[IF:%.*]], label [[THEN:%.*]] |
| ; CHECK-ENABLED: if: |
| ; CHECK-ENABLED-NEXT: br i1 [[CMP2]], label [[THEN]], label [[END:%.*]] |
| ; CHECK-ENABLED: then: |
| ; CHECK-ENABLED-NEXT: store i32 [[PHI]], ptr [[P:%.*]], align 4 |
| ; CHECK-ENABLED-NEXT: br i1 [[CMP3]], label [[LOOP]], label [[END]] |
| ; CHECK-ENABLED: end: |
| ; CHECK-ENABLED-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 10, %add |
| br i1 %cmp2, label %then, label %end |
| |
| then: |
| %phi = phi i32 [ %add, %if ], [ %x, %loop ] |
| store i32 %phi, ptr %p |
| %cmp3 = icmp ne i32 %phi, 0 |
| br i1 %cmp3, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @diamond_phi_oneloopexit |
| define void @diamond_phi_oneloopexit(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %cmp2 = icmp sgt i32 10, %add |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-DAG: %sub = sub i32 %x, 1 |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]] |
| ; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ] |
| ; CHECK-ENABLED: %cmp3 = icmp ne i32 %phi, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 10, %add |
| br i1 %cmp2, label %then, label %end |
| |
| else: |
| %sub = sub i32 %x, 1 |
| br label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| then: |
| %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| store i32 %phi, ptr %p |
| %cmp3 = icmp ne i32 %phi, 0 |
| br i1 %cmp3, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @diamond_phi_twoloopexit |
| define void @diamond_phi_twoloopexit(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %sub = sub i32 %x, 1 |
| ; CHECK-DAG: %add = add i32 %x, 1 |
| ; CHECK-DAG: %cmp1 = icmp sgt i32 %x, 0 |
| ; CHECK-DAG: %cmp2 = icmp sgt i32 10, %add |
| ; CHECK-DAG: %cmp3 = icmp sgt i32 10, %sub |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]] |
| ; CHECK-ENABLED: %phi = phi i32 [ %add, %[[IF_LICM]] ], [ %sub, %[[ELSE_LICM]] ] |
| ; CHECK-ENABLED: %cmp4 = icmp ne i32 %phi, 0 |
| ; CHECK: br label %loop |
| |
| loop: |
| %cmp1 = icmp sgt i32 %x, 0 |
| br i1 %cmp1, label %if, label %else |
| |
| if: |
| %add = add i32 %x, 1 |
| %cmp2 = icmp sgt i32 10, %add |
| br i1 %cmp2, label %then, label %end |
| |
| else: |
| %sub = sub i32 %x, 1 |
| %cmp3 = icmp sgt i32 10, %sub |
| br i1 %cmp3, label %then, label %end |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| ; CHECK-DISABLED: %cmp4 = icmp ne i32 %phi, 0 |
| then: |
| %phi = phi i32 [ %add, %if ], [ %sub, %else ] |
| store i32 %phi, ptr %p |
| %cmp4 = icmp ne i32 %phi, 0 |
| br i1 %cmp4, label %loop, label %end |
| |
| end: |
| ret void |
| } |
| |
| ; The store cannot be hoisted, so add and shr cannot be hoisted into a |
| ; conditional block. |
| ; CHECK-LABEL: @conditional_use |
| define void @conditional_use(i32 %x, ptr %p) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cond = icmp ugt i32 %x, 0 |
| ; CHECK-DAG: %add = add i32 %x, 5 |
| ; CHECK-DAG: %shr = ashr i32 %add, 1 |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| loop: |
| %cond = icmp ugt i32 %x, 0 |
| br i1 %cond, label %if, label %else |
| |
| ; CHECK-LABEL: if: |
| ; CHECK: store i32 %shr, ptr %p, align 4 |
| if: |
| %add = add i32 %x, 5 |
| %shr = ashr i32 %add, 1 |
| store i32 %shr, ptr %p, align 4 |
| br label %then |
| |
| else: |
| br label %then |
| |
| then: |
| br label %loop |
| } |
| |
| ; A diamond with two triangles on the left and one on the right. This test is |
| ; to check that we have a unique loop preheader when we hoist the store (and so |
| ; don't fail an assertion). |
| ; CHECK-LABEL: @triangles_in_diamond |
| define void @triangles_in_diamond(ptr %ptr) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: store i32 0, ptr %ptr, align 4 |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| loop: |
| br i1 undef, label %left_triangle_1, label %right_triangle |
| |
| left_triangle_1: |
| br i1 undef, label %left_triangle_1_if, label %left_triangle_2 |
| |
| left_triangle_1_if: |
| br label %left_triangle_2 |
| |
| left_triangle_2: |
| br i1 undef, label %left_triangle_2_if, label %left_triangle_2_then |
| |
| left_triangle_2_if: |
| br label %left_triangle_2_then |
| |
| left_triangle_2_then: |
| br label %loop.end |
| |
| right_triangle: |
| br i1 undef, label %right_triangle.if, label %right_triangle.then |
| |
| right_triangle.if: |
| br label %right_triangle.then |
| |
| right_triangle.then: |
| br label %loop.end |
| |
| loop.end: |
| store i32 0, ptr %ptr, align 4 |
| br label %loop |
| } |
| |
| ; %cmp dominates its used after being hoisted, but not after %brmerge is rehoisted |
| ; CHECK-LABEL: @rehoist |
| define void @rehoist(ptr %this, i32 %x) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %sub = add nsw i32 %x, -1 |
| ; CHECK-DAG: %cmp = icmp eq i32 0, %sub |
| ; CHECK-DAG: %brmerge = or i1 %cmp, true |
| entry: |
| %sub = add nsw i32 %x, -1 |
| br label %loop |
| |
| loop: |
| br i1 undef, label %if1, label %else1 |
| |
| if1: |
| call void %this(ptr %this) |
| br label %then1 |
| |
| else1: |
| br label %then1 |
| |
| then1: |
| %cmp = icmp eq i32 0, %sub |
| br i1 %cmp, label %end, label %else2 |
| |
| else2: |
| %brmerge = or i1 %cmp, true |
| br i1 %brmerge, label %if3, label %end |
| |
| if3: |
| br label %end |
| |
| end: |
| br label %loop |
| } |
| |
| ; A test case that uses empty blocks in a way that can cause control flow |
| ; hoisting to get confused. |
| ; CHECK-LABEL: @empty_blocks_multiple_conditional_branches |
| define void @empty_blocks_multiple_conditional_branches(float %arg, ptr %ptr) { |
| ; CHECK-LABEL: entry |
| ; CHECK-DAG: %div1 = fmul float %arg, 4.000000e+00 |
| ; CHECK-DAG: %div2 = fmul float %arg, 2.000000e+00 |
| entry: |
| br label %loop |
| |
| ; The exact path to the phi isn't checked here, because it depends on whether |
| ; cond2 or cond3 is hoisted first |
| ; CHECK-ENABLED: %phi = phi float [ 0.000000e+00, %{{.*}} ], [ %div1, %{{.*}} ] |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 undef, label %backedge2, label %cond1 |
| |
| cond1: |
| br i1 undef, label %cond1.if, label %cond1.else |
| |
| cond1.else: |
| br label %cond3 |
| |
| cond1.if: |
| br label %cond1.if.next |
| |
| cond1.if.next: |
| br label %cond2 |
| |
| cond2: |
| %div1 = fmul float %arg, 4.000000e+00 |
| br i1 undef, label %cond2.if, label %cond2.then |
| |
| cond2.if: |
| br label %cond2.then |
| |
| ; CHECK-LABEL: cond2.then: |
| ; CHECK-DISABLED: %phi = phi float [ 0.000000e+00, %cond2 ], [ %div1, %cond2.if ] |
| cond2.then: |
| %phi = phi float [ 0.000000e+00, %cond2 ], [ %div1, %cond2.if ] |
| store float %phi, ptr %ptr |
| br label %backedge2 |
| |
| cond3: |
| br i1 undef, label %cond3.then, label %cond3.if |
| |
| cond3.if: |
| %div2 = fmul float %arg, 2.000000e+00 |
| store float %div2, ptr %ptr |
| br label %cond3.then |
| |
| cond3.then: |
| br label %loop |
| |
| backedge2: |
| br label %loop |
| } |
| |
| ; We can't do much here, so mainly just check that we don't crash. |
| ; CHECK-LABEL: @many_path_phi |
| define void @many_path_phi(ptr %ptr1, ptr %ptr2) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %gep3 = getelementptr inbounds i32, ptr %ptr2, i32 2 |
| ; CHECK-DAG: %gep2 = getelementptr inbounds i32, ptr %ptr2, i32 2 |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| loop: |
| %phi1 = phi i32 [ 0, %entry ], [ %phi2, %end ] |
| %cmp1 = icmp ugt i32 %phi1, 3 |
| br i1 %cmp1, label %cond2, label %cond1 |
| |
| cond1: |
| br i1 undef, label %end, label %cond1.else |
| |
| cond1.else: |
| %gep2 = getelementptr inbounds i32, ptr %ptr2, i32 2 |
| %val2 = load i32, ptr %gep2, align 4 |
| %cmp2 = icmp eq i32 %val2, 13 |
| br i1 %cmp2, label %cond1.end, label %end |
| |
| cond1.end: |
| br label %end |
| |
| cond2: |
| br i1 undef, label %end, label %cond2.else |
| |
| cond2.else: |
| %gep3 = getelementptr inbounds i32, ptr %ptr2, i32 2 |
| %val3 = load i32, ptr %gep3, align 4 |
| %cmp3 = icmp eq i32 %val3, 13 |
| br i1 %cmp3, label %cond2.end, label %end |
| |
| cond2.end: |
| br label %end |
| |
| end: |
| %phi2 = phi i32 [ 1, %cond1 ], [ 2, %cond1.else ], [ 3, %cond1.end ], [ 4, %cond2 ], [ 5, %cond2.else ], [ 6, %cond2.end ] |
| br label %loop |
| } |
| |
| ; Check that we correctly handle the hoisting of %gep when theres a critical |
| ; edge that branches to the preheader. |
| ; CHECK-LABEL: @crit_edge |
| define void @crit_edge(ptr %ptr, i32 %idx, i1 %cond1, i1 %cond2) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %gep = getelementptr inbounds i32, ptr %ptr, i32 %idx |
| ; CHECK: br label %preheader |
| entry: |
| br label %preheader |
| |
| preheader: |
| br label %loop |
| |
| loop: |
| br i1 %cond1, label %then, label %if |
| |
| if: |
| %gep = getelementptr inbounds i32, ptr %ptr, i32 %idx |
| %val = load i32, ptr %gep |
| br label %then |
| |
| then: |
| %phi = phi i32 [ %val, %if ], [ 0, %loop ] |
| store i32 %phi, ptr %ptr |
| br i1 %cond2, label %loop, label %crit_edge |
| |
| crit_edge: |
| br label %preheader |
| } |
| |
| ; Check that the conditional sub is correctly hoisted from the inner loop to the |
| ; preheader of the outer loop. |
| ; CHECK-LABEL: @hoist_from_innermost_loop |
| define void @hoist_from_innermost_loop(i32 %nx, ptr %ptr) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %sub = sub nsw i32 0, %nx |
| ; CHECK: br label %outer_loop |
| entry: |
| br label %outer_loop |
| |
| outer_loop: |
| br label %middle_loop |
| |
| middle_loop: |
| br label %inner_loop |
| |
| inner_loop: |
| br i1 undef, label %inner_loop_end, label %if |
| |
| if: |
| %sub = sub nsw i32 0, %nx |
| store i32 %sub, ptr %ptr, align 4 |
| br label %inner_loop_end |
| |
| inner_loop_end: |
| br i1 undef, label %inner_loop, label %middle_loop_end |
| |
| middle_loop_end: |
| br i1 undef, label %middle_loop, label %outer_loop_end |
| |
| outer_loop_end: |
| br label %outer_loop |
| } |
| |
| ; We have a diamond starting from %if, but %if.if is also reachable from %loop, |
| ; so %gep should not be conditionally hoisted. |
| ; CHECK-LABEL: @diamond_with_extra_in_edge |
| define void @diamond_with_extra_in_edge(ptr %ptr1, ptr %ptr2, i32 %arg) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp2 = icmp ne i32 0, %arg |
| ; CHECK-DAG: %gep = getelementptr i32, ptr %ptr1, i32 4 |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| loop: |
| %phi1 = phi i32 [ 0, %entry ], [ %phi2, %then ] |
| %cmp1 = icmp ugt i32 16, %phi1 |
| br i1 %cmp1, label %if, label %if.if |
| |
| if: |
| %cmp2 = icmp ne i32 0, %arg |
| br i1 %cmp2, label %if.if, label %if.else |
| |
| if.if: |
| %gep = getelementptr i32, ptr %ptr1, i32 4 |
| %val = load i32, ptr %gep, align 4 |
| br label %then |
| |
| if.else: |
| br label %then |
| |
| then: |
| %phi2 = phi i32 [ %val, %if.if ], [ %phi1, %if.else ] |
| store i32 %phi2, ptr %ptr2, align 4 |
| br label %loop |
| } |
| |
| ; %loop/%if/%then form a triangle, but %loop/%if/%then/%end also form a diamond. |
| ; The triangle should be picked for conditional hoisting. |
| ; CHECK-LABEL: @both_triangle_and_diamond |
| define void @both_triangle_and_diamond(ptr %ptr1, ptr %ptr2, i32 %arg) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp ne i32 0, %arg |
| ; CHECK-DAG: %gep = getelementptr i32, ptr %ptr1, i32 4 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_LICM:.*]], label %[[THEN_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_LICM]]: |
| ; CHECK-ENABLED: br label %[[THEN_LICM]] |
| |
| ; CHECK-ENABLED: [[THEN_LICM]]: |
| ; CHECK-ENABLED: %phi2 = phi i32 [ 0, %[[IF_LICM]] ], [ 1, %entry ] |
| ; CHECK: br label %loop |
| |
| loop: |
| %phi1 = phi i32 [ 0, %entry ], [ %phi3, %end ] |
| %cmp1 = icmp ne i32 0, %arg |
| br i1 %cmp1, label %if, label %then |
| |
| if: |
| %gep = getelementptr i32, ptr %ptr1, i32 4 |
| %val = load i32, ptr %gep, align 4 |
| %cmp2 = icmp ugt i32 16, %phi1 |
| br i1 %cmp2, label %end, label %then |
| |
| ; CHECK-LABEL: then: |
| ; CHECK-DISABLED: %phi2 = phi i32 [ 0, %if ], [ 1, %loop ] |
| then: |
| %phi2 = phi i32 [ 0, %if ], [ 1, %loop ] |
| br label %end |
| |
| end: |
| %phi3 = phi i32 [ %phi2, %then ], [ %val, %if ] |
| store i32 %phi3, ptr %ptr2, align 4 |
| br label %loop |
| } |
| |
| ; We shouldn't duplicate the branch at the end of %loop and should instead hoist |
| ; %val to %entry. |
| ; CHECK-LABEL: @same_destination_branch |
| define i32 @same_destination_branch(i32 %arg1, i32 %arg2) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp ne i32 %arg2, 0 |
| ; CHECK-DAG: %val = add i32 %arg1, 1 |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| ; CHECK-LABEL: loop: |
| ; CHECK: %phi = phi i32 [ 0, %entry ], [ %add, %then ] |
| loop: |
| %phi = phi i32 [ 0, %entry ], [ %add, %then ] |
| %add = add i32 %phi, 1 |
| %cmp1 = icmp ne i32 %arg2, 0 |
| br i1 %cmp1, label %if, label %if |
| |
| if: |
| %val = add i32 %arg1, 1 |
| br label %then |
| |
| then: |
| %cmp2 = icmp ne i32 %val, %phi |
| br i1 %cmp2, label %loop, label %end |
| |
| end: |
| ret i32 %val |
| } |
| |
| ; Diamond-like control flow but the left/right blocks actually have the same |
| ; destinations. |
| ; TODO: We could potentially hoist all of phi2-4, but currently only hoist phi2. |
| ; CHECK-LABEL: @diamond_like_same_destinations |
| define i32 @diamond_like_same_destinations(i32 %arg1, i32 %arg2) { |
| ; CHECK-LABEL: entry: |
| ; CHECK-DAG: %cmp1 = icmp ne i32 %arg1, 0 |
| ; CHECK-DAG: %cmp2 = icmp ugt i32 %arg2, 1 |
| ; CHECK-DAG: %cmp3 = icmp ugt i32 %arg2, 2 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[LEFT1_LICM:.*]], label %[[RIGHT1_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[LEFT1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LEFT2_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[RIGHT1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LEFT2_LICM]] |
| |
| ; CHECK-ENABLED: [[LEFT2_LICM]]: |
| ; CHECK-ENABLED: %phi2 = phi i32 [ 0, %[[LEFT1_LICM]] ], [ 1, %[[RIGHT1_LICM]] ] |
| ; CHECK: br label %loop |
| |
| loop: |
| %phi1 = phi i32 [ 0, %entry ], [ %add, %loopend ] |
| %add = add i32 %phi1, 1 |
| %cmp1 = icmp ne i32 %arg1, 0 |
| br i1 %cmp1, label %left1, label %right1 |
| |
| left1: |
| %cmp2 = icmp ugt i32 %arg2, 1 |
| br i1 %cmp2, label %left2, label %right2 |
| |
| right1: |
| %cmp3 = icmp ugt i32 %arg2, 2 |
| br i1 %cmp3, label %left2, label %right2 |
| |
| ; CHECK-LABEL: left2: |
| ; CHECK-DISABLED: %phi2 = phi i32 [ 0, %left1 ], [ 1, %right1 ] |
| left2: |
| %phi2 = phi i32 [ 0, %left1 ], [ 1, %right1 ] |
| br label %loopend |
| |
| ; CHECK-LABEL: right2: |
| ; CHECK: %phi3 = phi i32 [ 2, %left1 ], [ 3, %right1 ] |
| right2: |
| %phi3 = phi i32 [ 2, %left1 ], [ 3, %right1 ] |
| br label %loopend |
| |
| ; CHECK-LABEL: loopend: |
| ; CHECK: %phi4 = phi i32 [ %phi2, %left2 ], [ %phi3, %right2 ] |
| loopend: |
| %phi4 = phi i32 [ %phi2, %left2 ], [ %phi3, %right2 ] |
| %cmp4 = icmp ne i32 %phi1, 32 |
| br i1 %cmp4, label %loop, label %end |
| |
| end: |
| ret i32 %phi4 |
| } |
| |
| ; A phi with multiple incoming values for the same block due to a branch with |
| ; two destinations that are actually the same. We can't hoist this. |
| ; TODO: This could be hoisted by erasing one of the incoming values. |
| ; CHECK-LABEL: @phi_multiple_values_same_block |
| define i32 @phi_multiple_values_same_block(i32 %arg) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp = icmp sgt i32 %arg, 4 |
| ; CHECK-NOT: phi |
| ; CHECK: br label %loop |
| entry: |
| br label %loop |
| |
| loop: |
| %cmp = icmp sgt i32 %arg, 4 |
| br i1 %cmp, label %if, label %then |
| |
| if: |
| br i1 undef, label %then, label %then |
| |
| then: |
| %phi = phi i32 [ %arg, %loop ], [ 1, %if ], [ 1, %if ] |
| br i1 undef, label %exit, label %loop |
| |
| exit: |
| ret i32 %phi |
| } |
| |
| ; %phi is conditionally used in %d, and the store that %d is used in cannot be |
| ; hoisted. This means that we have to rehoist %d, but have to make sure to |
| ; rehoist it after %phi. |
| ; CHECK-LABEL: @phi_conditional_use |
| define i64 @phi_conditional_use(i32 %f, ptr %g) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp eq i32 %f, 1 |
| ; CHECK: %cmp2 = icmp eq i32 %f, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_END_LICM:.*]], label %[[IF_THEN_LICM:.*]] |
| entry: |
| %cmp1 = icmp eq i32 %f, 1 |
| %cmp2 = icmp eq i32 %f, 0 |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_THEN_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_END_LICM]] |
| |
| ; CHECK-ENABLED: [[IF_END_LICM]]: |
| ; CHECK-ENABLED: %phi = phi i64 [ 0, %entry ], [ 1, %[[IF_THEN_LICM]] ] |
| ; CHECK-ENABLED: %d = getelementptr inbounds i32, ptr %g, i64 %phi |
| ; CHECK-ENABLED: i1 %cmp2, label %[[LOOP_BACKEDGE_LICM:.*]], label %[[IF_THEN2_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_THEN2_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]] |
| |
| ; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]: |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 %cmp1, label %if.end, label %if.then |
| |
| if.then: |
| br label %if.end |
| |
| ; CHECK-LABEL: if.end: |
| ; CHECK-DISABLED: %phi = phi i64 [ 0, %loop ], [ 1, %if.then ] |
| if.end: |
| %phi = phi i64 [ 0, %loop ], [ 1, %if.then ] |
| br i1 %cmp2, label %loop.backedge, label %if.then2 |
| |
| ; CHECK-LABEL: if.then2: |
| ; CHECK-DISABLED: %d = getelementptr inbounds i32, ptr %g, i64 %phi |
| if.then2: |
| %d = getelementptr inbounds i32, ptr %g, i64 %phi |
| store i32 1, ptr %d, align 4 |
| br label %loop.backedge |
| |
| loop.backedge: |
| br label %loop |
| } |
| |
| ; As above, but we have two such phis |
| ; CHECK-LABEL: @phi_conditional_use_twice |
| define i64 @phi_conditional_use_twice(i32 %f, ptr %g) { |
| ; CHECK-LABEL: entry: |
| ; CHECK: %cmp1 = icmp eq i32 %f, 1 |
| ; CHECK: %cmp2 = icmp eq i32 %f, 0 |
| ; CHECK-ENABLED: br i1 %cmp1, label %[[IF_END_LICM:.*]], label %[[IF_THEN_LICM:.*]] |
| entry: |
| %cmp1 = icmp eq i32 %f, 1 |
| %cmp2 = icmp eq i32 %f, 0 |
| %cmp3 = icmp sgt i32 %f, 0 |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF_THEN_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_END_LICM]] |
| |
| ; CHECK-ENABLED: [[IF_END_LICM]]: |
| ; CHECK-ENABLED: %phi1 = phi i64 [ 0, %entry ], [ 1, %[[IF_THEN_LICM]] ] |
| ; CHECK-ENABLED: %d = getelementptr inbounds i32, ptr %g, i64 %phi1 |
| ; CHECK-ENABLED: i1 %cmp2, label %[[IF_END2_LICM:.*]], label %[[IF_THEN2_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_THEN2_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF_END2_LICM]] |
| |
| ; CHECK-ENABLED: [[IF_END2_LICM]]: |
| ; CHECK-ENABLED: %phi2 = phi i64 [ 2, %[[IF_END_LICM]] ], [ 3, %[[IF_THEN2_LICM]] ] |
| ; CHECK-ENABLED: %e = getelementptr inbounds i32, ptr %g, i64 %phi2 |
| ; CHECK-ENABLED: i1 %cmp3, label %[[LOOP_BACKEDGE_LICM:.*]], label %[[IF_THEN3_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF_THEN3_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]] |
| |
| ; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]: |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 %cmp1, label %if.end, label %if.then |
| |
| if.then: |
| br label %if.end |
| |
| ; CHECK-LABEL: if.end: |
| ; CHECK-DISABLED: %phi1 = phi i64 [ 0, %loop ], [ 1, %if.then ] |
| if.end: |
| %phi1 = phi i64 [ 0, %loop ], [ 1, %if.then ] |
| br i1 %cmp2, label %if.end2, label %if.then2 |
| |
| ; CHECK-LABEL: if.then2: |
| ; CHECK-DISABLED: %d = getelementptr inbounds i32, ptr %g, i64 %phi1 |
| if.then2: |
| %d = getelementptr inbounds i32, ptr %g, i64 %phi1 |
| store i32 1, ptr %d, align 4 |
| br label %if.end2 |
| |
| ; CHECK-LABEL: if.end2: |
| ; CHECK-DISABLED: %phi2 = phi i64 [ 2, %if.end ], [ 3, %if.then2 ] |
| if.end2: |
| %phi2 = phi i64 [ 2, %if.end ], [ 3, %if.then2 ] |
| br i1 %cmp3, label %loop.backedge, label %if.then3 |
| |
| ; CHECK-LABEL: if.then3: |
| ; CHECK-DISABLED: %e = getelementptr inbounds i32, ptr %g, i64 %phi2 |
| if.then3: |
| %e = getelementptr inbounds i32, ptr %g, i64 %phi2 |
| store i32 1, ptr %e, align 4 |
| br label %loop.backedge |
| |
| loop.backedge: |
| br label %loop |
| } |
| |
| ; The order that we hoist instructions from the loop is different to the textual |
| ; order in the function. Check that we can rehoist this correctly. |
| ; CHECK-LABEL: @rehoist_wrong_order_1 |
| define void @rehoist_wrong_order_1(ptr %ptr) { |
| ; CHECK-LABEL: entry |
| ; CHECK-DAG: %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| ; CHECK-DAG: %gep3 = getelementptr inbounds i32, ptr %ptr, i64 3 |
| ; CHECK-DAG: %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| ; CHECK-ENABLED: br i1 undef, label %[[IF1_LICM:.*]], label %[[ELSE1_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]] |
| |
| ; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]: |
| ; CHECK-ENABLED: br i1 undef, label %[[IF3_LICM:.*]], label %[[END_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF3_LICM]]: |
| ; CHECK-ENABLED: br label %[[END_LICM]] |
| |
| ; CHECK-ENABLED: [[END_LICM]]: |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 undef, label %if1, label %else1 |
| |
| if1: |
| %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| store i32 0, ptr %gep1, align 4 |
| br label %loop.backedge |
| |
| else1: |
| %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| store i32 0, ptr %gep2, align 4 |
| br i1 undef, label %if2, label %loop.backedge |
| |
| if2: |
| br i1 undef, label %if3, label %end |
| |
| if3: |
| %gep3 = getelementptr inbounds i32, ptr %ptr, i64 3 |
| store i32 0, ptr %gep3, align 4 |
| br label %end |
| |
| end: |
| br label %loop.backedge |
| |
| loop.backedge: |
| br label %loop |
| |
| } |
| |
| ; CHECK-LABEL: @rehoist_wrong_order_2 |
| define void @rehoist_wrong_order_2(ptr %ptr) { |
| ; CHECK-LABEL: entry |
| ; CHECK-DAG: %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| ; CHECK-DAG: %gep3 = getelementptr inbounds i32, ptr %gep2, i64 3 |
| ; CHECK-DAG: %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| ; CHECK-ENABLED: br i1 undef, label %[[IF1_LICM:.*]], label %[[ELSE1_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE1_LICM]]: |
| ; CHECK-ENABLED: br label %[[LOOP_BACKEDGE_LICM]] |
| |
| ; CHECK-ENABLED: [[LOOP_BACKEDGE_LICM]]: |
| ; CHECK-ENABLED: br i1 undef, label %[[IF3_LICM:.*]], label %[[END_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF3_LICM]]: |
| ; CHECK-ENABLED: br label %[[END_LICM]] |
| |
| ; CHECK-ENABLED: [[END_LICM]]: |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 undef, label %if1, label %else1 |
| |
| if1: |
| %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| store i32 0, ptr %gep1, align 4 |
| br label %loop.backedge |
| |
| else1: |
| %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| store i32 0, ptr %gep2, align 4 |
| br i1 undef, label %if2, label %loop.backedge |
| |
| if2: |
| br i1 undef, label %if3, label %end |
| |
| if3: |
| %gep3 = getelementptr inbounds i32, ptr %gep2, i64 3 |
| store i32 0, ptr %gep3, align 4 |
| br label %end |
| |
| end: |
| br label %loop.backedge |
| |
| loop.backedge: |
| br label %loop |
| } |
| |
| ; CHECK-LABEL: @rehoist_wrong_order_3 |
| define void @rehoist_wrong_order_3(ptr %ptr) { |
| ; CHECK-LABEL: entry |
| ; CHECK-DAG: %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| ; CHECK-DAG: %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| ; CHECK-ENABLED: br i1 undef, label %[[IF1_LICM:.*]], label %[[ELSE1_LICM:.*]] |
| entry: |
| br label %loop |
| |
| ; CHECK-ENABLED: [[IF1_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF2_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[ELSE1_LICM]]: |
| ; CHECK-ENABLED: br label %[[IF2_LICM]] |
| |
| ; CHECK-ENABLED: [[IF2_LICM]]: |
| ; CHECK-ENABLED: %phi = phi ptr [ %gep1, %[[IF1_LICM]] ], [ %gep2, %[[ELSE1_LICM]] ] |
| ; CHECK-ENABLED: %gep3 = getelementptr inbounds i32, ptr %phi, i64 3 |
| ; CHECK-ENABLED: br i1 undef, label %[[IF3_LICM:.*]], label %[[END_LICM:.*]] |
| |
| ; CHECK-ENABLED: [[IF3_LICM]]: |
| ; CHECK-ENABLED: br label %[[END_LICM]] |
| |
| ; CHECK-ENABLED: [[END_LICM]]: |
| ; CHECK: br label %loop |
| |
| loop: |
| br i1 undef, label %if1, label %else1 |
| |
| if1: |
| %gep1 = getelementptr inbounds i32, ptr %ptr, i64 1 |
| store i32 0, ptr %gep1, align 4 |
| br label %if2 |
| |
| else1: |
| %gep2 = getelementptr inbounds i32, ptr %ptr, i64 2 |
| store i32 0, ptr %gep2, align 4 |
| br i1 undef, label %if2, label %loop.backedge |
| |
| if2: |
| %phi = phi ptr [ %gep1, %if1 ], [ %gep2, %else1 ] |
| br i1 undef, label %if3, label %end |
| |
| if3: |
| %gep3 = getelementptr inbounds i32, ptr %phi, i64 3 |
| store i32 0, ptr %gep3, align 4 |
| br label %end |
| |
| end: |
| br label %loop.backedge |
| |
| loop.backedge: |
| br label %loop |
| } |