blob: 56add8e762ddc1e2665b18522a3104f78eb33b91 [file] [edit]
// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" -split-input-file | FileCheck %s
// Implicit `deviceptr` classification keys on device residency, not device
// accessibility.
//
// A `managed`/`unified` attribute makes storage reachable from the device
// (device-accessible), but the storage is physically shared with the host and may
// migrate on demand, so it is not guaranteed to be in device memory. Such data
// must be mapped so the runtime can attach rather than be treated as `deviceptr`.
//
// `device` (and `constant`/`shared`) storage is physically in device memory and
// is therefore given `deviceptr`.
// A managed POINTER descriptor must be mapped, not deviceptr.
func.func @managed_ptr_desc() {
%0 = cuf.alloc !fir.box<!fir.ptr<!fir.array<?xf64>>> uniq_name("_QFEp") bindc_name("p") data_attr(managed) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_ptr_desc
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// CHECK: acc.copyout accPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) to varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// -----
// A managed ALLOCATABLE descriptor must be mapped, not deviceptr (same rule).
func.func @managed_alloc_desc() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> uniq_name("_QFEa") bindc_name("a") data_attr(managed) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_alloc_desc
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// CHECK: acc.copyout accPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) to varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// -----
// A unified ALLOCATABLE descriptor must be mapped, not deviceptr.
func.func @unified_alloc_desc() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> uniq_name("_QFEu") bindc_name("u") data_attr(unified) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @unified_alloc_desc
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// -----
// A device descriptor is physically in device memory and stays deviceptr.
func.func @device_alloc_desc() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xf64>>> uniq_name("_QFEd") bindc_name("d") data_attr(device) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @device_alloc_desc
// CHECK-NOT: acc.copyin
// CHECK: acc.deviceptr varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) implicit(true)
// -----
// The underlying-storage walk sees through fir.declare (partial-entity access):
// a managed descriptor stays mapped even behind a declare.
func.func @managed_declare() {
%0 = cuf.alloc !fir.box<!fir.ptr<!fir.array<?xf64>>> uniq_name("_QFEp") bindc_name("p") data_attr(managed) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
%1 = fir.declare %0 uniq_name("_QFEp") : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
acc.parallel {
%2 = fir.load %1 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_declare
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)
// -----
// A managed module global reached via fir.address_of must be mapped, not
// deviceptr. The walk follows the address-of edge to the global's attribute.
fir.global @mg_managed {data_attr = #cuf.cuda<managed>} : !fir.array<10xf64> {
%0 = fir.zero_bits !fir.array<10xf64>
fir.has_value %0 : !fir.array<10xf64>
}
func.func @managed_global() {
%0 = fir.address_of(@mg_managed) : !fir.ref<!fir.array<10xf64>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.array<10xf64>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_global
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf64>>) dataClause(acc_copy) implicit(true)
// -----
// A device module global is in device memory and stays deviceptr.
fir.global @mg_device {data_attr = #cuf.cuda<device>} : !fir.array<10xf64> {
%0 = fir.zero_bits !fir.array<10xf64>
fir.has_value %0 : !fir.array<10xf64>
}
func.func @device_global() {
%0 = fir.address_of(@mg_device) : !fir.ref<!fir.array<10xf64>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.array<10xf64>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @device_global
// CHECK-NOT: acc.copyin
// CHECK: acc.deviceptr varPtr(%{{.*}} : !fir.ref<!fir.array<10xf64>>) implicit(true)
// -----
// A managed dummy argument must be mapped, not deviceptr. The walk reads the
// attribute off the enclosing function argument.
func.func @managed_dummy(%arg0: !fir.ref<!fir.array<10xf64>> {cuf.data_attr = #cuf.cuda<managed>, fir.bindc_name = "a"}) {
acc.parallel {
%0 = fir.load %arg0 : !fir.ref<!fir.array<10xf64>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_dummy
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.array<10xf64>>) dataClause(acc_copy) implicit(true)
// -----
// A device dummy argument is in device memory and stays deviceptr.
func.func @device_dummy(%arg0: !fir.ref<!fir.array<10xf64>> {cuf.data_attr = #cuf.cuda<device>, fir.bindc_name = "a"}) {
acc.parallel {
%0 = fir.load %arg0 : !fir.ref<!fir.array<10xf64>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @device_dummy
// CHECK-NOT: acc.copyin
// CHECK: acc.deviceptr varPtr(%{{.*}} : !fir.ref<!fir.array<10xf64>>) implicit(true)
// -----
// A managed polymorphic (CLASS) POINTER descriptor must be mapped, not
// deviceptr -- same rule as the non-polymorphic descriptors.
func.func @managed_class_ptr() {
%0 = cuf.alloc !fir.class<!fir.ptr<!fir.array<?xf64>>> uniq_name("_QFEcp") bindc_name("cp") data_attr(managed) -> !fir.ref<!fir.class<!fir.ptr<!fir.array<?xf64>>>>
acc.parallel {
%1 = fir.load %0 : !fir.ref<!fir.class<!fir.ptr<!fir.array<?xf64>>>>
acc.yield
}
return
}
// CHECK-LABEL: func.func @managed_class_ptr
// CHECK-NOT: acc.deviceptr
// CHECK: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.class<!fir.ptr<!fir.array<?xf64>>>>) dataClause(acc_copy) implicit(true)