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