You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
8.5 KiB
294 lines
8.5 KiB
// RUN: mlir-opt %s -pass-pipeline='func(canonicalize)' | FileCheck %s
|
|
|
|
func @single_iteration(%A: memref<?x?x?xi32>) {
|
|
%c0 = constant 0 : index
|
|
%c1 = constant 1 : index
|
|
%c2 = constant 2 : index
|
|
%c3 = constant 3 : index
|
|
%c6 = constant 6 : index
|
|
%c7 = constant 7 : index
|
|
%c10 = constant 10 : index
|
|
scf.parallel (%i0, %i1, %i2) = (%c0, %c3, %c7) to (%c1, %c6, %c10) step (%c1, %c2, %c3) {
|
|
%c42 = constant 42 : i32
|
|
store %c42, %A[%i0, %i1, %i2] : memref<?x?x?xi32>
|
|
scf.yield
|
|
}
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: func @single_iteration(
|
|
// CHECK-SAME: [[ARG0:%.*]]: memref<?x?x?xi32>) {
|
|
// CHECK: [[C0:%.*]] = constant 0 : index
|
|
// CHECK: [[C2:%.*]] = constant 2 : index
|
|
// CHECK: [[C3:%.*]] = constant 3 : index
|
|
// CHECK: [[C6:%.*]] = constant 6 : index
|
|
// CHECK: [[C7:%.*]] = constant 7 : index
|
|
// CHECK: [[C42:%.*]] = constant 42 : i32
|
|
// CHECK: scf.parallel ([[V0:%.*]]) = ([[C3]]) to ([[C6]]) step ([[C2]]) {
|
|
// CHECK: store [[C42]], [[ARG0]]{{\[}}[[C0]], [[V0]], [[C7]]] : memref<?x?x?xi32>
|
|
// CHECK: scf.yield
|
|
// CHECK: }
|
|
// CHECK: return
|
|
|
|
// -----
|
|
|
|
func @one_unused(%cond: i1) -> (index) {
|
|
%c0 = constant 0 : index
|
|
%c1 = constant 1 : index
|
|
%0, %1 = scf.if %cond -> (index, index) {
|
|
scf.yield %c0, %c1 : index, index
|
|
} else {
|
|
scf.yield %c0, %c1 : index, index
|
|
}
|
|
return %1 : index
|
|
}
|
|
|
|
// CHECK-LABEL: func @one_unused
|
|
// CHECK: [[C0:%.*]] = constant 1 : index
|
|
// CHECK: [[V0:%.*]] = scf.if %{{.*}} -> (index) {
|
|
// CHECK: scf.yield [[C0]] : index
|
|
// CHECK: } else
|
|
// CHECK: scf.yield [[C0]] : index
|
|
// CHECK: }
|
|
// CHECK: return [[V0]] : index
|
|
|
|
// -----
|
|
|
|
func @nested_unused(%cond1: i1, %cond2: i1) -> (index) {
|
|
%c0 = constant 0 : index
|
|
%c1 = constant 1 : index
|
|
%0, %1 = scf.if %cond1 -> (index, index) {
|
|
%2, %3 = scf.if %cond2 -> (index, index) {
|
|
scf.yield %c0, %c1 : index, index
|
|
} else {
|
|
scf.yield %c0, %c1 : index, index
|
|
}
|
|
scf.yield %2, %3 : index, index
|
|
} else {
|
|
scf.yield %c0, %c1 : index, index
|
|
}
|
|
return %1 : index
|
|
}
|
|
|
|
// CHECK-LABEL: func @nested_unused
|
|
// CHECK: [[C0:%.*]] = constant 1 : index
|
|
// CHECK: [[V0:%.*]] = scf.if {{.*}} -> (index) {
|
|
// CHECK: [[V1:%.*]] = scf.if {{.*}} -> (index) {
|
|
// CHECK: scf.yield [[C0]] : index
|
|
// CHECK: } else
|
|
// CHECK: scf.yield [[C0]] : index
|
|
// CHECK: }
|
|
// CHECK: scf.yield [[V1]] : index
|
|
// CHECK: } else
|
|
// CHECK: scf.yield [[C0]] : index
|
|
// CHECK: }
|
|
// CHECK: return [[V0]] : index
|
|
|
|
// -----
|
|
|
|
func private @side_effect()
|
|
func @all_unused(%cond: i1) {
|
|
%c0 = constant 0 : index
|
|
%c1 = constant 1 : index
|
|
%0, %1 = scf.if %cond -> (index, index) {
|
|
call @side_effect() : () -> ()
|
|
scf.yield %c0, %c1 : index, index
|
|
} else {
|
|
call @side_effect() : () -> ()
|
|
scf.yield %c0, %c1 : index, index
|
|
}
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: func @all_unused
|
|
// CHECK: scf.if %{{.*}} {
|
|
// CHECK: call @side_effect() : () -> ()
|
|
// CHECK: } else
|
|
// CHECK: call @side_effect() : () -> ()
|
|
// CHECK: }
|
|
// CHECK: return
|
|
|
|
// -----
|
|
|
|
func private @make_i32() -> i32
|
|
|
|
func @for_yields_2(%lb : index, %ub : index, %step : index) -> i32 {
|
|
%a = call @make_i32() : () -> (i32)
|
|
%b = scf.for %i = %lb to %ub step %step iter_args(%0 = %a) -> i32 {
|
|
scf.yield %0 : i32
|
|
}
|
|
return %b : i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @for_yields_2
|
|
// CHECK-NEXT: %[[R:.*]] = call @make_i32() : () -> i32
|
|
// CHECK-NEXT: return %[[R]] : i32
|
|
|
|
func @for_yields_3(%lb : index, %ub : index, %step : index) -> (i32, i32, i32) {
|
|
%a = call @make_i32() : () -> (i32)
|
|
%b = call @make_i32() : () -> (i32)
|
|
%r:3 = scf.for %i = %lb to %ub step %step iter_args(%0 = %a, %1 = %a, %2 = %b) -> (i32, i32, i32) {
|
|
%c = call @make_i32() : () -> (i32)
|
|
scf.yield %0, %c, %2 : i32, i32, i32
|
|
}
|
|
return %r#0, %r#1, %r#2 : i32, i32, i32
|
|
}
|
|
|
|
// CHECK-LABEL: func @for_yields_3
|
|
// CHECK-NEXT: %[[a:.*]] = call @make_i32() : () -> i32
|
|
// CHECK-NEXT: %[[b:.*]] = call @make_i32() : () -> i32
|
|
// CHECK-NEXT: %[[r1:.*]] = scf.for {{.*}} iter_args(%arg4 = %[[a]]) -> (i32) {
|
|
// CHECK-NEXT: %[[c:.*]] = call @make_i32() : () -> i32
|
|
// CHECK-NEXT: scf.yield %[[c]] : i32
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: return %[[a]], %[[r1]], %[[b]] : i32, i32, i32
|
|
|
|
// CHECK-LABEL: @replace_true_if
|
|
func @replace_true_if() {
|
|
%true = constant true
|
|
// CHECK-NOT: scf.if
|
|
// CHECK: "test.op"
|
|
scf.if %true {
|
|
"test.op"() : () -> ()
|
|
scf.yield
|
|
}
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @remove_false_if
|
|
func @remove_false_if() {
|
|
%false = constant false
|
|
// CHECK-NOT: scf.if
|
|
// CHECK-NOT: "test.op"
|
|
scf.if %false {
|
|
"test.op"() : () -> ()
|
|
scf.yield
|
|
}
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @replace_true_if_with_values
|
|
func @replace_true_if_with_values() {
|
|
%true = constant true
|
|
// CHECK-NOT: scf.if
|
|
// CHECK: %[[VAL:.*]] = "test.op"
|
|
%0 = scf.if %true -> (i32) {
|
|
%1 = "test.op"() : () -> i32
|
|
scf.yield %1 : i32
|
|
} else {
|
|
%2 = "test.other_op"() : () -> i32
|
|
scf.yield %2 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[VAL]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @replace_false_if_with_values
|
|
func @replace_false_if_with_values() {
|
|
%false = constant false
|
|
// CHECK-NOT: scf.if
|
|
// CHECK: %[[VAL:.*]] = "test.other_op"
|
|
%0 = scf.if %false -> (i32) {
|
|
%1 = "test.op"() : () -> i32
|
|
scf.yield %1 : i32
|
|
} else {
|
|
%2 = "test.other_op"() : () -> i32
|
|
scf.yield %2 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[VAL]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @remove_zero_iteration_loop
|
|
func @remove_zero_iteration_loop() {
|
|
%c42 = constant 42 : index
|
|
%c1 = constant 1 : index
|
|
// CHECK: %[[INIT:.*]] = "test.init"
|
|
%init = "test.init"() : () -> i32
|
|
// CHECK-NOT: scf.for
|
|
%0 = scf.for %i = %c42 to %c1 step %c1 iter_args(%arg = %init) -> (i32) {
|
|
%1 = "test.op"(%i, %arg) : (index, i32) -> i32
|
|
scf.yield %1 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[INIT]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @remove_zero_iteration_loop_vals
|
|
func @remove_zero_iteration_loop_vals(%arg0: index) {
|
|
%c2 = constant 2 : index
|
|
// CHECK: %[[INIT:.*]] = "test.init"
|
|
%init = "test.init"() : () -> i32
|
|
// CHECK-NOT: scf.for
|
|
// CHECK-NOT: test.op
|
|
%0 = scf.for %i = %arg0 to %arg0 step %c2 iter_args(%arg = %init) -> (i32) {
|
|
%1 = "test.op"(%i, %arg) : (index, i32) -> i32
|
|
scf.yield %1 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[INIT]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @replace_single_iteration_loop
|
|
func @replace_single_iteration_loop() {
|
|
// CHECK: %[[LB:.*]] = constant 42
|
|
%c42 = constant 42 : index
|
|
%c43 = constant 43 : index
|
|
%c1 = constant 1 : index
|
|
// CHECK: %[[INIT:.*]] = "test.init"
|
|
%init = "test.init"() : () -> i32
|
|
// CHECK-NOT: scf.for
|
|
// CHECK: %[[VAL:.*]] = "test.op"(%[[LB]], %[[INIT]])
|
|
%0 = scf.for %i = %c42 to %c43 step %c1 iter_args(%arg = %init) -> (i32) {
|
|
%1 = "test.op"(%i, %arg) : (index, i32) -> i32
|
|
scf.yield %1 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[VAL]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @replace_single_iteration_loop_non_unit_step
|
|
func @replace_single_iteration_loop_non_unit_step() {
|
|
// CHECK: %[[LB:.*]] = constant 42
|
|
%c42 = constant 42 : index
|
|
%c47 = constant 47 : index
|
|
%c5 = constant 5 : index
|
|
// CHECK: %[[INIT:.*]] = "test.init"
|
|
%init = "test.init"() : () -> i32
|
|
// CHECK-NOT: scf.for
|
|
// CHECK: %[[VAL:.*]] = "test.op"(%[[LB]], %[[INIT]])
|
|
%0 = scf.for %i = %c42 to %c47 step %c5 iter_args(%arg = %init) -> (i32) {
|
|
%1 = "test.op"(%i, %arg) : (index, i32) -> i32
|
|
scf.yield %1 : i32
|
|
}
|
|
// CHECK: "test.consume"(%[[VAL]])
|
|
"test.consume"(%0) : (i32) -> ()
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @remove_empty_parallel_loop
|
|
func @remove_empty_parallel_loop(%lb: index, %ub: index, %s: index) {
|
|
// CHECK: %[[INIT:.*]] = "test.init"
|
|
%init = "test.init"() : () -> f32
|
|
// CHECK-NOT: scf.parallel
|
|
// CHECK-NOT: test.produce
|
|
// CHECK-NOT: test.transform
|
|
%0 = scf.parallel (%i, %j, %k) = (%lb, %ub, %lb) to (%ub, %ub, %ub) step (%s, %s, %s) init(%init) -> f32 {
|
|
%1 = "test.produce"() : () -> f32
|
|
scf.reduce(%1) : f32 {
|
|
^bb0(%lhs: f32, %rhs: f32):
|
|
%2 = "test.transform"(%lhs, %rhs) : (f32, f32) -> f32
|
|
scf.reduce.return %2 : f32
|
|
}
|
|
scf.yield
|
|
}
|
|
// CHECK: "test.consume"(%[[INIT]])
|
|
"test.consume"(%0) : (f32) -> ()
|
|
return
|
|
}
|