Skip to content

Commit 3d6c19f

Browse files
author
Razvan Lupusoru
committed
[flang][acc] Implement PointerLikeType API gen[Allocate/Free/Copy]
Implements genAllocate, genFree, and genCopy for FIR pointer types (fir.ref, fir.ptr, fir.heap, fir.llvm_ptr) in the OpenACC PointerLikeType interface. - genAllocate: Uses fir.alloca for stack types, fir.allocmem for heap types. Returns null for dynamic/unknown types (unlimited polymorphic, dynamic arrays, dynamic character lengths). - genFree: Generates fir.freemem for heap allocations. - genCopy: Uses fir.load+fir.store for trivial types (scalars), hlfir.assign for non-trivial types (arrays, derived types, characters). Returns false for unsupported dynamic types. Adds comprehensive MLIR tests covering various FIR types and edge cases.
1 parent 16dfd31 commit 3d6c19f

File tree

7 files changed

+578
-1
lines changed

7 files changed

+578
-1
lines changed

flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ struct OpenACCPointerLikeModel
2929
getPointeeTypeCategory(mlir::Type pointer,
3030
mlir::TypedValue<mlir::acc::PointerLikeType> varPtr,
3131
mlir::Type varType) const;
32+
33+
mlir::Value genAllocate(mlir::Type pointer, mlir::OpBuilder &builder,
34+
mlir::Location loc, llvm::StringRef varName,
35+
mlir::Type varType, mlir::Value originalVar,
36+
bool &needsFree) const;
37+
38+
bool genFree(mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
39+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
40+
mlir::Value allocRes, mlir::Type varType) const;
41+
42+
bool genCopy(mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
43+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
44+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
45+
mlir::Type varType) const;
3246
};
3347

3448
template <typename T>

flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,4 +751,226 @@ template bool OpenACCMappableModel<fir::PointerType>::generatePrivateDestroy(
751751
mlir::Type type, mlir::OpBuilder &builder, mlir::Location loc,
752752
mlir::Value privatized) const;
753753

754+
template <typename Ty>
755+
mlir::Value OpenACCPointerLikeModel<Ty>::genAllocate(
756+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
757+
llvm::StringRef varName, mlir::Type varType, mlir::Value originalVar,
758+
bool &needsFree) const {
759+
760+
// Get the element type from the pointer type
761+
mlir::Type eleTy = mlir::cast<Ty>(pointer).getElementType();
762+
763+
// Unlimited polymorphic (class(*)) cannot be handled - size unknown
764+
if (fir::isUnlimitedPolymorphicType(eleTy))
765+
return {};
766+
767+
// Character types with dynamic length cannot be handled without a descriptor.
768+
if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy))
769+
if (charTy.hasDynamicLen())
770+
return {};
771+
772+
// Return null for dynamic or unknown shape arrays because the size of the
773+
// allocation cannot be determined simply from the type.
774+
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(eleTy))
775+
if (seqTy.hasDynamicExtents() || seqTy.hasUnknownShape())
776+
return {};
777+
778+
// Use heap allocation for fir.heap, stack allocation for others (fir.ref,
779+
// fir.ptr, fir.llvm_ptr). For fir.ptr, which is supposed to represent a
780+
// Fortran pointer type, it feels a bit odd to "allocate" since it is meant
781+
// to point to an existing entity - but one can imagine where a pointee is
782+
// privatized - thus it makes sense to issue an allocate.
783+
mlir::Value allocation;
784+
if (std::is_same_v<Ty, fir::HeapType>) {
785+
needsFree = true;
786+
allocation = fir::AllocMemOp::create(builder, loc, eleTy);
787+
} else {
788+
needsFree = false;
789+
allocation = fir::AllocaOp::create(builder, loc, eleTy);
790+
}
791+
792+
// Convert to the requested pointer type if needed.
793+
// This means converting from a fir.ref to either a fir.llvm_ptr or a fir.ptr.
794+
// fir.heap is already correct type in this case.
795+
if (allocation.getType() != pointer) {
796+
assert(!(std::is_same_v<Ty, fir::HeapType>) &&
797+
"fir.heap is already correct type because of allocmem");
798+
return fir::ConvertOp::create(builder, loc, pointer, allocation);
799+
}
800+
801+
return allocation;
802+
}
803+
804+
template mlir::Value OpenACCPointerLikeModel<fir::ReferenceType>::genAllocate(
805+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
806+
llvm::StringRef varName, mlir::Type varType, mlir::Value originalVar,
807+
bool &needsFree) const;
808+
809+
template mlir::Value OpenACCPointerLikeModel<fir::PointerType>::genAllocate(
810+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
811+
llvm::StringRef varName, mlir::Type varType, mlir::Value originalVar,
812+
bool &needsFree) const;
813+
814+
template mlir::Value OpenACCPointerLikeModel<fir::HeapType>::genAllocate(
815+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
816+
llvm::StringRef varName, mlir::Type varType, mlir::Value originalVar,
817+
bool &needsFree) const;
818+
819+
template mlir::Value OpenACCPointerLikeModel<fir::LLVMPointerType>::genAllocate(
820+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
821+
llvm::StringRef varName, mlir::Type varType, mlir::Value originalVar,
822+
bool &needsFree) const;
823+
824+
static mlir::Value stripCasts(mlir::Value value, bool stripDeclare = true) {
825+
mlir::Value currentValue = value;
826+
827+
while (currentValue) {
828+
auto *definingOp = currentValue.getDefiningOp();
829+
if (!definingOp)
830+
break;
831+
832+
if (auto convertOp = mlir::dyn_cast<fir::ConvertOp>(definingOp)) {
833+
currentValue = convertOp.getValue();
834+
continue;
835+
}
836+
837+
if (auto viewLike = mlir::dyn_cast<mlir::ViewLikeOpInterface>(definingOp)) {
838+
currentValue = viewLike.getViewSource();
839+
continue;
840+
}
841+
842+
if (stripDeclare) {
843+
if (auto declareOp = mlir::dyn_cast<hlfir::DeclareOp>(definingOp)) {
844+
currentValue = declareOp.getMemref();
845+
continue;
846+
}
847+
848+
if (auto declareOp = mlir::dyn_cast<fir::DeclareOp>(definingOp)) {
849+
currentValue = declareOp.getMemref();
850+
continue;
851+
}
852+
}
853+
break;
854+
}
855+
856+
return currentValue;
857+
}
858+
859+
template <typename Ty>
860+
bool OpenACCPointerLikeModel<Ty>::genFree(
861+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
862+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
863+
mlir::Value allocRes, mlir::Type varType) const {
864+
865+
// If pointer type is HeapType, assume it's a heap allocation
866+
if (std::is_same_v<Ty, fir::HeapType>) {
867+
fir::FreeMemOp::create(builder, loc, varToFree);
868+
return true;
869+
}
870+
871+
// Use allocRes if provided to determine the allocation type
872+
mlir::Value valueToInspect = allocRes ? allocRes : varToFree;
873+
874+
// Strip casts and declare operations to find the original allocation
875+
mlir::Value strippedValue = stripCasts(valueToInspect);
876+
mlir::Operation *originalAlloc = strippedValue.getDefiningOp();
877+
878+
// If we found an AllocMemOp (heap allocation), free it
879+
if (mlir::isa_and_nonnull<fir::AllocMemOp>(originalAlloc)) {
880+
mlir::Value toFree = varToFree;
881+
if (!mlir::isa<fir::HeapType>(valueToInspect.getType()))
882+
toFree = fir::ConvertOp::create(
883+
builder, loc,
884+
fir::HeapType::get(varToFree.getType().getElementType()), toFree);
885+
fir::FreeMemOp::create(builder, loc, toFree);
886+
return true;
887+
}
888+
889+
// If we found an AllocaOp (stack allocation), no deallocation needed
890+
if (mlir::isa_and_nonnull<fir::AllocaOp>(originalAlloc))
891+
return true;
892+
893+
// Unable to determine allocation type
894+
return false;
895+
}
896+
897+
template bool OpenACCPointerLikeModel<fir::ReferenceType>::genFree(
898+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
899+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
900+
mlir::Value allocRes, mlir::Type varType) const;
901+
902+
template bool OpenACCPointerLikeModel<fir::PointerType>::genFree(
903+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
904+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
905+
mlir::Value allocRes, mlir::Type varType) const;
906+
907+
template bool OpenACCPointerLikeModel<fir::HeapType>::genFree(
908+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
909+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
910+
mlir::Value allocRes, mlir::Type varType) const;
911+
912+
template bool OpenACCPointerLikeModel<fir::LLVMPointerType>::genFree(
913+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
914+
mlir::TypedValue<mlir::acc::PointerLikeType> varToFree,
915+
mlir::Value allocRes, mlir::Type varType) const;
916+
917+
template <typename Ty>
918+
bool OpenACCPointerLikeModel<Ty>::genCopy(
919+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
920+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
921+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
922+
mlir::Type varType) const {
923+
924+
// Check that source and destination types match
925+
if (source.getType() != destination.getType())
926+
return false;
927+
928+
mlir::Type eleTy = mlir::cast<Ty>(pointer).getElementType();
929+
930+
// Unlimited polymorphic (class(*)) cannot be handled because source and
931+
// destination types are not known.
932+
if (fir::isUnlimitedPolymorphicType(eleTy))
933+
return false;
934+
935+
// Dynamic lengths without descriptor do not have size information.
936+
if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy))
937+
if (charTy.hasDynamicLen())
938+
return false;
939+
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(eleTy))
940+
if (seqTy.hasDynamicExtents() || seqTy.hasUnknownShape())
941+
return false;
942+
943+
if (fir::isa_trivial(eleTy)) {
944+
auto loadVal = fir::LoadOp::create(builder, loc, source);
945+
fir::StoreOp::create(builder, loc, loadVal, destination);
946+
} else {
947+
hlfir::AssignOp::create(builder, loc, source, destination);
948+
}
949+
return true;
950+
}
951+
952+
template bool OpenACCPointerLikeModel<fir::ReferenceType>::genCopy(
953+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
954+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
955+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
956+
mlir::Type varType) const;
957+
958+
template bool OpenACCPointerLikeModel<fir::PointerType>::genCopy(
959+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
960+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
961+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
962+
mlir::Type varType) const;
963+
964+
template bool OpenACCPointerLikeModel<fir::HeapType>::genCopy(
965+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
966+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
967+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
968+
mlir::Type varType) const;
969+
970+
template bool OpenACCPointerLikeModel<fir::LLVMPointerType>::genCopy(
971+
mlir::Type pointer, mlir::OpBuilder &builder, mlir::Location loc,
972+
mlir::TypedValue<mlir::acc::PointerLikeType> destination,
973+
mlir::TypedValue<mlir::acc::PointerLikeType> source,
974+
mlir::Type varType) const;
975+
754976
} // namespace fir::acc
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// RUN: fir-opt %s --split-input-file --pass-pipeline="builtin.module(func.func(test-acc-pointer-like-interface{test-mode=alloc}))" 2>&1 | FileCheck %s
2+
3+
// The tests here use a synthetic hlfir.declare in order to ensure that the hlfir dialect is
4+
// loaded. This is required because the pass used is part of OpenACC test passes outside of
5+
// flang and the APIs being test may generate hlfir even when it does not appear.
6+
7+
func.func @test_ref_scalar_alloc() {
8+
%0 = fir.alloca f32 {test.ptr}
9+
%1:2 = hlfir.declare %0 {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
10+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.alloca f32 {test.ptr}
11+
// CHECK: Generated: %{{.*}} = fir.alloca f32
12+
return
13+
}
14+
15+
// -----
16+
17+
func.func @test_ref_static_array_alloc() {
18+
%0 = fir.alloca !fir.array<10x20xf32> {test.ptr}
19+
%var = fir.alloca f32
20+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
21+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.alloca !fir.array<10x20xf32> {test.ptr}
22+
// CHECK: Generated: %{{.*}} = fir.alloca !fir.array<10x20xf32>
23+
return
24+
}
25+
26+
// -----
27+
28+
func.func @test_ref_derived_type_alloc() {
29+
%0 = fir.alloca !fir.type<_QTt{i:i32}> {test.ptr}
30+
%var = fir.alloca f32
31+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
32+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.alloca !fir.type<_QTt{i:i32}> {test.ptr}
33+
// CHECK: Generated: %{{.*}} = fir.alloca !fir.type<_QTt{i:i32}>
34+
return
35+
}
36+
37+
// -----
38+
39+
func.func @test_heap_scalar_alloc() {
40+
%0 = fir.allocmem f32 {test.ptr}
41+
%var = fir.alloca f32
42+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
43+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.allocmem f32 {test.ptr}
44+
// CHECK: Generated: %{{.*}} = fir.allocmem f32
45+
return
46+
}
47+
48+
// -----
49+
50+
func.func @test_heap_static_array_alloc() {
51+
%0 = fir.allocmem !fir.array<10x20xf32> {test.ptr}
52+
%var = fir.alloca f32
53+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
54+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.allocmem !fir.array<10x20xf32> {test.ptr}
55+
// CHECK: Generated: %{{.*}} = fir.allocmem !fir.array<10x20xf32>
56+
return
57+
}
58+
59+
// -----
60+
61+
func.func @test_ptr_scalar_alloc() {
62+
%0 = fir.alloca f32
63+
%1 = fir.convert %0 {test.ptr} : (!fir.ref<f32>) -> !fir.ptr<f32>
64+
%2:2 = hlfir.declare %0 {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
65+
// CHECK: Successfully generated alloc for operation
66+
// CHECK: Generated: %{{.*}} = fir.alloca f32
67+
// CHECK: Generated: %{{.*}} = fir.convert %{{.*}} : (!fir.ref<f32>) -> !fir.ptr<f32>
68+
return
69+
}
70+
71+
// -----
72+
73+
func.func @test_llvm_ptr_scalar_alloc() {
74+
%0 = fir.alloca f32
75+
%1 = fir.convert %0 {test.ptr} : (!fir.ref<f32>) -> !fir.llvm_ptr<f32>
76+
%2:2 = hlfir.declare %0 {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
77+
// CHECK: Successfully generated alloc for operation
78+
// CHECK: Generated: %{{.*}} = fir.alloca f32
79+
// CHECK: Generated: %{{.*}} = fir.convert %{{.*}} : (!fir.ref<f32>) -> !fir.llvm_ptr<f32>
80+
return
81+
}
82+
83+
// -----
84+
85+
func.func @test_dynamic_array_alloc_fails(%arg0: !fir.ref<!fir.array<?xf32>>) {
86+
%0 = fir.convert %arg0 {test.ptr} : (!fir.ref<!fir.array<?xf32>>) -> !fir.llvm_ptr<!fir.array<?xf32>>
87+
%var = fir.alloca f32
88+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
89+
// CHECK: Failed to generate alloc for operation: %{{.*}} = fir.convert %{{.*}} {test.ptr} : (!fir.ref<!fir.array<?xf32>>) -> !fir.llvm_ptr<!fir.array<?xf32>>
90+
return
91+
}
92+
93+
// -----
94+
95+
func.func @test_unlimited_polymorphic_alloc_fails() {
96+
%0 = fir.alloca !fir.class<none> {test.ptr}
97+
%var = fir.alloca f32
98+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
99+
// CHECK: Failed to generate alloc for operation: %{{.*}} = fir.alloca !fir.class<none> {test.ptr}
100+
return
101+
}
102+
103+
// -----
104+
105+
func.func @test_dynamic_char_alloc_fails(%arg0: !fir.ref<!fir.char<1,?>>) {
106+
%0 = fir.convert %arg0 {test.ptr} : (!fir.ref<!fir.char<1,?>>) -> !fir.llvm_ptr<!fir.char<1,?>>
107+
%var = fir.alloca f32
108+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
109+
// CHECK: Failed to generate alloc for operation: %{{.*}} = fir.convert %{{.*}} {test.ptr} : (!fir.ref<!fir.char<1,?>>) -> !fir.llvm_ptr<!fir.char<1,?>>
110+
return
111+
}
112+
113+
// -----
114+
115+
func.func @test_static_char_alloc() {
116+
%0 = fir.alloca !fir.char<1,10> {test.ptr}
117+
%var = fir.alloca f32
118+
%1:2 = hlfir.declare %var {uniq_name = "load_hlfir"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
119+
// CHECK: Successfully generated alloc for operation: %{{.*}} = fir.alloca !fir.char<1,10> {test.ptr}
120+
// CHECK: Generated: %{{.*}} = fir.alloca !fir.char<1,10>
121+
return
122+
}

0 commit comments

Comments
 (0)