/// 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) { %0 = fir.undefined !fir.dscope %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "a"} : (!fir.ref, !fir.dscope) -> !fir.ref %2 = fir.load %1 : !fir.ref %3 = fir.load %1 : !fir.ref return } // CHECK-LABEL: func.func @load_array1d // CHECK-COUNT-1: fir.convert // CHECK-NOT: fir.convert func.func @load_array1d(%arg0: !fir.ref>) { %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.shape<1>, !fir.dscope) -> !fir.ref> %2 = fir.array_coor %1(%shape) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref %3 = fir.load %2 : !fir.ref %4 = fir.array_coor %1(%shape) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref %5 = fir.load %4 : !fir.ref return } // CHECK-LABEL: func.func @store_scalar // CHECK-COUNT-1: fir.convert // CHECK-NOT: fir.convert func.func @store_scalar(%arg0: !fir.ref) { %c7_i32 = arith.constant 7 : i32 %0 = fir.undefined !fir.dscope %1 = fir.declare %arg0 dummy_scope %0 {uniq_name = "a"} : (!fir.ref, !fir.dscope) -> !fir.ref fir.store %c7_i32 to %1 : !fir.ref %2 = fir.load %1 : !fir.ref return } // CHECK-LABEL: func.func @store_array1d // CHECK-COUNT-1: fir.convert // CHECK-NOT: fir.convert func.func @store_array1d(%arg0: !fir.ref>) { %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.shape<1>, !fir.dscope) -> !fir.ref> %2 = fir.array_coor %1(%shape) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref fir.store %c7_i32 to %2 : !fir.ref %3 = fir.array_coor %1(%shape) %c1 : (!fir.ref>, !fir.shape<1>, index) -> !fir.ref %4 = fir.load %3 : !fir.ref 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>>) -> memref func.func @optional_optimized(%arg0: !fir.box>> {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) -> !fir.ref %3 = fir.declare %arg0 dummy_scope %0 {uniq_name = "r"} : (!fir.box>>, !fir.dscope) -> !fir.box>> %6 = fir.is_present %3 : (!fir.box>>) -> 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 %10 = fir.load %2 : !fir.ref %11 = arith.extsi %10 : i32 to i64 %box_addr = fir.box_addr %3 : (!fir.box>>) -> !fir.ref>> %lb, %extent, %stride = fir.box_dims %3, %c0 : (!fir.box>>, index) -> (index, index, index) %shape = fir.shape %extent : (index) -> !fir.shape<1> %12 = fir.array_coor %box_addr(%shape) %11 : (!fir.ref>>, !fir.shape<1>, i64) -> !fir.ref> %13 = fir.load %12 : !fir.ref> %15 = arith.addi %arg1, %c1 : index %16 = fir.load %2 : !fir.ref %17 = arith.addi %16, %7 : i32 scf.yield %17 : i32 } fir.store %9 to %2 : !fir.ref } 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>>) -> memref func.func @optional(%arg0: !fir.box>> {fir.optional}, %arg1: !fir.ref> {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.dscope) -> !fir.ref> %2 = fir.alloca i32 {uniq_name = "i"} %3 = fir.declare %2 {uniq_name = "i"} : (!fir.ref) -> !fir.ref %4 = fir.declare %arg0 dummy_scope %0 {uniq_name = "r"} : (!fir.box>>, !fir.dscope) -> !fir.box>> %7 = fir.is_present %1 : (!fir.ref>) -> 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 %11 = fir.load %3 : !fir.ref %12 = arith.extsi %11 : i32 to i64 %box_addr = fir.box_addr %4 : (!fir.box>>) -> !fir.ref>> %lb, %extent, %stride = fir.box_dims %4, %c0 : (!fir.box>>, index) -> (index, index, index) %shape = fir.shape %extent : (index) -> !fir.shape<1> %13 = fir.array_coor %box_addr(%shape) %12 : (!fir.ref>>, !fir.shape<1>, i64) -> !fir.ref> %14 = fir.load %13 : !fir.ref> %16 = arith.addi %arg2, %c1 : index %17 = fir.load %3 : !fir.ref %18 = arith.addi %17, %8 : i32 scf.yield %18 : i32 } fir.store %10 to %3 : !fir.ref } 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 // 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) -> memref // CHECK: memref.load [[CONVERT0]][] : memref func.func @optional_declare() { %c1 = arith.constant 1 : index %c0_i32 = arith.constant 0 : i32 %5 = fir.absent !fir.ref %6 = fir.undefined !fir.dscope %7 = fir.declare %5 dummy_scope %6 {uniq_name = "_QFFtestEd"} : (!fir.ref, !fir.dscope) -> !fir.ref %9 = fir.is_present %7 : (!fir.ref) -> i1 scf.if %9 { %10 = fir.load %7 : !fir.ref %11 = arith.cmpi eq, %10, %c0_i32 : i32 scf.if %11 { %15 = fir.load %7 : !fir.ref } } return }