| /// Verify that fir.convert are only generated one per fir.declare and that | |
| /// optional conversions are optimized appropriately. | |
| /// | |
| /// RUN: fir-opt --enable-fir-convert-opts %s --fir-to-memref --allow-unregistered-dialect | FileCheck %s | |
| // CHECK-LABEL: func.func @load_scalar | |
| // CHECK-COUNT-1: fir.convert | |
| // CHECK-NOT: fir.convert | |
| func.func @load_scalar(%arg0: !fir.ref<f32>) { | |
| %0 = fir.undefined !fir.dscope | |
| %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "a"} : | |
| (!fir.ref<f32>, !fir.dscope) -> !fir.ref<f32> | |
| %2 = fir.load %1 : !fir.ref<f32> | |
| %3 = fir.load %1 : !fir.ref<f32> | |
| return | |
| } | |
| // CHECK-LABEL: func.func @load_array1d | |
| // CHECK-COUNT-1: fir.convert | |
| // CHECK-NOT: fir.convert | |
| func.func @load_array1d(%arg0: !fir.ref<!fir.array<3xf32>>) { | |
| %c1 = arith.constant 1 : index | |
| %c3 = arith.constant 3 : index | |
| %0 = fir.undefined !fir.dscope | |
| %shape = fir.shape %c3 : (index) -> !fir.shape<1> | |
| %1 = fir.declare %arg0(%shape) dummy_scope %0 {uniq_name = "a"} : | |
| (!fir.ref<!fir.array<3xf32>>, !fir.shape<1>, !fir.dscope) -> | |
| !fir.ref<!fir.array<3xf32>> | |
| %2 = fir.array_coor %1(%shape) %c1 : | |
| (!fir.ref<!fir.array<3xf32>>, !fir.shape<1>, index) -> !fir.ref<f32> | |
| %3 = fir.load %2 : !fir.ref<f32> | |
| %4 = fir.array_coor %1(%shape) %c1 : | |
| (!fir.ref<!fir.array<3xf32>>, !fir.shape<1>, index) -> !fir.ref<f32> | |
| %5 = fir.load %4 : !fir.ref<f32> | |
| return | |
| } | |
| // CHECK-LABEL: func.func @store_scalar | |
| // CHECK-COUNT-1: fir.convert | |
| // CHECK-NOT: fir.convert | |
| func.func @store_scalar(%arg0: !fir.ref<i32>) { | |
| %c7_i32 = arith.constant 7 : i32 | |
| %0 = fir.undefined !fir.dscope | |
| %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "a"} : | |
| (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> | |
| fir.store %c7_i32 to %1 : !fir.ref<i32> | |
| %2 = fir.load %1 : !fir.ref<i32> | |
| return | |
| } | |
| // CHECK-LABEL: func.func @store_array1d | |
| // CHECK-COUNT-1: fir.convert | |
| // CHECK-NOT: fir.convert | |
| func.func @store_array1d(%arg0: !fir.ref<!fir.array<3xi32>>) { | |
| %c1 = arith.constant 1 : index | |
| %c7_i32 = arith.constant 7 : i32 | |
| %c3 = arith.constant 3 : index | |
| %0 = fir.undefined !fir.dscope | |
| %shape = fir.shape %c3 : (index) -> !fir.shape<1> | |
| %1 = fir.declare %arg0(%shape) dummy_scope %0 {uniq_name = "a"} : | |
| (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>, !fir.dscope) -> | |
| !fir.ref<!fir.array<3xi32>> | |
| %2 = fir.array_coor %1(%shape) %c1 : | |
| (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>, index) -> !fir.ref<i32> | |
| fir.store %c7_i32 to %2 : !fir.ref<i32> | |
| %3 = fir.array_coor %1(%shape) %c1 : | |
| (!fir.ref<!fir.array<3xi32>>, !fir.shape<1>, index) -> !fir.ref<i32> | |
| %4 = fir.load %3 : !fir.ref<i32> | |
| return | |
| } | |
| // When Present() checks the same optional memref than the one accessed inside | |
| // the if statement, the convert is hoisted near the if statement. | |
| // CHECK-LABEL: func.func @optional_optimized | |
| // CHECK: [[DECLARE0:%[0-9]]] = fir.declare | |
| // CHECK: [[DECLARE:%[0-9]]] = fir.declare %arg0 | |
| // CHECK: [[PRESENT:%[0-9]]] = fir.is_present [[DECLARE]] | |
| // CHECK-NEXT: scf.if [[PRESENT]] | |
| // CHECK: [[BOXADDR:%[0-9]+]] = fir.box_addr [[DECLARE]] | |
| // CHECK: [[CONVERT:%[0-9]+]] = fir.convert [[BOXADDR]] : (!fir.ref<!fir.array<?x!fir.logical<4>>>) -> memref<?xi32> | |
| func.func @optional_optimized(%arg0: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.optional}) { | |
| %c1 = arith.constant 1 : index | |
| %c3 = arith.constant 3 : index | |
| %c0 = arith.constant 0 : index | |
| %0 = fir.undefined !fir.dscope | |
| %1 = fir.alloca i32 {uniq_name = "i"} | |
| %2 = fir.declare %1 {uniq_name = "i"} : (!fir.ref<i32>) -> !fir.ref<i32> | |
| %3 = fir.declare %arg0 dummy_scope %0 {uniq_name = "r"} : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.dscope) -> | |
| !fir.box<!fir.array<?x!fir.logical<4>>> | |
| %6 = fir.is_present %3 : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> i1 | |
| scf.if %6 { | |
| %7 = arith.index_cast %c1 : index to i32 | |
| %c1_0 = arith.constant 1 : index | |
| %8 = arith.addi %c3, %c1_0 : index | |
| %9 = scf.for %arg1 = %c1 to %8 step %c1 iter_args(%arg2 = %7) -> (i32) { | |
| fir.store %arg2 to %2 : !fir.ref<i32> | |
| %10 = fir.load %2 : !fir.ref<i32> | |
| %11 = arith.extsi %10 : i32 to i64 | |
| %box_addr = fir.box_addr %3 : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>) -> | |
| !fir.ref<!fir.array<?x!fir.logical<4>>> | |
| %lb, %extent, %stride = fir.box_dims %3, %c0 : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>, index) -> | |
| (index, index, index) | |
| %shape = fir.shape %extent : (index) -> !fir.shape<1> | |
| %12 = fir.array_coor %box_addr(%shape) %11 : | |
| (!fir.ref<!fir.array<?x!fir.logical<4>>>, !fir.shape<1>, i64) -> | |
| !fir.ref<!fir.logical<4>> | |
| %13 = fir.load %12 : !fir.ref<!fir.logical<4>> | |
| %15 = arith.addi %arg1, %c1 : index | |
| %16 = fir.load %2 : !fir.ref<i32> | |
| %17 = arith.addi %16, %7 : i32 | |
| scf.yield %17 : i32 | |
| } | |
| fir.store %9 to %2 : !fir.ref<i32> | |
| } else { | |
| } | |
| return | |
| } | |
| // When optional memref access is not control dependent on a check of it, no | |
| // hoisting is applied and the convert is placed closer to the load/store | |
| // (inside the loop). | |
| // CHECK-LABEL: func.func @optional | |
| // CHECK: [[DECLARE1:%[0-9]]] = fir.declare %arg1 | |
| // CHECK: [[DECLARE2:%[0-9]]] = fir.declare | |
| // CHECK: [[DECLARE0:%[0-9]]] = fir.declare %arg0 | |
| // CHECK: [[PRESENT:%[0-9]]] = fir.is_present [[DECLARE1]] | |
| // CHECK: scf.if [[PRESENT]] | |
| // CHECK: scf.for | |
| // CHECK: [[BOXADDR:%.+]] = fir.box_addr [[DECLARE0]] | |
| // CHECK: [[CONVERT:%.+]] = fir.convert [[BOXADDR]] : (!fir.ref<!fir.array<?x!fir.logical<4>>>) -> memref<?xi32> | |
| func.func @optional(%arg0: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.optional}, | |
| %arg1: !fir.ref<!fir.logical<4>> {fir.optional}) { | |
| %c1 = arith.constant 1 : index | |
| %c3 = arith.constant 3 : index | |
| %c0 = arith.constant 0 : index | |
| %0 = fir.undefined !fir.dscope | |
| %1 = fir.declare %arg1 dummy_scope %0 {uniq_name = "d"} : | |
| (!fir.ref<!fir.logical<4>>, !fir.dscope) -> !fir.ref<!fir.logical<4>> | |
| %2 = fir.alloca i32 {uniq_name = "i"} | |
| %3 = fir.declare %2 {uniq_name = "i"} : (!fir.ref<i32>) -> !fir.ref<i32> | |
| %4 = fir.declare %arg0 dummy_scope %0 {uniq_name = "r"} : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.dscope) -> | |
| !fir.box<!fir.array<?x!fir.logical<4>>> | |
| %7 = fir.is_present %1 : (!fir.ref<!fir.logical<4>>) -> i1 | |
| scf.if %7 { | |
| %8 = arith.index_cast %c1 : index to i32 | |
| %c1_0 = arith.constant 1 : index | |
| %9 = arith.addi %c3, %c1_0 : index | |
| %10 = scf.for %arg2 = %c1 to %9 step %c1 iter_args(%arg3 = %8) -> (i32) { | |
| fir.store %arg3 to %3 : !fir.ref<i32> | |
| %11 = fir.load %3 : !fir.ref<i32> | |
| %12 = arith.extsi %11 : i32 to i64 | |
| %box_addr = fir.box_addr %4 : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>) -> | |
| !fir.ref<!fir.array<?x!fir.logical<4>>> | |
| %lb, %extent, %stride = fir.box_dims %4, %c0 : | |
| (!fir.box<!fir.array<?x!fir.logical<4>>>, index) -> | |
| (index, index, index) | |
| %shape = fir.shape %extent : (index) -> !fir.shape<1> | |
| %13 = fir.array_coor %box_addr(%shape) %12 : | |
| (!fir.ref<!fir.array<?x!fir.logical<4>>>, !fir.shape<1>, i64) -> | |
| !fir.ref<!fir.logical<4>> | |
| %14 = fir.load %13 : !fir.ref<!fir.logical<4>> | |
| %16 = arith.addi %arg2, %c1 : index | |
| %17 = fir.load %3 : !fir.ref<i32> | |
| %18 = arith.addi %17, %8 : i32 | |
| scf.yield %18 : i32 | |
| } | |
| fir.store %10 to %3 : !fir.ref<i32> | |
| } else { | |
| } | |
| return | |
| } | |
| // Derived from a real-world example: ensure that we only generate one convert | |
| // for an absent optional argument and reuse it for multiple loads. | |
| // CHECK-LABEL: func.func @optional_declare | |
| // CHECK: [[ABSENT:%[0-9]+]] = fir.absent !fir.ref<i32> | |
| // CHECK: [[DUMMY:%[0-9]+]] = fir.undefined !fir.dscope | |
| // CHECK: [[DECLARE0:%[0-9]+]] = fir.declare [[ABSENT]] dummy_scope [[DUMMY]] | |
| // CHECK: [[PRESENT:%[0-9]+]] = fir.is_present [[DECLARE0]] | |
| // CHECK: scf.if [[PRESENT]] | |
| // CHECK: [[CONVERT0:%.+]] = fir.convert [[DECLARE0]] : (!fir.ref<i32>) -> memref<i32> | |
| // CHECK: memref.load [[CONVERT0]][] : memref<i32> | |
| func.func @optional_declare() { | |
| %c1 = arith.constant 1 : index | |
| %c0_i32 = arith.constant 0 : i32 | |
| %5 = fir.absent !fir.ref<i32> | |
| %6 = fir.undefined !fir.dscope | |
| %7 = fir.declare %5 dummy_scope %6 {uniq_name = "_QFFtestEd"} : | |
| (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> | |
| %9 = fir.is_present %7 : (!fir.ref<i32>) -> i1 | |
| scf.if %9 { | |
| %10 = fir.load %7 : !fir.ref<i32> | |
| %11 = arith.cmpi eq, %10, %c0_i32 : i32 | |
| scf.if %11 { | |
| %15 = fir.load %7 : !fir.ref<i32> | |
| } | |
| } | |
| return | |
| } | |