Files
TheRock/compiler/amd-llvm/mlir/test/Dialect/SCF/mem2reg.mlir
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1121 lines
34 KiB
MLIR

// RUN: mlir-opt %s --mem2reg --split-input-file | FileCheck %s \
// RUN: -implicit-check-not "memref.alloca" \
// RUN: -implicit-check-not "memref.load" \
// RUN: -implicit-check-not "memref.store"
// Check regions within if are promoted.
// CHECK-LABEL: func.func @if_load_only
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.if %[[COND]] -> (i32)
// CHECK: scf.yield %[[C5]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @if_load_only(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
%res = scf.if %cond -> i32 {
%load = memref.load %alloca[] : memref<i32>
scf.yield %load : i32
} else {
scf.yield %c5 : i32
}
return %res : i32
}
// -----
// Check load promotion through an if with no else branch.
func.func private @use(i32)
// CHECK-LABEL: func.func @if_no_else_load
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.if %[[COND]] {
// CHECK: call @use(%[[C5]])
// CHECK: }
// CHECK: call @use(%[[C5]])
func.func @if_no_else_load(%cond: i1) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
func.call @use(%load2) : (i32) -> ()
return
}
// -----
// Check store promotion through an if with no else branch.
func.func private @use(i32)
// CHECK-LABEL: func.func @if_no_else_store
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[IF:.*]] = scf.if %[[COND]] -> (i32)
// CHECK: scf.yield %[[C7]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: call @use(%[[IF]])
func.func @if_no_else_store(%cond: i1) {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
return
}
// -----
// Check store promotion through nested ifs with no else branches.
func.func private @use(i32)
// CHECK-LABEL: func.func @if_nested_store
// CHECK-SAME: (%[[COND0:.*]]: i1, %[[COND1:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[OUTER:.*]] = scf.if %[[COND0]] -> (i32)
// CHECK: %[[INNER:.*]] = scf.if %[[COND1]] -> (i32)
// CHECK: scf.yield %[[C7]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: scf.yield %[[INNER]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: call @use(%[[OUTER]])
func.func @if_nested_store(%cond0: i1, %cond1: i1) {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond0 {
scf.if %cond1 {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
return
}
// -----
// Check that a store coming from a load of the same slot is correctly promoted.
// CHECK-LABEL: func.func @if_load_into_store
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.if %[[COND]] -> (i32) {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @if_load_into_store(%arg1 : i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %arg1 {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
%loaded2 = memref.load %alloca[] : memref<i32>
return %loaded2 : i32
}
// -----
// Check promotion of a load followed by a nested if containing a store of
// the loaded value.
// CHECK-LABEL: func.func @if_load_then_nested_if_store
// CHECK-SAME: (%[[COND0:.*]]: i1, %[[COND1:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.if %[[COND0]] -> (i32) {
// CHECK: %[[INNER:.*]] = scf.if %[[COND1]] -> (i32) {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: scf.yield %[[INNER]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @if_load_then_nested_if_store(%cond0: i1, %cond1: i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond0 {
%loaded = memref.load %alloca[] : memref<i32>
scf.if %cond1 {
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check load promotion through execute_region.
func.func private @use(i32)
// CHECK-LABEL: func.func @execute_region_load
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.execute_region {
// CHECK: call @use(%[[C5]])
// CHECK: scf.yield
// CHECK: }
func.func @execute_region_load() {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
return
}
// -----
// Check store promotion through execute_region.
// CHECK-LABEL: func.func @execute_region_store
// CHECK: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: scf.yield %[[C7]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_store() -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check promotion through an execute_region with CFG control flow and a
// nested if containing a load. This ensures a block argument is created
// even in blocks with no direct slot use.
func.func private @use(i32)
// CHECK-LABEL: func.func @execute_region_cfg
// CHECK-SAME: (%[[COND0:.*]]: i1, %[[COND1:.*]]: i1)
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK-DAG: %[[C9:.*]] = arith.constant 9 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND0]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: cf.br ^[[BB3:.*]](%[[C7]] : i32)
// CHECK: ^[[BB2]]:
// CHECK: cf.br ^[[BB3]](%[[C9]] : i32)
// CHECK: ^[[BB3]](%[[VAL:.*]]: i32):
// CHECK: scf.if %[[COND1]] {
// CHECK: call @use(%[[VAL]])
// CHECK: }
// CHECK: scf.yield %[[VAL]] : i32
// CHECK: }
// CHECK: call @use(%[[RES]])
func.func @execute_region_cfg(%cond0: i1, %cond1: i1) {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%c9 = arith.constant 9 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
cf.cond_br %cond0, ^bb1, ^bb2
^bb1:
memref.store %c7, %alloca[] : memref<i32>
cf.br ^bb3
^bb2:
memref.store %c9, %alloca[] : memref<i32>
cf.br ^bb3
^bb3:
scf.if %cond1 {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
func.call @use(%load2) : (i32) -> ()
return
}
// CHECK-LABEL: func.func @execute_region_cfg_no_use_at_all
// CHECK-SAME: (%[[COND0:.*]]: i1, %[[COND1:.*]]: i1)
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK-DAG: %[[C9:.*]] = arith.constant 9 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND0]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: cf.br ^[[BB3:.*]](%[[C7]] : i32)
// CHECK: ^[[BB2]]:
// CHECK: cf.br ^[[BB3]](%[[C9]] : i32)
// CHECK: ^[[BB3]](%[[VAL:.*]]: i32):
// CHECK: scf.yield %[[VAL]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_cfg_no_use_at_all(%cond0: i1, %cond1: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%c9 = arith.constant 9 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
cf.cond_br %cond0, ^bb1, ^bb2
^bb1:
memref.store %c7, %alloca[] : memref<i32>
cf.br ^bb3
^bb2:
memref.store %c9, %alloca[] : memref<i32>
cf.br ^bb3
^bb3:
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// CHECK-LABEL: func.func @execute_region_cfg_with_store
// CHECK-SAME: (%[[COND0:.*]]: i1, %[[COND1:.*]]: i1)
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK-DAG: %[[C9:.*]] = arith.constant 9 : i32
// CHECK-DAG: %[[C11:.*]] = arith.constant 11 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND0]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: cf.br ^[[BB3:.*]]
// CHECK: ^[[BB2]]:
// CHECK: cf.br ^[[BB3]]
// CHECK: ^[[BB3]]:
// CHECK: scf.yield %[[C11]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_cfg_with_store(%cond0: i1, %cond1: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%c9 = arith.constant 9 : i32
%c11 = arith.constant 11 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
cf.cond_br %cond0, ^bb1, ^bb2
^bb1:
memref.store %c7, %alloca[] : memref<i32>
cf.br ^bb3
^bb2:
memref.store %c9, %alloca[] : memref<i32>
cf.br ^bb3
^bb3:
memref.store %c11, %alloca[] : memref<i32>
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check promotion through an execute_region with multiple yield terminators
// having different reaching definitions.
// CHECK-LABEL: func.func @execute_region_multiple_yields
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK-DAG: %[[C9:.*]] = arith.constant 9 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: scf.yield %[[C7]] : i32
// CHECK: ^[[BB2]]:
// CHECK: scf.yield %[[C9]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_multiple_yields(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%c9 = arith.constant 9 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
cf.cond_br %cond, ^bb1, ^bb2
^bb1:
memref.store %c7, %alloca[] : memref<i32>
scf.yield
^bb2:
memref.store %c9, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check promotion when both yield terminators share the same reaching
// definition from a store in the entry block.
// CHECK-LABEL: func.func @execute_region_same_reaching_def
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: scf.yield %[[C7]] : i32
// CHECK: ^[[BB2]]:
// CHECK: scf.yield %[[C7]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_same_reaching_def(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
memref.store %c7, %alloca[] : memref<i32>
cf.cond_br %cond, ^bb1, ^bb2
^bb1:
scf.yield
^bb2:
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check that a load-then-store of the same slot in the same block is promoted.
// CHECK-LABEL: func.func @execute_region_load_into_store_same_block
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_load_into_store_same_block() -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check that a load-then-store of the same slot across blocks is promoted.
// CHECK-LABEL: func.func @execute_region_load_into_store_diff_block
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.execute_region -> i32 {
// CHECK: cf.cond_br %[[COND]], ^[[BB1:.*]], ^[[BB2:.*]]
// CHECK: ^[[BB1]]:
// CHECK: scf.yield %[[C5]] : i32
// CHECK: ^[[BB2]]:
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @execute_region_load_into_store_diff_block(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.execute_region {
%loaded = memref.load %alloca[] : memref<i32>
cf.cond_br %cond, ^bb1, ^bb2
^bb1:
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
^bb2:
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check promotion through a for loop with a load and store in the body.
// CHECK-LABEL: func.func @for_load_and_store
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
// CHECK: %[[RES:.*]] = scf.for %{{.*}} = %[[LB]] to %[[UB]] step %[[STEP]] iter_args(%[[ARG:.*]] = %[[C5]]) -> (i32) {
// CHECK: %[[NEW:.*]] = arith.addi %[[ARG]], %[[C1]] : i32
// CHECK: scf.yield %[[NEW]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @for_load_and_store(%lb: index, %ub: index, %step: index) -> i32 {
%c5 = arith.constant 5 : i32
%c1 = arith.constant 1 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.for %i = %lb to %ub step %step {
%load = memref.load %alloca[] : memref<i32>
%new = arith.addi %load, %c1 : i32
memref.store %new, %alloca[] : memref<i32>
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check promotion adds a second iter_arg when one already exists.
// CHECK-LABEL: func.func @for_existing_iter_arg
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index, %[[INIT:.*]]: i32)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
// CHECK: %[[RES:.*]]:2 = scf.for %{{.*}} = %[[LB]] to %[[UB]] step %[[STEP]] iter_args(%[[MUL_ARG:.*]] = %[[INIT]], %[[SLOT_ARG:.*]] = %[[C5]]) -> (i32, i32) {
// CHECK: %[[MUL:.*]] = arith.muli %[[MUL_ARG]], %[[MUL_ARG]] : i32
// CHECK: %[[NEW:.*]] = arith.addi %[[SLOT_ARG]], %[[C1]] : i32
// CHECK: scf.yield %[[MUL]], %[[NEW]] : i32, i32
// CHECK: }
// CHECK: return %[[RES]]#1 : i32
func.func @for_existing_iter_arg(%lb: index, %ub: index, %step: index, %init: i32) -> i32 {
%c5 = arith.constant 5 : i32
%c1 = arith.constant 1 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
%mul_res = scf.for %i = %lb to %ub step %step iter_args(%mul_arg = %init) -> i32 {
%mul = arith.muli %mul_arg, %mul_arg : i32
%load = memref.load %alloca[] : memref<i32>
%new = arith.addi %load, %c1 : i32
memref.store %new, %alloca[] : memref<i32>
scf.yield %mul : i32
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check load-only promotion through a for loop generates no iter_arg.
func.func private @use(i32)
// CHECK-LABEL: func.func @for_load_only
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.for %{{.*}} = %[[LB]] to %[[UB]] step %[[STEP]] {
// CHECK: call @use(%[[C5]])
// CHECK: }
// CHECK: return %[[C5]] : i32
func.func @for_load_only(%lb: index, %ub: index, %step: index) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.for %i = %lb to %ub step %step {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check promotion through a for loop with a store inside an if in the body.
// CHECK-LABEL: func.func @for_if_store
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index, %[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.for %{{.*}} = %[[LB]] to %[[UB]] step %[[STEP]] iter_args(%[[ARG:.*]] = %[[C5]]) -> (i32) {
// CHECK: %[[IF:.*]] = scf.if %[[COND]] -> (i32) {
// CHECK: scf.yield %[[C7]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[ARG]] : i32
// CHECK: }
// CHECK: scf.yield %[[IF]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @for_if_store(%lb: index, %ub: index, %step: index, %cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.for %i = %lb to %ub step %step {
scf.if %cond {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check that a load-then-store of the same slot in a for loop is promoted.
// CHECK-LABEL: func.func @for_load_into_store
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.for %{{.*}} = %[[LB]] to %[[UB]] step %[[STEP]] iter_args(%[[ARG:.*]] = %[[C5]]) -> (i32) {
// CHECK: scf.yield %[[ARG]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @for_load_into_store(%lb: index, %ub: index, %step: index) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.for %i = %lb to %ub step %step {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check load promotion through a forall.
func.func private @use(i32)
// CHECK-LABEL: func.func @forall_load
// CHECK-SAME: (%[[UB:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.forall (%{{.*}}) in (%[[UB]]) {
// CHECK: call @use(%[[C5]])
// CHECK: }
func.func @forall_load(%ub: index) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.forall (%i) in (%ub) {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
}
return
}
// -----
// Check promotion through a forall nested inside an if with a store.
func.func private @use(i32)
// CHECK-LABEL: func.func @forall_in_if
// CHECK-SAME: (%[[UB:.*]]: index, %[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.if %[[COND]] -> (i32) {
// CHECK: scf.forall (%{{.*}}) in (%[[UB]]) {
// CHECK: call @use(%[[C7]])
// CHECK: }
// CHECK: scf.yield %[[C7]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @forall_in_if(%ub: index, %cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond {
memref.store %c7, %alloca[] : memref<i32>
scf.forall (%i) in (%ub) {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
}
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check load promotion through a parallel.
func.func private @use(i32)
// CHECK-LABEL: func.func @parallel_load
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.parallel (%{{.*}}) = (%[[LB]]) to (%[[UB]]) step (%[[STEP]]) {
// CHECK: call @use(%[[C5]])
// CHECK: scf.reduce
// CHECK: }
func.func @parallel_load(%lb: index, %ub: index, %step: index) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.parallel (%i) = (%lb) to (%ub) step (%step) {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.reduce
}
return
}
// -----
// Check promotion through a parallel nested inside an if with a store.
func.func private @use(i32)
// CHECK-LABEL: func.func @parallel_in_if
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index, %[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.if %[[COND]] -> (i32) {
// CHECK: scf.parallel (%{{.*}}) = (%[[LB]]) to (%[[UB]]) step (%[[STEP]]) {
// CHECK: call @use(%[[C7]])
// CHECK: scf.reduce
// CHECK: }
// CHECK: scf.yield %[[C7]] : i32
// CHECK: } else {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @parallel_in_if(%lb: index, %ub: index, %step: index, %cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.if %cond {
memref.store %c7, %alloca[] : memref<i32>
scf.parallel (%i) = (%lb) to (%ub) step (%step) {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.reduce
}
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check load promotion inside a reduce region.
// CHECK-LABEL: func.func @parallel_reduce_load
// CHECK-SAME: (%[[LB:.*]]: index, %[[UB:.*]]: index, %[[STEP:.*]]: index)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32
// CHECK: %[[RES:.*]] = scf.parallel (%{{.*}}) = (%[[LB]]) to (%[[UB]]) step (%[[STEP]]) init (%[[C0]]) -> i32 {
// CHECK: %[[C1:.*]] = arith.constant 1 : i32
// CHECK: scf.reduce(%[[C1]] : i32) {
// CHECK: ^{{.*}}(%[[LHS:.*]]: i32, %[[RHS:.*]]: i32):
// CHECK: %[[SUM:.*]] = arith.addi %[[LHS]], %[[RHS]] : i32
// CHECK: %[[MUL:.*]] = arith.muli %[[SUM]], %[[C5]] : i32
// CHECK: scf.reduce.return %[[MUL]] : i32
// CHECK: }
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @parallel_reduce_load(%lb: index, %ub: index, %step: index) -> i32 {
%c5 = arith.constant 5 : i32
%c0 = arith.constant 0 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
%res = scf.parallel (%i) = (%lb) to (%ub) step (%step) init (%c0) -> i32 {
%c1 = arith.constant 1 : i32
scf.reduce(%c1 : i32) {
^bb0(%lhs: i32, %rhs: i32):
%sum = arith.addi %lhs, %rhs : i32
%load = memref.load %alloca[] : memref<i32>
%mul = arith.muli %sum, %load : i32
scf.reduce.return %mul : i32
}
}
return %res : i32
}
// -----
// Check load promotion in the before region of a while.
func.func private @use(i32)
// CHECK-LABEL: func.func @while_load_before
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.while : () -> () {
// CHECK: call @use(%[[C5]])
// CHECK: scf.condition(%[[COND]])
// CHECK: } do {
// CHECK: scf.yield
// CHECK: }
func.func @while_load_before(%cond: i1) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.condition(%cond)
} do {
scf.yield
}
return
}
// -----
// Check load promotion in the after region of a while.
func.func private @use(i32)
// CHECK-LABEL: func.func @while_load_after
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.while : () -> () {
// CHECK: scf.condition(%[[COND]])
// CHECK: } do {
// CHECK: call @use(%[[C5]])
// CHECK: scf.yield
// CHECK: }
func.func @while_load_after(%cond: i1) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
scf.condition(%cond)
} do {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
return
}
// -----
// Check promotion with a store in the before region and a load in the after.
func.func private @use(i32)
// CHECK-LABEL: func.func @while_store_before_load_after
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: scf.while (%[[BEFORE:.*]] = %[[C5]]) : (i32) -> i32 {
// CHECK: scf.condition(%[[COND]]) %[[C7]] : i32
// CHECK: } do {
// CHECK: ^{{.*}}(%[[AFTER:.*]]: i32):
// CHECK: call @use(%[[AFTER]])
// CHECK: scf.yield %[[AFTER]] : i32
// CHECK: }
func.func @while_store_before_load_after(%cond: i1) {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
memref.store %c7, %alloca[] : memref<i32>
scf.condition(%cond)
} do {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
return
}
// -----
// Check promotion with a store in the before region and a load after the loop.
// CHECK-LABEL: func.func @while_store_before_load_after_loop
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.while (%[[BEFORE:.*]] = %[[C5]]) : (i32) -> i32 {
// CHECK: scf.condition(%[[COND]]) %[[C7]] : i32
// CHECK: } do {
// CHECK: ^{{.*}}(%[[AFTER:.*]]: i32):
// CHECK: scf.yield %[[AFTER]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @while_store_before_load_after_loop(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
memref.store %c7, %alloca[] : memref<i32>
scf.condition(%cond)
} do {
scf.yield
}
%res = memref.load %alloca[] : memref<i32>
return %res : i32
}
// -----
// Check store promotion through a while implementing a for loop from 0 to 10.
// CHECK-LABEL: func.func @while_store
// CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32
// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
// CHECK-DAG: %[[C10:.*]] = arith.constant 10 : i32
// CHECK: %[[RES:.*]] = scf.while (%[[BEFORE:.*]] = %[[C0]]) : (i32) -> i32 {
// CHECK: %[[COND:.*]] = arith.cmpi slt, %[[BEFORE]], %[[C10]] : i32
// CHECK: scf.condition(%[[COND]]) %[[BEFORE]] : i32
// CHECK: } do {
// CHECK: ^{{.*}}(%[[AFTER:.*]]: i32):
// CHECK: %[[NEW:.*]] = arith.addi %[[AFTER]], %[[C1]] : i32
// CHECK: scf.yield %[[NEW]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @while_store() -> i32 {
%c0 = arith.constant 0 : i32
%c1 = arith.constant 1 : i32
%c10 = arith.constant 10 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c0, %alloca[] : memref<i32>
scf.while : () -> () {
%val = memref.load %alloca[] : memref<i32>
%cond = arith.cmpi slt, %val, %c10 : i32
scf.condition(%cond)
} do {
%val = memref.load %alloca[] : memref<i32>
%new = arith.addi %val, %c1 : i32
memref.store %new, %alloca[] : memref<i32>
scf.yield
}
%res = memref.load %alloca[] : memref<i32>
return %res : i32
}
// -----
// Check that a load-then-store in the before region of a while is promoted.
// CHECK-LABEL: func.func @while_load_into_store_before
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.while (%[[BEFORE:.*]] = %[[C5]]) : (i32) -> i32 {
// CHECK: scf.condition(%[[COND]]) %[[BEFORE]] : i32
// CHECK: } do {
// CHECK: ^{{.*}}(%[[AFTER:.*]]: i32):
// CHECK: scf.yield %[[AFTER]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @while_load_into_store_before(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.condition(%cond)
} do {
scf.yield
}
%res = memref.load %alloca[] : memref<i32>
return %res : i32
}
// -----
// Check that a load-then-store in the after region of a while is promoted.
// CHECK-LABEL: func.func @while_load_into_store
// CHECK-SAME: (%[[COND:.*]]: i1)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.while (%[[BEFORE:.*]] = %[[C5]]) : (i32) -> i32 {
// CHECK: scf.condition(%[[COND]]) %[[BEFORE]] : i32
// CHECK: } do {
// CHECK: ^{{.*}}(%[[AFTER:.*]]: i32):
// CHECK: scf.yield %[[AFTER]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @while_load_into_store(%cond: i1) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.while : () -> () {
scf.condition(%cond)
} do {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
%res = memref.load %alloca[] : memref<i32>
return %res : i32
}
// -----
// Check load promotion through an index_switch default branch.
func.func private @use(i32)
// CHECK-LABEL: func.func @index_switch_load_default
// CHECK-SAME: (%[[IDX:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: scf.index_switch %[[IDX]]
// CHECK: default {
// CHECK: call @use(%[[C5]])
// CHECK: }
func.func @index_switch_load_default(%idx: index) {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.index_switch %idx
default {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
return
}
// -----
// Check store promotion through an index_switch default branch.
// CHECK-LABEL: func.func @index_switch_store_default
// CHECK-SAME: (%[[IDX:.*]]: index)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.index_switch %[[IDX]] -> i32
// CHECK: default {
// CHECK: scf.yield %[[C7]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @index_switch_store_default(%idx: index) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.index_switch %idx
default {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}
// -----
// Check promotion with a store in a case and a load in the default branch.
func.func private @use(i32)
// CHECK-LABEL: func.func @index_switch_store_case_load_default
// CHECK-SAME: (%[[IDX:.*]]: index)
// CHECK-DAG: %[[C5:.*]] = arith.constant 5 : i32
// CHECK-DAG: %[[C7:.*]] = arith.constant 7 : i32
// CHECK: %[[RES:.*]] = scf.index_switch %[[IDX]] -> i32
// CHECK: case 0 {
// CHECK: scf.yield %[[C7]] : i32
// CHECK: }
// CHECK: default {
// CHECK: call @use(%[[C5]])
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @index_switch_store_case_load_default(%idx: index) -> i32 {
%c5 = arith.constant 5 : i32
%c7 = arith.constant 7 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.index_switch %idx
case 0 {
memref.store %c7, %alloca[] : memref<i32>
scf.yield
}
default {
%load = memref.load %alloca[] : memref<i32>
func.call @use(%load) : (i32) -> ()
scf.yield
}
%load2 = memref.load %alloca[] : memref<i32>
return %load2 : i32
}
// -----
// Check that load-then-store of the same slot in an index_switch is promoted.
// CHECK-LABEL: func.func @index_switch_load_into_store
// CHECK-SAME: (%[[IDX:.*]]: index)
// CHECK: %[[C5:.*]] = arith.constant 5 : i32
// CHECK: %[[RES:.*]] = scf.index_switch %[[IDX]] -> i32
// CHECK: case 0 {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: default {
// CHECK: scf.yield %[[C5]] : i32
// CHECK: }
// CHECK: return %[[RES]] : i32
func.func @index_switch_load_into_store(%idx: index) -> i32 {
%c5 = arith.constant 5 : i32
%alloca = memref.alloca() : memref<i32>
memref.store %c5, %alloca[] : memref<i32>
scf.index_switch %idx
case 0 {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
default {
%loaded = memref.load %alloca[] : memref<i32>
memref.store %loaded, %alloca[] : memref<i32>
scf.yield
}
%load = memref.load %alloca[] : memref<i32>
return %load : i32
}