| // RUN: mlir-opt --lift-cf-to-scf -split-input-file %s | FileCheck %s |
| |
| func.func @simple_if() { |
| %cond = "test.test1"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb2 |
| ^bb1: |
| "test.test2"() : () -> () |
| cf.br ^bb3 |
| ^bb2: |
| "test.test3"() : () -> () |
| cf.br ^bb3 |
| ^bb3: |
| "test.test4"() : () -> () |
| return |
| } |
| |
| // CHECK-LABEL: func @simple_if |
| // CHECK: %[[COND:.*]] = "test.test1"() |
| // CHECK-NEXT: scf.if %[[COND]] |
| // CHECK-NEXT: "test.test2"() |
| // CHECK-NEXT: else |
| // CHECK-NEXT: "test.test3"() |
| // CHECK-NEXT: } |
| // CHECK-NEXT: "test.test4"() |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func @if_with_block_args() -> index { |
| %cond = "test.test1"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb2 |
| ^bb1: |
| %1 = "test.test2"() : () -> (index) |
| cf.br ^bb3(%1: index) |
| ^bb2: |
| %2 = "test.test3"() : () -> (index) |
| cf.br ^bb3(%2: index) |
| ^bb3(%3: index): |
| "test.test4"() : () -> () |
| return %3 : index |
| } |
| |
| // CHECK-LABEL: func @if_with_block_args |
| // CHECK: %[[COND:.*]] = "test.test1"() |
| // CHECK-NEXT: %[[RES:.*]] = scf.if %[[COND]] |
| // CHECK-NEXT: %[[VAL1:.*]] = "test.test2"() |
| // CHECK-NEXT: scf.yield %[[VAL1]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: %[[VAL2:.*]] = "test.test3"() |
| // CHECK-NEXT: scf.yield %[[VAL2]] |
| // CHECK: "test.test4"() |
| // CHECK-NEXT: return %[[RES]] |
| |
| // ----- |
| |
| func.func @empty_else() { |
| %cond = "test.test1"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb3 |
| ^bb1: |
| "test.test2"() : () -> () |
| cf.br ^bb3 |
| ^bb3: |
| "test.test4"() : () -> () |
| return |
| } |
| |
| // CHECK-LABEL: func @empty_else |
| // CHECK: %[[COND:.*]] = "test.test1"() |
| // CHECK-NEXT: scf.if %[[COND]] |
| // CHECK-NEXT: "test.test2"() |
| // CHECK: else |
| // CHECK-NEXT: } |
| // CHECK-NEXT: "test.test4"() |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func @while_loop() { |
| "test.test1"() : () -> () |
| cf.br ^bb1 |
| ^bb1: |
| %cond = "test.test2"() : () -> i1 |
| cf.cond_br %cond, ^bb2, ^bb3 |
| ^bb2: |
| "test.test3"() : () -> () |
| cf.br ^bb1 |
| ^bb3: |
| "test.test4"() : () -> () |
| return |
| } |
| |
| // CHECK-LABEL: func @while_loop |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-NEXT: "test.test1"() |
| // CHECK-NEXT: scf.while |
| // CHECK-NEXT: %[[COND:.*]] = "test.test2"() |
| // CHECK-NEXT: %[[RES:.*]]:2 = scf.if %[[COND]] |
| // CHECK-NEXT: "test.test3"() |
| // CHECK-NEXT: scf.yield %[[C0]], %[[C1]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C1]], %[[C0]] |
| // CHECK: %[[COND:.*]] = arith.trunci %[[RES]]#1 |
| // CHECK-NEXT: scf.condition(%[[COND]]) |
| // CHECK-NEXT: do |
| // CHECK-NEXT: scf.yield |
| // CHECK: "test.test4"() |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func @while_loop_with_block_args() -> i64{ |
| %1 = "test.test1"() : () -> index |
| cf.br ^bb1(%1: index) |
| ^bb1(%2: index): |
| %cond:2 = "test.test2"() : () -> (i1, i64) |
| cf.cond_br %cond#0, ^bb2(%cond#1: i64), ^bb3(%cond#1: i64) |
| ^bb2(%3: i64): |
| %4 = "test.test3"(%3) : (i64) -> index |
| cf.br ^bb1(%4: index) |
| ^bb3(%5: i64): |
| "test.test4"() : () -> () |
| return %5 : i64 |
| } |
| |
| // CHECK-LABEL: func @while_loop_with_block_args |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-DAG: %[[POISON_i64:.*]] = ub.poison : i64 |
| // CHECK-DAG: %[[POISON_index:.*]] = ub.poison : index |
| // CHECK-NEXT: %[[VAL1:.*]] = "test.test1"() |
| // CHECK-NEXT: %[[RES:.*]]:2 = scf.while (%[[ARG0:.*]] = %[[VAL1]], %[[ARG1:.*]] = %[[POISON_i64]]) |
| // CHECK-NEXT: %[[COND:.*]]:2 = "test.test2"() |
| // CHECK-NEXT: %[[IF_VALS:.*]]:4 = scf.if %[[COND]]#0 |
| // CHECK-NEXT: %[[VAL:.*]] = "test.test3"(%[[COND]]#1) |
| // CHECK-NEXT: scf.yield %[[VAL]], %[[POISON_i64]], %[[C0]], %[[C1]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[POISON_index]], %[[COND]]#1, %[[C1]], %[[C0]] |
| // CHECK: %[[TRUNC:.*]] = arith.trunci %[[IF_VALS]]#3 |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) %[[IF_VALS]]#0, %[[IF_VALS]]#1 |
| // CHECK-NEXT: do |
| // CHECK-NEXT: ^{{.+}}( |
| // CHECK-SAME: [[ARG0:[[:alnum:]]+]]: |
| // CHECK-SAME: [[ARG1:[[:alnum:]]+]]: |
| // CHECK-NEXT: scf.yield %[[ARG0]], %[[ARG1]] |
| // CHECK: "test.test4"() : () -> () |
| // CHECK-NEXT: return %[[RES]]#1 : i64 |
| |
| // ----- |
| |
| func.func @multi_exit_loop() { |
| "test.test1"() : () -> () |
| cf.br ^bb1 |
| ^bb1: |
| %cond = "test.test2"() : () -> i1 |
| cf.cond_br %cond, ^bb2, ^bb3 |
| ^bb2: |
| %cond2 = "test.test3"() : () -> i1 |
| cf.cond_br %cond2, ^bb3, ^bb1 |
| ^bb3: |
| "test.test4"() : () -> () |
| return |
| } |
| |
| // CHECK-LABEL: func @multi_exit_loop |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-NEXT: "test.test1"() |
| // CHECK-NEXT: scf.while |
| // CHECK-NEXT: %[[COND:.*]] = "test.test2"() |
| // CHECK-NEXT: %[[IF_PAIR:.*]]:2 = scf.if %[[COND]] |
| // CHECK-NEXT: %[[COND2:.*]] = "test.test3"() |
| // CHECK-NEXT: %[[IF_PAIR2:.*]]:2 = scf.if %[[COND2]] |
| // CHECK-NEXT: scf.yield %[[C1]], %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C0]], %[[C1]] |
| // CHECK: scf.yield %[[IF_PAIR2]]#0, %[[IF_PAIR2]]#1 |
| // CHECK: %[[TRUNC:.*]] = arith.trunci %[[IF_PAIR]]#1 |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) |
| // CHECK-NEXT: do |
| // CHECK-NEXT: scf.yield |
| // CHECK: "test.test4"() |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func private @foo(%arg: f32) -> f32 |
| func.func private @bar(%arg: f32) |
| |
| func.func @switch_with_fallthrough(%flag: i32, %arg1 : f32, %arg2 : f32) { |
| cf.switch %flag : i32, [ |
| default: ^bb1(%arg1 : f32), |
| 0: ^bb2(%arg2 : f32), |
| 1: ^bb3 |
| ] |
| |
| ^bb1(%arg3 : f32): |
| %0 = call @foo(%arg3) : (f32) -> f32 |
| cf.br ^bb2(%0 : f32) |
| |
| ^bb2(%arg4 : f32): |
| call @bar(%arg4) : (f32) -> () |
| cf.br ^bb3 |
| |
| ^bb3: |
| return |
| } |
| |
| // CHECK-LABEL: func @switch_with_fallthrough |
| // CHECK-SAME: %[[ARG0:[[:alnum:]]+]] |
| // CHECK-SAME: %[[ARG1:[[:alnum:]]+]] |
| // CHECK-SAME: %[[ARG2:[[:alnum:]]+]] |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-DAG: %[[POISON:.*]] = ub.poison |
| // CHECK-NEXT: %[[INDEX_CAST:.*]] = arith.index_castui %[[ARG0]] |
| // CHECK-NEXT: %[[SWITCH_PAIR:.*]]:2 = scf.index_switch %[[INDEX_CAST]] |
| // CHECK-NEXT: case 0 |
| // CHECK-NEXT: scf.yield %[[ARG2]], %[[C0]] |
| // CHECK: case 1 |
| // CHECK-NEXT: scf.yield %[[POISON]], %[[C1]] |
| // CHECK: default |
| // CHECK-NEXT: %[[RES:.*]] = func.call @foo(%[[ARG1]]) |
| // CHECK-NEXT: scf.yield %[[RES]], %[[C0]] |
| // CHECK: %[[INDEX_CAST:.*]] = arith.index_castui %[[SWITCH_PAIR]]#1 |
| // CHECK-NEXT: scf.index_switch %[[INDEX_CAST]] |
| // CHECK-NEXT: case 0 |
| // CHECK-NEXT: call @bar(%[[SWITCH_PAIR]]#0) |
| // CHECK-NEXT: scf.yield |
| // CHECK: default { |
| // CHECK-NEXT: } |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func private @bar(%arg: f32) -> (i1, f32) |
| |
| func.func @already_structured_loop(%arg: f32) -> f32 { |
| cf.br ^bb0 |
| |
| ^bb0: |
| %cond, %value = call @bar(%arg) : (f32) -> (i1, f32) |
| cf.cond_br %cond, ^bb1, ^bb0 |
| |
| ^bb1: |
| return %value : f32 |
| } |
| |
| // CHECK-LABEL: @already_structured_loop |
| // CHECK-SAME: %[[ARG:.*]]: f32 |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-DAG: %[[POISON:.*]] = ub.poison |
| // CHECK-NEXT: %[[RES:.*]] = scf.while (%[[ARG1:.*]] = %[[POISON]]) |
| // CHECK-NEXT: %[[CALL_PAIR:.*]]:2 = func.call @bar(%[[ARG]]) |
| // CHECK-NEXT: %[[IF_PAIR:.*]]:2 = scf.if %[[CALL_PAIR]]#0 |
| // CHECK-NEXT: scf.yield %[[C1]], %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C0]], %[[C1]] |
| // CHECK: %[[TRUNC:.*]] = arith.trunci %[[IF_PAIR]]#1 |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) %[[CALL_PAIR]]#1 |
| // CHECK: return %[[RES]] |
| |
| // ----- |
| |
| func.func private @bar(%arg: f32) -> (i1, f32) |
| |
| // This test makes sure that the exit block using an iteration variable works |
| // correctly. |
| |
| func.func @exit_loop_iter_use(%arg: f32) -> f32 { |
| cf.br ^bb0(%arg : f32) |
| |
| ^bb0(%arg1: f32): |
| %cond, %value = call @bar(%arg1) : (f32) -> (i1, f32) |
| cf.cond_br %cond, ^bb1, ^bb0(%value : f32) |
| |
| ^bb1: |
| return %arg1 : f32 |
| } |
| |
| // CHECK-LABEL: @exit_loop_iter_use |
| // CHECK-SAME: %[[ARG:.*]]: |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-DAG: %[[POISON:.*]] = ub.poison |
| // CHECK-NEXT: %[[RES:.*]]:2 = scf.while |
| // CHECK-SAME: %[[ARG1:.*]] = %[[ARG]] |
| // CHECK-SAME: %[[ARG2:.*]] = %[[POISON]] |
| // CHECK-NEXT: %[[CALL_PAIR:.*]]:2 = func.call @bar(%[[ARG1]]) |
| // CHECK-NEXT: %[[IF_RES:.*]]:3 = scf.if %[[CALL_PAIR]]#0 |
| // CHECK-NEXT: scf.yield %[[POISON]], %[[C1]], %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[CALL_PAIR]]#1, %[[C0]], %[[C1]] |
| // CHECK: %[[TRUNC:.*]] = arith.trunci %[[IF_RES]]#2 |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) %[[IF_RES]]#0, %[[ARG1]] |
| // CHECK: return %[[RES]]#1 |
| |
| // ----- |
| |
| func.func private @bar(%arg: f32) -> f32 |
| |
| func.func @infinite_loop(%arg: f32) -> f32 { |
| cf.br ^bb1(%arg: f32) |
| |
| ^bb1(%arg1: f32): |
| %0 = call @bar(%arg1) : (f32) -> f32 |
| cf.br ^bb1(%0 : f32) |
| } |
| |
| // CHECK-LABEL: @infinite_loop |
| // CHECK-SAME: %[[ARG:.*]]: |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-NEXT: scf.while |
| // CHECK-SAME: %[[ARG1:.*]] = %[[ARG]] |
| // CHECK-NEXT: %[[CALL:.*]] = func.call @bar(%[[ARG1]]) |
| // CHECK-NEXT: %[[TRUNC:.*]] = arith.trunci %[[C1]] |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) %[[CALL]] |
| // CHECK: %[[POISON:.*]] = ub.poison |
| // CHECK: return %[[POISON]] |
| |
| // ----- |
| |
| func.func @multi_return() -> i32 { |
| %cond = "test.test1"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb3 |
| ^bb1: |
| %0 = "test.test2"() : () -> i32 |
| return %0 : i32 |
| ^bb3: |
| %1 = "test.test4"() : () -> i32 |
| return %1 : i32 |
| } |
| |
| // CHECK-LABEL: func @multi_return |
| // CHECK: %[[COND:.*]] = "test.test1"() |
| // CHECK-NEXT: %[[RES:.*]] = scf.if %[[COND]] |
| // CHECK-NEXT: %[[VAL2:.*]] = "test.test2"() |
| // CHECK: scf.yield %[[VAL2]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: %[[VAL4:.*]] = "test.test4"() |
| // CHECK: scf.yield %[[VAL4]] |
| // CHECK: return %[[RES]] |
| |
| // ----- |
| |
| func.func private @bar(%arg: f32) -> f32 |
| |
| func.func @conditional_infinite_loop(%arg: f32, %cond: i1) -> f32 { |
| cf.cond_br %cond, ^bb1(%arg: f32), ^bb2 |
| |
| ^bb1(%arg1: f32): |
| %0 = call @bar(%arg1) : (f32) -> f32 |
| cf.br ^bb1(%0 : f32) |
| |
| ^bb2: |
| return %arg : f32 |
| } |
| |
| // CHECK-LABEL: @conditional_infinite_loop |
| // CHECK-SAME: %[[ARG0:[[:alnum:]]+]]: |
| // CHECK-SAME: %[[ARG1:[[:alnum:]]+]]: |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-NEXT: %[[RES:.*]] = scf.if %[[ARG1]] |
| // CHECK-NEXT: scf.while |
| // CHECK-SAME: %[[ARG2:.*]] = %[[ARG0]] |
| // CHECK-NEXT: %[[CALL:.*]] = func.call @bar(%[[ARG2]]) |
| // CHECK-NEXT: %[[TRUNC:.*]] = arith.trunci %[[C1]] |
| // CHECK-NEXT: scf.condition(%[[TRUNC]]) %[[CALL]] |
| // CHECK: else |
| // CHECK: scf.yield %[[ARG0]] |
| // CHECK: return %[[RES]] |
| |
| // ----- |
| |
| // Different return-like terminators lead one control flow op remaining in the top level region. |
| // Each of the blocks the control flow op leads to are transformed into regions nevertheless. |
| |
| func.func private @bar(%arg: i32) |
| |
| func.func @mixing_return_like(%cond: i1, %cond2: i1, %cond3: i1) { |
| %0 = arith.constant 0 : i32 |
| %1 = arith.constant 1 : i32 |
| cf.cond_br %cond, ^bb1, ^bb3 |
| |
| ^bb1: |
| cf.cond_br %cond2, ^bb2(%0 : i32), ^bb2(%1 : i32) |
| ^bb2(%arg: i32): |
| call @bar(%arg) : (i32) -> () |
| "test.returnLike"() : () -> () |
| |
| ^bb3: |
| cf.cond_br %cond3, ^bb4(%1 : i32), ^bb4(%0 : i32) |
| ^bb4(%arg2: i32): |
| call @bar(%arg2) : (i32) -> () |
| return |
| } |
| |
| // CHECK-LABEL: @mixing_return_like |
| // CHECK-SAME: %[[COND1:[[:alnum:]]+]]: |
| // CHECK-SAME: %[[COND2:[[:alnum:]]+]]: |
| // CHECK-SAME: %[[COND3:[[:alnum:]]+]]: |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK: cf.cond_br %[[COND1]], ^[[BB1:.*]], ^[[BB2:[[:alnum:]]+]] |
| |
| // CHECK: ^[[BB1]]: |
| // CHECK: scf.if |
| // CHECK-NOT: cf.{{(switch|(cond_)?br)}} |
| // CHECK: call @bar |
| // CHECK: "test.returnLike" |
| |
| // CHECK: ^[[BB2]]: |
| // CHECK: scf.if |
| // CHECK-NOT: cf.{{(switch|(cond_)?br)}} |
| // CHECK: call @bar |
| // CHECK: return |
| |
| // ----- |
| |
| // cf.switch here only has some successors with different return-like ops. |
| // This test makes sure that if there are at least two successors branching to |
| // the same region, that this region gets properly turned to structured control |
| // flow. |
| |
| func.func @some_successors_with_different_return(%flag: i32) -> i32 { |
| %0 = arith.constant 5 : i32 |
| %1 = arith.constant 6 : i32 |
| cf.switch %flag : i32, [ |
| default: ^bb1, |
| 0: ^bb3(%0 : i32), |
| 1: ^bb3(%1 : i32) |
| ] |
| |
| ^bb1: |
| "test.returnLike"() : () -> () |
| |
| ^bb3(%arg: i32): |
| cf.br ^bb3(%arg : i32) |
| } |
| |
| // CHECK-LABEL: @some_successors_with_different_return |
| // CHECK-SAME: %[[FLAG:[[:alnum:]]+]]: |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32 |
| // CHECK-DAG: %[[POISON:.*]] = ub.poison |
| // CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32 |
| // CHECK-DAG: %[[C6:.*]] = arith.constant 6 : i32 |
| // CHECK: %[[INDEX_CAST:.*]] = arith.index_castui %[[FLAG]] |
| // CHECK-NEXT: %[[INDEX_SWITCH:.*]]:2 = scf.index_switch %[[INDEX_CAST]] |
| // CHECK: case 0 |
| // CHECK-NEXT: scf.yield %[[C5]], %[[C1]] |
| // CHECK: case 1 |
| // CHECK-NEXT: scf.yield %[[C6]], %[[C1]] |
| // CHECK: default |
| // CHECK-NEXT: scf.yield %[[POISON]], %[[C0]] |
| // CHECK: cf.switch %[[INDEX_SWITCH]]#1 |
| // CHECK-NEXT: default: ^[[BB2:[[:alnum:]]+]] |
| // CHECK-SAME: %[[INDEX_SWITCH]]#0 |
| // CHECK-NEXT: 0: ^[[BB1:[[:alnum:]]+]] |
| // CHECK-NEXT: ] |
| |
| // CHECK: ^[[BB2]]{{.*}}: |
| // CHECK: scf.while |
| // CHECK-NOT: cf.{{(switch|(cond_)?br)}} |
| // CHECK: return |
| |
| // CHECK: ^[[BB1]]: |
| // CHECK-NEXT: "test.returnLike" |
| |
| // ----- |
| |
| func.func @select_like(%cond: i1) -> i32 { |
| %0 = arith.constant 0 : i32 |
| %1 = arith.constant 1 : i32 |
| cf.cond_br %cond, ^bb0(%0 : i32), ^bb0(%1 : i32) |
| ^bb0(%arg: i32): |
| return %arg : i32 |
| } |
| |
| // CHECK-LABEL: func @select_like |
| // CHECK-SAME: %[[COND:.*]]: i1 |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-NEXT: %[[RES:.*]] = scf.if %[[COND]] |
| // CHECK-NEXT: scf.yield %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C1]] |
| // CHECK: return %[[RES]] |
| |
| // ----- |
| |
| func.func @return_like_dominated_by_condition(%cond1: i1, %cond2: i1, %cond3: i1) { |
| cf.cond_br %cond1, ^bb1, ^bb2 |
| |
| ^bb1: |
| return |
| |
| ^bb2: |
| cf.cond_br %cond2, ^bb3, ^bb4 |
| |
| ^bb3: |
| "test.unreachable"() : () -> () |
| |
| ^bb4: |
| cf.cond_br %cond3, ^bb3, ^bb5 |
| |
| ^bb5: |
| return |
| } |
| |
| // CHECK-LABEL: func @return_like_dominated_by_condition |
| // CHECK-SAME: %[[COND1:[[:alnum:]]+]] |
| // CHECK-SAME: %[[COND2:[[:alnum:]]+]] |
| // CHECK-SAME: %[[COND3:[[:alnum:]]+]] |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK: %[[IF:.*]] = scf.if %[[COND1]] |
| // CHECK-NEXT: scf.yield |
| // CHECK: else |
| // CHECK: scf.if %[[COND2]] |
| // CHECK-NEXT: scf.yield |
| // CHECK: else |
| // CHECK: scf.if %[[COND3]] |
| // CHECK-NEXT: scf.yield |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield |
| |
| // CHECK: cf.switch %[[IF]] |
| // CHECK-NEXT: default: ^[[BB1:.*]], |
| // CHECK-NEXT: 0: ^[[BB2:[[:alnum:]]+]] |
| |
| // CHECK: ^[[BB2]]: |
| // CHECK-NEXT: return |
| |
| // CHECK: ^[[BB1]]: |
| // CHECK-NEXT: "test.unreachable" |
| |
| // ----- |
| |
| func.func @dominator_issue(%cond: i1, %cond2: i1) -> i32 { |
| cf.cond_br %cond, ^bb1, ^bb2 |
| |
| ^bb1: |
| "test.unreachable"() : () -> () |
| |
| ^bb2: |
| %value = "test.def"() : () -> i32 |
| cf.cond_br %cond2, ^bb1, ^bb4 |
| |
| ^bb4: |
| return %value : i32 |
| } |
| |
| // CHECK-LABEL: func @dominator_issue |
| // CHECK-SAME: %[[COND1:[[:alnum:]]+]] |
| // CHECK-SAME: %[[COND2:[[:alnum:]]+]] |
| // CHECK: %[[IF:.*]]:2 = scf.if %[[COND1]] |
| // CHECK: else |
| // CHECK: %[[VALUE:.*]] = "test.def" |
| // CHECK: %[[IF_RES:.*]]:2 = scf.if %[[COND2]] |
| // CHECK: else |
| // CHECK-NEXT: scf.yield %[[VALUE]] |
| // CHECK: scf.yield %[[IF_RES]]#0 |
| // CHECK: cf.switch %[[IF]]#1 |
| // CHECK-NEXT: default: ^[[BB2:[[:alnum:]]+]]( |
| // CHECK-SAME: %[[IF]]#0 |
| // CHECK-NEXT: 0: ^[[BB1:[[:alnum:]]+]] |
| // CHECK: ^[[BB1]]: |
| // CHECK-NEXT: "test.unreachable" |
| // CHECK: ^[[BB2]] |
| // CHECK-SAME: %[[ARG:[[:alnum:]]+]] |
| // CHECK-NEXT: return %[[ARG]] |
| |
| // ----- |
| |
| // Test that %value gets properly passed to ^bb4. |
| |
| func.func private @bar(i32) |
| |
| func.func @dominator_issue_loop(%cond: i1, %cond2: i1) -> i32 { |
| %0 = arith.constant 5 : i32 |
| cf.br ^bb0 |
| |
| ^bb0: |
| cf.cond_br %cond, ^bb1, ^bb3 |
| |
| ^bb1: |
| %value = "test.def"() : () -> i32 |
| cf.cond_br %cond2, ^bb0, ^bb4 |
| |
| ^bb3: |
| return %0 : i32 |
| |
| ^bb4: |
| return %value : i32 |
| } |
| |
| // CHECK-LABEL: func @dominator_issue_loop |
| // CHECK-SAME: %[[COND1:[[:alnum:]]+]] |
| // CHECK-SAME: %[[COND2:[[:alnum:]]+]] |
| // CHECK: %[[WHILE:.*]]:2 = scf.while |
| // CHECK: %[[IF:.*]]:3 = scf.if %[[COND1]] |
| // CHECK: %[[DEF:.*]] = "test.def" |
| // CHECK: %[[IF2:.*]]:3 = scf.if %[[COND2]] |
| // CHECK: scf.yield |
| // CHECK-SAME: %[[DEF]] |
| // CHECK: else |
| // CHECK: scf.yield %{{.*}}, %{{.*}}, %[[DEF]] |
| // CHECK: scf.yield %[[IF2]]#0, %[[IF2]]#1, %[[IF2]]#2 |
| // CHECK: scf.condition(%{{.*}}) %[[IF]]#2, %[[IF]]#0 |
| |
| // CHECK: %[[SWITCH:.*]] = scf.index_switch |
| // CHECK: scf.yield %[[WHILE]]#0 |
| // CHECK: return %[[SWITCH]] |
| |
| // ----- |
| |
| // Multi entry loops generally produce code in dire need of canonicalization. |
| |
| func.func private @comp1(%arg: i32) -> i1 |
| func.func private @comp2(%arg: i32) -> i1 |
| func.func private @foo(%arg: i32) |
| |
| func.func @multi_entry_loop(%cond: i1) { |
| %0 = arith.constant 6 : i32 |
| %1 = arith.constant 5 : i32 |
| cf.cond_br %cond, ^bb0, ^bb1 |
| |
| ^bb0: |
| %exit = call @comp1(%0) : (i32) -> i1 |
| cf.cond_br %exit, ^bb2(%0 : i32), ^bb1 |
| |
| ^bb1: |
| %exit2 = call @comp2(%1) : (i32) -> i1 |
| cf.cond_br %exit2, ^bb2(%1 : i32), ^bb0 |
| |
| ^bb2(%arg3 : i32): |
| call @foo(%arg3) : (i32) -> () |
| return |
| } |
| |
| // CHECK-LABEL: func @multi_entry_loop |
| // CHECK-SAME: %[[ARG0:[[:alnum:]]+]] |
| // CHECK-DAG: %[[C0:.*]] = arith.constant 0 |
| // CHECK-DAG: %[[UB:.*]] = ub.poison |
| // CHECK-DAG: %[[C1:.*]] = arith.constant 1 |
| // CHECK-DAG: %[[C6:.*]] = arith.constant 6 |
| // CHECK-DAG: %[[C5:.*]] = arith.constant 5 |
| // CHECK: %[[IF:.*]]:{{.*}} = scf.if %[[ARG0]] |
| // CHECK-NEXT: scf.yield %[[C1]], %[[UB]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C0]], %[[UB]] |
| // CHECK: %[[WHILE:.*]]:{{.*}} = scf.while |
| // CHECK-SAME: %[[ARG1:.*]] = %[[IF]]#0 |
| // CHECK-SAME: %[[ARG2:.*]] = %[[IF]]#1 |
| // CHECK-NEXT: %[[FLAG:.*]] = arith.index_castui %[[ARG1]] |
| // CHECK-NEXT: %[[SWITCH:.*]]:{{.*}} = scf.index_switch %[[FLAG]] |
| // CHECK-NEXT: case 0 |
| // CHECK-NEXT: %[[EXIT:.*]] = func.call @comp2(%[[C5]]) |
| // CHECK-NEXT: %[[IF_RES:.*]]:{{.*}} = scf.if %[[EXIT]] |
| // CHECK-NEXT: scf.yield %[[UB]], %[[C5]], %[[C1]], %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C1]], %[[UB]], %[[C0]], %[[C1]] |
| // CHECK: scf.yield %[[IF_RES]]#0, %[[IF_RES]]#1, %[[IF_RES]]#2, %[[IF_RES]]#3 |
| // CHECK: default |
| // CHECK-NEXT: %[[EXIT:.*]] = func.call @comp1(%[[C6]]) |
| // CHECK-NEXT: %[[IF_RES:.*]]:{{.*}} = scf.if %[[EXIT]] |
| // CHECK-NEXT: scf.yield %[[UB]], %[[C6]], %[[C1]], %[[C0]] |
| // CHECK-NEXT: else |
| // CHECK-NEXT: scf.yield %[[C0]], %[[UB]], %[[C0]], %[[C1]] |
| // CHECK: scf.yield %[[IF_RES]]#0, %[[IF_RES]]#1, %[[IF_RES]]#2, %[[IF_RES]]#3 |
| // CHECK: %[[COND:.*]] = arith.trunci %[[SWITCH]]#3 |
| // CHECK-NEXT: scf.condition(%[[COND]]) %[[SWITCH]]#0, %[[SWITCH]]#1 |
| // CHECK: do |
| // CHECK: scf.yield |
| // CHECK: call @foo(%[[WHILE]]#1) |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func @nested_region() { |
| scf.execute_region { |
| %cond = "test.test1"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb2 |
| ^bb1: |
| "test.test2"() : () -> () |
| cf.br ^bb3 |
| ^bb2: |
| "test.test3"() : () -> () |
| cf.br ^bb3 |
| ^bb3: |
| "test.test4"() : () -> () |
| scf.yield |
| } |
| return |
| } |
| |
| // CHECK-LABEL: func @nested_region |
| // CHECK: scf.execute_region { |
| // CHECK: %[[COND:.*]] = "test.test1"() |
| // CHECK-NEXT: scf.if %[[COND]] |
| // CHECK-NEXT: "test.test2"() |
| // CHECK-NEXT: else |
| // CHECK-NEXT: "test.test3"() |
| // CHECK-NEXT: } |
| // CHECK-NEXT: "test.test4"() |
| // CHECK-NEXT: scf.yield |
| // CHECK-NEXT: } |
| // CHECK-NEXT: return |
| |
| // ----- |
| |
| func.func @nested_region_inside_loop_use() { |
| cf.br ^bb1 |
| |
| ^bb1: |
| %3 = "test.test1"() : () -> i32 |
| scf.execute_region { |
| "test.foo"(%3) : (i32) -> () |
| scf.yield |
| } |
| cf.br ^bb1 |
| } |
| |
| // CHECK-LABEL: func @nested_region_inside_loop_use |
| // CHECK: scf.while |
| // CHECK-NEXT: %[[DEF:.*]] = "test.test1"() |
| // CHECK-NEXT: scf.execute_region |
| // CHECK-NEXT: "test.foo"(%[[DEF]]) |
| |
| // ----- |
| |
| func.func @nested_region_outside_loop_use() { |
| cf.br ^bb1 |
| |
| ^bb1: |
| %3 = "test.test1"() : () -> i32 |
| %cond = "test.test2"() : () -> i1 |
| cf.cond_br %cond, ^bb1, ^bb2 |
| |
| ^bb2: |
| scf.execute_region { |
| "test.foo"(%3) : (i32) -> () |
| scf.yield |
| } |
| return |
| } |
| |
| // CHECK-LABEL: func @nested_region_outside_loop_use |
| // CHECK: %[[RES:.*]] = scf.while |
| // CHECK: %[[DEF:.*]] = "test.test1"() |
| // CHECK: scf.condition(%{{.*}}) %[[DEF]] |
| |
| // CHECK: scf.execute_region |
| // CHECK-NEXT: "test.foo"(%[[RES]]) |