From a273117c3971fb3080d9efb830902204704781c5 Mon Sep 17 00:00:00 2001 From: Bryan Chan Date: Wed, 20 Sep 2023 08:00:45 -0400 Subject: [PATCH] [flang2] byval and sret attributes should have associated types 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 --- test/llvm_ir_correct/byval-sret-aarch64.f90 | 67 +++++++++++++++++++++ test/llvm_ir_correct/byval-sret-x86.f90 | 63 +++++++++++++++++++ tools/flang2/flang2exe/cgmain.cpp | 12 +++- tools/flang2/flang2exe/llutil.cpp | 30 ++++++--- 4 files changed, 160 insertions(+), 12 deletions(-) create mode 100644 test/llvm_ir_correct/byval-sret-aarch64.f90 create mode 100644 test/llvm_ir_correct/byval-sret-x86.f90 diff --git a/test/llvm_ir_correct/byval-sret-aarch64.f90 b/test/llvm_ir_correct/byval-sret-aarch64.f90 new file mode 100644 index 00000000000..6739a4e7227 --- /dev/null +++ b/test/llvm_ir_correct/byval-sret-aarch64.f90 @@ -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 diff --git a/test/llvm_ir_correct/byval-sret-x86.f90 b/test/llvm_ir_correct/byval-sret-x86.f90 new file mode 100644 index 00000000000..26472951184 --- /dev/null +++ b/test/llvm_ir_correct/byval-sret-x86.f90 @@ -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 diff --git a/tools/flang2/flang2exe/cgmain.cpp b/tools/flang2/flang2exe/cgmain.cpp index 28bc756614a..e05585630db 100644 --- a/tools/flang2/flang2exe/cgmain.cpp +++ b/tools/flang2/flang2exe/cgmain.cpp @@ -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 @@ -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); @@ -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); @@ -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)); diff --git a/tools/flang2/flang2exe/llutil.cpp b/tools/flang2/flang2exe/llutil.cpp index 4e47c5fbd02..5e6ebf23915 100644 --- a/tools/flang2/flang2exe/llutil.cpp +++ b/tools/flang2/flang2exe/llutil.cpp @@ -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; @@ -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