// RUN: mlir-opt %s --pass-pipeline='builtin.module(func.func(mem2reg{region-simplify=false}))' --split-input-file | FileCheck %s // CHECK-LABEL: func.func @basic func.func @basic() -> i32 { // CHECK-NOT: = memref.alloca // CHECK: %[[RES:.*]] = arith.constant 5 : i32 // CHECK-NOT: = memref.alloca %0 = arith.constant 5 : i32 %1 = memref.alloca() : memref memref.store %0, %1[] : memref %2 = memref.load %1[] : memref // CHECK: return %[[RES]] : i32 return %2 : i32 } // ----- // CHECK-LABEL: func.func @basic_default func.func @basic_default() -> i32 { // CHECK-NOT: = memref.alloca // CHECK: %[[RES:.*]] = ub.poison : i32 // CHECK-NOT: = memref.alloca %0 = arith.constant 5 : i32 %1 = memref.alloca() : memref %2 = memref.load %1[] : memref // CHECK-NOT: memref.store memref.store %0, %1[] : memref // CHECK: return %[[RES]] : i32 return %2 : i32 } // ----- // CHECK-LABEL: func.func @basic_float func.func @basic_float() -> f32 { // CHECK-NOT: = memref.alloca // CHECK: %[[RES:.*]] = arith.constant {{.*}} : f32 %0 = arith.constant 5.2 : f32 // CHECK-NOT: = memref.alloca %1 = memref.alloca() : memref memref.store %0, %1[] : memref %2 = memref.load %1[] : memref // CHECK: return %[[RES]] : f32 return %2 : f32 } // ----- // CHECK-LABEL: func.func @basic_ranked func.func @basic_ranked() -> i32 { // CHECK-NOT: = memref.alloca // CHECK: %[[RES:.*]] = arith.constant 5 : i32 // CHECK-NOT: = memref.alloca %0 = arith.constant 0 : index %1 = arith.constant 5 : i32 %2 = memref.alloca() : memref<1x1xi32> memref.store %1, %2[%0, %0] : memref<1x1xi32> %3 = memref.load %2[%0, %0] : memref<1x1xi32> // CHECK: return %[[RES]] : i32 return %3 : i32 } // ----- // CHECK-LABEL: func.func @reject_multiple_elements func.func @reject_multiple_elements() -> i32 { // CHECK: %[[INDEX:.*]] = arith.constant 0 : index %0 = arith.constant 0 : index // CHECK: %[[STORED:.*]] = arith.constant 5 : i32 %1 = arith.constant 5 : i32 // CHECK: %[[ALLOCA:.*]] = memref.alloca() %2 = memref.alloca() : memref<1x2xi32> // CHECK: memref.store %[[STORED]], %[[ALLOCA]][%[[INDEX]], %[[INDEX]]] memref.store %1, %2[%0, %0] : memref<1x2xi32> // CHECK: %[[RES:.*]] = memref.load %[[ALLOCA]][%[[INDEX]], %[[INDEX]]] %3 = memref.load %2[%0, %0] : memref<1x2xi32> // CHECK: return %[[RES]] : i32 return %3 : i32 } // ----- // CHECK-LABEL: func.func @cycle // CHECK-SAME: (%[[ARG0:.*]]: i64, %[[ARG1:.*]]: i1, %[[ARG2:.*]]: i64) func.func @cycle(%arg0: i64, %arg1: i1, %arg2: i64) { // CHECK-NOT: = memref.alloca %alloca = memref.alloca() : memref memref.store %arg2, %alloca[] : memref // CHECK: cf.cond_br %[[ARG1:.*]], ^[[BB1:.*]](%[[ARG2]] : i64), ^[[BB2:.*]](%[[ARG2]] : i64) cf.cond_br %arg1, ^bb1, ^bb2 // CHECK: ^[[BB1]](%[[USE:.*]]: i64): ^bb1: %use = memref.load %alloca[] : memref // CHECK: call @use(%[[USE]]) func.call @use(%use) : (i64) -> () memref.store %arg0, %alloca[] : memref // CHECK: cf.br ^[[BB2]](%[[ARG0]] : i64) cf.br ^bb2 // CHECK: ^[[BB2]](%[[FWD:.*]]: i64): ^bb2: // CHECK: cf.br ^[[BB1]](%[[FWD]] : i64) cf.br ^bb1 } func.func @use(%arg: i64) { return } // ----- // CHECK-LABEL: func.func @recursive // CHECK-SAME: (%[[ARG:.*]]: i64) func.func @recursive(%arg: i64) -> i64 { // CHECK-NOT: = memref.alloca() %alloca0 = memref.alloca() : memref>> %alloca1 = memref.alloca() : memref> %alloca2 = memref.alloca() : memref memref.store %arg, %alloca2[] : memref memref.store %alloca2, %alloca1[] : memref> memref.store %alloca1, %alloca0[] : memref>> %load0 = memref.load %alloca0[] : memref>> %load1 = memref.load %load0[] : memref> %load2 = memref.load %load1[] : memref // CHECK: return %[[ARG]] : i64 return %load2 : i64 } // ----- // CHECK-LABEL: func.func @deny_store_of_alloca // CHECK-SAME: (%[[ARG:.*]]: memref>) func.func @deny_store_of_alloca(%arg: memref>) -> i32 { // CHECK: %[[VALUE:.*]] = arith.constant 5 : i32 %0 = arith.constant 5 : i32 // CHECK: %[[ALLOCA:.*]] = memref.alloca %1 = memref.alloca() : memref // Storing into the memref is allowed. // CHECK: memref.store %[[VALUE]], %[[ALLOCA]][] memref.store %0, %1[] : memref // Storing the memref itself is NOT allowed. // CHECK: memref.store %[[ALLOCA]], %[[ARG]][] memref.store %1, %arg[] : memref> // CHECK: %[[RES:.*]] = memref.load %[[ALLOCA]][] %2 = memref.load %1[] : memref // CHECK: return %[[RES]] : i32 return %2 : i32 } // ----- // CHECK-LABEL: func.func @promotable_nonpromotable_intertwined func.func @promotable_nonpromotable_intertwined() -> i32 { // CHECK: %[[NON_PROMOTED:.*]] = memref.alloca() : memref %0 = memref.alloca() : memref // CHECK-NOT: = memref.alloca() : memref> %1 = memref.alloca() : memref> memref.store %0, %1[] : memref> %2 = memref.load %1[] : memref> // CHECK: call @use(%[[NON_PROMOTED]]) call @use(%0) : (memref) -> () // CHECK: %[[RES:.*]] = memref.load %[[NON_PROMOTED]][] %3 = memref.load %0[] : memref // CHECK: return %[[RES]] : i32 return %3 : i32 } func.func @use(%arg: memref) { return } // ----- // CHECK-LABEL: func.func @unused_alloca_store_loop func.func @unused_alloca_store_loop() { // CHECK-NOT: memref.alloca %cst = arith.constant 1 : i32 %alloca = memref.alloca() : memref // CHECK: cf.br ^[[BB1:.*]] cf.br ^bb1 // CHECK: ^[[BB1]]: ^bb1: // CHECK-NOT: memref.store memref.store %cst, %alloca[] : memref // CHECK: cf.br ^[[BB1]] cf.br ^bb1 } // ----- // CHECK-LABEL: func.func @store_back_to_alloca // CHECK-SAME: (%[[COND:.*]]: i1) func.func @store_back_to_alloca(%cond: i1) -> i32 { // CHECK-NOT: memref.alloca // CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32 %c0 = arith.constant 0 : i32 // CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32 %c1 = arith.constant 1 : i32 // CHECK-NOT: memref.alloca %alloca = memref.alloca() : memref memref.store %c0, %alloca[] : memref %loaded = memref.load %alloca[] : memref // CHECK: cf.cond_br %[[COND]], ^[[STORE_BACK:.*]], ^[[SKIP:.*]] cf.cond_br %cond, ^store_back, ^skip // CHECK: ^[[STORE_BACK]]: ^store_back: memref.store %loaded, %alloca[] : memref // CHECK: cf.br ^[[MERGE:.*]](%[[C0]] : i32) cf.br ^merge // CHECK: ^[[SKIP]]: ^skip: memref.store %c1, %alloca[] : memref // CHECK: cf.br ^[[MERGE]](%[[C1]] : i32) cf.br ^merge // CHECK: ^[[MERGE]](%[[RESULT:.*]]: i32): ^merge: %result = memref.load %alloca[] : memref // CHECK: return %[[RESULT]] : i32 return %result : i32 } // ----- // Ensure that a merge point used by an erased operation is not considered used. // CHECK-LABEL: func.func @merge_point_used_by_erased_op // CHECK-SAME: (%[[COND:.*]]: i1) func.func @merge_point_used_by_erased_op(%cond: i1) -> i32 { // CHECK-NOT: memref.alloca // CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32 %c0 = arith.constant 0 : i32 // CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32 %c1 = arith.constant 1 : i32 // CHECK-NOT: memref.alloca %alloca = memref.alloca() : memref // CHECK: cf.cond_br %[[COND]], ^[[PRED1:.*]], ^[[PRED2:.*]] cf.cond_br %cond, ^pred1, ^pred2 // CHECK: ^[[PRED1]]: ^pred1: memref.store %c0, %alloca[] : memref // CHECK: cf.br ^[[MERGE:.*]]{{$}} cf.br ^merge // CHECK: ^[[PRED2]]: ^pred2: memref.store %c1, %alloca[] : memref // CHECK: cf.br ^[[MERGE]]{{$}} cf.br ^merge // CHECK: ^[[MERGE]]: ^merge: %result = memref.load %alloca[] : memref memref.store %result, %alloca[] : memref // CHECK: return %[[C0]] : i32 return %c0 : i32 } // ----- // Two consecutive merge points: pred1 and pred2 merge at merge1, then merge1 // and pred3 merge at merge2. // CHECK-LABEL: func.func @two_consecutive_merge_points // CHECK-SAME: (%[[COND1:.*]]: i1, %[[COND2:.*]]: i1) func.func @two_consecutive_merge_points(%cond1: i1, %cond2: i1) -> i32 { // CHECK-NOT: memref.alloca // CHECK-DAG: %[[C0:.*]] = arith.constant 0 : i32 // CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32 // CHECK-DAG: %[[C2:.*]] = arith.constant 2 : i32 // CHECK-NOT: memref.alloca %c0 = arith.constant 0 : i32 %c1 = arith.constant 1 : i32 %c2 = arith.constant 2 : i32 %alloca = memref.alloca() : memref // CHECK: cf.cond_br %[[COND1]], ^[[PRED1:.*]], ^[[MID:.*]] cf.cond_br %cond1, ^pred1, ^mid // CHECK: ^[[MID]]: ^mid: // CHECK: cf.cond_br %[[COND2]], ^[[PRED2:.*]], ^[[PRED3:.*]] cf.cond_br %cond2, ^pred2, ^pred3 // CHECK: ^[[PRED1]]: ^pred1: memref.store %c0, %alloca[] : memref // CHECK: cf.br ^[[MERGE1:.*]](%[[C0]] : i32) cf.br ^merge1 // CHECK: ^[[PRED2]]: ^pred2: memref.store %c1, %alloca[] : memref // CHECK: cf.br ^[[MERGE1]](%[[C1]] : i32) cf.br ^merge1 // CHECK: ^[[MERGE1]](%[[MARG:.*]]: i32): ^merge1: // CHECK: cf.br ^[[MERGE2:.*]](%[[MARG]] : i32) cf.br ^merge2 // CHECK: ^[[PRED3]]: ^pred3: memref.store %c2, %alloca[] : memref // CHECK: cf.br ^[[MERGE2]](%[[C2]] : i32) cf.br ^merge2 // CHECK: ^[[MERGE2]](%[[RESULT:.*]]: i32): ^merge2: %result = memref.load %alloca[] : memref // CHECK: return %[[RESULT]] : i32 return %result : i32 }