diff mbox

[2/2] DWARF: make it possible to emit debug info for declarations only

Message ID 20170529075011.15538-2-derodat@adacore.com
State New
Headers show

Commit Message

Pierre-Marie de Rodat May 29, 2017, 7:50 a.m. UTC
Hello,

The DWARF back-end used to systematically ignore file-scope function and
variable declarations.  While this is justified in language like C/C++,
where such declarations can appear in several translation units and thus
bloat uselessly the debug info, this behavior is counter-productive in
languages with a well-defined module system.  Specifically, it prevents
the description of imported entities, that belong to foreign languages,
making them unavailable from debuggers.

Take for instance:

    package C_Binding is
        function My_C_Function (I : Integer) return Integer;
        pragma Import (C, My_C_Function, "my_c_function");
    end C_Binding;

This makes available for Ada programs the C function "my_c_function"
under the following name: C_Binding.My_C_Function.  When GCC compiles
it, though, it is represented as a FUNCTION_DECL node with DECL_EXTERNAL
set and a null DECL_INITIAL, which used to be discarded unconditionally
in the DWARF back-end.

This patch introduces a new DECL language hook:
emit_debug_info_for_decl_p, which the DWARF back-end uses to determine
whether it should emit debug info for some declaration.  This makes it
possible for front-ends to decide the appropriate behavior.

This patch also updates the Ada front-end to override this hook, so that
declarations such as the above do generate debugging information.

Bootstrapped and reg-tested on x86_64-linux.  Ok to commit? Thank you in
advance!

gcc/
	* langhooks.h
	(lang_hooks_for_decls::emit_debug_info_for_decl_p): New field.
	* langhooks-def.h
	(LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P): New macro.
	* dwarf2out.c (gen_decl_die): Use the new hook to determine
	whether to ignore file-scope declarations.
	(dwarf2out_early_global_decl): Likewise.
	(dwaf2out_decl): Likewise.

gcc/ada
	* gcc-interface/ada-tree.h (DECL_FUNCTION_IS_DEF): New macro.
	* gcc-interface/misc.c (gnat_emit_debug_info_for_decl_p): New
	function.
	(LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P): Override macro.
	* gcc-interface/trans.c (Compilation_Unit_to_gnu): Tag the
	elaboration procedure as a definition.
	(Subprogram_Body_to_gnu): Tag the subprogram as a definition.
	* gcc-interface/decl.c (gnat_to_gnu_entity): Tag declarations of
	imported subprograms for the current compilation unit as
	definitions.  Disable debug info for references to variables.
	* gcc-interface/utils.c (create_subprog_decl): Add a DEFINITION
	parameter.  If it is true, tag the function as a definition.
	Update all callers.
	(gnat_pushdecl): Add external DECLs that are not built-in
	functions to their binding scope.
	(gnat_write_global_declarations): Emit debug info for imported
	functions.  Filter out external variables for which debug info
	is disabled.
	* gcc-interface/gigi.c (create_subprog_decl): Update
	declaration.

git-svn-id: svn+ssh://svn.us.adacore.com/Dev/trunk/gcc-interfaces@328405 f8352e7e-cb20-0410-8ce7-b5d9e71c585c
---
 gcc/ada/gcc-interface/ada-tree.h |  7 +++++-
 gcc/ada/gcc-interface/decl.c     | 19 +++++++++------
 gcc/ada/gcc-interface/gigi.h     |  5 +++-
 gcc/ada/gcc-interface/misc.c     | 11 +++++++++
 gcc/ada/gcc-interface/trans.c    | 52 ++++++++++++++++++++++++----------------
 gcc/ada/gcc-interface/utils.c    | 34 +++++++++++++++++++-------
 gcc/dwarf2out.c                  | 13 ++++++----
 gcc/langhooks-def.h              |  2 ++
 gcc/langhooks.h                  |  9 +++++++
 9 files changed, 109 insertions(+), 43 deletions(-)

Comments

Pierre-Marie de Rodat May 29, 2017, 1:19 p.m. UTC | #1
On 05/29/2017 09:50 AM, Pierre-Marie de Rodat wrote:
> Bootstrapped and reg-tested on x86_64-linux.  Ok to commit? Thank you in
> advance!

I just realized that I forgot to add the testcase: here it is, sorry!
Richard Biener May 30, 2017, 11:59 a.m. UTC | #2
On Mon, May 29, 2017 at 9:50 AM, Pierre-Marie de Rodat
<derodat@adacore.com> wrote:
> Hello,
>
> The DWARF back-end used to systematically ignore file-scope function and
> variable declarations.  While this is justified in language like C/C++,
> where such declarations can appear in several translation units and thus
> bloat uselessly the debug info, this behavior is counter-productive in
> languages with a well-defined module system.  Specifically, it prevents
> the description of imported entities, that belong to foreign languages,
> making them unavailable from debuggers.
>
> Take for instance:
>
>     package C_Binding is
>         function My_C_Function (I : Integer) return Integer;
>         pragma Import (C, My_C_Function, "my_c_function");
>     end C_Binding;
>
> This makes available for Ada programs the C function "my_c_function"
> under the following name: C_Binding.My_C_Function.  When GCC compiles
> it, though, it is represented as a FUNCTION_DECL node with DECL_EXTERNAL
> set and a null DECL_INITIAL, which used to be discarded unconditionally
> in the DWARF back-end.
>
> This patch introduces a new DECL language hook:
> emit_debug_info_for_decl_p, which the DWARF back-end uses to determine
> whether it should emit debug info for some declaration.  This makes it
> possible for front-ends to decide the appropriate behavior.
>
> This patch also updates the Ada front-end to override this hook, so that
> declarations such as the above do generate debugging information.
>
> Bootstrapped and reg-tested on x86_64-linux.  Ok to commit? Thank you in
> advance!

I think the issue is unfortunate in the C frontend as well.  So I believe we can
go without a new langhook and instead make sure dwarf2out_early_global_decl
is not called for uninteresting decls (which means eventually pushing the
call(s) of that hook more towards the FEs).

For C/C++ it would be reasonable to output debug info for external declarations
that end up being used for example.

Richard.

> gcc/
>         * langhooks.h
>         (lang_hooks_for_decls::emit_debug_info_for_decl_p): New field.
>         * langhooks-def.h
>         (LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P): New macro.
>         * dwarf2out.c (gen_decl_die): Use the new hook to determine
>         whether to ignore file-scope declarations.
>         (dwarf2out_early_global_decl): Likewise.
>         (dwaf2out_decl): Likewise.
>
> gcc/ada
>         * gcc-interface/ada-tree.h (DECL_FUNCTION_IS_DEF): New macro.
>         * gcc-interface/misc.c (gnat_emit_debug_info_for_decl_p): New
>         function.
>         (LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P): Override macro.
>         * gcc-interface/trans.c (Compilation_Unit_to_gnu): Tag the
>         elaboration procedure as a definition.
>         (Subprogram_Body_to_gnu): Tag the subprogram as a definition.
>         * gcc-interface/decl.c (gnat_to_gnu_entity): Tag declarations of
>         imported subprograms for the current compilation unit as
>         definitions.  Disable debug info for references to variables.
>         * gcc-interface/utils.c (create_subprog_decl): Add a DEFINITION
>         parameter.  If it is true, tag the function as a definition.
>         Update all callers.
>         (gnat_pushdecl): Add external DECLs that are not built-in
>         functions to their binding scope.
>         (gnat_write_global_declarations): Emit debug info for imported
>         functions.  Filter out external variables for which debug info
>         is disabled.
>         * gcc-interface/gigi.c (create_subprog_decl): Update
>         declaration.
>
> git-svn-id: svn+ssh://svn.us.adacore.com/Dev/trunk/gcc-interfaces@328405 f8352e7e-cb20-0410-8ce7-b5d9e71c585c
> ---
>  gcc/ada/gcc-interface/ada-tree.h |  7 +++++-
>  gcc/ada/gcc-interface/decl.c     | 19 +++++++++------
>  gcc/ada/gcc-interface/gigi.h     |  5 +++-
>  gcc/ada/gcc-interface/misc.c     | 11 +++++++++
>  gcc/ada/gcc-interface/trans.c    | 52 ++++++++++++++++++++++++----------------
>  gcc/ada/gcc-interface/utils.c    | 34 +++++++++++++++++++-------
>  gcc/dwarf2out.c                  | 13 ++++++----
>  gcc/langhooks-def.h              |  2 ++
>  gcc/langhooks.h                  |  9 +++++++
>  9 files changed, 109 insertions(+), 43 deletions(-)
>
> diff --git a/gcc/ada/gcc-interface/ada-tree.h b/gcc/ada/gcc-interface/ada-tree.h
> index a3d38b1b22e..511a0bd8173 100644
> --- a/gcc/ada/gcc-interface/ada-tree.h
> +++ b/gcc/ada/gcc-interface/ada-tree.h
> @@ -6,7 +6,7 @@
>   *                                                                          *
>   *                              C Header File                               *
>   *                                                                          *
> - *          Copyright (C) 1992-2016, Free Software Foundation, Inc.         *
> + *          Copyright (C) 1992-2017, Free Software Foundation, Inc.         *
>   *                                                                          *
>   * GNAT is free software;  you can  redistribute it  and/or modify it under *
>   * terms of the  GNU General Public License as published  by the Free Soft- *
> @@ -463,6 +463,11 @@ do {                                                  \
>     a discriminant of a discriminated type without default expression.  */
>  #define DECL_INVARIANT_P(NODE) DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE))
>
> +/* Nonzero in a FUNCTION_DECL if this is a definition, i.e. if it was created
> +   by a call to gnat_to_gnu_entity with definition set to True.  */
> +#define DECL_FUNCTION_IS_DEF(NODE) \
> +  DECL_LANG_FLAG_4 (FUNCTION_DECL_CHECK (NODE))
> +
>  /* Nonzero in a VAR_DECL if it is a temporary created to hold the return
>     value of a function call or 'reference to a function call.  */
>  #define DECL_RETURN_VALUE_P(NODE) DECL_LANG_FLAG_5 (VAR_DECL_CHECK (NODE))
> diff --git a/gcc/ada/gcc-interface/decl.c b/gcc/ada/gcc-interface/decl.c
> index eab244e910c..83b9d0749fe 100644
> --- a/gcc/ada/gcc-interface/decl.c
> +++ b/gcc/ada/gcc-interface/decl.c
> @@ -1392,7 +1392,8 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
>               = create_var_decl (create_concat_name (gnat_entity, "ALIGN"),
>                                  NULL_TREE, gnu_new_type, NULL_TREE,
>                                  false, false, false, false, false,
> -                                true, debug_info_p, NULL, gnat_entity);
> +                                true, debug_info_p && definition, NULL,
> +                                gnat_entity);
>
>             /* Initialize the aligned field if we have an initializer.  */
>             if (gnu_expr)
> @@ -1441,7 +1442,8 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
>                                       NULL_TREE, gnu_type, gnu_expr,
>                                       const_flag, Is_Public (gnat_entity),
>                                       imported_p || !definition, static_flag,
> -                                     volatile_flag, true, debug_info_p,
> +                                     volatile_flag, true,
> +                                     debug_info_p && definition,
>                                       NULL, gnat_entity);
>                 gnu_expr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_unc_var);
>                 TREE_CONSTANT (gnu_expr) = 1;
> @@ -1492,8 +1494,9 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
>           = create_var_decl (gnu_entity_name, gnu_ext_name, gnu_type,
>                              gnu_expr, const_flag, Is_Public (gnat_entity),
>                              imported_p || !definition, static_flag,
> -                            volatile_flag, artificial_p, debug_info_p,
> -                            attr_list, gnat_entity, !renamed_obj);
> +                            volatile_flag, artificial_p,
> +                            debug_info_p && definition, attr_list,
> +                            gnat_entity, !renamed_obj);
>         DECL_BY_REF_P (gnu_decl) = used_by_ref;
>         DECL_POINTS_TO_READONLY_P (gnu_decl) = used_by_ref && inner_const_flag;
>         DECL_CAN_NEVER_BE_NULL_P (gnu_decl) = Can_Never_Be_Null (gnat_entity);
> @@ -1545,8 +1548,8 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
>               = create_var_decl (gnu_entity_name, gnu_ext_name, gnu_type,
>                                  gnu_expr, true, Is_Public (gnat_entity),
>                                  !definition, static_flag, volatile_flag,
> -                                artificial_p, debug_info_p, attr_list,
> -                                gnat_entity, false);
> +                                artificial_p, debug_info_p && definition,
> +                                attr_list, gnat_entity, false);
>
>             SET_DECL_CONST_CORRESPONDING_VAR (gnu_decl, gnu_corr_var);
>           }
> @@ -4083,7 +4086,9 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
>                                          gnu_type, gnu_param_list,
>                                          inline_status, public_flag,
>                                          extern_flag, artificial_p,
> -                                        debug_info_p, attr_list, gnat_entity);
> +                                        debug_info_p,
> +                                        definition && imported_p, attr_list,
> +                                        gnat_entity);
>
>                 DECL_STUBBED_P (gnu_decl)
>                   = (Convention (gnat_entity) == Convention_Stubbed);
> diff --git a/gcc/ada/gcc-interface/gigi.h b/gcc/ada/gcc-interface/gigi.h
> index b1fb34ad620..0e25b6129a3 100644
> --- a/gcc/ada/gcc-interface/gigi.h
> +++ b/gcc/ada/gcc-interface/gigi.h
> @@ -720,6 +720,8 @@ extern tree create_label_decl (tree name, Node_Id gnat_node);
>
>     DEBUG_INFO_P is true if we need to write debug information for it.
>
> +   DEFINITION is true if the subprogram is to be considered as a definition.
> +
>     ATTR_LIST is the list of attributes to be attached to the subprogram.
>
>     GNAT_NODE is used for the position of the decl.  */
> @@ -728,7 +730,8 @@ extern tree create_subprog_decl (tree name, tree asm_name, tree type,
>                                  enum inline_status_t inline_status,
>                                  bool public_flag, bool extern_flag,
>                                  bool artificial_p, bool debug_info_p,
> -                                struct attrib *attr_list, Node_Id gnat_node);
> +                                bool definition, struct attrib *attr_list,
> +                                Node_Id gnat_node);
>
>  /* Given a subprogram declaration DECL, its assembler name and its type,
>     finish constructing the subprogram declaration from ASM_NAME and TYPE.  */
> diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c
> index 18c825ca13e..cd0467ef8d9 100644
> --- a/gcc/ada/gcc-interface/misc.c
> +++ b/gcc/ada/gcc-interface/misc.c
> @@ -1348,6 +1348,15 @@ gnat_eh_personality (void)
>    return gnat_eh_personality_decl;
>  }
>
> +/* Given DECL, which is a reference (we don't have a definition for it in this
> +   compilation unit), return whether we should emit debug info for it.  */
> +
> +static bool
> +gnat_emit_debug_info_for_decl_p (tree decl)
> +{
> +  return TREE_CODE (decl) != FUNCTION_DECL || DECL_FUNCTION_IS_DEF (decl);
> +}
> +
>  /* Initialize language-specific bits of tree_contains_struct.  */
>
>  static void
> @@ -1451,6 +1460,8 @@ get_lang_specific (tree node)
>  #define LANG_HOOKS_DEEP_UNSHARING      true
>  #undef  LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS
>  #define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS true
> +#undef LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P
> +#define LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P gnat_emit_debug_info_for_decl_p
>
>  struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
>
> diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
> index 2542626d0ca..79d099538c9 100644
> --- a/gcc/ada/gcc-interface/trans.c
> +++ b/gcc/ada/gcc-interface/trans.c
> @@ -398,7 +398,7 @@ gigi (Node_Id gnat_root,
>      = create_subprog_decl (get_identifier ("__gnat_malloc"), NULL_TREE,
>                            ftype,
>                            NULL_TREE, is_disabled, true, true, true, false,
> -                          NULL, Empty);
> +                          false, NULL, Empty);
>    DECL_IS_MALLOC (malloc_decl) = 1;
>
>    ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
> @@ -406,7 +406,7 @@ gigi (Node_Id gnat_root,
>      = create_subprog_decl (get_identifier ("__gnat_free"), NULL_TREE,
>                            ftype,
>                            NULL_TREE, is_disabled, true, true, true, false,
> -                          NULL, Empty);
> +                          false, NULL, Empty);
>
>    ftype = build_function_type_list (ptr_type_node, ptr_type_node, sizetype,
>                                     NULL_TREE);
> @@ -414,7 +414,7 @@ gigi (Node_Id gnat_root,
>      = create_subprog_decl (get_identifier ("__gnat_realloc"), NULL_TREE,
>                            ftype,
>                            NULL_TREE, is_disabled, true, true, true, false,
> -                          NULL, Empty);
> +                          false, NULL, Empty);
>
>    /* This is used for 64-bit multiplication with overflow checking.  */
>    int64_type = gnat_type_for_size (64, 0);
> @@ -423,7 +423,7 @@ gigi (Node_Id gnat_root,
>                            build_function_type_list (int64_type, int64_type,
>                                                      int64_type, NULL_TREE),
>                            NULL_TREE, is_disabled, true, true, true, false,
> -                          NULL, Empty);
> +                          false, NULL, Empty);
>
>    /* Name of the _Parent field in tagged record types.  */
>    parent_name_id = get_identifier (Get_Name_String (Name_uParent));
> @@ -446,21 +446,21 @@ gigi (Node_Id gnat_root,
>      = create_subprog_decl
>        (get_identifier ("system__soft_links__get_jmpbuf_address_soft"),
>         NULL_TREE, build_function_type_list (jmpbuf_ptr_type, NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>
>    set_jmpbuf_decl
>      = create_subprog_decl
>        (get_identifier ("system__soft_links__set_jmpbuf_address_soft"),
>         NULL_TREE, build_function_type_list (void_type_node, jmpbuf_ptr_type,
>                                             NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>
>    get_excptr_decl
>      = create_subprog_decl
>        (get_identifier ("system__soft_links__get_gnat_exception"), NULL_TREE,
>         build_function_type_list (build_pointer_type (except_type_node),
>                                  NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>
>    not_handled_by_others_decl = get_identifier ("not_handled_by_others");
>    for (t = TYPE_FIELDS (except_type_node); t; t = DECL_CHAIN (t))
> @@ -478,7 +478,7 @@ gigi (Node_Id gnat_root,
>        (get_identifier ("__builtin_setjmp"), NULL_TREE,
>         build_function_type_list (integer_type_node, jmpbuf_ptr_type,
>                                  NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>    DECL_BUILT_IN_CLASS (setjmp_decl) = BUILT_IN_NORMAL;
>    DECL_FUNCTION_CODE (setjmp_decl) = BUILT_IN_SETJMP;
>
> @@ -488,7 +488,7 @@ gigi (Node_Id gnat_root,
>      = create_subprog_decl
>        (get_identifier ("__builtin_update_setjmp_buf"), NULL_TREE,
>         build_function_type_list (void_type_node, jmpbuf_ptr_type, NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>    DECL_BUILT_IN_CLASS (update_setjmp_buf_decl) = BUILT_IN_NORMAL;
>    DECL_FUNCTION_CODE (update_setjmp_buf_decl) = BUILT_IN_UPDATE_SETJMP_BUF;
>
> @@ -500,14 +500,14 @@ gigi (Node_Id gnat_root,
>    raise_nodefer_decl
>      = create_subprog_decl
>        (get_identifier ("__gnat_raise_nodefer_with_msg"), NULL_TREE, ftype,
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>
>    set_exception_parameter_decl
>      = create_subprog_decl
>        (get_identifier ("__gnat_set_exception_parameter"), NULL_TREE,
>         build_function_type_list (void_type_node, ptr_type_node, ptr_type_node,
>                                  NULL_TREE),
> -       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
>
>    /* Hooks to call when entering/leaving an exception handler.  */
>    ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
> @@ -515,26 +515,30 @@ gigi (Node_Id gnat_root,
>    begin_handler_decl
>      = create_subprog_decl (get_identifier ("__gnat_begin_handler"), NULL_TREE,
>                            ftype, NULL_TREE,
> -                          is_disabled, true, true, true, false, NULL, Empty);
> +                          is_disabled, true, true, true, false, false, NULL,
> +                          Empty);
>    /* __gnat_begin_handler is a dummy procedure.  */
>    TREE_NOTHROW (begin_handler_decl) = 1;
>
>    end_handler_decl
>      = create_subprog_decl (get_identifier ("__gnat_end_handler"), NULL_TREE,
>                            ftype, NULL_TREE,
> -                          is_disabled, true, true, true, false, NULL, Empty);
> +                          is_disabled, true, true, true, false, false, NULL,
> +                          Empty);
>
>    unhandled_except_decl
>      = create_subprog_decl (get_identifier ("__gnat_unhandled_except_handler"),
>                            NULL_TREE, ftype, NULL_TREE,
> -                          is_disabled, true, true, true, false, NULL, Empty);
> +                          is_disabled, true, true, true, false, false, NULL,
> +                          Empty);
>
>    /* Indicate that it never returns.  */
>    ftype = build_qualified_type (ftype, TYPE_QUAL_VOLATILE);
>    reraise_zcx_decl
>      = create_subprog_decl (get_identifier ("__gnat_reraise_zcx"), NULL_TREE,
>                            ftype, NULL_TREE,
> -                          is_disabled, true, true, true, false, NULL, Empty);
> +                          is_disabled, true, true, true, false, false, NULL,
> +                          Empty);
>
>    /* Dummy objects to materialize "others" and "all others" in the exception
>       tables.  These are exported by a-exexpr-gcc.adb, so see this unit for
> @@ -573,7 +577,8 @@ gigi (Node_Id gnat_root,
>        tree decl
>         = create_subprog_decl
>           (get_identifier ("__gnat_last_chance_handler"), NULL_TREE, ftype,
> -          NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
> +          NULL_TREE, is_disabled, true, true, true, false, false, NULL,
> +          Empty);
>        for (i = 0; i < (int) ARRAY_SIZE (gnat_raise_decls); i++)
>         gnat_raise_decls[i] = decl;
>      }
> @@ -739,7 +744,7 @@ build_raise_check (int check, enum exception_info_kind kind)
>    result
>      = create_subprog_decl (get_identifier (Name_Buffer), NULL_TREE, ftype,
>                            NULL_TREE, is_disabled, true, true, true, false,
> -                          NULL, Empty);
> +                          false, NULL, Empty);
>
>    return result;
>  }
> @@ -3745,6 +3750,7 @@ Subprogram_Body_to_gnu (Node_Id gnat_node)
>      = gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE,
>                           Acts_As_Spec (gnat_node)
>                           && !present_gnu_tree (gnat_subprog_id));
> +  DECL_FUNCTION_IS_DEF (gnu_subprog_decl) = true;
>    gnu_result_decl = DECL_RESULT (gnu_subprog_decl);
>    gnu_subprog_type = TREE_TYPE (gnu_subprog_decl);
>    gnu_cico_list = TYPE_CI_CO_LIST (gnu_subprog_type);
> @@ -5417,12 +5423,15 @@ Compilation_Unit_to_gnu (Node_Id gnat_node)
>    const Entity_Id gnat_unit_entity = Defining_Entity (gnat_unit);
>    Entity_Id gnat_entity;
>    Node_Id gnat_pragma;
> -  /* Make the decl for the elaboration procedure.  */
> +  /* Make the decl for the elaboration procedure.  Emit debug info for it, so
> +     that users can break into their elaboration code in debuggers.  Kludge:
> +     don't consider it as a definition so that we have a line map for its body,
> +     but no subprogram description in debug info. */
>    tree gnu_elab_proc_decl
>      = create_subprog_decl
>        (create_concat_name (gnat_unit_entity, body_p ? "elabb" : "elabs"),
>         NULL_TREE, void_ftype, NULL_TREE,
> -       is_disabled, true, false, true, true, NULL, gnat_unit);
> +       is_disabled, true, false, true, true, false, NULL, gnat_unit);
>    struct elab_info *info;
>
>    vec_safe_push (gnu_elab_proc_stack, gnu_elab_proc_decl);
> @@ -6453,7 +6462,7 @@ gnat_to_gnu (Node_Id gnat_node)
>                 gnu_prefix = gnat_to_gnu (gnat_prefix);
>                 gnu_prefix = maybe_implicit_deref (gnu_prefix);
>               }
> -
> +
>             gnu_result
>               = build_component_ref (gnu_prefix, gnu_field,
>                                      (Nkind (Parent (gnat_node))
> @@ -6484,7 +6493,8 @@ gnat_to_gnu (Node_Id gnat_node)
>                                  (Entity (Prefix (gnat_node)),
>                                   attr == Attr_Elab_Body ? "elabb" : "elabs"),
>                                  NULL_TREE, void_ftype, NULL_TREE, is_disabled,
> -                                true, true, true, true, NULL, gnat_node);
> +                                true, true, true, true, false, NULL,
> +                                gnat_node);
>
>         gnu_result = Attribute_to_gnu (gnat_node, &gnu_result_type, attr);
>        }
> diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
> index b8c5d3d31f6..af53edcfa3f 100644
> --- a/gcc/ada/gcc-interface/utils.c
> +++ b/gcc/ada/gcc-interface/utils.c
> @@ -763,11 +763,13 @@ gnat_pushdecl (tree decl, Node_Id gnat_node)
>    if (!(TREE_CODE (decl) == TYPE_DECL
>          && TREE_CODE (TREE_TYPE (decl)) == UNCONSTRAINED_ARRAY_TYPE))
>      {
> -      if (DECL_EXTERNAL (decl))
> -       {
> -         if (TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl))
> -           vec_safe_push (builtin_decls, decl);
> -       }
> +      /* External declarations must go to the binding level they belong to.
> +        This will make corresponding imported entities are available in the
> +        debugger at the proper time.  */
> +      if (DECL_EXTERNAL (decl)
> +         && TREE_CODE (decl) == FUNCTION_DECL
> +         && DECL_BUILT_IN (decl))
> +       vec_safe_push (builtin_decls, decl);
>        else if (global_bindings_p ())
>         vec_safe_push (global_decls, decl);
>        else
> @@ -3189,6 +3191,8 @@ create_label_decl (tree name, Node_Id gnat_node)
>
>     DEBUG_INFO_P is true if we need to write debug information for it.
>
> +   DEFINITION is true if the subprogram is to be considered as a definition.
> +
>     ATTR_LIST is the list of attributes to be attached to the subprogram.
>
>     GNAT_NODE is used for the position of the decl.  */
> @@ -3197,7 +3201,8 @@ tree
>  create_subprog_decl (tree name, tree asm_name, tree type, tree param_decl_list,
>                      enum inline_status_t inline_status, bool public_flag,
>                      bool extern_flag, bool artificial_p, bool debug_info_p,
> -                    struct attrib *attr_list, Node_Id gnat_node)
> +                    bool definition, struct attrib *attr_list,
> +                    Node_Id gnat_node)
>  {
>    tree subprog_decl = build_decl (input_location, FUNCTION_DECL, name, type);
>    DECL_ARGUMENTS (subprog_decl) = param_decl_list;
> @@ -3208,6 +3213,8 @@ create_subprog_decl (tree name, tree asm_name, tree type, tree param_decl_list,
>
>    if (!debug_info_p)
>      DECL_IGNORED_P (subprog_decl) = 1;
> +  if (definition)
> +    DECL_FUNCTION_IS_DEF (subprog_decl) = 1;
>
>    switch (inline_status)
>      {
> @@ -5523,10 +5530,21 @@ gnat_write_global_declarations (void)
>      if (TREE_CODE (iter) == TYPE_DECL && !DECL_IGNORED_P (iter))
>        debug_hooks->type_decl (iter, false);
>
> +  /* Output imported functions.  */
> +  FOR_EACH_VEC_SAFE_ELT (global_decls, i, iter)
> +    if (TREE_CODE (iter) == FUNCTION_DECL
> +       && DECL_EXTERNAL (iter)
> +       && DECL_INITIAL (iter) == NULL
> +       && !DECL_IGNORED_P (iter))
> +      debug_hooks->early_global_decl (iter);
> +
>    /* Then output the global variables.  We need to do that after the debug
> -     information for global types is emitted so that they are finalized.  */
> +     information for global types is emitted so that they are finalized.  Skip
> +     external global variables, unless we need to emit debug info for them:
> +     this is useful for imported variables, for instance.  */
>    FOR_EACH_VEC_SAFE_ELT (global_decls, i, iter)
> -    if (TREE_CODE (iter) == VAR_DECL)
> +    if (TREE_CODE (iter) == VAR_DECL
> +       && (!DECL_EXTERNAL (iter) || !DECL_IGNORED_P (iter)))
>        rest_of_decl_compilation (iter, true, 0);
>
>    /* Output the imported modules/declarations.  In GNAT, these are only
> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
> index 5ff45eb4efd..e2a7f546034 100644
> --- a/gcc/dwarf2out.c
> +++ b/gcc/dwarf2out.c
> @@ -25271,7 +25271,8 @@ gen_decl_die (tree decl, tree origin, struct vlr_context *ctx,
>        if (DECL_INITIAL (decl_or_origin) == NULL_TREE
>            && DECL_FILE_SCOPE_P (decl_or_origin)
>           && (current_function_decl == NULL_TREE
> -             || DECL_ARTIFICIAL (decl_or_origin)))
> +             || DECL_ARTIFICIAL (decl_or_origin))
> +         && !lang_hooks.decls.emit_debug_info_for_decl_p (decl_or_origin))
>         break;
>
>  #if 0
> @@ -25478,9 +25479,10 @@ dwarf2out_early_global_decl (tree decl)
>        tree save_fndecl = current_function_decl;
>        if (TREE_CODE (decl) == FUNCTION_DECL)
>         {
> -         /* No cfun means the symbol has no body, so there's nothing
> -            to emit.  */
> -         if (!DECL_STRUCT_FUNCTION (decl))
> +         /* Unless we are asked to emit debug info for declarations, no cfun
> +            means the symbol has no body, so there's nothing to emit.  */
> +         if (!DECL_STRUCT_FUNCTION (decl)
> +             && !lang_hooks.decls.emit_debug_info_for_decl_p (decl))
>             goto early_decl_exit;
>
>           /* For nested functions, make sure we have DIEs for the parents first
> @@ -25770,7 +25772,8 @@ dwarf2out_decl (tree decl)
>          where the inlined function is output in a different LTRANS unit
>          or not at all.  */
>        if (DECL_INITIAL (decl) == NULL_TREE
> -         && ! DECL_ABSTRACT_P (decl))
> +         && ! DECL_ABSTRACT_P (decl)
> +         && ! lang_hooks.decls.emit_debug_info_for_decl_p (decl))
>         return;
>
>        /* If we're a nested function, initially use a parent of NULL; if we're
> diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
> index ea2006ccec5..e210e9e61ca 100644
> --- a/gcc/langhooks-def.h
> +++ b/gcc/langhooks-def.h
> @@ -225,6 +225,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
>
>  /* Declaration hooks.  */
>  #define LANG_HOOKS_GLOBAL_BINDINGS_P global_bindings_p
> +#define LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P hook_bool_tree_false
>  #define LANG_HOOKS_PUSHDECL    pushdecl
>  #define LANG_HOOKS_GETDECLS    getdecls
>  #define LANG_HOOKS_DECL_DWARF_ATTRIBUTE lhd_decl_dwarf_attribute
> @@ -247,6 +248,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
>
>  #define LANG_HOOKS_DECLS { \
>    LANG_HOOKS_GLOBAL_BINDINGS_P, \
> +  LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P, \
>    LANG_HOOKS_PUSHDECL, \
>    LANG_HOOKS_GETDECLS, \
>    LANG_HOOKS_DECL_DWARF_ATTRIBUTE, \
> diff --git a/gcc/langhooks.h b/gcc/langhooks.h
> index b2f0f922b89..27fd5638d21 100644
> --- a/gcc/langhooks.h
> +++ b/gcc/langhooks.h
> @@ -181,6 +181,15 @@ struct lang_hooks_for_decls
>       level, i.e. declared outside subprograms.  */
>    bool (*global_bindings_p) (void);
>
> +  /* Given some declaration, if this returns true, we should produce debug info
> +     for it.  We should not do this for languages with poor modules definition
> +     like C/C++ because translation units make us process a lot of declarations
> +     that are common to a lot of translation units, bloating the debug info.
> +     However this is desirable in languages like Ada, where we know we must not
> +     produce debug info for the declaration of functions that come from other
> +     units (unless in special cases, like cross-unit inlining).  */
> +  bool (*emit_debug_info_for_decl_p) (tree);
> +
>    /* Function to add a decl to the current scope level.  Takes one
>       argument, a decl to add.  Returns that decl, or, if the same
>       symbol is already declared, may return a different decl for that
> --
> 2.13.0
>
Pierre-Marie de Rodat May 30, 2017, 3:47 p.m. UTC | #3
Thank you for your review, Richard.

On 05/30/2017 01:59 PM, Richard Biener wrote:
> I think the issue is unfortunate in the C frontend as well.  So I believe we can
> go without a new langhook and instead make sure dwarf2out_early_global_decl
> is not called for uninteresting decls (which means eventually pushing the
> call(s) of that hook more towards the FEs).

It is called by rest_of_decl_compilation, which seems itself to be 
called a lot on FUNCTION_DECL nodes. Before I dive into this consequent 
change: this would lead for instance to add a parameter to 
rest_of_compilation to control whether it must call the 
early_global_decl hook, and then to update all callers accordingly. Is 
this what you actually have in mind?

> For C/C++ it would be reasonable to output debug info for external declarations
> that end up being used for example.

I guess that could be done indeed. :-)
Richard Biener May 31, 2017, 7:34 a.m. UTC | #4
On Tue, May 30, 2017 at 5:47 PM, Pierre-Marie de Rodat
<derodat@adacore.com> wrote:
> Thank you for your review, Richard.
>
> On 05/30/2017 01:59 PM, Richard Biener wrote:
>>
>> I think the issue is unfortunate in the C frontend as well.  So I believe
>> we can
>> go without a new langhook and instead make sure
>> dwarf2out_early_global_decl
>> is not called for uninteresting decls (which means eventually pushing the
>> call(s) of that hook more towards the FEs).
>
>
> It is called by rest_of_decl_compilation, which seems itself to be called a
> lot on FUNCTION_DECL nodes. Before I dive into this consequent change: this
> would lead for instance to add a parameter to rest_of_compilation to control
> whether it must call the early_global_decl hook, and then to update all
> callers accordingly. Is this what you actually have in mind?

Actually for the bigger picture I'd refactor rest_of_decl_compilation, not
calling it from the frontends but rely on finalize_decl/function.  The missing
part would then be calling the dwarf hook which should eventually be done
at some of the places the frontends now call rest_of_decl_compliation.

>
>> For C/C++ it would be reasonable to output debug info for external
>> declarations
>> that end up being used for example.
>
>
> I guess that could be done indeed. :-)

But for an easier way (you might still explore the above ;)) just remove
the guards from dwarf2out.c and handle it more like types that we
prune if they end up being unused (OTOH I guess we don't refer to
the decl DIEs from "calls" because not all calls are refered to with
standard DWARF -- the GNU callsite stuff refers them I think but those
get generated too late).

That said, when early_finish is called the cgraph and IPA references
exists and thus you can
sort-of see which functions are "used".

Richard.

> --
> Pierre-Marie de Rodat
Pierre-Marie de Rodat May 31, 2017, 9:08 a.m. UTC | #5
On 05/31/2017 09:34 AM, Richard Biener wrote:
> Actually for the bigger picture I'd refactor rest_of_decl_compilation, not
> calling it from the frontends but rely on finalize_decl/function.  The missing
> part would then be calling the dwarf hook which should eventually be done
> at some of the places the frontends now call rest_of_decl_compliation.
> […]
> But for an easier way (you might still explore the above ;)) just remove
> the guards from dwarf2out.c and handle it more like types that we
> prune if they end up being unused (OTOH I guess we don't refer to
> the decl DIEs from "calls" because not all calls are refered to with
> standard DWARF -- the GNU callsite stuff refers them I think but those
> get generated too late).
> 
> That said, when early_finish is called the cgraph and IPA references
> exists and thus you can
> sort-of see which functions are "used".

Ok, thanks. I’ll give a try to the first option, then. :-)
diff mbox

Patch

diff --git a/gcc/ada/gcc-interface/ada-tree.h b/gcc/ada/gcc-interface/ada-tree.h
index a3d38b1b22e..511a0bd8173 100644
--- a/gcc/ada/gcc-interface/ada-tree.h
+++ b/gcc/ada/gcc-interface/ada-tree.h
@@ -6,7 +6,7 @@ 
  *                                                                          *
  *                              C Header File                               *
  *                                                                          *
- *          Copyright (C) 1992-2016, Free Software Foundation, Inc.         *
+ *          Copyright (C) 1992-2017, Free Software Foundation, Inc.         *
  *                                                                          *
  * GNAT is free software;  you can  redistribute it  and/or modify it under *
  * terms of the  GNU General Public License as published  by the Free Soft- *
@@ -463,6 +463,11 @@  do {						   \
    a discriminant of a discriminated type without default expression.  */
 #define DECL_INVARIANT_P(NODE) DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE))
 
+/* Nonzero in a FUNCTION_DECL if this is a definition, i.e. if it was created
+   by a call to gnat_to_gnu_entity with definition set to True.  */
+#define DECL_FUNCTION_IS_DEF(NODE) \
+  DECL_LANG_FLAG_4 (FUNCTION_DECL_CHECK (NODE))
+
 /* Nonzero in a VAR_DECL if it is a temporary created to hold the return
    value of a function call or 'reference to a function call.  */
 #define DECL_RETURN_VALUE_P(NODE) DECL_LANG_FLAG_5 (VAR_DECL_CHECK (NODE))
diff --git a/gcc/ada/gcc-interface/decl.c b/gcc/ada/gcc-interface/decl.c
index eab244e910c..83b9d0749fe 100644
--- a/gcc/ada/gcc-interface/decl.c
+++ b/gcc/ada/gcc-interface/decl.c
@@ -1392,7 +1392,8 @@  gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
 	      = create_var_decl (create_concat_name (gnat_entity, "ALIGN"),
 				 NULL_TREE, gnu_new_type, NULL_TREE,
 				 false, false, false, false, false,
-				 true, debug_info_p, NULL, gnat_entity);
+				 true, debug_info_p && definition, NULL,
+				 gnat_entity);
 
 	    /* Initialize the aligned field if we have an initializer.  */
 	    if (gnu_expr)
@@ -1441,7 +1442,8 @@  gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
 				      NULL_TREE, gnu_type, gnu_expr,
 				      const_flag, Is_Public (gnat_entity),
 				      imported_p || !definition, static_flag,
-				      volatile_flag, true, debug_info_p,
+				      volatile_flag, true,
+				      debug_info_p && definition,
 				      NULL, gnat_entity);
 		gnu_expr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_unc_var);
 		TREE_CONSTANT (gnu_expr) = 1;
@@ -1492,8 +1494,9 @@  gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
 	  = create_var_decl (gnu_entity_name, gnu_ext_name, gnu_type,
 			     gnu_expr, const_flag, Is_Public (gnat_entity),
 			     imported_p || !definition, static_flag,
-			     volatile_flag, artificial_p, debug_info_p,
-			     attr_list, gnat_entity, !renamed_obj);
+			     volatile_flag, artificial_p,
+			     debug_info_p && definition, attr_list,
+			     gnat_entity, !renamed_obj);
 	DECL_BY_REF_P (gnu_decl) = used_by_ref;
 	DECL_POINTS_TO_READONLY_P (gnu_decl) = used_by_ref && inner_const_flag;
 	DECL_CAN_NEVER_BE_NULL_P (gnu_decl) = Can_Never_Be_Null (gnat_entity);
@@ -1545,8 +1548,8 @@  gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
 	      = create_var_decl (gnu_entity_name, gnu_ext_name, gnu_type,
 				 gnu_expr, true, Is_Public (gnat_entity),
 				 !definition, static_flag, volatile_flag,
-				 artificial_p, debug_info_p, attr_list,
-				 gnat_entity, false);
+				 artificial_p, debug_info_p && definition,
+				 attr_list, gnat_entity, false);
 
 	    SET_DECL_CONST_CORRESPONDING_VAR (gnu_decl, gnu_corr_var);
 	  }
@@ -4083,7 +4086,9 @@  gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
 					 gnu_type, gnu_param_list,
 					 inline_status, public_flag,
 					 extern_flag, artificial_p,
-					 debug_info_p, attr_list, gnat_entity);
+					 debug_info_p,
+					 definition && imported_p, attr_list,
+					 gnat_entity);
 
 		DECL_STUBBED_P (gnu_decl)
 		  = (Convention (gnat_entity) == Convention_Stubbed);
diff --git a/gcc/ada/gcc-interface/gigi.h b/gcc/ada/gcc-interface/gigi.h
index b1fb34ad620..0e25b6129a3 100644
--- a/gcc/ada/gcc-interface/gigi.h
+++ b/gcc/ada/gcc-interface/gigi.h
@@ -720,6 +720,8 @@  extern tree create_label_decl (tree name, Node_Id gnat_node);
 
    DEBUG_INFO_P is true if we need to write debug information for it.
 
+   DEFINITION is true if the subprogram is to be considered as a definition.
+
    ATTR_LIST is the list of attributes to be attached to the subprogram.
 
    GNAT_NODE is used for the position of the decl.  */
@@ -728,7 +730,8 @@  extern tree create_subprog_decl (tree name, tree asm_name, tree type,
 				 enum inline_status_t inline_status,
 				 bool public_flag, bool extern_flag,
 				 bool artificial_p, bool debug_info_p,
-				 struct attrib *attr_list, Node_Id gnat_node);
+				 bool definition, struct attrib *attr_list,
+				 Node_Id gnat_node);
 
 /* Given a subprogram declaration DECL, its assembler name and its type,
    finish constructing the subprogram declaration from ASM_NAME and TYPE.  */
diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c
index 18c825ca13e..cd0467ef8d9 100644
--- a/gcc/ada/gcc-interface/misc.c
+++ b/gcc/ada/gcc-interface/misc.c
@@ -1348,6 +1348,15 @@  gnat_eh_personality (void)
   return gnat_eh_personality_decl;
 }
 
+/* Given DECL, which is a reference (we don't have a definition for it in this
+   compilation unit), return whether we should emit debug info for it.  */
+
+static bool
+gnat_emit_debug_info_for_decl_p (tree decl)
+{
+  return TREE_CODE (decl) != FUNCTION_DECL || DECL_FUNCTION_IS_DEF (decl);
+}
+
 /* Initialize language-specific bits of tree_contains_struct.  */
 
 static void
@@ -1451,6 +1460,8 @@  get_lang_specific (tree node)
 #define LANG_HOOKS_DEEP_UNSHARING	true
 #undef  LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS
 #define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS true
+#undef LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P
+#define LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P gnat_emit_debug_info_for_decl_p
 
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 2542626d0ca..79d099538c9 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -398,7 +398,7 @@  gigi (Node_Id gnat_root,
     = create_subprog_decl (get_identifier ("__gnat_malloc"), NULL_TREE,
 			   ftype,
 			   NULL_TREE, is_disabled, true, true, true, false,
-			   NULL, Empty);
+			   false, NULL, Empty);
   DECL_IS_MALLOC (malloc_decl) = 1;
 
   ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
@@ -406,7 +406,7 @@  gigi (Node_Id gnat_root,
     = create_subprog_decl (get_identifier ("__gnat_free"), NULL_TREE,
 			   ftype,
 			   NULL_TREE, is_disabled, true, true, true, false,
-			   NULL, Empty);
+			   false, NULL, Empty);
 
   ftype = build_function_type_list (ptr_type_node, ptr_type_node, sizetype,
 				    NULL_TREE);
@@ -414,7 +414,7 @@  gigi (Node_Id gnat_root,
     = create_subprog_decl (get_identifier ("__gnat_realloc"), NULL_TREE,
 			   ftype,
 			   NULL_TREE, is_disabled, true, true, true, false,
-			   NULL, Empty);
+			   false, NULL, Empty);
 
   /* This is used for 64-bit multiplication with overflow checking.  */
   int64_type = gnat_type_for_size (64, 0);
@@ -423,7 +423,7 @@  gigi (Node_Id gnat_root,
 			   build_function_type_list (int64_type, int64_type,
 						     int64_type, NULL_TREE),
 			   NULL_TREE, is_disabled, true, true, true, false,
-			   NULL, Empty);
+			   false, NULL, Empty);
 
   /* Name of the _Parent field in tagged record types.  */
   parent_name_id = get_identifier (Get_Name_String (Name_uParent));
@@ -446,21 +446,21 @@  gigi (Node_Id gnat_root,
     = create_subprog_decl
       (get_identifier ("system__soft_links__get_jmpbuf_address_soft"),
        NULL_TREE, build_function_type_list (jmpbuf_ptr_type, NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
 
   set_jmpbuf_decl
     = create_subprog_decl
       (get_identifier ("system__soft_links__set_jmpbuf_address_soft"),
        NULL_TREE, build_function_type_list (void_type_node, jmpbuf_ptr_type,
 					    NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
 
   get_excptr_decl
     = create_subprog_decl
       (get_identifier ("system__soft_links__get_gnat_exception"), NULL_TREE,
        build_function_type_list (build_pointer_type (except_type_node),
 				 NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
 
   not_handled_by_others_decl = get_identifier ("not_handled_by_others");
   for (t = TYPE_FIELDS (except_type_node); t; t = DECL_CHAIN (t))
@@ -478,7 +478,7 @@  gigi (Node_Id gnat_root,
       (get_identifier ("__builtin_setjmp"), NULL_TREE,
        build_function_type_list (integer_type_node, jmpbuf_ptr_type,
 				 NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
   DECL_BUILT_IN_CLASS (setjmp_decl) = BUILT_IN_NORMAL;
   DECL_FUNCTION_CODE (setjmp_decl) = BUILT_IN_SETJMP;
 
@@ -488,7 +488,7 @@  gigi (Node_Id gnat_root,
     = create_subprog_decl
       (get_identifier ("__builtin_update_setjmp_buf"), NULL_TREE,
        build_function_type_list (void_type_node, jmpbuf_ptr_type, NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
   DECL_BUILT_IN_CLASS (update_setjmp_buf_decl) = BUILT_IN_NORMAL;
   DECL_FUNCTION_CODE (update_setjmp_buf_decl) = BUILT_IN_UPDATE_SETJMP_BUF;
 
@@ -500,14 +500,14 @@  gigi (Node_Id gnat_root,
   raise_nodefer_decl
     = create_subprog_decl
       (get_identifier ("__gnat_raise_nodefer_with_msg"), NULL_TREE, ftype,
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
 
   set_exception_parameter_decl
     = create_subprog_decl
       (get_identifier ("__gnat_set_exception_parameter"), NULL_TREE,
        build_function_type_list (void_type_node, ptr_type_node, ptr_type_node,
 				 NULL_TREE),
-       NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+       NULL_TREE, is_disabled, true, true, true, false, false, NULL, Empty);
 
   /* Hooks to call when entering/leaving an exception handler.  */
   ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
@@ -515,26 +515,30 @@  gigi (Node_Id gnat_root,
   begin_handler_decl
     = create_subprog_decl (get_identifier ("__gnat_begin_handler"), NULL_TREE,
 			   ftype, NULL_TREE,
-			   is_disabled, true, true, true, false, NULL, Empty);
+			   is_disabled, true, true, true, false, false, NULL,
+			   Empty);
   /* __gnat_begin_handler is a dummy procedure.  */
   TREE_NOTHROW (begin_handler_decl) = 1;
 
   end_handler_decl
     = create_subprog_decl (get_identifier ("__gnat_end_handler"), NULL_TREE,
 			   ftype, NULL_TREE,
-			   is_disabled, true, true, true, false, NULL, Empty);
+			   is_disabled, true, true, true, false, false, NULL,
+			   Empty);
 
   unhandled_except_decl
     = create_subprog_decl (get_identifier ("__gnat_unhandled_except_handler"),
 			   NULL_TREE, ftype, NULL_TREE,
-			   is_disabled, true, true, true, false, NULL, Empty);
+			   is_disabled, true, true, true, false, false, NULL,
+			   Empty);
 
   /* Indicate that it never returns.  */
   ftype = build_qualified_type (ftype, TYPE_QUAL_VOLATILE);
   reraise_zcx_decl
     = create_subprog_decl (get_identifier ("__gnat_reraise_zcx"), NULL_TREE,
 			   ftype, NULL_TREE,
-			   is_disabled, true, true, true, false, NULL, Empty);
+			   is_disabled, true, true, true, false, false, NULL,
+			   Empty);
 
   /* Dummy objects to materialize "others" and "all others" in the exception
      tables.  These are exported by a-exexpr-gcc.adb, so see this unit for
@@ -573,7 +577,8 @@  gigi (Node_Id gnat_root,
       tree decl
 	= create_subprog_decl
 	  (get_identifier ("__gnat_last_chance_handler"), NULL_TREE, ftype,
-	   NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+	   NULL_TREE, is_disabled, true, true, true, false, false, NULL,
+	   Empty);
       for (i = 0; i < (int) ARRAY_SIZE (gnat_raise_decls); i++)
 	gnat_raise_decls[i] = decl;
     }
@@ -739,7 +744,7 @@  build_raise_check (int check, enum exception_info_kind kind)
   result
     = create_subprog_decl (get_identifier (Name_Buffer), NULL_TREE, ftype,
 			   NULL_TREE, is_disabled, true, true, true, false,
-			   NULL, Empty);
+			   false, NULL, Empty);
 
   return result;
 }
@@ -3745,6 +3750,7 @@  Subprogram_Body_to_gnu (Node_Id gnat_node)
     = gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE,
 			  Acts_As_Spec (gnat_node)
 			  && !present_gnu_tree (gnat_subprog_id));
+  DECL_FUNCTION_IS_DEF (gnu_subprog_decl) = true;
   gnu_result_decl = DECL_RESULT (gnu_subprog_decl);
   gnu_subprog_type = TREE_TYPE (gnu_subprog_decl);
   gnu_cico_list = TYPE_CI_CO_LIST (gnu_subprog_type);
@@ -5417,12 +5423,15 @@  Compilation_Unit_to_gnu (Node_Id gnat_node)
   const Entity_Id gnat_unit_entity = Defining_Entity (gnat_unit);
   Entity_Id gnat_entity;
   Node_Id gnat_pragma;
-  /* Make the decl for the elaboration procedure.  */
+  /* Make the decl for the elaboration procedure.  Emit debug info for it, so
+     that users can break into their elaboration code in debuggers.  Kludge:
+     don't consider it as a definition so that we have a line map for its body,
+     but no subprogram description in debug info. */
   tree gnu_elab_proc_decl
     = create_subprog_decl
       (create_concat_name (gnat_unit_entity, body_p ? "elabb" : "elabs"),
        NULL_TREE, void_ftype, NULL_TREE,
-       is_disabled, true, false, true, true, NULL, gnat_unit);
+       is_disabled, true, false, true, true, false, NULL, gnat_unit);
   struct elab_info *info;
 
   vec_safe_push (gnu_elab_proc_stack, gnu_elab_proc_decl);
@@ -6453,7 +6462,7 @@  gnat_to_gnu (Node_Id gnat_node)
 		gnu_prefix = gnat_to_gnu (gnat_prefix);
 		gnu_prefix = maybe_implicit_deref (gnu_prefix);
 	      }
-		
+
 	    gnu_result
 	      = build_component_ref (gnu_prefix, gnu_field,
 				     (Nkind (Parent (gnat_node))
@@ -6484,7 +6493,8 @@  gnat_to_gnu (Node_Id gnat_node)
 				 (Entity (Prefix (gnat_node)),
 				  attr == Attr_Elab_Body ? "elabb" : "elabs"),
 				 NULL_TREE, void_ftype, NULL_TREE, is_disabled,
-				 true, true, true, true, NULL, gnat_node);
+				 true, true, true, true, false, NULL,
+				 gnat_node);
 
 	gnu_result = Attribute_to_gnu (gnat_node, &gnu_result_type, attr);
       }
diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
index b8c5d3d31f6..af53edcfa3f 100644
--- a/gcc/ada/gcc-interface/utils.c
+++ b/gcc/ada/gcc-interface/utils.c
@@ -763,11 +763,13 @@  gnat_pushdecl (tree decl, Node_Id gnat_node)
   if (!(TREE_CODE (decl) == TYPE_DECL
         && TREE_CODE (TREE_TYPE (decl)) == UNCONSTRAINED_ARRAY_TYPE))
     {
-      if (DECL_EXTERNAL (decl))
-	{
-	  if (TREE_CODE (decl) == FUNCTION_DECL && DECL_BUILT_IN (decl))
-	    vec_safe_push (builtin_decls, decl);
-	}
+      /* External declarations must go to the binding level they belong to.
+	 This will make corresponding imported entities are available in the
+	 debugger at the proper time.  */
+      if (DECL_EXTERNAL (decl)
+	  && TREE_CODE (decl) == FUNCTION_DECL
+	  && DECL_BUILT_IN (decl))
+	vec_safe_push (builtin_decls, decl);
       else if (global_bindings_p ())
 	vec_safe_push (global_decls, decl);
       else
@@ -3189,6 +3191,8 @@  create_label_decl (tree name, Node_Id gnat_node)
 
    DEBUG_INFO_P is true if we need to write debug information for it.
 
+   DEFINITION is true if the subprogram is to be considered as a definition.
+
    ATTR_LIST is the list of attributes to be attached to the subprogram.
 
    GNAT_NODE is used for the position of the decl.  */
@@ -3197,7 +3201,8 @@  tree
 create_subprog_decl (tree name, tree asm_name, tree type, tree param_decl_list,
 		     enum inline_status_t inline_status, bool public_flag,
 		     bool extern_flag, bool artificial_p, bool debug_info_p,
-		     struct attrib *attr_list, Node_Id gnat_node)
+		     bool definition, struct attrib *attr_list,
+		     Node_Id gnat_node)
 {
   tree subprog_decl = build_decl (input_location, FUNCTION_DECL, name, type);
   DECL_ARGUMENTS (subprog_decl) = param_decl_list;
@@ -3208,6 +3213,8 @@  create_subprog_decl (tree name, tree asm_name, tree type, tree param_decl_list,
 
   if (!debug_info_p)
     DECL_IGNORED_P (subprog_decl) = 1;
+  if (definition)
+    DECL_FUNCTION_IS_DEF (subprog_decl) = 1;
 
   switch (inline_status)
     {
@@ -5523,10 +5530,21 @@  gnat_write_global_declarations (void)
     if (TREE_CODE (iter) == TYPE_DECL && !DECL_IGNORED_P (iter))
       debug_hooks->type_decl (iter, false);
 
+  /* Output imported functions.  */
+  FOR_EACH_VEC_SAFE_ELT (global_decls, i, iter)
+    if (TREE_CODE (iter) == FUNCTION_DECL
+	&& DECL_EXTERNAL (iter)
+	&& DECL_INITIAL (iter) == NULL
+	&& !DECL_IGNORED_P (iter))
+      debug_hooks->early_global_decl (iter);
+
   /* Then output the global variables.  We need to do that after the debug
-     information for global types is emitted so that they are finalized.  */
+     information for global types is emitted so that they are finalized.  Skip
+     external global variables, unless we need to emit debug info for them:
+     this is useful for imported variables, for instance.  */
   FOR_EACH_VEC_SAFE_ELT (global_decls, i, iter)
-    if (TREE_CODE (iter) == VAR_DECL)
+    if (TREE_CODE (iter) == VAR_DECL
+	&& (!DECL_EXTERNAL (iter) || !DECL_IGNORED_P (iter)))
       rest_of_decl_compilation (iter, true, 0);
 
   /* Output the imported modules/declarations.  In GNAT, these are only
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 5ff45eb4efd..e2a7f546034 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -25271,7 +25271,8 @@  gen_decl_die (tree decl, tree origin, struct vlr_context *ctx,
       if (DECL_INITIAL (decl_or_origin) == NULL_TREE
           && DECL_FILE_SCOPE_P (decl_or_origin)
 	  && (current_function_decl == NULL_TREE
-	      || DECL_ARTIFICIAL (decl_or_origin)))
+	      || DECL_ARTIFICIAL (decl_or_origin))
+	  && !lang_hooks.decls.emit_debug_info_for_decl_p (decl_or_origin))
 	break;
 
 #if 0
@@ -25478,9 +25479,10 @@  dwarf2out_early_global_decl (tree decl)
       tree save_fndecl = current_function_decl;
       if (TREE_CODE (decl) == FUNCTION_DECL)
 	{
-	  /* No cfun means the symbol has no body, so there's nothing
-	     to emit.  */
-	  if (!DECL_STRUCT_FUNCTION (decl))
+	  /* Unless we are asked to emit debug info for declarations, no cfun
+	     means the symbol has no body, so there's nothing to emit.  */
+	  if (!DECL_STRUCT_FUNCTION (decl)
+	      && !lang_hooks.decls.emit_debug_info_for_decl_p (decl))
 	    goto early_decl_exit;
 
 	  /* For nested functions, make sure we have DIEs for the parents first
@@ -25770,7 +25772,8 @@  dwarf2out_decl (tree decl)
 	 where the inlined function is output in a different LTRANS unit
 	 or not at all.  */
       if (DECL_INITIAL (decl) == NULL_TREE
-	  && ! DECL_ABSTRACT_P (decl))
+	  && ! DECL_ABSTRACT_P (decl)
+	  && ! lang_hooks.decls.emit_debug_info_for_decl_p (decl))
 	return;
 
       /* If we're a nested function, initially use a parent of NULL; if we're
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index ea2006ccec5..e210e9e61ca 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -225,6 +225,7 @@  extern tree lhd_unit_size_without_reusable_padding (tree);
 
 /* Declaration hooks.  */
 #define LANG_HOOKS_GLOBAL_BINDINGS_P global_bindings_p
+#define LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P hook_bool_tree_false
 #define LANG_HOOKS_PUSHDECL	pushdecl
 #define LANG_HOOKS_GETDECLS	getdecls
 #define LANG_HOOKS_DECL_DWARF_ATTRIBUTE lhd_decl_dwarf_attribute
@@ -247,6 +248,7 @@  extern tree lhd_unit_size_without_reusable_padding (tree);
 
 #define LANG_HOOKS_DECLS { \
   LANG_HOOKS_GLOBAL_BINDINGS_P, \
+  LANG_HOOKS_EMIT_DEBUG_INFO_FOR_DECL_P, \
   LANG_HOOKS_PUSHDECL, \
   LANG_HOOKS_GETDECLS, \
   LANG_HOOKS_DECL_DWARF_ATTRIBUTE, \
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index b2f0f922b89..27fd5638d21 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -181,6 +181,15 @@  struct lang_hooks_for_decls
      level, i.e. declared outside subprograms.  */
   bool (*global_bindings_p) (void);
 
+  /* Given some declaration, if this returns true, we should produce debug info
+     for it.  We should not do this for languages with poor modules definition
+     like C/C++ because translation units make us process a lot of declarations
+     that are common to a lot of translation units, bloating the debug info.
+     However this is desirable in languages like Ada, where we know we must not
+     produce debug info for the declaration of functions that come from other
+     units (unless in special cases, like cross-unit inlining).  */
+  bool (*emit_debug_info_for_decl_p) (tree);
+
   /* Function to add a decl to the current scope level.  Takes one
      argument, a decl to add.  Returns that decl, or, if the same
      symbol is already declared, may return a different decl for that