759 lines
20 KiB
MLIR
759 lines
20 KiB
MLIR
|
// 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]])
|