Skip to content

Commit

Permalink
[flang2] byval and sret attributes should have associated types
Browse files Browse the repository at this point in the history
When the return value of the function is a structure, the ABI might require
that the caller pass the structure into the function call as an implicit
input argument; in such cases the generated IR for the argument will
have the sret attribute. Similarly, pass-by-value arguments are given
the byval attribute in the IR. Since the adoption of opaque pointers,
such attributes are required to be accompanied with the pointee type. This
patch fixes flang2 to generate correct IR for such argument/return types.

Co-authored-by: Liuyunlong <liuyunlong16@huawei.com>
  • Loading branch information
bryanpkc and liuyunlong16 committed Sep 21, 2023
1 parent 88c8ea4 commit f8fa3a7
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 12 deletions.
67 changes: 67 additions & 0 deletions test/llvm_ir_correct/byval-sret-aarch64.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
! Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
! See https://llvm.org/LICENSE.txt for license information.
! SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
!
! Check that correct opaque pointer-compatible IR is generated for
! pass-by-value arguments and struct-type return values, which typically
! appear in C binding interfaces.
!
! Note that the byval attribute is not currently generated on AArch64;
! tools/flang2/flang2exe/aarch64/ll_abi.cpp selects LL_ARG_INDIRECT_BUFFERED
! instead of LL_ARG_BYVAL.

! REQUIRES: aarch64-registered-target
! RUN: %flang -S -emit-flang-llvm %s -o %t
! RUN: FileCheck %s < %t

! CHECK: [[C_TYPE:%struct.c_type.*]] = type <{ double, double, double, double, double}>
! CHECK: [[C_FUNPTR:%struct.c_funptr.*]] = type <{ i64}>
! CHECK: define void @c_interface_byval_sub_({{.*}}, i64 %_V_fp.coerce)
! CHECK: call void @f90_c_f_procptr (ptr [[BSS:@\..+]], ptr {{.*}})
! CHECK: [[TMP:%.*]] = load i64, ptr [[BSS]]
! CHECK: call void @c_interface_byval_sub_ ({{.*}}, i64 [[TMP]])
! CHECK: [[TMP:%.*]] = load ptr, ptr %dur
! CHECK: call void @c_function (ptr sret([[C_TYPE]]) {{%.*}}, ptr [[TMP]])
! CHECK: declare void @c_function(ptr sret([[C_TYPE]]), ptr)

module c_interface
use, intrinsic :: iso_c_binding
type, bind(c) :: c_type
real(kind = c_double) :: year = 0, month = 0, day = 0, hour = 0, minute = 0
end type
interface
type(c_type) function c_function(dur) bind(c)
use iso_c_binding
import :: c_type
type(c_type), value :: dur
end function
end interface
contains
! Reproducer from https://github.com/flang-compiler/flang/issues/1419.
subroutine byval_sub(x1, x2, x3, x4, x5, x6, fp)
implicit none
integer(c_int), intent(in), value :: x1
integer(c_int), intent(in), value :: x2
integer(c_int), intent(in), value :: x3
integer(c_int), intent(in), value :: x4
integer(c_int), intent(in), value :: x5
integer(c_int), intent(in), value :: x6
type(c_funptr), intent(in), value :: fp
end subroutine
end module

module test
use c_interface
contains
function ss(dur) result(res)
implicit none
integer(c_int), parameter :: x = 42
type(c_funptr) :: fp
type(c_type), intent(in) :: dur
type(c_type) :: res
procedure(byval_sub), pointer :: proc
call c_f_procpointer(fp, proc)
call byval_sub(x, x, x, x, x, x, fp)
res = c_function(dur)
end function
end module
63 changes: 63 additions & 0 deletions test/llvm_ir_correct/byval-sret-x86.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
! Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
! See https://llvm.org/LICENSE.txt for license information.
! SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
!
! Check that correct opaque pointer-compatible IR is generated for
! pass-by-value arguments and struct-type return values, which typically
! appear in C binding interfaces.

! REQUIRES: x86-registered-target
! RUN: %flang -S -emit-flang-llvm %s -o %t
! RUN: FileCheck %s < %t

! CHECK: [[C_TYPE:%struct.c_type.*]] = type <{ double, double, double, double, double}>
! CHECK: [[C_FUNPTR:%struct.c_funptr.*]] = type <{ i64}>
! CHECK: define void @c_interface_byval_sub_({{.*}}, ptr byval([[C_FUNPTR]]) %_V_fp.arg)
! CHECK: call void (ptr, ptr, ...) @f90_c_f_procptr (ptr [[BSS:@\..+]], ptr {{.*}})
! CHECK: [[TMP:%.*]] = load i64, ptr [[BSS]]
! CHECK: [[TMP2:%.*]] = inttoptr i64 [[TMP]]
! CHECK: call void @c_interface_byval_sub_ ({{.*}}, ptr byval([[C_FUNPTR]]) [[TMP2]])
! CHECK: call void @c_function (ptr sret([[C_TYPE]]) {{%.*}}, ptr byval([[C_TYPE]]) %dur)
! CHECK: declare void @c_function(ptr sret([[C_TYPE]]), ptr byval([[C_TYPE]]))

module c_interface
use, intrinsic :: iso_c_binding
type, bind(c) :: c_type
real(kind = c_double) :: year = 0, month = 0, day = 0, hour = 0, minute = 0
end type
interface
type(c_type) function c_function(dur) bind(c)
use iso_c_binding
import :: c_type
type(c_type), value :: dur
end function
end interface
contains
! Reproducer from https://github.com/flang-compiler/flang/issues/1419.
subroutine byval_sub(x1, x2, x3, x4, x5, x6, fp)
implicit none
integer(c_int), intent(in), value :: x1
integer(c_int), intent(in), value :: x2
integer(c_int), intent(in), value :: x3
integer(c_int), intent(in), value :: x4
integer(c_int), intent(in), value :: x5
integer(c_int), intent(in), value :: x6
type(c_funptr), intent(in), value :: fp
end subroutine
end module

module test
use c_interface
contains
function ss(dur) result(res)
implicit none
integer(c_int), parameter :: x = 42
type(c_funptr) :: fp
type(c_type), intent(in) :: dur
type(c_type) :: res
procedure(byval_sub), pointer :: proc
call c_f_procpointer(fp, proc)
call byval_sub(x, x, x, x, x, x, fp)
res = c_function(dur)
end function
end module
12 changes: 9 additions & 3 deletions tools/flang2/flang2exe/cgmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ ll_check_struct_return(DTYPE dtype)
*
* %struct.S = type { [10 x i32] }
*
* define void @f(%struct.S* noalias sret %agg.result) ...
* define void @f(ptr noalias sret(%struct.S) %agg.result) ...
*
* Some structs can be returned in registers, depending on ABI-specific rules.
* For example, x86-64 can return a struct {long x, y; } struct in registers
Expand Down Expand Up @@ -7279,6 +7279,8 @@ gen_arg_operand(LL_ABI_Info *abi, unsigned abi_arg, int arg_ili)
operand = gen_llvm_expr(value_ili, arg_type);
if (arg->kind == LL_ARG_BYVAL && !missing)
operand->flags |= OPF_SRARG_TYPE;
if (arg->kind == LL_ARG_INDIRECT && !missing && (abi_arg == 0))
operand->flags |= OPF_SRET_TYPE;
return operand;

arg_type = make_lltype_from_abi_arg(arg);
Expand Down Expand Up @@ -13701,7 +13703,9 @@ print_arg_attributes(LL_ABI_ArgInfo *arg)
print_token(" signext");
break;
case LL_ARG_BYVAL:
print_token(" byval");
print_token(" byval(");
print_token(arg->type->sub_types[0]->str);
print_token(")");
break;
default:
interr("Unknown argument kind", arg->kind, ERR_Fatal);
Expand Down Expand Up @@ -13760,7 +13764,9 @@ print_function_signature(int func_sptr, const char *fn_name, LL_ABI_Info *abi,
/* Hidden sret argument for struct returns. */
if (LL_ABI_HAS_SRET(abi)) {
print_token(abi->arg[0].type->str);
print_token(" sret");
print_token(" sret(");
print_token(abi->arg[0].type->sub_types[0]->str);
print_token(")");
if (print_arg_names) {
print_space(1);
print_token(SNAME(ret_info.sret_sptr));
Expand Down
30 changes: 21 additions & 9 deletions tools/flang2/flang2exe/llutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2349,10 +2349,16 @@ write_operand(OPERAND *p, const char *punc_string, int flags)
#endif
if (!(flags & FLG_OMIT_OP_TYPE))
write_type(pllt);
if (p->flags & OPF_SRET_TYPE)
print_token(" sret");
if (p->flags & OPF_SRARG_TYPE)
print_token(" byval");
if (p->flags & OPF_SRET_TYPE) {
print_token(" sret(");
print_token(p->ll_type->sub_types[0]->str);
print_token(")");
}
if (p->flags & OPF_SRARG_TYPE) {
print_token(" byval(");
print_token(p->ll_type->sub_types[0]->str);
print_token(")");
}
print_space(1);
print_token(p->string);
break;
Expand All @@ -2369,12 +2375,18 @@ write_operand(OPERAND *p, const char *punc_string, int flags)
if (!(flags & FLG_OMIT_OP_TYPE)) {
assert(p->ll_type, "write_operand(): missing type information", 0, ERR_Fatal);
write_type(p->ll_type);
print_space(1);
}
if (p->flags & OPF_SRET_TYPE)
print_token(" sret ");
if (p->flags & OPF_SRARG_TYPE)
print_token(" byval ");
if (p->flags & OPF_SRET_TYPE) {
print_token(" sret(");
print_token(p->ll_type->sub_types[0]->str);
print_token(")");
}
if (p->flags & OPF_SRARG_TYPE) {
print_token(" byval(");
print_token(p->ll_type->sub_types[0]->str);
print_token(")");
}
print_space(1);
if (p->tmps)
print_tmp_name(p->tmps);
else
Expand Down

0 comments on commit f8fa3a7

Please sign in to comment.