>> "Jose E. Marchesi" <jose.march...@oracle.com> writes: >>> There are many places in GCC where alternative local sequences are >>> tried in order to determine what is the cheapest or best alternative >>> to use in the current target. When any of these sequences involve a >>> libcall, the current implementation of emit_library_call_value_1 >>> introduce a side-effect consisting on emitting an external declaration >>> for the funcall (such as __divdi3) which is thus emitted even if the >>> sequence that does the libcall is not retained. >>> >>> This is problematic in targets such as BPF, because the kernel loader >>> chokes on the spurious symbol __divdi3 and makes the resulting BPF >>> object unloadable. Note that BPF objects are not linked before being >>> loaded. >>> >>> This patch changes asssemble_external_libcall to defer emitting >>> declarations of external libcall symbols, by saving the call tree >>> nodes in a temporary list pending_libcall_symbols and letting >>> process_pending_assembly_externals to emit them only if they have been >>> referenced. Solution suggested and sketched by Richard Sandiford. >>> >>> Regtested in x86_64-linux-gnu. >>> Tested with host x86_64-linux-gnu with target bpf-unknown-none. >>> >>> gcc/ChangeLog >>> >>> * varasm.cc (pending_libcall_symbols): New variable. >>> (process_pending_assemble_externals): Process >>> pending_libcall_symbols. >>> (assemble_external_libcall): Defer emitting external libcall >>> symbols to process_pending_assemble_externals. >>> >>> gcc/testsuite/ChangeLog >>> >>> * gcc.target/bpf/divmod-libcall-1.c: New test. >>> * gcc.target/bpf/divmod-libcall-2.c: Likewise. >>> * gcc.c-torture/compile/libcall-2.c: Likewise. >> >> OK, thanks. > > Thank you. > Pushed.
I installed the following fix, since the built got broken in targets that do not define ASM_OUTPUT_EXTERNAL. diff --git a/gcc/varasm.cc b/gcc/varasm.cc index deb7eab7af9..167aea87091 100644 --- a/gcc/varasm.cc +++ b/gcc/varasm.cc @@ -2607,7 +2607,9 @@ assemble_external_libcall (rtx fun) /* Declare library function name external when first used, if nec. */ if (! SYMBOL_REF_USED (fun)) { +#ifdef ASM_OUTPUT_EXTERNAL gcc_assert (!pending_assemble_externals_processed); +#endif SYMBOL_REF_USED (fun) = 1; /* Make sure the libcall symbol is in the symtab so any reference to it will mark its tree node as referenced, via -- 2.30.2 > >> Richard >> >>> --- >>> .../gcc.c-torture/compile/libcall-2.c | 8 +++++++ >>> .../gcc.target/bpf/divmod-libcall-1.c | 19 ++++++++++++++++ >>> .../gcc.target/bpf/divmod-libcall-2.c | 16 ++++++++++++++ >>> gcc/varasm.cc | 22 ++++++++++++++++++- >>> 4 files changed, 64 insertions(+), 1 deletion(-) >>> create mode 100644 gcc/testsuite/gcc.c-torture/compile/libcall-2.c >>> create mode 100644 gcc/testsuite/gcc.target/bpf/divmod-libcall-1.c >>> create mode 100644 gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c >>> >>> diff --git a/gcc/testsuite/gcc.c-torture/compile/libcall-2.c >>> b/gcc/testsuite/gcc.c-torture/compile/libcall-2.c >>> new file mode 100644 >>> index 00000000000..b33944c83ff >>> --- /dev/null >>> +++ b/gcc/testsuite/gcc.c-torture/compile/libcall-2.c >>> @@ -0,0 +1,8 @@ >>> +/* Make sure that external refences for libcalls are generated even for >>> + indirect calls. */ >>> + >>> +/* { dg-do compile } */ >>> +/* { dg-options "-O2 -mcmodel=large" { target x86_64-*-* } } */ >>> +/* { dg-final { scan-assembler "globl\t__divti3" } } */ >>> + >>> +__int128 a, b; void foo () { a = a / b; } >>> diff --git a/gcc/testsuite/gcc.target/bpf/divmod-libcall-1.c >>> b/gcc/testsuite/gcc.target/bpf/divmod-libcall-1.c >>> new file mode 100644 >>> index 00000000000..7481076602a >>> --- /dev/null >>> +++ b/gcc/testsuite/gcc.target/bpf/divmod-libcall-1.c >>> @@ -0,0 +1,19 @@ >>> +/* This test makes sure that no spurious external symbol declarations are >>> + emitted for libcalls in tried but eventually not used code sequences. >>> */ >>> + >>> +/* { dg-do compile } */ >>> +/* { dg-options "-O2 -mcpu=v3" } */ >>> +/* { dg-final { scan-assembler-not "global\t__divdi3" } } */ >>> +/* { dg-final { scan-assembler-not "global\t__moddi3" } } */ >>> + >>> +int >>> +foo (unsigned int len) >>> +{ >>> + return ((unsigned long)len) * 234 / 5; >>> +} >>> + >>> +int >>> +bar (unsigned int len) >>> +{ >>> + return ((unsigned long)len) * 234 % 5; >>> +} >>> diff --git a/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c >>> b/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c >>> new file mode 100644 >>> index 00000000000..792d689395a >>> --- /dev/null >>> +++ b/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c >>> @@ -0,0 +1,16 @@ >>> +/* { dg-do compile } */ >>> +/* { dg-options "-O2 -mcpu=v3" } */ >>> +/* { dg-final { scan-assembler "global\t__divdi3" } } */ >>> +/* { dg-final { scan-assembler "global\t__moddi3" } } */ >>> + >>> +int >>> +foo (unsigned int len) >>> +{ >>> + return ((long)len) * 234 / 5; >>> +} >>> + >>> +int >>> +bar (unsigned int len) >>> +{ >>> + return ((long)len) * 234 % 5; >>> +} >>> diff --git a/gcc/varasm.cc b/gcc/varasm.cc >>> index 6ae35edc5ae..deb7eab7af9 100644 >>> --- a/gcc/varasm.cc >>> +++ b/gcc/varasm.cc >>> @@ -2461,6 +2461,10 @@ contains_pointers_p (tree type) >>> it all the way to final. See PR 17982 for further discussion. */ >>> static GTY(()) tree pending_assemble_externals; >>> >>> +/* A similar list of pending libcall symbols. We only want to declare >>> + symbols that are actually used in the final assembly. */ >>> +static GTY(()) rtx pending_libcall_symbols; >>> + >>> #ifdef ASM_OUTPUT_EXTERNAL >>> /* Some targets delay some output to final using TARGET_ASM_FILE_END. >>> As a result, assemble_external can be called after the list of externals >>> @@ -2520,8 +2524,17 @@ process_pending_assemble_externals (void) >>> for (list = pending_assemble_externals; list; list = TREE_CHAIN (list)) >>> assemble_external_real (TREE_VALUE (list)); >>> >>> + for (rtx list = pending_libcall_symbols; list; list = XEXP (list, 1)) >>> + { >>> + rtx symbol = XEXP (list, 0); >>> + tree id = get_identifier (XSTR (symbol, 0)); >>> + if (TREE_SYMBOL_REFERENCED (id)) >>> + targetm.asm_out.external_libcall (symbol); >>> + } >>> + >>> pending_assemble_externals = 0; >>> pending_assemble_externals_processed = true; >>> + pending_libcall_symbols = NULL_RTX; >>> delete pending_assemble_externals_set; >>> #endif >>> } >>> @@ -2594,8 +2607,15 @@ assemble_external_libcall (rtx fun) >>> /* Declare library function name external when first used, if nec. */ >>> if (! SYMBOL_REF_USED (fun)) >>> { >>> + gcc_assert (!pending_assemble_externals_processed); >>> SYMBOL_REF_USED (fun) = 1; >>> - targetm.asm_out.external_libcall (fun); >>> + /* Make sure the libcall symbol is in the symtab so any >>> + reference to it will mark its tree node as referenced, via >>> + assemble_name_resolve. These are eventually emitted, if >>> + used, in process_pending_assemble_externals. */ >>> + get_identifier (XSTR (fun, 0)); >>> + pending_libcall_symbols = gen_rtx_EXPR_LIST (VOIDmode, fun, >>> + pending_libcall_symbols); >>> } >>> }