This is an archive of the discontinued LLVM Phabricator instance.

[flang] Support codegen of procedure pointer component
ClosedPublic

Authored by peixin on Oct 27 2022, 5:45 AM.

Details

Summary

This supports the codegen for procedure pointer component in
BoxedProcedure pass. Also fix the FIR in ProcedurePointer.md so that
all the cases can be run using tco to generate the LLVM IR.

Diff Detail

Event Timeline

peixin created this revision.Oct 27 2022, 5:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 27 2022, 5:45 AM
peixin requested review of this revision.Oct 27 2022, 5:45 AM
jeanPerier added inline comments.Oct 28 2022, 9:13 AM
flang/lib/Optimizer/CodeGen/TypeConverter.h
63 ↗(On Diff #471141)

This TODO is quite old. The fir.boxproc type is actually being rewritten in lib/Optimizer/CodeGen/BoxedProcedure.cpp, and I think it may be better to also do the same for fir.ref<fir.boxproc<T>> to be consistent with the choice regarding what a fir.boxproc should be.

peixin added inline comments.Oct 29 2022, 5:57 AM
flang/lib/Optimizer/CodeGen/TypeConverter.h
63 ↗(On Diff #471141)

I guess this is added either for the unimplemented codegen of fir.emboxproc, fir.boxproc_host, fir.unboxproc, or for the conversion of BOXPROCType in procedure component. If BoxedProcedurePass is mature. All these unimplemented codegen may can be removed.

I think it may be better to also do the same for fir.ref<fir.boxproc<T>> to be consistent with the choice regarding what a fir.boxproc should be.

I don't get it. fir.ref<fir.boxproc<T>> is converted to mlir::LLVM::LLVMPointerType::get(convertType(ty.getEleTy())), which is expected.

jeanPerier added inline comments.Nov 3 2022, 2:09 AM
flang/lib/Optimizer/CodeGen/TypeConverter.h
63 ↗(On Diff #471141)

What I am saying is that right now, the place that dictates how a fir.boxproc<T> is translated is lib/Optimizer/CodeGen/BoxedProcedure.cpp (here https://github.com/llvm/llvm-project/blob/67d0d7ac0acb0665d6a09f61278fbcf51f0114c2/flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp#L95), so I do not really understand why a conversion needs to be defined again here. From a single point of truth perspective, I would prefer BoxedProcedure.cpp to be the only place dealing with fir.boxproc<T>.

peixin added a comment.Nov 3 2022, 4:15 AM

From a single point of truth perspective, I would prefer BoxedProcedure.cpp to be the only place dealing with fir.boxproc<T>.

Let me try this.

peixin updated this revision to Diff 474742.Nov 11 2022, 6:27 AM
peixin retitled this revision from [flang] Add type conversion for BoxProcType to [flang] Support codegen of procedure pointer component.
peixin edited the summary of this revision. (Show Details)

Support codegen of procedure pointer component by support type conversion for boxproc in derived type.

jeanPerier added inline comments.Nov 14 2022, 2:31 AM
flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
136

Are you sure it is allowed to update the data of an MLIR type on the flight like this ?

It is not quite clear to me at which scale the type instances are shared, and if we could ever run in weird bug due to multithreaded passes while updating the storage of an mlir type instance like this.

peixin updated this revision to Diff 476038.Nov 17 2022, 1:02 AM

Are you sure it is allowed to update the data of an MLIR type on the flight like this ?
It is not quite clear to me at which scale the type instances are shared, and if we could ever run in weird bug due to multithreaded passes while updating the storage of an mlir type> instance like this.

I didn't know these passes can be run in parallel. Thanks for letting me know. Then, one new record type can be created with one mangled name. For performance consideration, only convert the record type once.

I am not very found that a new record type with a new name has to be created, but I do not really see another nice solution. The only alternative I can think of would be to add an integer "version" flag in the recordType that could be increased every time some pass updates derived type, so that we do not touch the mangled name, but an extra string also makes some sense, although we will probably want to make them "offical" in the RecordType definition and add a way to find back the to the original name.

flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
129

Does this work OK with the codegen code that is later using the mangled derived type names to find the derived type descriptor global ?

140

You should probably assert that the original type had no length parameters/add a TODO so that we do not forget about propagating those here.

I am not very found that a new record type with a new name has to be created, but I do not really see another nice solution. The only alternative I can think of would be to add an integer "version" flag in the recordType that could be increased every time some pass updates derived type, so that we do not touch the mangled name, but an extra string also makes some sense, although we will probably want to make them "offical" in the RecordType definition and add a way to find back the to the original name.

Yes, I agree. Usually, adding one flag should be avoided if there is no other way. The record type name _QFtestTmatrix is actually already mangled before codegen. I also checked interoperability with C (Fortran 2018 18.3.3(3) Note 3), and there is no need to keep its original type name. So, there is no real need to keep this type name. What do you think?

flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
129

I don't think out any case which will fail with this change. The test case in flang/test/Fir/boxproc-2.fir can be run using tco to generate LLVM IR. Here is the output with this patch:

; ModuleID = 'FIRModule'
source_filename = "FIRModule"
target triple = "aarch64-unknown-linux-gnu"

%_QFtestTmatrixUnboxProc = type { [2 x [2 x float]], ptr }

declare ptr @malloc(i64)

declare void @free(ptr)

declare void @foo1(ptr)

declare void @foo2(ptr)

define void @proc_pointer_component(ptr %0, ptr %1) {
  %3 = alloca %_QFtestTmatrixUnboxProc, i64 1, align 8
  %4 = getelementptr %_QFtestTmatrixUnboxProc, ptr %3, i32 0, i32 1
  store ptr %0, ptr %4, align 8
  %5 = load ptr, ptr %4, align 8
  %6 = call float %5(ptr %1)
  ret void
}

!llvm.module.flags = !{!0}

!0 = !{i32 2, !"Debug Info Version", i32 3}
140

I don't get it. Why does it need one assert or TODO if there is no length parameter? In addition, will there be special handling here if there is length parameter? Will rec.finalize(ps, cs) not work?

I am not very found that a new record type with a new name has to be created, but I do not really see another nice solution. The only alternative I can think of would be to add an integer "version" flag in the recordType that could be increased every time some pass updates derived type, so that we do not touch the mangled name, but an extra string also makes some sense, although we will probably want to make them "offical" in the RecordType definition and add a way to find back the to the original name.

Yes, I agree. Usually, adding one flag should be avoided if there is no other way. The record type name _QFtestTmatrix is actually already mangled before codegen. I also checked interoperability with C (Fortran 2018 18.3.3(3) Note 3), and there is no need to keep its original type name. So, there is no real need to keep this type name. What do you think?

The type name is important for polymorphic entities. It is used to find the type descriptor and the binding tables. So we at least need a way to find back the original type name so we can use the functions in flang/include/flang/Optimizer/Support/InternalNames.h like getTypeDescriptorName and getTypeDescriptorBindingTableName that takes the mangled type name as input.

I am not very found that a new record type with a new name has to be created, but I do not really see another nice solution. The only alternative I can think of would be to add an integer "version" flag in the recordType that could be increased every time some pass updates derived type, so that we do not touch the mangled name, but an extra string also makes some sense, although we will probably want to make them "offical" in the RecordType definition and add a way to find back the to the original name.

Yes, I agree. Usually, adding one flag should be avoided if there is no other way. The record type name _QFtestTmatrix is actually already mangled before codegen. I also checked interoperability with C (Fortran 2018 18.3.3(3) Note 3), and there is no need to keep its original type name. So, there is no real need to keep this type name. What do you think?

The type name is important for polymorphic entities. It is used to find the type descriptor and the binding tables. So we at least need a way to find back the original type name so we can use the functions in flang/include/flang/Optimizer/Support/InternalNames.h like getTypeDescriptorName and getTypeDescriptorBindingTableName that takes the mangled type name as input.

@clementval Thanks for the notice. I took a look at the case of polymorphic entities. The typeDesc and typeDescBindingTable do not have the boxproc component, and they won't be converted, so the only problem is how to handle the function getDerivedTypeObjectName. The simple method is unwrap the mangledTypeName by removing the suffix string UnboxProc before calling fir::NameUniquer::deconstruct(mangledTypeName). Is that OK with you?

@clementval Thanks for the notice. I took a look at the case of polymorphic entities. The typeDesc and typeDescBindingTable do not have the boxproc component, and they won't be converted, so the only problem is how to handle the function getDerivedTypeObjectName. The simple method is unwrap the mangledTypeName by removing the suffix string UnboxProc before calling fir::NameUniquer::deconstruct(mangledTypeName). Is that OK with you?

Yeah that should be fine. Can you define the suffix in InternalName.h so it is centralized?

Like this or smth similar.

static constexpr llvm::StringRef boxprocSuffix = "UnboxProc";

@clementval Thanks for the notice. I took a look at the case of polymorphic entities. The typeDesc and typeDescBindingTable do not have the boxproc component, and they won't be converted, so the only problem is how to handle the function getDerivedTypeObjectName. The simple method is unwrap the mangledTypeName by removing the suffix string UnboxProc before calling fir::NameUniquer::deconstruct(mangledTypeName). Is that OK with you?

Yeah that should be fine. Can you define the suffix in InternalName.h so it is centralized?

Like this or smth similar.

static constexpr llvm::StringRef boxprocSuffix = "UnboxProc";

Good point. Will do it. Thanks a lot.

jeanPerier added inline comments.Nov 23 2022, 1:34 AM
flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
129

This test case does not exercise an operation that will require retrieving the type descriptor in codegen. Embox would expose that need:

func.func private @bar(!fir.box<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>) -> ()

func.func @proc_pointer_component() {
  %0 = fir.alloca !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>
  %1 = fir.embox %0 : (!fir.ref<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>) -> !fir.box<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>
  call @bar(%1) : (!fir.box<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>) -> ()
  return
}
140

Sorry, I look too quickly, you are indeed copying them, no need to add a TODO.

@clementval Thanks for the notice. I took a look at the case of polymorphic entities. The typeDesc and typeDescBindingTable do not have the boxproc component, and they won't be converted, so the only problem is how to handle the function getDerivedTypeObjectName. The simple method is unwrap the mangledTypeName by removing the suffix string UnboxProc before calling fir::NameUniquer::deconstruct(mangledTypeName). Is that OK with you?

Yeah that should be fine. Can you define the suffix in InternalName.h so it is centralized?

Like this or smth similar.

static constexpr llvm::StringRef boxprocSuffix = "UnboxProc";

+1

peixin added inline comments.Nov 23 2022, 1:56 AM
flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
129

This case has the same problem as the polymorphic entities. This will be handled later. Thanks for this case.

BTW, I hit one error for the following case:

func.func private @bar(!fir.box<!fir.type<_QFtestTmatrix2{element:!fir.array<2x2xf32>}>>) -> ()

func.func @proc_pointer_component2() {
  %0 = fir.alloca !fir.type<_QFtestTmatrix2{element:!fir.array<2x2xf32>}>
  %1 = fir.embox %0 : (!fir.ref<!fir.type<_QFtestTmatrix2{element:!fir.array<2x2xf32>}>>) -> !fir.box<!fir.type<_QFtestTmatrix2{element:!fir.array<2x2xf32>}>>
  call @bar(%1) : (!fir.box<!fir.type<_QFtestTmatrix2{element:!fir.array<2x2xf32>}>>) -> ()
  return
}
$ tco case01.fir 
loc("case01.fir":87:8): error: runtime derived type info descriptor was not generated

@clementval Is this one bug or TODO or invalid input?

clementval added inline comments.Nov 23 2022, 2:09 AM
flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
129

Input is invalid since it does not contains the fir.global that represent the type descriptor.

peixin updated this revision to Diff 477476.Nov 23 2022, 5:22 AM
peixin edited the summary of this revision. (Show Details)

Address the comments from @jeanPerier and @clementval .

jeanPerier accepted this revision.Dec 6 2022, 9:54 AM

LGTM, thanks

This revision is now accepted and ready to land.Dec 6 2022, 9:54 AM
This revision was automatically updated to reflect the committed changes.