Hello world, the attached patch fixes a rather bad ABI violation on POWER systems.
In the absence of an explicit interface and if a procedure is not in the same file, gfortran currently generates wrong function decls - a longstanding problem that also creates problems with LTO, because it (correctly) complains about mismatched declarations. Usually, we got lucky because the actual calling sequences generated by the compiler with the wrong info happened to match the ones with the correct info. However, our luck ran out on POWER because as soon as arguments were passed in memory, things did not work any more. The test case in question (see attachments) produced wrong code on POWER, but merely warned with LTO on other systems. The method for solving this problem can be seen in the patch - if there is no backend decl for an external procedure, simply generate a formal argument list from the arguments. Regression tests turned up a few ICEs (now fixed), plus two very invalid test cases, which I think are not worth saving. I suspect that this will also fix a few LTO bugs, but we can always check that after this has been committed. I'd still like confirmation from one of the POWER people that this also fixes the bug on that architecture. Should this still go into gcc-9? Richard has indicated in the PR that he thinks so. I think so too, because of the severity of the bug(s) this fixes. Any bugs resulting from this could be either a) ICE-on-valid (easily fixed) or b) somehow generating a wrong decl, but we are already doing this as of this moment, so things can not really be made much worse, and a lot better. So, ok for trunk and for backport (with some time in between) once gcc-8 has re-opened? Regards Thomas 2019-02-17 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/87689 * trans-decl.c (gfc_get_extern_function_decl): Add argument actual_args and pass it through to gfc_get_function_type. * trans-expr.c (conv_function_val): Add argument actual_args and pass it on to gfc_get_extern_function_decl. (conv_procedure_call): Pass actual arguments to conv_function_val. * trans-types.c (get_formal_from_actual_arglist): New function. (gfc_get_function_type): Add argument actual_args. Generate formal args from actual args if necessary. * trans-types.h (gfc_get_function_type): Add optional argument. * trans.h (gfc_get_extern_function_decl): Add optional argument. 2019-02-17 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/87689 * gfortran.dg/lto/20091028-1_0.f90: Remove invalid test case. * gfortran.dg/lto/20091028-1_1.c: Likewise. * gfortran.dg/lto/20091028-2_0.f90: Likewise. * gfortran.dg/lto/20091028-2_1.c: Likewise. * gfortran.dg/lto/pr87689_0.f: New file. * gfortran.dg/lto/pr87689_1.f: New file.
Index: trans-decl.c =================================================================== --- trans-decl.c (Revision 268968) +++ trans-decl.c (Arbeitskopie) @@ -1962,7 +1962,7 @@ get_proc_pointer_decl (gfc_symbol *sym) /* Get a basic decl for an external function. */ tree -gfc_get_extern_function_decl (gfc_symbol * sym) +gfc_get_extern_function_decl (gfc_symbol * sym, gfc_actual_arglist *actual_args) { tree type; tree fndecl; @@ -2135,7 +2135,7 @@ module_sym: mangled_name = gfc_sym_mangled_function_id (sym); } - type = gfc_get_function_type (sym); + type = gfc_get_function_type (sym, actual_args); fndecl = build_decl (input_location, FUNCTION_DECL, name, type); Index: trans-expr.c =================================================================== --- trans-expr.c (Revision 268968) +++ trans-expr.c (Arbeitskopie) @@ -3895,7 +3895,8 @@ conv_base_obj_fcn_val (gfc_se * se, tree base_obje static void -conv_function_val (gfc_se * se, gfc_symbol * sym, gfc_expr * expr) +conv_function_val (gfc_se * se, gfc_symbol * sym, gfc_expr * expr, + gfc_actual_arglist *actual_args) { tree tmp; @@ -3913,7 +3914,7 @@ static void else { if (!sym->backend_decl) - sym->backend_decl = gfc_get_extern_function_decl (sym); + sym->backend_decl = gfc_get_extern_function_decl (sym, actual_args); TREE_USED (sym->backend_decl) = 1; @@ -6580,7 +6581,7 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * /* Generate the actual call. */ if (base_object == NULL_TREE) - conv_function_val (se, sym, expr); + conv_function_val (se, sym, expr, args); else conv_base_obj_fcn_val (se, base_object, expr); Index: trans-types.c =================================================================== --- trans-types.c (Revision 268968) +++ trans-types.c (Arbeitskopie) @@ -2970,9 +2970,54 @@ create_fn_spec (gfc_symbol *sym, tree fntype) return build_type_attribute_variant (fntype, tmp); } +/* Helper function - if we do not find an interface for a procedure, + construct it from the actual arglist. Luckily, this can only + happen for call by reference, so the information we actually need + to provide (and which would be impossible to guess from the call + itself) is not actually needed. */ +static void +get_formal_from_actual_arglist (gfc_symbol *sym, gfc_actual_arglist *actual_args) +{ + gfc_actual_arglist *a; + gfc_formal_arglist **f; + gfc_symbol *s; + char name[GFC_MAX_SYMBOL_LEN + 1]; + static int var_num; + + f = &sym->formal; + for (a = actual_args; a != NULL; a = a->next) + { + if (a->expr) + { + (*f) = gfc_get_formal_arglist (); + snprintf (name, GFC_MAX_SYMBOL_LEN, "_formal_%d", var_num ++); + gfc_get_symbol (name, NULL, &s); + if (a->expr->ts.type == BT_PROCEDURE) + { + s->attr.flavor = FL_PROCEDURE; + } + else + { + s->ts = a->expr->ts; + s->attr.flavor = FL_VARIABLE; + if (a->expr->rank > 0) + { + s->attr.dimension = 1; + s->as = gfc_get_array_spec (); + s->as->type = AS_ASSUMED_SIZE; + } + } + s->attr.dummy = 1; + s->attr.intent = INTENT_UNKNOWN; + (*f)->sym = s; + } + f = &((*f)->next); + } +} + tree -gfc_get_function_type (gfc_symbol * sym) +gfc_get_function_type (gfc_symbol * sym, gfc_actual_arglist *actual_args) { tree type; vec<tree, va_gc> *typelist = NULL; @@ -3030,6 +3075,10 @@ tree vec_safe_push (typelist, build_pointer_type(gfc_charlen_type_node)); } } + if (sym->backend_decl == error_mark_node && actual_args != NULL + && sym->formal == NULL && (sym->attr.proc == PROC_EXTERNAL + || sym->attr.proc == PROC_UNKNOWN)) + get_formal_from_actual_arglist (sym, actual_args); /* Build the argument types for the function. */ for (f = gfc_sym_get_dummy_args (sym); f; f = f->next) Index: trans-types.h =================================================================== --- trans-types.h (Revision 268968) +++ trans-types.h (Arbeitskopie) @@ -88,7 +88,7 @@ tree gfc_sym_type (gfc_symbol *); tree gfc_typenode_for_spec (gfc_typespec *, int c = 0); int gfc_copy_dt_decls_ifequal (gfc_symbol *, gfc_symbol *, bool); -tree gfc_get_function_type (gfc_symbol *); +tree gfc_get_function_type (gfc_symbol *, gfc_actual_arglist *args = NULL); tree gfc_type_for_size (unsigned, int); tree gfc_type_for_mode (machine_mode, int); Index: trans.h =================================================================== --- trans.h (Revision 268968) +++ trans.h (Arbeitskopie) @@ -580,7 +580,8 @@ void gfc_merge_block_scope (stmtblock_t * block); tree gfc_get_label_decl (gfc_st_label *); /* Return the decl for an external function. */ -tree gfc_get_extern_function_decl (gfc_symbol *); +tree gfc_get_extern_function_decl (gfc_symbol *, + gfc_actual_arglist *args = NULL); /* Return the decl for a function. */ tree gfc_get_function_decl (gfc_symbol *);
! { dg-lto-run } ! { dg-lto-options {{ -Wno-lto-type-mismatch }} } ! PR 87689 - this used to fail for POWER, plus it used to ! give warnings about mismatches with LTO. ! Original test case by Judicaël Grasset. program main implicit none character :: c character(len=20) :: res, doesntwork_p8 external doesntwork_p8 c = 'o' res = doesntwork_p8(c,1,2,3,4,5,6) if (res /= 'foo') stop 3 end program main
function doesntwork_p8(c,a1,a2,a3,a4,a5,a6) implicit none character(len=20) :: doesntwork_p8 character :: c integer :: a1,a2,a3,a4,a5,a6 if (a1 /= 1 .or. a2 /= 2 .or. a3 /= 3 .or. a4 /= 4 .or. a5 /= 5 & .or. a6 /= 6) stop 1 if (c /= 'o ') stop 2 doesntwork_p8 = 'foo' return end