From: Richard Sandiford Date: Mon, 30 Sep 2019 16:19:38 +0000 (+0000) Subject: Add function_abi.{h,cc} X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bd785b44932274f7067105de417938597289962c;p=gcc.git 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 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index df8a3d22269..858f5962cf4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2019-09-30 Richard Sandiford + + * 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 PR target/85978 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2cf0c79c977..d796c149ca0 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 \ diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 7643bf9cefb..320aab52976 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -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 diff --git a/gcc/function-abi.cc b/gcc/function-abi.cc new file mode 100644 index 00000000000..e7c85816a12 --- /dev/null +++ b/gcc/function-abi.cc @@ -0,0 +1,145 @@ +/* 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 +. */ + +#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)); +} diff --git a/gcc/function-abi.h b/gcc/function-abi.h new file mode 100644 index 00000000000..05e502e1013 --- /dev/null +++ b/gcc/function-abi.h @@ -0,0 +1,269 @@ +/* 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 +. */ + +#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 diff --git a/gcc/function.c b/gcc/function.c index 9c158cec121..c3aa6a5e9c5 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -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 (); diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c index f41f54a0d4a..b87ea301e8e 100644 --- a/gcc/read-rtl-function.c +++ b/gcc/read-rtl-function.c @@ -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 (); diff --git a/gcc/reginfo.c b/gcc/reginfo.c index 22d0e6874a9..a3fbbe67c52 100644 --- a/gcc/reginfo.c +++ b/gcc/reginfo.c @@ -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]) diff --git a/gcc/target-globals.c b/gcc/target-globals.c index 8928fc19fd3..359677e68af 100644 --- a/gcc/target-globals.c +++ b/gcc/target-globals.c @@ -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 (); 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); diff --git a/gcc/target-globals.h b/gcc/target-globals.h index ceb216a6a9b..9c42913beff 100644 --- a/gcc/target-globals.h +++ b/gcc/target-globals.h @@ -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;