Beispiel #1
0
void
use_thunk (tree thunk_fndecl, bool emit_p)
{
    tree a, t, function, alias;
    tree virtual_offset;
    HOST_WIDE_INT fixed_offset, virtual_value;
    bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);

    /* We should have called finish_thunk to give it a name.  */
    gcc_assert (DECL_NAME (thunk_fndecl));

    /* We should never be using an alias, always refer to the
       aliased thunk.  */
    gcc_assert (!THUNK_ALIAS (thunk_fndecl));

    if (TREE_ASM_WRITTEN (thunk_fndecl))
        return;

    function = THUNK_TARGET (thunk_fndecl);
    if (DECL_RESULT (thunk_fndecl))
        /* We already turned this thunk into an ordinary function.
           There's no need to process this thunk again.  */
        return;

    if (DECL_THUNK_P (function))
        /* The target is itself a thunk, process it now.  */
        use_thunk (function, emit_p);

    /* Thunks are always addressable; they only appear in vtables.  */
    TREE_ADDRESSABLE (thunk_fndecl) = 1;

    /* Figure out what function is being thunked to.  It's referenced in
       this translation unit.  */
    TREE_ADDRESSABLE (function) = 1;
    mark_used (function);
    if (!emit_p)
        return;

    if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function))
        alias = make_alias_for_thunk (function);
    else
        alias = function;

    fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
    virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);

    if (virtual_offset)
    {
        if (!this_adjusting)
            virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
        virtual_value = tree_low_cst (virtual_offset, /*pos=*/0);
        gcc_assert (virtual_value);
    }
    else
        virtual_value = 0;

    /* And, if we need to emit the thunk, it's used.  */
    mark_used (thunk_fndecl);
    /* This thunk is actually defined.  */
    DECL_EXTERNAL (thunk_fndecl) = 0;
    /* The linkage of the function may have changed.  FIXME in linkage
       rewrite.  */
    TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function);
    DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function);
    DECL_VISIBILITY_SPECIFIED (thunk_fndecl)
        = DECL_VISIBILITY_SPECIFIED (function);
    if (DECL_ONE_ONLY (function))
        make_decl_one_only (thunk_fndecl);

    if (flag_syntax_only)
    {
        TREE_ASM_WRITTEN (thunk_fndecl) = 1;
        return;
    }

    push_to_top_level ();

    if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
            && targetm.have_named_sections)
    {
        resolve_unique_section (function, 0, flag_function_sections);

        if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function))
        {
            resolve_unique_section (thunk_fndecl, 0, flag_function_sections);

            /* Output the thunk into the same section as function.  */
            DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function);
        }
    }

    /* The back-end expects DECL_INITIAL to contain a BLOCK, so we
       create one.  */
    DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);

    /* Set up cloned argument trees for the thunk.  */
    t = NULL_TREE;
    for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
    {
        tree x = copy_node (a);
        TREE_CHAIN (x) = t;
        DECL_CONTEXT (x) = thunk_fndecl;
        SET_DECL_RTL (x, NULL_RTX);
        DECL_HAS_VALUE_EXPR_P (x) = 0;
        t = x;
    }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
    BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = a;

    if (this_adjusting
            && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
                    virtual_value, alias))
    {
        const char *fnname;
        current_function_decl = thunk_fndecl;
        DECL_RESULT (thunk_fndecl)
            = build_decl (RESULT_DECL, 0, integer_type_node);
        fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
        init_function_start (thunk_fndecl);
        current_function_is_thunk = 1;
        assemble_start_function (thunk_fndecl, fnname);

        targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
                                         fixed_offset, virtual_value, alias);

        assemble_end_function (thunk_fndecl, fnname);
        init_insn_lengths ();
        current_function_decl = 0;
        cfun = 0;
        TREE_ASM_WRITTEN (thunk_fndecl) = 1;
    }
    else
    {
        /* If this is a covariant thunk, or we don't have the necessary
        code for efficient thunks, generate a thunk function that
         just makes a call to the real function.  Unfortunately, this
         doesn't work for varargs.  */

        if (varargs_function_p (function))
            error ("generic thunk code fails for method %q#D which uses %<...%>",
                   function);

        DECL_RESULT (thunk_fndecl) = NULL_TREE;

        start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
        /* We don't bother with a body block for thunks.  */

        /* There's no need to check accessibility inside the thunk body.  */
        push_deferring_access_checks (dk_no_check);

        t = a;
        if (this_adjusting)
            t = thunk_adjust (t, /*this_adjusting=*/1,
                              fixed_offset, virtual_offset);

        /* Build up the call to the real function.  */
        t = tree_cons (NULL_TREE, t, NULL_TREE);
        for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
            t = tree_cons (NULL_TREE, a, t);
        t = nreverse (t);
        t = build_call (alias, t);
        CALL_FROM_THUNK_P (t) = 1;

        if (VOID_TYPE_P (TREE_TYPE (t)))
            finish_expr_stmt (t);
        else
        {
            if (!this_adjusting)
            {
                tree cond = NULL_TREE;

                if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
                {
                    /* If the return type is a pointer, we need to
                       protect against NULL.  We know there will be an
                       adjustment, because that's why we're emitting a
                       thunk.  */
                    t = save_expr (t);
                    cond = cp_convert (boolean_type_node, t);
                }

                t = thunk_adjust (t, /*this_adjusting=*/0,
                                  fixed_offset, virtual_offset);
                if (cond)
                    t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
                                cp_convert (TREE_TYPE (t), integer_zero_node));
            }
            if (IS_AGGR_TYPE (TREE_TYPE (t)))
                t = build_cplus_new (TREE_TYPE (t), t);
            finish_return_stmt (t);
        }

        /* Since we want to emit the thunk, we explicitly mark its name as
        referenced.  */
        mark_decl_referenced (thunk_fndecl);

        /* But we don't want debugging information about it.  */
        DECL_IGNORED_P (thunk_fndecl) = 1;

        /* Re-enable access control.  */
        pop_deferring_access_checks ();

        thunk_fndecl = finish_function (0);
        tree_lowering_passes (thunk_fndecl);
        expand_body (thunk_fndecl);
    }

    pop_from_top_level ();
}
Beispiel #2
0
void
synthesize_method (tree fndecl)
{
    bool nested = (current_function_decl != NULL_TREE);
    tree context = decl_function_context (fndecl);
    bool need_body = true;
    tree stmt;
    location_t save_input_location = input_location;
    int error_count = errorcount;
    int warning_count = warningcount;

    /* Reset the source location, we might have been previously
       deferred, and thus have saved where we were first needed.  */
    DECL_SOURCE_LOCATION (fndecl)
        = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));

    /* If we've been asked to synthesize a clone, just synthesize the
       cloned function instead.  Doing so will automatically fill in the
       body for the clone.  */
    if (DECL_CLONED_FUNCTION_P (fndecl))
        fndecl = DECL_CLONED_FUNCTION (fndecl);

    /* We may be in the middle of deferred access check.  Disable
       it now.  */
    push_deferring_access_checks (dk_no_deferred);

    if (! context)
        push_to_top_level ();
    else if (nested)
        push_function_context_to (context);

    input_location = DECL_SOURCE_LOCATION (fndecl);

    start_preparsed_function (fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
    stmt = begin_function_body ();

    if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
    {
        do_build_assign_ref (fndecl);
        need_body = false;
    }
    else if (DECL_CONSTRUCTOR_P (fndecl))
    {
        tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
        if (arg_chain != void_list_node)
            do_build_copy_constructor (fndecl);
        else
            finish_mem_initializers (NULL_TREE);
    }

    /* If we haven't yet generated the body of the function, just
       generate an empty compound statement.  */
    if (need_body)
    {
        tree compound_stmt;
        compound_stmt = begin_compound_stmt (BCS_FN_BODY);
        finish_compound_stmt (compound_stmt);
    }

    finish_function_body (stmt);
    expand_or_defer_fn (finish_function (0));

    input_location = save_input_location;

    if (! context)
        pop_from_top_level ();
    else if (nested)
        pop_function_context_from (context);

    pop_deferring_access_checks ();

    if (error_count != errorcount || warning_count != warningcount)
        inform ("%Hsynthesized method %qD first required here ",
                &input_location, fndecl);
}
Beispiel #3
0
void
synthesize_method (tree fndecl)
{
  bool nested = (current_function_decl != NULL_TREE);
  tree context = decl_function_context (fndecl);
  bool need_body = true;
  tree stmt;

  if (at_eof)
    import_export_decl (fndecl);

  /* If we've been asked to synthesize a clone, just synthesize the
     cloned function instead.  Doing so will automatically fill in the
     body for the clone.  */
  if (DECL_CLONED_FUNCTION_P (fndecl))
    {
      synthesize_method (DECL_CLONED_FUNCTION (fndecl));
      return;
    }

  /* We may be in the middle of deferred access check.  Disable
     it now.  */
  push_deferring_access_checks (dk_no_deferred);

  if (! context)
    push_to_top_level ();
  else if (nested)
    push_function_context_to (context);

  /* Put the function definition at the position where it is needed,
     rather than within the body of the class.  That way, an error
     during the generation of the implicit body points at the place
     where the attempt to generate the function occurs, giving the
     user a hint as to why we are attempting to generate the
     function.  */
  DECL_SOURCE_LOCATION (fndecl) = input_location;

  interface_unknown = 1;
  start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
  clear_last_expr ();
  stmt = begin_function_body ();

  if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
    {
      do_build_assign_ref (fndecl);
      need_body = false;
    }
  else if (DECL_CONSTRUCTOR_P (fndecl))
    {
      tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
      if (arg_chain != void_list_node)
	do_build_copy_constructor (fndecl);
      else if (TYPE_NEEDS_CONSTRUCTING (current_class_type))
	finish_mem_initializers (NULL_TREE);
    }

  /* If we haven't yet generated the body of the function, just
     generate an empty compound statement.  */
  if (need_body)
    {
      tree compound_stmt;
      compound_stmt = begin_compound_stmt (/*has_no_scope=*/false);
      finish_compound_stmt (compound_stmt);
    }

  finish_function_body (stmt);
  expand_or_defer_fn (finish_function (0));

  extract_interface_info ();
  if (! context)
    pop_from_top_level ();
  else if (nested)
    pop_function_context_from (context);

  pop_deferring_access_checks ();
}