blob: d4e9a6a4479f3398aba4883513eabf1e957eceaf [file]
// RUN: fir-opt -cuf-alloc-delay --split-input-file %s | FileCheck %s
// Test 1: Basic delay — cuf.alloc of box type is moved to just before
// the first use (AllocatableSetBounds).
func.func @_QPbasic() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFbasicEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFbasicEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c10 = arith.constant 10 : index
%c1 = arith.constant 1 : index
%5 = fir.convert %c1 : (index) -> i32
%6 = fir.convert %c10 : (index) -> i64
fir.call @_FortranAAllocatableSetBounds(%4, %5, %6, %6) : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i32, i64, i64) -> ()
%7 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
return
}
func.func private @_FortranAAllocatableSetBounds(!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i32, i64, i64)
// CHECK-LABEL: func.func @_QPbasic()
// The embox/zero_bits/shape stay at entry; cuf.alloc+store+declare move to
// just before the first use (AllocatableSetBounds).
// CHECK: fir.zero_bits
// CHECK: fir.shape
// CHECK: fir.embox
// CHECK: arith.constant 10
// CHECK: arith.constant 1
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: fir.call @_FortranAAllocatableSetBounds
// CHECK: cuf.allocate
// -----
// Test 2: Non-box cuf.alloc (fixed-size device array) should NOT be moved.
func.func @_QPfixed_array() {
%0 = cuf.alloc !fir.array<10xf32> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFfixed_arrayEa"} -> !fir.ref<!fir.array<10xf32>>
%c10 = arith.constant 10 : index
%1 = fir.shape %c10 : (index) -> !fir.shape<1>
%2 = fir.declare %0(%1) {data_attr = #cuf.cuda<device>, uniq_name = "_QFfixed_arrayEa"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xf32>>
%c42 = arith.constant 42 : index
return
}
// CHECK-LABEL: func.func @_QPfixed_array()
// CHECK: cuf.alloc !fir.array<10xf32>
// CHECK: arith.constant 10
// CHECK: fir.shape
// CHECK: fir.declare
// CHECK: arith.constant 42
// -----
// Test 3: First use inside fir.if — cuf.alloc should be moved before the
// fir.if, not inside it.
func.func @_QPuse_in_if(%arg0: !fir.ref<!fir.logical<4>>) {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFuse_in_ifEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFuse_in_ifEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c42 = arith.constant 42 : index
%5 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
%6 = fir.convert %5 : (!fir.logical<4>) -> i1
fir.if %6 {
%7 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
}
return
}
// CHECK-LABEL: func.func @_QPuse_in_if
// cuf.alloc+store+declare move to just before fir.if (not inside it).
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 42
// CHECK: fir.load
// CHECK: fir.convert
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: fir.if
// CHECK: cuf.allocate
// -----
// Test 4: cuf.free stays at function end after the alloc is delayed.
func.func @_QPwith_free() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFwith_freeEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFwith_freeEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c10 = arith.constant 10 : index
%c1 = arith.constant 1 : index
%5 = fir.convert %c1 : (index) -> i32
%6 = fir.convert %c10 : (index) -> i64
fir.call @_FortranAAllocatableSetBounds(%4, %5, %6, %6) : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i32, i64, i64) -> ()
%7 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
func.func private @_FortranAAllocatableSetBounds(!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i32, i64, i64)
// CHECK-LABEL: func.func @_QPwith_free()
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: fir.call @_FortranAAllocatableSetBounds
// CHECK: cuf.allocate
// CHECK: cuf.free
// CHECK: return
// -----
// Test 5: Multiple allocatable descriptors are delayed independently.
func.func @_QPmultiple() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFmultipleEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFmultipleEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%10 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> {bindc_name = "b", data_attr = #cuf.cuda<device>, uniq_name = "_QFmultipleEb"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%11 = fir.zero_bits !fir.heap<!fir.array<?xf64>>
%12 = fir.shape %c0 : (index) -> !fir.shape<1>
%13 = fir.embox %11(%12) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf64>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf64>>>
fir.store %13 to %10 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%14 = fir.declare %10 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFmultipleEb"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c42 = arith.constant 42 : index
%20 = cuf.allocate %14 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>> {data_attr = #cuf.cuda<device>} -> i32
%c99 = arith.constant 99 : index
%21 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
cuf.free %14 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>> {data_attr = #cuf.cuda<device>}
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
// CHECK-LABEL: func.func @_QPmultiple()
// Both allocs move past the constants to just before their first real use.
// "b" is used first (cuf.allocate %b), "a" is used later (cuf.allocate %a).
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 42
// CHECK: cuf.alloc {{.*}}uniq_name = "_QFmultipleEb"
// CHECK: fir.store
// CHECK: fir.declare {{.*}}uniq_name = "_QFmultipleEb"
// CHECK: cuf.allocate
// CHECK: arith.constant 99
// CHECK: cuf.alloc {{.*}}uniq_name = "_QFmultipleEa"
// CHECK: fir.store
// CHECK: fir.declare {{.*}}uniq_name = "_QFmultipleEa"
// CHECK: cuf.allocate
// CHECK: cuf.free
// CHECK: cuf.free
// CHECK: return
// -----
// Test 6: Dead descriptor (declare with only cuf.free user) — still moves
// to just before the free.
func.func @_QPdead_var() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFdead_varEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFdead_varEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c42 = arith.constant 42 : index
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
// CHECK-LABEL: func.func @_QPdead_var()
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 42
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: cuf.free
// CHECK: return
// -----
// Test 7: First use inside fir.do_loop — cuf.alloc moves before the loop.
func.func @_QPuse_in_loop() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFuse_in_loopEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFuse_in_loopEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c99 = arith.constant 99 : index
%c1 = arith.constant 1 : index
%c10 = arith.constant 10 : index
fir.do_loop %i = %c1 to %c10 step %c1 {
%5 = fir.load %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
}
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
// CHECK-LABEL: func.func @_QPuse_in_loop()
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 99
// CHECK: arith.constant 1
// CHECK: arith.constant 10
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: fir.do_loop
// CHECK: fir.load
// CHECK: cuf.free
// CHECK: return
// -----
// Test 8: All uses in a successor block (e.g. after a branch from
// stop-in-if). The cuf.alloc should move into that block before the
// first use so it still dominates the successor.
func.func @_QPuse_in_successor(%arg0: !fir.ref<i32>) {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFuse_in_successorEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFuse_in_successorEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c42 = arith.constant 42 : index
%5 = fir.load %arg0 : !fir.ref<i32>
%c0_i32 = arith.constant 0 : i32
%6 = arith.cmpi slt, %5, %c0_i32 : i32
cf.cond_br %6, ^bb1, ^bb2
^bb1:
fir.call @_FortranAStopStatementText(%c0_i32, %c0_i32) : (i32, i32) -> none
fir.unreachable
^bb2:
%7 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
func.func private @_FortranAStopStatementText(i32, i32) -> none
func.func private @_FortranAAllocatableSetBounds(!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>, i32, i64, i64)
// CHECK-LABEL: func.func @_QPuse_in_successor
// All uses are in ^bb2 only, so cuf.alloc+store+declare move directly
// into ^bb2 before the first use.
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 42
// CHECK: fir.load
// CHECK: arith.cmpi
// CHECK: cf.cond_br
// CHECK: ^bb1:
// CHECK: fir.unreachable
// CHECK: ^bb2:
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: cuf.allocate
// CHECK: cuf.free
// CHECK: return
// -----
// Test 9: Uses in multiple successor blocks — cuf.alloc moves to just
// before the entry block's terminator (the branch) so it dominates both.
func.func @_QPuse_in_multi_successor(%arg0: !fir.ref<i32>) {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFuse_in_multi_successorEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFuse_in_multi_successorEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%c42 = arith.constant 42 : index
%5 = fir.load %arg0 : !fir.ref<i32>
%c0_i32 = arith.constant 0 : i32
%6 = arith.cmpi slt, %5, %c0_i32 : i32
cf.cond_br %6, ^bb1, ^bb2
^bb1:
%7 = fir.load %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
^bb2:
%8 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
// CHECK-LABEL: func.func @_QPuse_in_multi_successor
// Uses span ^bb1 and ^bb2 — alloc moves to before the entry block's
// terminator (cf.cond_br).
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: arith.constant 42
// CHECK: fir.load
// CHECK: arith.constant 0
// CHECK: arith.cmpi
// CHECK: cuf.alloc
// CHECK: fir.store
// CHECK: fir.declare
// CHECK: cf.cond_br
// CHECK: ^bb1:
// CHECK: fir.load
// CHECK: cuf.free
// CHECK: ^bb2:
// CHECK: cuf.allocate
// CHECK: cuf.free
// -----
// Test 10: A host-association store (fir.store to fir.llvm_ptr) is a use, so
// the group sinks to just before it and the store is not moved.
func.func @_QPhost_assoc() {
%tuple = fir.alloca tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%c0_i32 = arith.constant 0 : i32
%slot = fir.coordinate_of %tuple, %c0_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFhost_assocEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 2 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFhost_assocEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
fir.store %4 to %slot : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%c99 = arith.constant 99 : index
%5 = cuf.allocate %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>} -> i32
fir.call @_QFPcontained(%tuple) : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>) -> ()
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<device>}
return
}
func.func private @_QFPcontained(!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>)
// CHECK-LABEL: func.func @_QPhost_assoc
// CHECK: fir.alloca
// CHECK: fir.coordinate_of
// CHECK: fir.zero_bits
// CHECK: fir.embox
// CHECK: cuf.alloc
// CHECK: fir.store {{.*}} : !fir.ref<!fir.box
// CHECK: fir.declare
// CHECK: fir.store {{.*}} : !fir.llvm_ptr
// CHECK: arith.constant 99
// CHECK: cuf.allocate
// CHECK: fir.call @_QFPcontained
// CHECK: cuf.free
// CHECK: return
// -----
// Test 11: The descriptor's only entry-block use is its host-association store
// (the allocate happens inside the callee). The group and store stay before the
// call, so the callee sees an initialized descriptor.
func.func @_QPhost_assoc_call() {
%tuple = fir.alloca tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%c0_i32 = arith.constant 0 : i32
%slot = fir.coordinate_of %tuple, %c0_i32 : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<managed>, uniq_name = "_QFhost_assoc_callEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 3 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFhost_assoc_callEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
fir.store %4 to %slot : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
fir.call @_QFPgo(%tuple) : (!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>) -> ()
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<managed>}
return
}
func.func private @_QFPgo(!fir.ref<tuple<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>)
// CHECK-LABEL: func.func @_QPhost_assoc_call
// CHECK: cuf.alloc
// CHECK: fir.store {{.*}} : !fir.ref<!fir.box
// CHECK: fir.declare
// CHECK: fir.store {{.*}} : !fir.llvm_ptr
// CHECK: fir.call @_QFPgo
// CHECK: cuf.free
// CHECK: return
// -----
// Test 12: A shared tuple whose slots fill in order, so the descriptor's own
// slot (index 1) is defined after an earlier coordinate_of of the same tuple
// (index 0). The group must sink to before its host-association store.
func.func @_QPhost_assoc_late_slot() {
%tuple = fir.alloca tuple<!fir.ref<i32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
%c0_i32 = arith.constant 0 : i32
%c1_i32 = arith.constant 1 : i32
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf32>>> {bindc_name = "a", data_attr = #cuf.cuda<managed>, uniq_name = "_QFhost_assoc_late_slotEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%1 = fir.zero_bits !fir.heap<!fir.array<?xf32>>
%c0 = arith.constant 0 : index
%2 = fir.shape %c0 : (index) -> !fir.shape<1>
%3 = fir.embox %1(%2) {allocator_idx = 3 : i32} : (!fir.heap<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.box<!fir.heap<!fir.array<?xf32>>>
fir.store %3 to %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%4 = fir.declare %0 {data_attr = #cuf.cuda<managed>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFhost_assoc_late_slotEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
%slot0 = fir.coordinate_of %tuple, %c0_i32 : (!fir.ref<tuple<!fir.ref<i32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<i32>>
%scalar = fir.alloca i32
fir.store %scalar to %slot0 : !fir.llvm_ptr<!fir.ref<i32>>
%slot1 = fir.coordinate_of %tuple, %c1_i32 : (!fir.ref<tuple<!fir.ref<i32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>, i32) -> !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
fir.store %4 to %slot1 : !fir.llvm_ptr<!fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>
fir.call @_QFPgo2(%tuple) : (!fir.ref<tuple<!fir.ref<i32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>) -> ()
cuf.free %4 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>> {data_attr = #cuf.cuda<managed>}
return
}
func.func private @_QFPgo2(!fir.ref<tuple<!fir.ref<i32>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>>>)
// CHECK-LABEL: func.func @_QPhost_assoc_late_slot
// CHECK: fir.coordinate_of {{.*}} -> !fir.llvm_ptr<!fir.ref<i32>>
// CHECK: fir.store {{.*}} : !fir.llvm_ptr<!fir.ref<i32>>
// CHECK: fir.coordinate_of {{.*}} -> !fir.llvm_ptr<!fir.ref<!fir.box
// CHECK: cuf.alloc
// CHECK: fir.store {{.*}} : !fir.ref<!fir.box
// CHECK: fir.declare
// CHECK: fir.store {{.*}} : !fir.llvm_ptr<!fir.ref<!fir.box
// CHECK: fir.call @_QFPgo2
// CHECK: cuf.free
// CHECK: return