Commit bd785b44 by Richard Sandiford Committed by Richard Sandiford

Add function_abi.{h,cc}

This patch adds new structures and functions for handling
multiple ABIs in a translation unit.  The structures are:

- predefined_function_abi: describes a static, predefined ABI
- function_abi: describes either a predefined ABI or a local
  variant of one (e.g. taking -fipa-ra into account)

The patch adds functions for getting the ABI from a given type
or decl; a later patch will also add a function for getting the
ABI of the target of a call insn.

Although ABIs are about much more than call-clobber/saved choices,
I wanted to keep the name general in case we add more ABI-related
information in future.

2019-09-30  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* Makefile.in (OBJS): Add function-abi.o.
	(GTFILES): Add function-abi.h.
	* function-abi.cc: New file.
	* function-abi.h: Likewise.
	* emit-rtl.h (rtl_data::abi): New field.
	* function.c: Include function-abi.h.
	(prepare_function_start): Initialize crtl->abi.
	* read-rtl-function.c: Include regs.h and function-abi.h.
	(read_rtl_function_body): Initialize crtl->abi.
	(read_rtl_function_body_from_file_range): Likewise.
	* reginfo.c: Include function-abi.h.
	(init_reg_sets_1): Initialize default_function_abi.
	(globalize_reg): Call add_full_reg_clobber for each predefined ABI
	when making a register global.
	* target-globals.h (this_target_function_abi_info): Declare.
	(target_globals::function_abi_info): New field.
	(restore_target_globals): Copy it.
	* target-globals.c: Include function-abi.h.
	(default_target_globals): Initialize the function_abi_info field.
	(target_globals): Allocate it.
	(save_target_globals): Free it.

From-SVN: r276307
parent 0c88d078
2019-09-30 Richard Sandiford <richard.sandiford@arm.com>
* Makefile.in (OBJS): Add function-abi.o.
(GTFILES): Add function-abi.h.
* function-abi.cc: New file.
* function-abi.h: Likewise.
* emit-rtl.h (rtl_data::abi): New field.
* function.c: Include function-abi.h.
(prepare_function_start): Initialize crtl->abi.
* read-rtl-function.c: Include regs.h and function-abi.h.
(read_rtl_function_body): Initialize crtl->abi.
(read_rtl_function_body_from_file_range): Likewise.
* reginfo.c: Include function-abi.h.
(init_reg_sets_1): Initialize default_function_abi.
(globalize_reg): Call add_full_reg_clobber for each predefined ABI
when making a register global.
* target-globals.h (this_target_function_abi_info): Declare.
(target_globals::function_abi_info): New field.
(restore_target_globals): Copy it.
* target-globals.c: Include function-abi.h.
(default_target_globals): Initialize the function_abi_info field.
(target_globals): Allocate it.
(save_target_globals): Free it.
2019-09-30 Nick Clifton <nickc@redhat.com>
PR target/85978
......
......@@ -1306,6 +1306,7 @@ OBJS = \
fold-const.o \
fold-const-call.o \
function.o \
function-abi.o \
function-tests.o \
fwprop.o \
gcc-rich-location.o \
......@@ -2523,6 +2524,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
$(srcdir)/libfuncs.h $(SYMTAB_H) \
$(srcdir)/real.h $(srcdir)/function.h $(srcdir)/insn-addr.h $(srcdir)/hwint.h \
$(srcdir)/fixed-value.h \
$(srcdir)/function-abi.h \
$(srcdir)/output.h $(srcdir)/cfgloop.h $(srcdir)/cfg.h $(srcdir)/profile-count.h \
$(srcdir)/cselib.h $(srcdir)/basic-block.h $(srcdir)/ipa-ref.h $(srcdir)/cgraph.h \
$(srcdir)/reload.h $(srcdir)/caller-save.c $(srcdir)/symtab.c \
......
......@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
class temp_slot;
typedef class temp_slot *temp_slot_p;
class predefined_function_abi;
/* Information mainlined about RTL representation of incoming arguments. */
struct GTY(()) incoming_args {
......@@ -64,6 +65,14 @@ struct GTY(()) rtl_data {
struct function_subsections subsections;
struct rtl_eh eh;
/* The ABI of the function, i.e. the interface it presents to its callers.
This is the ABI that should be queried to see which registers the
function needs to save before it uses them.
Other functions (including those called by this function) might use
different ABIs. */
const predefined_function_abi *GTY((skip)) abi;
/* For function.c */
/* # of bytes of outgoing arguments. If ACCUMULATE_OUTGOING_ARGS is
......
/* Information about fuunction binary interfaces.
Copyright (C) 2019 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 (NULL, 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 (NULL, 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 (NULL, 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 (NULL, 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 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));
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);
return fntype_abi (TREE_TYPE (fndecl));
}
/* Information about fuunction binary interfaces.
Copyright (C) 2019 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/>. */
#ifndef GCC_FUNCTION_ABI_H
#define GCC_FUNCTION_ABI_H
/* Most targets use the same ABI for all functions in a translation
unit, but some targets support interoperability between several ABIs.
Each such ABI has a unique 0-based identifier, with 0 always being
the default choice of ABI.
NUM_ABI_IDS is the maximum number of such ABIs that GCC can handle at once.
A bitfield with this number of bits can represent any combinaion of the
supported ABIs. */
const size_t NUM_ABI_IDS = 8;
/* Information about one of the target's predefined ABIs. */
class predefined_function_abi
{
public:
/* A target-specific identifier for this ABI. The value must be in
the range [0, NUM_ABI_IDS - 1]. */
unsigned int id () const { return m_id; }
/* True if this ABI has been initialized. */
bool initialized_p () const { return m_initialized; }
/* Return true if a function call is allowed to alter every bit of
register REGNO, so that the register contains an arbitrary value
on return. If so, the register cannot hold any part of a value
that is live across a call. */
bool
clobbers_full_reg_p (unsigned int regno) const
{
return TEST_HARD_REG_BIT (m_full_reg_clobbers, regno);
}
/* Return true if a function call is allowed to alter some or all bits
of register REGNO.
This is true whenever clobbers_full_reg_p (REGNO) is true. It is
also true if, for example, the ABI says that a call must preserve the
low 32 or 64 bits of REGNO, but can clobber the upper bits of REGNO.
In the latter case, it is possible for REGNO to hold values that
are live across a call, provided that the value occupies only the
call-preserved part of the register. */
bool
clobbers_at_least_part_of_reg_p (unsigned int regno) const
{
return TEST_HARD_REG_BIT (m_full_and_partial_reg_clobbers, regno);
}
/* Return true if a function call is allowed to clobber at least part
of (reg:MODE REGNO). If so, it is not possible for the register
as a whole to be live across a call. */
bool
clobbers_reg_p (machine_mode mode, unsigned int regno) const
{
return overlaps_hard_reg_set_p (m_mode_clobbers[mode], mode, regno);
}
/* Return the set of registers that a function call is allowed to
alter completely, so that the registers contain arbitrary values
on return. This doesn't include registers that a call can only
partly clobber (as per TARGET_HARD_REGNO_CALL_PART_CLOBBERED).
These registers cannot hold any part of a value that is live across
a call. */
HARD_REG_SET full_reg_clobbers () const { return m_full_reg_clobbers; }
/* Return the set of registers that a function call is allowed to alter
to some degree. For example, if an ABI says that a call must preserve
the low 32 or 64 bits of a register R, but can clobber the upper bits
of R, R would be in this set but not in full_reg_clobbers ().
This set is a superset of full_reg_clobbers (). It is possible for a
register in full_and_partial_reg_clobbers () & ~full_reg_clobbers ()
to contain values that are live across a call, provided that the live
value only occupies the call-preserved part of the register. */
HARD_REG_SET
full_and_partial_reg_clobbers () const
{
return m_full_and_partial_reg_clobbers;
}
/* Return the set of registers that cannot be used to hold a value of
mode MODE across a function call. That is:
(reg:REGNO MODE)
might be clobbered by a call whenever:
overlaps_hard_reg_set (mode_clobbers (MODE), MODE, REGNO)
In allocation terms, the registers in the returned set conflict
with any value of mode MODE that is live across a call. */
HARD_REG_SET
mode_clobbers (machine_mode mode) const
{
return m_mode_clobbers[mode];
}
void initialize (unsigned int, const_hard_reg_set);
void add_full_reg_clobber (unsigned int);
private:
unsigned int m_id : NUM_ABI_IDS;
unsigned int m_initialized : 1;
HARD_REG_SET m_full_reg_clobbers;
HARD_REG_SET m_full_and_partial_reg_clobbers;
HARD_REG_SET m_mode_clobbers[NUM_MACHINE_MODES];
};
/* Describes either a predefined ABI or the ABI of a particular function.
In the latter case, the ABI might make use of extra function-specific
information, such as for -fipa-ra. */
class function_abi
{
public:
/* Initialize the structure for a general function with the given ABI. */
function_abi (const predefined_function_abi &base_abi)
: m_base_abi (&base_abi),
m_mask (base_abi.full_and_partial_reg_clobbers ()) {}
/* Initialize the structure for a function that has the given ABI and
that is known not to clobber registers outside MASK. */
function_abi (const predefined_function_abi &base_abi,
const_hard_reg_set mask)
: m_base_abi (&base_abi), m_mask (mask) {}
/* The predefined ABI from which this ABI is derived. */
const predefined_function_abi &base_abi () const { return *m_base_abi; }
/* The target-specific identifier of the predefined ABI. */
unsigned int id () const { return m_base_abi->id (); }
/* See the corresponding predefined_function_abi functions for
details about the following functions. */
HARD_REG_SET
full_reg_clobbers () const
{
return m_mask & m_base_abi->full_reg_clobbers ();
}
HARD_REG_SET
full_and_partial_reg_clobbers () const
{
return m_mask & m_base_abi->full_and_partial_reg_clobbers ();
}
HARD_REG_SET
mode_clobbers (machine_mode mode) const
{
return m_mask & m_base_abi->mode_clobbers (mode);
}
bool
clobbers_full_reg_p (unsigned int regno) const
{
return (TEST_HARD_REG_BIT (m_mask, regno)
& m_base_abi->clobbers_full_reg_p (regno));
}
bool
clobbers_at_least_part_of_reg_p (unsigned int regno) const
{
return (TEST_HARD_REG_BIT (m_mask, regno)
& m_base_abi->clobbers_at_least_part_of_reg_p (regno));
}
bool
clobbers_reg_p (machine_mode mode, unsigned int regno) const
{
return overlaps_hard_reg_set_p (mode_clobbers (mode), mode, regno);
}
bool
operator== (const function_abi &other) const
{
return m_base_abi == other.m_base_abi && m_mask == other.m_mask;
}
bool
operator!= (const function_abi &other) const
{
return !operator== (other);
}
protected:
const predefined_function_abi *m_base_abi;
HARD_REG_SET m_mask;
};
struct target_function_abi_info
{
/* An array of all the target ABIs that are available in this
translation unit. Not all entries are used for all targets,
but the structures are relatively small, and using a fixed-size
array avoids extra indirection.
There are various ways of getting an ABI descriptor:
* fndecl_abi (FNDECL) is the ABI of function FNDECL.
* fntype_abi (FNTYPE) is the ABI of a function with type FNTYPE.
* crtl->abi is the ABI of the function that we are currently
compiling to rtl.
* eh_edge_abi is the "ABI" used when taking an EH edge from an
exception-throwing statement to an exception handler. Catching
exceptions from calls can be treated as an abnormal return from
those calls, and this ABI therefore describes the ABI of functions
on such an abnormal return. Statements that throw non-call
exceptions can be treated as being implicitly wrapped in a call
that has such an abnormal return.
At present, no target needs to support more than one EH ABI.
* function_abis[N] is the ABI with identifier N. This can be useful
when referring back to ABIs that have been collected by number in
a bitmask, such as after walking function calls in a particular
region of code.
* default_function_abi refers specifically to the target's default
choice of ABI, regardless of which (if any) functions actually
use it. This ABI and data derived from it do *not* provide
globally conservatively-correct information, so it is only
useful in very specific circumstances. */
predefined_function_abi x_function_abis[NUM_ABI_IDS];
};
extern target_function_abi_info default_target_function_abi_info;
#if SWITCHABLE_TARGET
extern target_function_abi_info *this_target_function_abi_info;
#else
#define this_target_function_abi_info (&default_target_function_abi_info)
#endif
/* See the comment above x_function_abis for when these macros should be used.
At present, eh_edge_abi is always the default ABI, but that could change
in future if a target needs it to. */
#define function_abis \
(this_target_function_abi_info->x_function_abis)
#define default_function_abi \
(this_target_function_abi_info->x_function_abis[0])
#define eh_edge_abi default_function_abi
extern const predefined_function_abi &fntype_abi (const_tree);
extern function_abi fndecl_abi (const_tree);
#endif
......@@ -79,6 +79,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "gimple.h"
#include "options.h"
#include "function-abi.h"
/* So we can assign to cfun in this file. */
#undef cfun
......@@ -4826,6 +4827,12 @@ static void
prepare_function_start (void)
{
gcc_assert (!get_last_insn ());
if (in_dummy_function)
crtl->abi = &default_function_abi;
else
crtl->abi = &fndecl_abi (cfun->decl).base_abi ();
init_temp_slots ();
init_emit ();
init_varasm_status ();
......
......@@ -41,6 +41,8 @@ along with GCC; see the file COPYING3. If not see
#include "read-rtl-function.h"
#include "selftest.h"
#include "selftest-rtl.h"
#include "regs.h"
#include "function-abi.h"
/* Forward decls. */
class function_reader;
......@@ -1611,6 +1613,7 @@ bool
read_rtl_function_body (const char *path)
{
initialize_rtl ();
crtl->abi = &default_function_abi;
init_emit ();
init_varasm_status ();
......@@ -1644,6 +1647,7 @@ read_rtl_function_body_from_file_range (location_t start_loc,
}
initialize_rtl ();
crtl->abi = &fndecl_abi (cfun->decl).base_abi ();
init_emit ();
init_varasm_status ();
......
......@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
#include "reload.h"
#include "output.h"
#include "tree-pass.h"
#include "function-abi.h"
/* Maximum register number used in this function, plus one. */
......@@ -419,6 +420,8 @@ init_reg_sets_1 (void)
}
}
}
default_function_abi.initialize (0, regs_invalidated_by_call);
}
/* Compute the table of register modes.
......@@ -728,7 +731,11 @@ globalize_reg (tree decl, int i)
appropriate regs_invalidated_by_call bit, even if it's already
set in fixed_regs. */
if (i != STACK_POINTER_REGNUM)
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
{
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
for (unsigned int i = 0; i < NUM_ABI_IDS; ++i)
function_abis[i].add_full_reg_clobber (i);
}
/* If already fixed, nothing else to do. */
if (fixed_regs[i])
......
......@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "gcse.h"
#include "bb-reorder.h"
#include "lower-subreg.h"
#include "function-abi.h"
#if SWITCHABLE_TARGET
class target_globals default_target_globals = {
......@@ -48,6 +49,7 @@ class target_globals default_target_globals = {
&default_target_rtl,
&default_target_recog,
&default_target_hard_regs,
&default_target_function_abi_info,
&default_target_reload,
&default_target_expmed,
&default_target_optabs,
......@@ -70,6 +72,7 @@ save_target_globals (void)
g->rtl = ggc_cleared_alloc<target_rtl> ();
g->recog = XCNEW (struct target_recog);
g->hard_regs = XCNEW (struct target_hard_regs);
g->function_abi_info = XCNEW (struct target_function_abi_info);
g->reload = XCNEW (struct target_reload);
g->expmed = XCNEW (struct target_expmed);
g->optabs = XCNEW (struct target_optabs);
......@@ -127,6 +130,7 @@ target_globals::~target_globals ()
XDELETE (regs);
XDELETE (recog);
XDELETE (hard_regs);
XDELETE (function_abi_info);
XDELETE (reload);
XDELETE (expmed);
XDELETE (optabs);
......
......@@ -26,6 +26,7 @@ extern struct target_regs *this_target_regs;
extern struct target_rtl *this_target_rtl;
extern struct target_recog *this_target_recog;
extern struct target_hard_regs *this_target_hard_regs;
extern struct target_function_abi_info *this_target_function_abi_info;
extern struct target_reload *this_target_reload;
extern struct target_expmed *this_target_expmed;
extern struct target_optabs *this_target_optabs;
......@@ -48,6 +49,7 @@ public:
struct target_rtl *rtl;
struct target_recog *GTY((skip)) recog;
struct target_hard_regs *GTY((skip)) hard_regs;
struct target_function_abi_info *GTY((skip)) function_abi_info;
struct target_reload *GTY((skip)) reload;
struct target_expmed *GTY((skip)) expmed;
struct target_optabs *GTY((skip)) optabs;
......@@ -75,6 +77,7 @@ restore_target_globals (class target_globals *g)
this_target_rtl = g->rtl;
this_target_recog = g->recog;
this_target_hard_regs = g->hard_regs;
this_target_function_abi_info = g->function_abi_info;
this_target_reload = g->reload;
this_target_expmed = g->expmed;
this_target_optabs = g->optabs;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment