mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-27 05:44:15 +08:00
255 lines
8.5 KiB
C++
255 lines
8.5 KiB
C++
/* Information about fuunction binary interfaces.
|
|
Copyright (C) 2019-2023 Free Software Foundation, Inc.
|
|
|
|
This file is part of GCC
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "backend.h"
|
|
#include "target.h"
|
|
#include "rtl.h"
|
|
#include "tree.h"
|
|
#include "regs.h"
|
|
#include "function-abi.h"
|
|
#include "varasm.h"
|
|
#include "cgraph.h"
|
|
|
|
target_function_abi_info default_target_function_abi_info;
|
|
#if SWITCHABLE_TARGET
|
|
target_function_abi_info *this_target_function_abi_info
|
|
= &default_target_function_abi_info;
|
|
#endif
|
|
|
|
/* Initialize a predefined function ABI with the given values of
|
|
ID and FULL_REG_CLOBBERS. */
|
|
|
|
void
|
|
predefined_function_abi::initialize (unsigned int id,
|
|
const_hard_reg_set full_reg_clobbers)
|
|
{
|
|
m_id = id;
|
|
m_initialized = true;
|
|
m_full_reg_clobbers = full_reg_clobbers;
|
|
|
|
/* Set up the value of m_full_and_partial_reg_clobbers.
|
|
|
|
If the ABI specifies that part of a hard register R is call-clobbered,
|
|
we should be able to find a single-register mode M for which
|
|
targetm.hard_regno_call_part_clobbered (m_id, R, M) is true.
|
|
In other words, it shouldn't be the case that R can hold all
|
|
single-register modes across a call, but can't hold part of
|
|
a multi-register mode.
|
|
|
|
If that assumption doesn't hold for a future target, we would need
|
|
to change the interface of TARGET_HARD_REGNO_CALL_PART_CLOBBERED so
|
|
that it tells us which registers in a multi-register value are
|
|
actually clobbered. */
|
|
m_full_and_partial_reg_clobbers = full_reg_clobbers;
|
|
for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
|
|
{
|
|
machine_mode mode = (machine_mode) i;
|
|
for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
|
|
if (targetm.hard_regno_mode_ok (regno, mode)
|
|
&& hard_regno_nregs (regno, mode) == 1
|
|
&& targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
|
|
SET_HARD_REG_BIT (m_full_and_partial_reg_clobbers, regno);
|
|
}
|
|
|
|
/* For each mode MODE, work out which registers are unable to hold
|
|
any part of a MODE value across a call, i.e. those for which no
|
|
overlapping call-preserved (reg:MODE REGNO) exists.
|
|
|
|
We assume that this can be flipped around to say that a call
|
|
preserves (reg:MODE REGNO) unless the register overlaps this set.
|
|
The usual reason for this being true is that if (reg:MODE REGNO)
|
|
contains a part-clobbered register, that register would be
|
|
part-clobbered regardless of which part of MODE it holds.
|
|
For example, if (reg:M 2) occupies two registers and if the
|
|
register 3 portion of it is part-clobbered, (reg:M 3) is usually
|
|
either invalid or also part-clobbered. */
|
|
for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
|
|
{
|
|
machine_mode mode = (machine_mode) i;
|
|
m_mode_clobbers[i] = m_full_and_partial_reg_clobbers;
|
|
for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
|
|
if (targetm.hard_regno_mode_ok (regno, mode)
|
|
&& !overlaps_hard_reg_set_p (m_full_reg_clobbers, mode, regno)
|
|
&& !targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
|
|
remove_from_hard_reg_set (&m_mode_clobbers[i], mode, regno);
|
|
}
|
|
|
|
/* Check that the assumptions above actually hold, i.e. that testing
|
|
for single-register modes makes sense, and that overlap tests for
|
|
mode_clobbers work as expected. */
|
|
if (flag_checking)
|
|
for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
|
|
{
|
|
machine_mode mode = (machine_mode) i;
|
|
const_hard_reg_set all_clobbers = m_full_and_partial_reg_clobbers;
|
|
for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno)
|
|
if (targetm.hard_regno_mode_ok (regno, mode)
|
|
&& !overlaps_hard_reg_set_p (m_full_reg_clobbers, mode, regno)
|
|
&& targetm.hard_regno_call_part_clobbered (m_id, regno, mode))
|
|
gcc_assert (overlaps_hard_reg_set_p (all_clobbers, mode, regno)
|
|
&& overlaps_hard_reg_set_p (m_mode_clobbers[i],
|
|
mode, regno));
|
|
}
|
|
}
|
|
|
|
/* If the ABI has been initialized, add REGNO to the set of registers
|
|
that can be completely altered by a call. */
|
|
|
|
void
|
|
predefined_function_abi::add_full_reg_clobber (unsigned int regno)
|
|
{
|
|
if (!m_initialized)
|
|
return;
|
|
|
|
SET_HARD_REG_BIT (m_full_reg_clobbers, regno);
|
|
SET_HARD_REG_BIT (m_full_and_partial_reg_clobbers, regno);
|
|
for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
|
|
SET_HARD_REG_BIT (m_mode_clobbers[i], regno);
|
|
}
|
|
|
|
/* Return the set of registers that the caller of the recorded functions must
|
|
save in order to honor the requirements of CALLER_ABI. */
|
|
|
|
HARD_REG_SET
|
|
function_abi_aggregator::
|
|
caller_save_regs (const function_abi &caller_abi) const
|
|
{
|
|
HARD_REG_SET result;
|
|
CLEAR_HARD_REG_SET (result);
|
|
for (unsigned int abi_id = 0; abi_id < NUM_ABI_IDS; ++abi_id)
|
|
{
|
|
const predefined_function_abi &callee_abi = function_abis[abi_id];
|
|
|
|
/* Skip cases that clearly aren't problematic. */
|
|
if (abi_id == caller_abi.id ()
|
|
|| hard_reg_set_empty_p (m_abi_clobbers[abi_id]))
|
|
continue;
|
|
|
|
/* Collect the set of registers that can be "more clobbered" by
|
|
CALLEE_ABI than by CALLER_ABI. */
|
|
HARD_REG_SET extra_clobbers;
|
|
CLEAR_HARD_REG_SET (extra_clobbers);
|
|
for (unsigned int i = 0; i < NUM_MACHINE_MODES; ++i)
|
|
{
|
|
machine_mode mode = (machine_mode) i;
|
|
extra_clobbers |= (callee_abi.mode_clobbers (mode)
|
|
& ~caller_abi.mode_clobbers (mode));
|
|
}
|
|
|
|
/* Restrict it to the set of registers that we actually saw
|
|
clobbers for (e.g. taking -fipa-ra into account). */
|
|
result |= (extra_clobbers & m_abi_clobbers[abi_id]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Return the set of registers that cannot be used to hold a value of
|
|
mode MODE across the calls in a region described by ABIS and MASK, where:
|
|
|
|
* Bit ID of ABIS is set if the region contains a call with
|
|
function_abi identifier ID.
|
|
|
|
* MASK contains all the registers that are fully or partially
|
|
clobbered by calls in the region.
|
|
|
|
This is not quite as accurate as testing each individual call,
|
|
but it's a close and conservatively-correct approximation.
|
|
It's much better for some targets than just using MASK. */
|
|
|
|
HARD_REG_SET
|
|
call_clobbers_in_region (unsigned int abis, const_hard_reg_set mask,
|
|
machine_mode mode)
|
|
{
|
|
HARD_REG_SET result;
|
|
CLEAR_HARD_REG_SET (result);
|
|
for (unsigned int id = 0; abis; abis >>= 1, ++id)
|
|
if (abis & 1)
|
|
result |= function_abis[id].mode_clobbers (mode);
|
|
return result & mask;
|
|
}
|
|
|
|
/* Return the predefined ABI used by functions with type TYPE. */
|
|
|
|
const predefined_function_abi &
|
|
fntype_abi (const_tree type)
|
|
{
|
|
gcc_assert (FUNC_OR_METHOD_TYPE_P (type));
|
|
if (targetm.calls.fntype_abi)
|
|
return targetm.calls.fntype_abi (type);
|
|
return default_function_abi;
|
|
}
|
|
|
|
/* Return the ABI of function decl FNDECL. */
|
|
|
|
function_abi
|
|
fndecl_abi (const_tree fndecl)
|
|
{
|
|
gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
|
|
const predefined_function_abi &base_abi = fntype_abi (TREE_TYPE (fndecl));
|
|
|
|
if (flag_ipa_ra && decl_binds_to_current_def_p (fndecl))
|
|
if (cgraph_rtl_info *info = cgraph_node::rtl_info (fndecl))
|
|
return function_abi (base_abi, info->function_used_regs);
|
|
|
|
return base_abi;
|
|
}
|
|
|
|
/* Return the ABI of the function called by INSN. */
|
|
|
|
function_abi
|
|
insn_callee_abi (const rtx_insn *insn)
|
|
{
|
|
gcc_assert (insn && CALL_P (insn));
|
|
|
|
if (flag_ipa_ra)
|
|
if (tree fndecl = get_call_fndecl (insn))
|
|
return fndecl_abi (fndecl);
|
|
|
|
if (targetm.calls.insn_callee_abi)
|
|
return targetm.calls.insn_callee_abi (insn);
|
|
|
|
return default_function_abi;
|
|
}
|
|
|
|
/* Return the ABI of the function called by CALL_EXPR EXP. Return the
|
|
default ABI for erroneous calls. */
|
|
|
|
function_abi
|
|
expr_callee_abi (const_tree exp)
|
|
{
|
|
gcc_assert (TREE_CODE (exp) == CALL_EXPR);
|
|
|
|
if (tree fndecl = get_callee_fndecl (exp))
|
|
return fndecl_abi (fndecl);
|
|
|
|
tree callee = CALL_EXPR_FN (exp);
|
|
if (callee == error_mark_node)
|
|
return default_function_abi;
|
|
|
|
tree type = TREE_TYPE (callee);
|
|
if (type == error_mark_node)
|
|
return default_function_abi;
|
|
|
|
gcc_assert (POINTER_TYPE_P (type));
|
|
return fntype_abi (TREE_TYPE (type));
|
|
}
|