This is an archive of the discontinued LLVM Phabricator instance.

[flang] Support lowering of C_PTR and C_FUNPTR argument with VALUE attribute
ClosedPublic

Authored by peixin on Aug 10 2022, 8:48 AM.

Details

Summary

As Fortran 2018 18.3.2, C_PTR is interoperable with any C object pointer
type. C_FUNPTR is interoperable with any C function pointer type. As
18.3.6, a C pointer can correspond to a Fortran dummy argument of type
C_PTR with the VALUE attribute.

The interface for type(C_PTR)/type(C_FUNPTR) argument with value
attribute is different from the the usual derived type. For type(C_PTR)
or type(C_FUNPTR), the component is the address, and the interface is
a pointer even with VALUE attribute. For a usual derived type such as
the drived type with the component of integer 64, the interface is a i64
value when it has VALUE attribute on aarch64 linux.

To lower the type(C_PTR)/type(C_FUNPTR) argument with value attribute,
get the value of the component of the type(C_PTR)/type(C_FUNPTR), which
is the address, and then convert it to the pointer and pass it.

Diff Detail

Event Timeline

peixin created this revision.Aug 10 2022, 8:48 AM
peixin requested review of this revision.Aug 10 2022, 8:49 AM
clementval added inline comments.Aug 12 2022, 6:41 AM
flang/lib/Lower/CallInterface.cpp
890–891

What happen if a user make a type with a field with the same name? Cannot you make sure we are dealing with a type(c_ptr)?

type mytype
  integer :: __address
end type
892

Expand auto

flang/lib/Lower/ConvertExpr.cpp
2481–2482

Expand auto here as well.

peixin updated this revision to Diff 452394.Aug 13 2022, 2:10 AM
peixin added inline comments.Aug 13 2022, 2:15 AM
flang/lib/Lower/CallInterface.cpp
890–891

Good catch. Update by checking if it is the type of builtin c_ptr/c_funptr.

892

Fixed.

flang/lib/Lower/ConvertExpr.cpp
2481–2482

Fixed.

BTW, when I read the lowering code, it uses auto sometimes, but uses mlir::Type or mlir::Value somtimes for the similar code. I am really confused about this. Is there one rule for this?

peixin updated this revision to Diff 452523.Aug 14 2022, 7:59 AM
peixin set the repository for this revision to rG LLVM Github Monorepo.
peixin removed a project: Restricted Project.

Add the check for the interface of "!fir.ref<i64>".

Herald added a project: Restricted Project. · View Herald TranscriptAug 14 2022, 7:59 AM
clementval added inline comments.Aug 15 2022, 2:21 AM
flang/lib/Lower/ConvertExpr.cpp
2481–2482

We follow LLVM guidelines. When the type is obvious you can use auto like you did in your code. Otherwise it is preferred to expand auto. We tried to update all occurrences of auto where it was needed during the upstreaming but we might have missed a couple.

auto recTy = fir::unwrapRefType(fromTy).dyn_cast_or_null<fir::RecordType>();
peixin added inline comments.Aug 15 2022, 4:01 AM
flang/lib/Lower/ConvertExpr.cpp
2481–2482

Got it. Thanks a lot.

Thanks for looking into this. So, on aarch64, a struct containing an intptr is not passed as a void* ?

Basically, if in C, typedef struct {intptr_t ptr;} builtin_cptr; is guaranteed to be passed the same way as a void*, then I do not think something special is needed here. But I am not sure this is guaranteed by C, although this may hold true on the targets of interests for flang. Otherwise, your patch makes some sense to me, but I am wondering if we should not lower the builtinCPtr type to "fir.llvm_ptr<none>" instead of special casing the function interface (I have not tested this, and you might hit a few issues if you go that way, like passing C_PTR by reference. I am not sure fir.ref<fir.llvm_ptr<none>> is a legal type).

The reason I am thinking this is that having both C_PTR values lowered to "fir.ref<none>" and a !fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}> might be a pain. For instance, with your patch the following would probably crash because the patch dealt with the call side, but not the callee side where the fir.ref<none> should be converted back to a !fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}> so that later usages of it will behave correctly:

subroutine issue(ptr1) bind(c)
  use, intrinsic :: iso_c_binding
  type(c_ptr), value :: ptr1
  type(c_ptr) :: local
  local = ptr1
end subroutine

So I am wondering if C_PTR should not be a C pointer in FIR, after all, we have pointers.

Thanks for looking into this. So, on aarch64, a struct containing an intptr is not passed as a void* ?

Basically, if in C, typedef struct {intptr_t ptr;} builtin_cptr; is guaranteed to be passed the same way as a void*, then I do not think something special is needed here. But I am not sure this is guaranteed by C, although this may hold true on the targets of interests for flang. Otherwise, your patch makes some sense to me, but I am wondering if we should not lower the builtinCPtr type to "fir.llvm_ptr<none>" instead of special casing the function interface (I have not tested this, and you might hit a few issues if you go that way, like passing C_PTR by reference. I am not sure fir.ref<fir.llvm_ptr<none>> is a legal type).

I don't think this is arch-dependent. Check the following case:

$ cat func.c 
#include<stdio.h>
struct builtin_cptr {
  __intptr_t ptr;
};

void c_func_(struct builtin_cptr t1) {
  printf("%ld\n", t1.ptr);
}

$ clang func.c -S -emit-llvm
$ cat func.ll 

target triple = "aarch64-unknown-linux-gnu"
%struct.builtin_cptr = type { i64 }
define dso_local void @c_func_(i64 %0) #0 {

$ clang func.c -S -emit-llvm --target=x86_64-unknown-linux-gnu
$ cat func.ll 
...
target triple = "x86_64-unknown-linux-gnu"
%struct.builtin_cptr = type { i64 }
define dso_local void @c_func_(i64 %0) #0 {

The reason I am thinking this is that having both C_PTR values lowered to "fir.ref<none>" and a !fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}> might be a pain. For instance, with your patch the following would probably crash because the patch dealt with the call side, but not the callee side where the fir.ref<none> should be converted back to a !fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}> so that later usages of it will behave correctly:

subroutine issue(ptr1) bind(c)
  use, intrinsic :: iso_c_binding
  type(c_ptr), value :: ptr1
  type(c_ptr) :: local
  local = ptr1
end subroutine

So I am wondering if C_PTR should not be a C pointer in FIR, after all, we have pointers.

Well, I missed this case. This is also not supported without this patch. Here is what LLVMIR classic-flang generated:

%struct.BSS1 = type <{ [8 x i8] }>
%struct_iso_c_binding_10_ = type <{ [16 x i8] }>
%struct.c_ptr.dt57 = type { i64 }

@.BSS1 = internal global %struct.BSS1 zeroinitializer, align 32
@_iso_c_binding_10_ = external global %struct_iso_c_binding_10_, align 64

define void @issue(i64* %_V_ptr1.arg) #0 !dbg !5 {
L.entry:
  %_V_ptr1.addr = alloca %struct.c_ptr.dt57, align 8
  %ptr1_343 = alloca %struct.c_ptr.dt57, align 8
  %"issue__$eq_342" = alloca [16 x i8], align 4
  %0 = bitcast %struct.c_ptr.dt57* %_V_ptr1.addr to i64**
  store i64* %_V_ptr1.arg, i64** %0, align 8
  %1 = bitcast %struct.c_ptr.dt57* %_V_ptr1.addr to i64*
  %2 = load volatile i64, i64* %1, align 8
  %3 = bitcast %struct.c_ptr.dt57* %ptr1_343 to i64*
  store i64 %2, i64* %3, align 8
  br label %L.LB1_361

L.LB1_361:                                        ; preds = %L.entry
  %4 = bitcast %struct.c_ptr.dt57* %ptr1_343 to i64*, !dbg !12
  %5 = load i64, i64* %4, align 8, !dbg !12
  %6 = bitcast %struct.BSS1* @.BSS1 to i64*, !dbg !12
  store i64 %5, i64* %6, align 8, !dbg !12
  ret void, !dbg !13
}

We can also allocate one temporary TYPE(C_PTR) and assign the value of argument to it and map the mlir::Value of temporary TYPE(C_PTR)` to the symbol ptr1`. Is that right?

We can also allocate one temporary TYPE(C_PTR) and assign the value of argument to it and map the mlir::Value of temporary TYPE(C_PTR) to the symbol ptr1. Is that right?

Yes, I think that would work too.

peixin updated this revision to Diff 455581.Aug 25 2022, 7:44 AM
  1. Support the callee side.
  1. Add TODO for derived type passed by value in callee, which has unexpected behaviors previously.

Overall strategy looks reasonable to me.

flang/lib/Lower/Bridge.cpp
2731 ↗(On Diff #455581)
flang/lib/Lower/CallInterface.cpp
893

It would make sense to me to have a fir::isa_builtin_cptr_type(fir::recordType) helper defined in FIRType.h/.cpp to centralize the magic string definition and test (it is done here and in ConvertExpr.cpp).

peixin updated this revision to Diff 456174.Aug 27 2022, 11:58 PM

Address the comments.

peixin added inline comments.Aug 27 2022, 11:59 PM
flang/lib/Lower/Bridge.cpp
2731 ↗(On Diff #455581)

Thanks for pointing out the helper function. Fixed.

flang/lib/Lower/CallInterface.cpp
893

Good point. Fixed.

jeanPerier accepted this revision.Aug 29 2022, 12:42 AM

LGTM, thanks for addressing the comments.

This revision is now accepted and ready to land.Aug 29 2022, 12:42 AM