| ; RUN: opt -S -jump-threading < %s | FileCheck %s |
| |
| declare void @foo() |
| declare void @bar() |
| declare void @baz() |
| declare void @quux() |
| |
| |
| ; Jump threading of branch with select as condition. |
| ; Mostly theoretical since instruction combining simplifies all selects of |
| ; booleans where at least one operand is true/false/undef. |
| |
| ; CHECK: @test_br |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br i1 %cond, label %L1, |
| define void @test_br(i1 %cond, i1 %value) nounwind { |
| entry: |
| br i1 %cond, label %L0, label %L3 |
| L0: |
| %expr = select i1 %cond, i1 true, i1 %value |
| br i1 %expr, label %L1, label %L2 |
| |
| L1: |
| call void @foo() |
| ret void |
| L2: |
| call void @bar() |
| ret void |
| L3: |
| call void @baz() |
| br label %L0 |
| } |
| |
| |
| ; Jump threading of switch with select as condition. |
| |
| ; CHECK: @test_switch |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br i1 %cond, label %L1, |
| define void @test_switch(i1 %cond, i8 %value) nounwind { |
| entry: |
| br i1 %cond, label %L0, label %L4 |
| L0: |
| %expr = select i1 %cond, i8 1, i8 %value |
| switch i8 %expr, label %L3 [i8 1, label %L1 i8 2, label %L2] |
| |
| L1: |
| call void @foo() |
| ret void |
| L2: |
| call void @bar() |
| ret void |
| L3: |
| call void @baz() |
| ret void |
| L4: |
| call void @quux() |
| br label %L0 |
| } |
| |
| ; Make sure the blocks in the indirectbr test aren't trivially removable as |
| ; successors by taking their addresses. |
| @anchor = constant [3 x i8*] [ |
| i8* blockaddress(@test_indirectbr, %L1), |
| i8* blockaddress(@test_indirectbr, %L2), |
| i8* blockaddress(@test_indirectbr, %L3) |
| ] |
| |
| |
| ; Jump threading of indirectbr with select as address. |
| |
| ; CHECK: @test_indirectbr |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br i1 %cond, label %L1, label %L3 |
| define void @test_indirectbr(i1 %cond, i8* %address) nounwind { |
| entry: |
| br i1 %cond, label %L0, label %L3 |
| L0: |
| %indirect.goto.dest = select i1 %cond, i8* blockaddress(@test_indirectbr, %L1), i8* %address |
| indirectbr i8* %indirect.goto.dest, [label %L1, label %L2, label %L3] |
| |
| L1: |
| call void @foo() |
| ret void |
| L2: |
| call void @bar() |
| ret void |
| L3: |
| call void @baz() |
| ret void |
| } |
| |
| |
| ; A more complicated case: the condition is a select based on a comparison. |
| |
| ; CHECK: @test_switch_cmp |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br i1 %cond, label %L0, label %[[THREADED:[A-Za-z.0-9]+]] |
| ; CHECK: [[THREADED]]: |
| ; CHECK-NEXT: call void @quux |
| ; CHECK-NEXT: br label %L1 |
| define void @test_switch_cmp(i1 %cond, i32 %val, i8 %value) nounwind { |
| entry: |
| br i1 %cond, label %L0, label %L4 |
| L0: |
| %val.phi = phi i32 [%val, %entry], [-1, %L4] |
| %cmp = icmp slt i32 %val.phi, 0 |
| %expr = select i1 %cmp, i8 1, i8 %value |
| switch i8 %expr, label %L3 [i8 1, label %L1 i8 2, label %L2] |
| |
| L1: |
| call void @foo() |
| ret void |
| L2: |
| call void @bar() |
| ret void |
| L3: |
| call void @baz() |
| ret void |
| L4: |
| call void @quux() |
| br label %L0 |
| } |
| |
| ; Make sure the edge value of %0 from entry to L2 includes 0 and L3 is |
| ; reachable. |
| ; CHECK: test_switch_default |
| ; CHECK: entry: |
| ; CHECK: load |
| ; CHECK: switch |
| ; CHECK: [[THREADED:[A-Za-z.0-9]+]]: |
| ; CHECK: store |
| ; CHECK: br |
| ; CHECK: L2: |
| ; CHECK: icmp |
| define void @test_switch_default(i32* nocapture %status) nounwind { |
| entry: |
| %0 = load i32* %status, align 4 |
| switch i32 %0, label %L2 [ |
| i32 5061, label %L1 |
| i32 0, label %L2 |
| ] |
| |
| L1: |
| store i32 10025, i32* %status, align 4 |
| br label %L2 |
| |
| L2: |
| %1 = load i32* %status, align 4 |
| %cmp57.i = icmp eq i32 %1, 0 |
| br i1 %cmp57.i, label %L3, label %L4 |
| |
| L3: |
| store i32 10000, i32* %status, align 4 |
| br label %L4 |
| |
| L4: |
| ret void |
| } |