2002-02-01 18:16:02 +00:00
|
|
|
|
/* Procedure integration for GCC.
|
|
|
|
|
Copyright (C) 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
|
|
|
|
|
1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
Contributed by Michael Tiemann (tiemann@cygnus.com)
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
This file is part of GCC.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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 2, or (at your option) any later
|
|
|
|
|
version.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2002-02-01 18:16:02 +00:00
|
|
|
|
along with GCC; see the file COPYING. If not, write to the Free
|
|
|
|
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
|
02111-1307, USA. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#include "config.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "system.h"
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "rtl.h"
|
|
|
|
|
#include "tree.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "tm_p.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "regs.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "flags.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "debug.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "insn-config.h"
|
|
|
|
|
#include "expr.h"
|
|
|
|
|
#include "output.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "recog.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "integrate.h"
|
|
|
|
|
#include "real.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "except.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#include "function.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "toplev.h"
|
1999-10-16 06:09:09 +00:00
|
|
|
|
#include "intl.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "loop.h"
|
|
|
|
|
#include "params.h"
|
|
|
|
|
#include "ggc.h"
|
|
|
|
|
#include "target.h"
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#include "obstack.h"
|
|
|
|
|
#define obstack_chunk_alloc xmalloc
|
|
|
|
|
#define obstack_chunk_free free
|
|
|
|
|
|
|
|
|
|
extern struct obstack *function_maybepermanent_obstack;
|
|
|
|
|
|
|
|
|
|
/* Similar, but round to the next highest integer that meets the
|
|
|
|
|
alignment. */
|
|
|
|
|
#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
|
|
|
|
|
|
|
|
|
|
/* Default max number of insns a function can have and still be inline.
|
|
|
|
|
This is overridden on RISC machines. */
|
|
|
|
|
#ifndef INTEGRATE_THRESHOLD
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* Inlining small functions might save more space then not inlining at
|
|
|
|
|
all. Assume 1 instruction for the call and 1.5 insns per argument. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#define INTEGRATE_THRESHOLD(DECL) \
|
1999-08-26 09:30:50 +00:00
|
|
|
|
(optimize_size \
|
1999-10-16 06:09:09 +00:00
|
|
|
|
? (1 + (3 * list_length (DECL_ARGUMENTS (DECL))) / 2) \
|
1999-08-26 09:30:50 +00:00
|
|
|
|
: (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Private type used by {get/has}_func_hard_reg_initial_val. */
|
|
|
|
|
typedef struct initial_value_pair {
|
|
|
|
|
rtx hard_reg;
|
|
|
|
|
rtx pseudo;
|
|
|
|
|
} initial_value_pair;
|
|
|
|
|
typedef struct initial_value_struct {
|
|
|
|
|
int num_entries;
|
|
|
|
|
int max_entries;
|
|
|
|
|
initial_value_pair *entries;
|
|
|
|
|
} initial_value_struct;
|
|
|
|
|
|
|
|
|
|
static void setup_initial_hard_reg_value_integration PARAMS ((struct function *, struct inline_remap *));
|
|
|
|
|
|
|
|
|
|
static rtvec initialize_for_inline PARAMS ((tree));
|
|
|
|
|
static void note_modified_parmregs PARAMS ((rtx, rtx, void *));
|
|
|
|
|
static void integrate_parm_decls PARAMS ((tree, struct inline_remap *,
|
|
|
|
|
rtvec));
|
|
|
|
|
static tree integrate_decl_tree PARAMS ((tree,
|
|
|
|
|
struct inline_remap *));
|
|
|
|
|
static void subst_constants PARAMS ((rtx *, rtx,
|
|
|
|
|
struct inline_remap *, int));
|
|
|
|
|
static void set_block_origin_self PARAMS ((tree));
|
|
|
|
|
static void set_block_abstract_flags PARAMS ((tree, int));
|
|
|
|
|
static void process_reg_param PARAMS ((struct inline_remap *, rtx,
|
|
|
|
|
rtx));
|
|
|
|
|
void set_decl_abstract_flags PARAMS ((tree, int));
|
|
|
|
|
static void mark_stores PARAMS ((rtx, rtx, void *));
|
|
|
|
|
static void save_parm_insns PARAMS ((rtx, rtx));
|
|
|
|
|
static void copy_insn_list PARAMS ((rtx, struct inline_remap *,
|
|
|
|
|
rtx));
|
|
|
|
|
static void copy_insn_notes PARAMS ((rtx, struct inline_remap *,
|
|
|
|
|
int));
|
|
|
|
|
static int compare_blocks PARAMS ((const PTR, const PTR));
|
|
|
|
|
static int find_block PARAMS ((const PTR, const PTR));
|
|
|
|
|
|
|
|
|
|
/* Used by copy_rtx_and_substitute; this indicates whether the function is
|
|
|
|
|
called for the purpose of inlining or some other purpose (i.e. loop
|
|
|
|
|
unrolling). This affects how constant pool references are handled.
|
|
|
|
|
This variable contains the FUNCTION_DECL for the inlined function. */
|
|
|
|
|
static struct function *inlining = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* Returns the Ith entry in the label_map contained in MAP. If the
|
|
|
|
|
Ith entry has not yet been set, return a fresh label. This function
|
|
|
|
|
performs a lazy initialization of label_map, thereby avoiding huge memory
|
|
|
|
|
explosions when the label_map gets very large. */
|
|
|
|
|
|
|
|
|
|
rtx
|
|
|
|
|
get_label_from_map (map, i)
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
int i;
|
|
|
|
|
{
|
|
|
|
|
rtx x = map->label_map[i];
|
|
|
|
|
|
|
|
|
|
if (x == NULL_RTX)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
x = map->label_map[i] = gen_label_rtx ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Return false if the function FNDECL cannot be inlined on account of its
|
|
|
|
|
attributes, true otherwise. */
|
|
|
|
|
bool
|
|
|
|
|
function_attribute_inlinable_p (fndecl)
|
|
|
|
|
tree fndecl;
|
|
|
|
|
{
|
|
|
|
|
bool has_machine_attr = false;
|
|
|
|
|
tree a;
|
|
|
|
|
|
|
|
|
|
for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
|
|
|
|
|
{
|
|
|
|
|
tree name = TREE_PURPOSE (a);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; targetm.attribute_table[i].name != NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
if (is_attribute_p (targetm.attribute_table[i].name, name))
|
|
|
|
|
{
|
|
|
|
|
has_machine_attr = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (has_machine_attr)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (has_machine_attr)
|
|
|
|
|
return (*targetm.function_attribute_inlinable_p) (fndecl);
|
|
|
|
|
else
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Zero if the current function (whose FUNCTION_DECL is FNDECL)
|
|
|
|
|
is safe and reasonable to integrate into other functions.
|
1999-10-16 06:09:09 +00:00
|
|
|
|
Nonzero means value is a warning msgid with a single %s
|
1996-09-18 05:35:50 +00:00
|
|
|
|
for the function's name. */
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
const char *
|
1996-09-18 05:35:50 +00:00
|
|
|
|
function_cannot_inline_p (fndecl)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree fndecl;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx insn;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
|
|
|
|
/* For functions marked as inline increase the maximum size to
|
2002-02-01 18:16:02 +00:00
|
|
|
|
MAX_INLINE_INSNS (-finline-limit-<n>). For regular functions
|
1999-10-16 06:09:09 +00:00
|
|
|
|
use the limit given by INTEGRATE_THRESHOLD. */
|
|
|
|
|
|
|
|
|
|
int max_insns = (DECL_INLINE (fndecl))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
? (MAX_INLINE_INSNS
|
1999-10-16 06:09:09 +00:00
|
|
|
|
+ 8 * list_length (DECL_ARGUMENTS (fndecl)))
|
|
|
|
|
: INTEGRATE_THRESHOLD (fndecl);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int ninsns = 0;
|
|
|
|
|
tree parms;
|
|
|
|
|
|
|
|
|
|
if (DECL_UNINLINABLE (fndecl))
|
|
|
|
|
return N_("function cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* No inlines with varargs. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if ((last && TREE_VALUE (last) != void_type_node)
|
|
|
|
|
|| current_function_varargs)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("varargs function cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (current_function_calls_alloca)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function using alloca cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (current_function_calls_setjmp)
|
|
|
|
|
return N_("function using setjmp cannot be inline");
|
|
|
|
|
|
|
|
|
|
if (current_function_calls_eh_return)
|
|
|
|
|
return N_("function uses __builtin_eh_return");
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (current_function_contains_functions)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function with nested functions cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (forced_labels)
|
|
|
|
|
return
|
|
|
|
|
N_("function with label addresses used in initializers cannot inline");
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
if (current_function_cannot_inline)
|
|
|
|
|
return current_function_cannot_inline;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* If its not even close, don't even look. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (get_max_uid () > 3 * max_insns)
|
|
|
|
|
return N_("function too large to be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Don't inline functions which do not specify a function prototype and
|
|
|
|
|
have BLKmode argument or take the address of a parameter. */
|
|
|
|
|
for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
|
|
|
|
|
{
|
|
|
|
|
if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode)
|
|
|
|
|
TREE_ADDRESSABLE (parms) = 1;
|
|
|
|
|
if (last == NULL_TREE && TREE_ADDRESSABLE (parms))
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("no prototype, and parameter address used; cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* We can't inline functions that return structures
|
|
|
|
|
the old-fashioned PCC way, copying into a static block. */
|
|
|
|
|
if (current_function_returns_pcc_struct)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("inline functions not supported for this return value type");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* We can't inline functions that return structures of varying size. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE
|
|
|
|
|
&& int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function with varying-size return value cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Cannot inline a function with a varying size argument or one that
|
|
|
|
|
receives a transparent union. */
|
|
|
|
|
for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
|
|
|
|
|
{
|
|
|
|
|
if (int_size_in_bytes (TREE_TYPE (parms)) < 0)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function with varying-size parameter cannot be inline");
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE
|
|
|
|
|
&& TYPE_TRANSPARENT_UNION (TREE_TYPE (parms)))
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function with transparent unit parameter cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (get_max_uid () > max_insns)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
1999-08-26 09:30:50 +00:00
|
|
|
|
for (ninsns = 0, insn = get_first_nonparm_insn ();
|
|
|
|
|
insn && ninsns < max_insns;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
insn = NEXT_INSN (insn))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (INSN_P (insn))
|
1999-08-26 09:30:50 +00:00
|
|
|
|
ninsns++;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (ninsns >= max_insns)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function too large to be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
/* We will not inline a function which uses computed goto. The addresses of
|
|
|
|
|
its local labels, which may be tucked into global storage, are of course
|
|
|
|
|
not constant across instantiations, which causes unexpected behaviour. */
|
|
|
|
|
if (current_function_has_computed_jump)
|
|
|
|
|
return N_("function with computed jump cannot inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* We cannot inline a nested function that jumps to a nonlocal label. */
|
|
|
|
|
if (current_function_has_nonlocal_goto)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
return N_("function with nonlocal goto cannot be inline");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* We can't inline functions that return a PARALLEL rtx. */
|
|
|
|
|
if (DECL_RTL_SET_P (DECL_RESULT (fndecl)))
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx result = DECL_RTL (DECL_RESULT (fndecl));
|
|
|
|
|
if (GET_CODE (result) == PARALLEL)
|
|
|
|
|
return N_("inline functions not supported for this return value type");
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If the function has a target specific attribute attached to it,
|
|
|
|
|
then we assume that we should not inline it. This can be overriden
|
|
|
|
|
by the target if it defines TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P. */
|
|
|
|
|
if (!function_attribute_inlinable_p (fndecl))
|
|
|
|
|
return N_("function with target specific attribute(s) cannot be inlined");
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return NULL;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Map pseudo reg number into the PARM_DECL for the parm living in the reg.
|
|
|
|
|
Zero for a reg that isn't a parm's home.
|
|
|
|
|
Only reg numbers less than max_parm_reg are mapped here. */
|
|
|
|
|
static tree *parmdecl_map;
|
|
|
|
|
|
|
|
|
|
/* In save_for_inline, nonzero if past the parm-initialization insns. */
|
|
|
|
|
static int in_nonparm_insns;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Subroutine for `save_for_inline'. Performs initialization
|
1996-09-18 05:35:50 +00:00
|
|
|
|
needed to save FNDECL's insns and info for future inline expansion. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
static rtvec
|
|
|
|
|
initialize_for_inline (fndecl)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree fndecl;
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtvec arg_vector;
|
|
|
|
|
tree parms;
|
|
|
|
|
|
|
|
|
|
/* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
memset ((char *) parmdecl_map, 0, max_parm_reg * sizeof (tree));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl)));
|
|
|
|
|
|
|
|
|
|
for (parms = DECL_ARGUMENTS (fndecl), i = 0;
|
|
|
|
|
parms;
|
|
|
|
|
parms = TREE_CHAIN (parms), i++)
|
|
|
|
|
{
|
|
|
|
|
rtx p = DECL_RTL (parms);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
/* If we have (mem (addressof (mem ...))), use the inner MEM since
|
|
|
|
|
otherwise the copy_rtx call below will not unshare the MEM since
|
|
|
|
|
it shares ADDRESSOF. */
|
|
|
|
|
if (GET_CODE (p) == MEM && GET_CODE (XEXP (p, 0)) == ADDRESSOF
|
|
|
|
|
&& GET_CODE (XEXP (XEXP (p, 0), 0)) == MEM)
|
|
|
|
|
p = XEXP (XEXP (p, 0), 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
RTVEC_ELT (arg_vector, i) = p;
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (p) == REG)
|
|
|
|
|
parmdecl_map[REGNO (p)] = parms;
|
|
|
|
|
else if (GET_CODE (p) == CONCAT)
|
|
|
|
|
{
|
|
|
|
|
rtx preal = gen_realpart (GET_MODE (XEXP (p, 0)), p);
|
|
|
|
|
rtx pimag = gen_imagpart (GET_MODE (preal), p);
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (preal) == REG)
|
|
|
|
|
parmdecl_map[REGNO (preal)] = parms;
|
|
|
|
|
if (GET_CODE (pimag) == REG)
|
|
|
|
|
parmdecl_map[REGNO (pimag)] = parms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This flag is cleared later
|
|
|
|
|
if the function ever modifies the value of the parm. */
|
|
|
|
|
TREE_READONLY (parms) = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return arg_vector;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Copy NODE (which must be a DECL, but not a PARM_DECL). The DECL
|
|
|
|
|
originally was in the FROM_FN, but now it will be in the
|
|
|
|
|
TO_FN. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree
|
|
|
|
|
copy_decl_for_inlining (decl, from_fn, to_fn)
|
|
|
|
|
tree decl;
|
|
|
|
|
tree from_fn;
|
|
|
|
|
tree to_fn;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree copy;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Copy the declaration. */
|
|
|
|
|
if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* For a parameter, we must make an equivalent VAR_DECL, not a
|
|
|
|
|
new PARM_DECL. */
|
|
|
|
|
copy = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
|
|
|
|
|
TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
|
|
|
|
|
TREE_READONLY (copy) = TREE_READONLY (decl);
|
|
|
|
|
TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = copy_node (decl);
|
|
|
|
|
if (DECL_LANG_SPECIFIC (copy))
|
|
|
|
|
copy_lang_decl (copy);
|
|
|
|
|
|
|
|
|
|
/* TREE_ADDRESSABLE isn't used to indicate that a label's
|
|
|
|
|
address has been taken; it's for internal bookkeeping in
|
|
|
|
|
expand_goto_internal. */
|
|
|
|
|
if (TREE_CODE (copy) == LABEL_DECL)
|
|
|
|
|
TREE_ADDRESSABLE (copy) = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what
|
|
|
|
|
declaration inspired this copy. */
|
|
|
|
|
DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The new variable/label has no RTL, yet. */
|
|
|
|
|
SET_DECL_RTL (copy, NULL_RTX);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* These args would always appear unused, if not for this. */
|
|
|
|
|
TREE_USED (copy) = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Set the context for the new declaration. */
|
|
|
|
|
if (!DECL_CONTEXT (decl))
|
|
|
|
|
/* Globals stay global. */
|
|
|
|
|
;
|
|
|
|
|
else if (DECL_CONTEXT (decl) != from_fn)
|
|
|
|
|
/* Things that weren't in the scope of the function we're inlining
|
|
|
|
|
from aren't in the scope we're inlining too, either. */
|
|
|
|
|
;
|
|
|
|
|
else if (TREE_STATIC (decl))
|
|
|
|
|
/* Function-scoped static variables should say in the original
|
|
|
|
|
function. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
;
|
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Ordinary automatic local variables are now in the scope of the
|
|
|
|
|
new function. */
|
|
|
|
|
DECL_CONTEXT (copy) = to_fn;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Make the insns and PARM_DECLs of the current function permanent
|
|
|
|
|
and record other information in DECL_SAVED_INSNS to allow inlining
|
|
|
|
|
of this function in subsequent calls.
|
|
|
|
|
|
|
|
|
|
This routine need not copy any insns because we are not going
|
|
|
|
|
to immediately compile the insns in the insn chain. There
|
|
|
|
|
are two cases when we would compile the insns for FNDECL:
|
|
|
|
|
(1) when FNDECL is expanded inline, and (2) when FNDECL needs to
|
|
|
|
|
be output at the end of other compilation, because somebody took
|
|
|
|
|
its address. In the first case, the insns of FNDECL are copied
|
|
|
|
|
as it is expanded inline, so FNDECL's saved insns are not
|
|
|
|
|
modified. In the second case, FNDECL is used for the last time,
|
|
|
|
|
so modifying the rtl is not a problem.
|
|
|
|
|
|
|
|
|
|
We don't have to worry about FNDECL being inline expanded by
|
|
|
|
|
other functions which are written at the end of compilation
|
|
|
|
|
because flag_no_inline is turned on when we begin writing
|
|
|
|
|
functions at the end of compilation. */
|
|
|
|
|
|
|
|
|
|
void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
save_for_inline (fndecl)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree fndecl;
|
|
|
|
|
{
|
|
|
|
|
rtx insn;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtvec argvec;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx first_nonparm_insn;
|
|
|
|
|
|
|
|
|
|
/* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
|
|
|
|
|
Later we set TREE_READONLY to 0 if the parm is modified inside the fn.
|
|
|
|
|
Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
|
|
|
|
|
for the parms, prior to elimination of virtual registers.
|
|
|
|
|
These values are needed for substituting parms properly. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! flag_no_inline)
|
|
|
|
|
parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Make and emit a return-label if we have not already done so. */
|
|
|
|
|
|
|
|
|
|
if (return_label == 0)
|
|
|
|
|
{
|
|
|
|
|
return_label = gen_label_rtx ();
|
|
|
|
|
emit_label (return_label);
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! flag_no_inline)
|
|
|
|
|
argvec = initialize_for_inline (fndecl);
|
|
|
|
|
else
|
|
|
|
|
argvec = NULL;
|
|
|
|
|
|
|
|
|
|
/* Delete basic block notes created by early run of find_basic_block.
|
|
|
|
|
The notes would be later used by find_basic_blocks to reuse the memory
|
|
|
|
|
for basic_block structures on already freed obstack. */
|
|
|
|
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
|
|
|
|
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK)
|
|
|
|
|
delete_related_insns (insn);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* If there are insns that copy parms from the stack into pseudo registers,
|
|
|
|
|
those insns are not copied. `expand_inline_function' must
|
|
|
|
|
emit the correct code to handle such things. */
|
|
|
|
|
|
|
|
|
|
insn = get_insns ();
|
|
|
|
|
if (GET_CODE (insn) != NOTE)
|
|
|
|
|
abort ();
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! flag_no_inline)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Get the insn which signals the end of parameter setup code. */
|
|
|
|
|
first_nonparm_insn = get_first_nonparm_insn ();
|
|
|
|
|
|
|
|
|
|
/* Now just scan the chain of insns to see what happens to our
|
|
|
|
|
PARM_DECLs. If a PARM_DECL is used but never modified, we
|
|
|
|
|
can substitute its rtl directly when expanding inline (and
|
|
|
|
|
perform constant folding when its incoming value is
|
|
|
|
|
constant). Otherwise, we have to copy its value into a new
|
|
|
|
|
register and track the new register's life. */
|
|
|
|
|
in_nonparm_insns = 0;
|
|
|
|
|
save_parm_insns (insn, first_nonparm_insn);
|
|
|
|
|
|
|
|
|
|
cfun->inl_max_label_num = max_label_num ();
|
|
|
|
|
cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
|
|
|
|
|
cfun->original_arg_vector = argvec;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun->original_decl_initial = DECL_INITIAL (fndecl);
|
|
|
|
|
cfun->no_debugging_symbols = (write_symbols == NO_DEBUG);
|
|
|
|
|
DECL_SAVED_INSNS (fndecl) = cfun;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Clean up. */
|
|
|
|
|
if (! flag_no_inline)
|
|
|
|
|
free (parmdecl_map);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Scan the chain of insns to see what happens to our PARM_DECLs. If a
|
|
|
|
|
PARM_DECL is used but never modified, we can substitute its rtl directly
|
|
|
|
|
when expanding inline (and perform constant folding when its incoming
|
|
|
|
|
value is constant). Otherwise, we have to copy its value into a new
|
|
|
|
|
register and track the new register's life. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
save_parm_insns (insn, first_nonparm_insn)
|
|
|
|
|
rtx insn;
|
|
|
|
|
rtx first_nonparm_insn;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (insn == NULL_RTX)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (insn == first_nonparm_insn)
|
|
|
|
|
in_nonparm_insns = 1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (INSN_P (insn))
|
|
|
|
|
{
|
|
|
|
|
/* Record what interesting things happen to our parameters. */
|
|
|
|
|
note_stores (PATTERN (insn), note_modified_parmregs, NULL);
|
|
|
|
|
|
|
|
|
|
/* If this is a CALL_PLACEHOLDER insn then we need to look into the
|
|
|
|
|
three attached sequences: normal call, sibling call and tail
|
|
|
|
|
recursion. */
|
|
|
|
|
if (GET_CODE (insn) == CALL_INSN
|
|
|
|
|
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
|
save_parm_insns (XEXP (PATTERN (insn), i),
|
|
|
|
|
first_nonparm_insn);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Note whether a parameter is modified or not. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
note_modified_parmregs (reg, x, data)
|
|
|
|
|
rtx reg;
|
|
|
|
|
rtx x ATTRIBUTE_UNUSED;
|
|
|
|
|
void *data ATTRIBUTE_UNUSED;
|
|
|
|
|
{
|
|
|
|
|
if (GET_CODE (reg) == REG && in_nonparm_insns
|
|
|
|
|
&& REGNO (reg) < max_parm_reg
|
|
|
|
|
&& REGNO (reg) >= FIRST_PSEUDO_REGISTER
|
|
|
|
|
&& parmdecl_map[REGNO (reg)] != 0)
|
|
|
|
|
TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unfortunately, we need a global copy of const_equiv map for communication
|
|
|
|
|
with a function called from note_stores. Be *very* careful that this
|
|
|
|
|
is used properly in the presence of recursion. */
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
varray_type global_const_equiv_varray;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#define FIXED_BASE_PLUS_P(X) \
|
|
|
|
|
(GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
|
|
|
|
|
&& GET_CODE (XEXP (X, 0)) == REG \
|
|
|
|
|
&& REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER \
|
|
|
|
|
&& REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER)
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
/* Called to set up a mapping for the case where a parameter is in a
|
|
|
|
|
register. If it is read-only and our argument is a constant, set up the
|
|
|
|
|
constant equivalence.
|
|
|
|
|
|
|
|
|
|
If LOC is REG_USERVAR_P, the usual case, COPY must also have that flag set
|
|
|
|
|
if it is a register.
|
|
|
|
|
|
|
|
|
|
Also, don't allow hard registers here; they might not be valid when
|
|
|
|
|
substituted into insns. */
|
|
|
|
|
static void
|
|
|
|
|
process_reg_param (map, loc, copy)
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
rtx loc, copy;
|
|
|
|
|
{
|
|
|
|
|
if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG)
|
|
|
|
|
|| (GET_CODE (copy) == REG && REG_USERVAR_P (loc)
|
|
|
|
|
&& ! REG_USERVAR_P (copy))
|
|
|
|
|
|| (GET_CODE (copy) == REG
|
|
|
|
|
&& REGNO (copy) < FIRST_PSEUDO_REGISTER))
|
|
|
|
|
{
|
|
|
|
|
rtx temp = copy_to_mode_reg (GET_MODE (loc), copy);
|
|
|
|
|
REG_USERVAR_P (temp) = REG_USERVAR_P (loc);
|
|
|
|
|
if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
|
|
|
|
|
SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM);
|
|
|
|
|
copy = temp;
|
|
|
|
|
}
|
|
|
|
|
map->reg_map[REGNO (loc)] = copy;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Compare two BLOCKs for qsort. The key we sort on is the
|
|
|
|
|
BLOCK_ABSTRACT_ORIGIN of the blocks. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
|
|
|
|
compare_blocks (v1, v2)
|
|
|
|
|
const PTR v1;
|
|
|
|
|
const PTR v2;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree b1 = *((const tree *) v1);
|
|
|
|
|
tree b2 = *((const tree *) v2);
|
|
|
|
|
|
|
|
|
|
return ((char *) BLOCK_ABSTRACT_ORIGIN (b1)
|
|
|
|
|
- (char *) BLOCK_ABSTRACT_ORIGIN (b2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compare two BLOCKs for bsearch. The first pointer corresponds to
|
|
|
|
|
an original block; the second to a remapped equivalent. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
find_block (v1, v2)
|
|
|
|
|
const PTR v1;
|
|
|
|
|
const PTR v2;
|
|
|
|
|
{
|
|
|
|
|
const union tree_node *b1 = (const union tree_node *) v1;
|
|
|
|
|
tree b2 = *((const tree *) v2);
|
|
|
|
|
|
|
|
|
|
return ((const char *) b1 - (char *) BLOCK_ABSTRACT_ORIGIN (b2));
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Integrate the procedure defined by FNDECL. Note that this function
|
|
|
|
|
may wind up calling itself. Since the static variables are not
|
|
|
|
|
reentrant, we do not assign them until after the possibility
|
|
|
|
|
of recursion is eliminated.
|
|
|
|
|
|
|
|
|
|
If IGNORE is nonzero, do not produce a value.
|
|
|
|
|
Otherwise store the value in TARGET if it is nonzero and that is convenient.
|
|
|
|
|
|
|
|
|
|
Value is:
|
|
|
|
|
(rtx)-1 if we could not substitute the function
|
|
|
|
|
0 if we substituted it and it does not produce a value
|
|
|
|
|
else an rtx for where the value is stored. */
|
|
|
|
|
|
|
|
|
|
rtx
|
1999-08-26 09:30:50 +00:00
|
|
|
|
expand_inline_function (fndecl, parms, target, ignore, type,
|
|
|
|
|
structure_value_addr)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree fndecl, parms;
|
|
|
|
|
rtx target;
|
|
|
|
|
int ignore;
|
|
|
|
|
tree type;
|
|
|
|
|
rtx structure_value_addr;
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct function *inlining_previous;
|
|
|
|
|
struct function *inl_f = DECL_SAVED_INSNS (fndecl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree formal, actual, block;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx parm_insns = inl_f->emit->x_first_insn;
|
|
|
|
|
rtx insns = (inl_f->inl_last_parm_insn
|
|
|
|
|
? NEXT_INSN (inl_f->inl_last_parm_insn)
|
|
|
|
|
: parm_insns);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree *arg_trees;
|
|
|
|
|
rtx *arg_vals;
|
|
|
|
|
int max_regno;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
|
|
|
|
int min_labelno = inl_f->emit->x_first_label_num;
|
|
|
|
|
int max_labelno = inl_f->inl_max_label_num;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int nargs;
|
|
|
|
|
rtx loc;
|
|
|
|
|
rtx stack_save = 0;
|
|
|
|
|
rtx temp;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
struct inline_remap *map = 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtvec arg_vector = (rtvec) inl_f->original_arg_vector;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx static_chain_value = 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int inl_max_uid;
|
|
|
|
|
int eh_region_offset;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* The pointer used to track the true location of the memory used
|
|
|
|
|
for MAP->LABEL_MAP. */
|
|
|
|
|
rtx *real_label_map = 0;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Allow for equivalences of the pseudos we make for virtual fp and ap. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
max_regno = inl_f->emit->x_reg_rtx_no + 3;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (max_regno < FIRST_PSEUDO_REGISTER)
|
|
|
|
|
abort ();
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Pull out the decl for the function definition; fndecl may be a
|
|
|
|
|
local declaration, which would break DECL_ABSTRACT_ORIGIN. */
|
|
|
|
|
fndecl = inl_f->decl;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
nargs = list_length (DECL_ARGUMENTS (fndecl));
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (cfun->preferred_stack_boundary < inl_f->preferred_stack_boundary)
|
|
|
|
|
cfun->preferred_stack_boundary = inl_f->preferred_stack_boundary;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Check that the parms type match and that sufficient arguments were
|
|
|
|
|
passed. Since the appropriate conversions or default promotions have
|
|
|
|
|
already been applied, the machine modes should match exactly. */
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
for (formal = DECL_ARGUMENTS (fndecl), actual = parms;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
formal;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
tree arg;
|
|
|
|
|
enum machine_mode mode;
|
|
|
|
|
|
|
|
|
|
if (actual == 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return (rtx) (size_t) -1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
arg = TREE_VALUE (actual);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
mode = TYPE_MODE (DECL_ARG_TYPE (formal));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (arg == error_mark_node
|
|
|
|
|
|| mode != TYPE_MODE (TREE_TYPE (arg))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* If they are block mode, the types should match exactly.
|
|
|
|
|
They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE,
|
|
|
|
|
which could happen if the parameter has incomplete type. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|| (mode == BLKmode
|
|
|
|
|
&& (TYPE_MAIN_VARIANT (TREE_TYPE (arg))
|
|
|
|
|
!= TYPE_MAIN_VARIANT (TREE_TYPE (formal)))))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return (rtx) (size_t) -1;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Extra arguments are valid, but will be ignored below, so we must
|
|
|
|
|
evaluate them here for side-effects. */
|
|
|
|
|
for (; actual; actual = TREE_CHAIN (actual))
|
|
|
|
|
expand_expr (TREE_VALUE (actual), const0_rtx,
|
|
|
|
|
TYPE_MODE (TREE_TYPE (TREE_VALUE (actual))), 0);
|
|
|
|
|
|
|
|
|
|
/* Expand the function arguments. Do this first so that any
|
|
|
|
|
new registers get created before we allocate the maps. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
arg_vals = (rtx *) xmalloc (nargs * sizeof (rtx));
|
|
|
|
|
arg_trees = (tree *) xmalloc (nargs * sizeof (tree));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0;
|
|
|
|
|
formal;
|
|
|
|
|
formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++)
|
|
|
|
|
{
|
|
|
|
|
/* Actual parameter, converted to the type of the argument within the
|
|
|
|
|
function. */
|
|
|
|
|
tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual));
|
|
|
|
|
/* Mode of the variable used within the function. */
|
|
|
|
|
enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal));
|
|
|
|
|
int invisiref = 0;
|
|
|
|
|
|
|
|
|
|
arg_trees[i] = arg;
|
|
|
|
|
loc = RTVEC_ELT (arg_vector, i);
|
|
|
|
|
|
|
|
|
|
/* If this is an object passed by invisible reference, we copy the
|
|
|
|
|
object into a stack slot and save its address. If this will go
|
|
|
|
|
into memory, we do nothing now. Otherwise, we just expand the
|
|
|
|
|
argument. */
|
|
|
|
|
if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
|
|
|
|
|
&& REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx stack_slot = assign_temp (TREE_TYPE (arg), 1, 1, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
store_expr (arg, stack_slot, 0);
|
|
|
|
|
arg_vals[i] = XEXP (stack_slot, 0);
|
|
|
|
|
invisiref = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (GET_CODE (loc) != MEM)
|
|
|
|
|
{
|
|
|
|
|
if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg)))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
int unsignedp = TREE_UNSIGNED (TREE_TYPE (formal));
|
|
|
|
|
enum machine_mode pmode = TYPE_MODE (TREE_TYPE (formal));
|
|
|
|
|
|
|
|
|
|
pmode = promote_mode (TREE_TYPE (formal), pmode,
|
|
|
|
|
&unsignedp, 0);
|
|
|
|
|
|
|
|
|
|
if (GET_MODE (loc) != pmode)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
/* The mode if LOC and ARG can differ if LOC was a variable
|
|
|
|
|
that had its mode promoted via PROMOTED_MODE. */
|
|
|
|
|
arg_vals[i] = convert_modes (pmode,
|
|
|
|
|
TYPE_MODE (TREE_TYPE (arg)),
|
|
|
|
|
expand_expr (arg, NULL_RTX, mode,
|
|
|
|
|
EXPAND_SUM),
|
|
|
|
|
unsignedp);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
|
|
|
|
arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
arg_vals[i] = 0;
|
|
|
|
|
|
|
|
|
|
if (arg_vals[i] != 0
|
|
|
|
|
&& (! TREE_READONLY (formal)
|
|
|
|
|
/* If the parameter is not read-only, copy our argument through
|
|
|
|
|
a register. Also, we cannot use ARG_VALS[I] if it overlaps
|
|
|
|
|
TARGET in any way. In the inline function, they will likely
|
|
|
|
|
be two different pseudos, and `safe_from_p' will make all
|
|
|
|
|
sorts of smart assumptions about their not conflicting.
|
|
|
|
|
But if ARG_VALS[I] overlaps TARGET, these assumptions are
|
|
|
|
|
wrong, so put ARG_VALS[I] into a fresh register.
|
|
|
|
|
Don't worry about invisible references, since their stack
|
|
|
|
|
temps will never overlap the target. */
|
|
|
|
|
|| (target != 0
|
|
|
|
|
&& ! invisiref
|
|
|
|
|
&& (GET_CODE (arg_vals[i]) == REG
|
|
|
|
|
|| GET_CODE (arg_vals[i]) == SUBREG
|
|
|
|
|
|| GET_CODE (arg_vals[i]) == MEM)
|
|
|
|
|
&& reg_overlap_mentioned_p (arg_vals[i], target))
|
|
|
|
|
/* ??? We must always copy a SUBREG into a REG, because it might
|
|
|
|
|
get substituted into an address, and not all ports correctly
|
|
|
|
|
handle SUBREGs in addresses. */
|
|
|
|
|
|| (GET_CODE (arg_vals[i]) == SUBREG)))
|
|
|
|
|
arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
if (arg_vals[i] != 0 && GET_CODE (arg_vals[i]) == REG
|
|
|
|
|
&& POINTER_TYPE_P (TREE_TYPE (formal)))
|
|
|
|
|
mark_reg_pointer (arg_vals[i],
|
2002-02-01 18:16:02 +00:00
|
|
|
|
TYPE_ALIGN (TREE_TYPE (TREE_TYPE (formal))));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Allocate the structures we use to remap things. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map = (struct inline_remap *) xcalloc (1, sizeof (struct inline_remap));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->fndecl = fndecl;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
VARRAY_TREE_INIT (map->block_map, 10, "block_map");
|
|
|
|
|
map->reg_map = (rtx *) xcalloc (max_regno, sizeof (rtx));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* We used to use alloca here, but the size of what it would try to
|
|
|
|
|
allocate would occasionally cause it to exceed the stack limit and
|
|
|
|
|
cause unpredictable core dumps. */
|
|
|
|
|
real_label_map
|
|
|
|
|
= (rtx *) xmalloc ((max_labelno) * sizeof (rtx));
|
|
|
|
|
map->label_map = real_label_map;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->local_return_label = NULL_RTX;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
inl_max_uid = (inl_f->emit->x_cur_insn_uid + 1);
|
|
|
|
|
map->insn_map = (rtx *) xcalloc (inl_max_uid, sizeof (rtx));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->min_insnno = 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->max_insnno = inl_max_uid;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
map->integrating = 1;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->compare_src = NULL_RTX;
|
|
|
|
|
map->compare_mode = VOIDmode;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
/* const_equiv_varray maps pseudos in our routine to constants, so
|
|
|
|
|
it needs to be large enough for all our pseudos. This is the
|
|
|
|
|
number we are currently using plus the number in the called
|
|
|
|
|
routine, plus 15 for each arg, five to compute the virtual frame
|
|
|
|
|
pointer, and five for the return value. This should be enough
|
|
|
|
|
for most cases. We do not reference entries outside the range of
|
|
|
|
|
the map.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
??? These numbers are quite arbitrary and were obtained by
|
|
|
|
|
experimentation. At some point, we should try to allocate the
|
|
|
|
|
table after all the parameters are set up so we an more accurately
|
|
|
|
|
estimate the number of pseudos we will need. */
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
VARRAY_CONST_EQUIV_INIT (map->const_equiv_varray,
|
|
|
|
|
(max_reg_num ()
|
|
|
|
|
+ (max_regno - FIRST_PSEUDO_REGISTER)
|
|
|
|
|
+ 15 * nargs
|
|
|
|
|
+ 10),
|
|
|
|
|
"expand_inline_function");
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->const_age = 0;
|
|
|
|
|
|
|
|
|
|
/* Record the current insn in case we have to set up pointers to frame
|
1999-08-26 09:30:50 +00:00
|
|
|
|
and argument memory blocks. If there are no insns yet, add a dummy
|
|
|
|
|
insn that can be used as an insertion point. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->insns_at_start = get_last_insn ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
if (map->insns_at_start == 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->insns_at_start = emit_note (NULL, NOTE_INSN_DELETED);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->regno_pointer_align = inl_f->emit->regno_pointer_align;
|
|
|
|
|
map->x_regno_reg_rtx = inl_f->emit->x_regno_reg_rtx;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Update the outgoing argument size to allow for those in the inlined
|
|
|
|
|
function. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (inl_f->outgoing_args_size > current_function_outgoing_args_size)
|
|
|
|
|
current_function_outgoing_args_size = inl_f->outgoing_args_size;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* If the inline function needs to make PIC references, that means
|
|
|
|
|
that this function's PIC offset table must be used. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (inl_f->uses_pic_offset_table)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
current_function_uses_pic_offset_table = 1;
|
|
|
|
|
|
|
|
|
|
/* If this function needs a context, set it up. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (inl_f->needs_context)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
static_chain_value = lookup_static_chain (fndecl);
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (parm_insns) == NOTE
|
|
|
|
|
&& NOTE_LINE_NUMBER (parm_insns) > 0)
|
|
|
|
|
{
|
|
|
|
|
rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns),
|
|
|
|
|
NOTE_LINE_NUMBER (parm_insns));
|
|
|
|
|
if (note)
|
|
|
|
|
RTX_INTEGRATED_P (note) = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process each argument. For each, set up things so that the function's
|
|
|
|
|
reference to the argument will refer to the argument being passed.
|
|
|
|
|
We only replace REG with REG here. Any simplifications are done
|
|
|
|
|
via const_equiv_map.
|
|
|
|
|
|
|
|
|
|
We make two passes: In the first, we deal with parameters that will
|
|
|
|
|
be placed into registers, since we need to ensure that the allocated
|
|
|
|
|
register number fits in const_equiv_map. Then we store all non-register
|
|
|
|
|
parameters into their memory location. */
|
|
|
|
|
|
|
|
|
|
/* Don't try to free temp stack slots here, because we may put one of the
|
|
|
|
|
parameters into a temp stack slot. */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
|
|
|
{
|
|
|
|
|
rtx copy = arg_vals[i];
|
|
|
|
|
|
|
|
|
|
loc = RTVEC_ELT (arg_vector, i);
|
|
|
|
|
|
|
|
|
|
/* There are three cases, each handled separately. */
|
|
|
|
|
if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
|
|
|
|
|
&& REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
|
|
|
|
|
{
|
|
|
|
|
/* This must be an object passed by invisible reference (it could
|
|
|
|
|
also be a variable-sized object, but we forbid inlining functions
|
|
|
|
|
with variable-sized arguments). COPY is the address of the
|
|
|
|
|
actual value (this computation will cause it to be copied). We
|
|
|
|
|
map that address for the register, noting the actual address as
|
|
|
|
|
an equivalent in case it can be substituted into the insns. */
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (copy) != REG)
|
|
|
|
|
{
|
|
|
|
|
temp = copy_addr_to_reg (copy);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
|
|
|
|
|
SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
copy = temp;
|
|
|
|
|
}
|
|
|
|
|
map->reg_map[REGNO (XEXP (loc, 0))] = copy;
|
|
|
|
|
}
|
|
|
|
|
else if (GET_CODE (loc) == MEM)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* This is the case of a parameter that lives in memory. It
|
|
|
|
|
will live in the block we allocate in the called routine's
|
1996-09-18 05:35:50 +00:00
|
|
|
|
frame that simulates the incoming argument area. Do nothing
|
2002-02-01 18:16:02 +00:00
|
|
|
|
with the parameter now; we will call store_expr later. In
|
|
|
|
|
this case, however, we must ensure that the virtual stack and
|
|
|
|
|
incoming arg rtx values are expanded now so that we can be
|
|
|
|
|
sure we have enough slots in the const equiv map since the
|
|
|
|
|
store_expr call can easily blow the size estimate. */
|
|
|
|
|
if (DECL_SAVED_INSNS (fndecl)->args_size != 0)
|
|
|
|
|
copy_rtx_and_substitute (virtual_incoming_args_rtx, map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else if (GET_CODE (loc) == REG)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
process_reg_param (map, loc, copy);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else if (GET_CODE (loc) == CONCAT)
|
|
|
|
|
{
|
|
|
|
|
rtx locreal = gen_realpart (GET_MODE (XEXP (loc, 0)), loc);
|
|
|
|
|
rtx locimag = gen_imagpart (GET_MODE (XEXP (loc, 0)), loc);
|
|
|
|
|
rtx copyreal = gen_realpart (GET_MODE (locreal), copy);
|
|
|
|
|
rtx copyimag = gen_imagpart (GET_MODE (locimag), copy);
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
process_reg_param (map, locreal, copyreal);
|
|
|
|
|
process_reg_param (map, locimag, copyimag);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Tell copy_rtx_and_substitute to handle constant pool SYMBOL_REFs
|
|
|
|
|
specially. This function can be called recursively, so we need to
|
|
|
|
|
save the previous value. */
|
|
|
|
|
inlining_previous = inlining;
|
|
|
|
|
inlining = inl_f;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Now do the parameters that will be placed in memory. */
|
|
|
|
|
|
|
|
|
|
for (formal = DECL_ARGUMENTS (fndecl), i = 0;
|
|
|
|
|
formal; formal = TREE_CHAIN (formal), i++)
|
|
|
|
|
{
|
|
|
|
|
loc = RTVEC_ELT (arg_vector, i);
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (loc) == MEM
|
|
|
|
|
/* Exclude case handled above. */
|
|
|
|
|
&& ! (GET_CODE (XEXP (loc, 0)) == REG
|
|
|
|
|
&& REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER))
|
|
|
|
|
{
|
|
|
|
|
rtx note = emit_note (DECL_SOURCE_FILE (formal),
|
|
|
|
|
DECL_SOURCE_LINE (formal));
|
|
|
|
|
if (note)
|
|
|
|
|
RTX_INTEGRATED_P (note) = 1;
|
|
|
|
|
|
|
|
|
|
/* Compute the address in the area we reserved and store the
|
|
|
|
|
value there. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
temp = copy_rtx_and_substitute (loc, map, 1);
|
|
|
|
|
subst_constants (&temp, NULL_RTX, map, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
apply_change_group ();
|
|
|
|
|
if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
|
|
|
|
|
temp = change_address (temp, VOIDmode, XEXP (temp, 0));
|
|
|
|
|
store_expr (arg_trees[i], temp, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Deal with the places that the function puts its result.
|
|
|
|
|
We are driven by what is placed into DECL_RESULT.
|
|
|
|
|
|
|
|
|
|
Initially, we assume that we don't have anything special handling for
|
|
|
|
|
REG_FUNCTION_RETURN_VALUE_P. */
|
|
|
|
|
|
|
|
|
|
map->inline_target = 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
loc = (DECL_RTL_SET_P (DECL_RESULT (fndecl))
|
|
|
|
|
? DECL_RTL (DECL_RESULT (fndecl)) : NULL_RTX);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (TYPE_MODE (type) == VOIDmode)
|
|
|
|
|
/* There is no return value to worry about. */
|
|
|
|
|
;
|
|
|
|
|
else if (GET_CODE (loc) == MEM)
|
|
|
|
|
{
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (GET_CODE (XEXP (loc, 0)) == ADDRESSOF)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
temp = copy_rtx_and_substitute (loc, map, 1);
|
|
|
|
|
subst_constants (&temp, NULL_RTX, map, 1);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
apply_change_group ();
|
|
|
|
|
target = temp;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (! structure_value_addr
|
|
|
|
|
|| ! aggregate_value_p (DECL_RESULT (fndecl)))
|
|
|
|
|
abort ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
/* Pass the function the address in which to return a structure
|
|
|
|
|
value. Note that a constructor can cause someone to call us
|
|
|
|
|
with STRUCTURE_VALUE_ADDR, but the initialization takes place
|
|
|
|
|
via the first parameter, rather than the struct return address.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
We have two cases: If the address is a simple register
|
|
|
|
|
indirect, use the mapping mechanism to point that register to
|
|
|
|
|
our structure return address. Otherwise, store the structure
|
|
|
|
|
return value into the place that it will be referenced from. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (GET_CODE (XEXP (loc, 0)) == REG)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
1999-10-16 06:09:09 +00:00
|
|
|
|
temp = force_operand (structure_value_addr, NULL_RTX);
|
|
|
|
|
temp = force_reg (Pmode, temp);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* A virtual register might be invalid in an insn, because
|
|
|
|
|
it can cause trouble in reload. Since we don't have access
|
|
|
|
|
to the expanders at map translation time, make sure we have
|
|
|
|
|
a proper register now.
|
|
|
|
|
If a virtual register is actually valid, cse or combine
|
|
|
|
|
can put it into the mapped insns. */
|
|
|
|
|
if (REGNO (temp) >= FIRST_VIRTUAL_REGISTER
|
|
|
|
|
&& REGNO (temp) <= LAST_VIRTUAL_REGISTER)
|
|
|
|
|
temp = copy_to_mode_reg (Pmode, temp);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
map->reg_map[REGNO (XEXP (loc, 0))] = temp;
|
|
|
|
|
|
|
|
|
|
if (CONSTANT_P (structure_value_addr)
|
|
|
|
|
|| GET_CODE (structure_value_addr) == ADDRESSOF
|
|
|
|
|
|| (GET_CODE (structure_value_addr) == PLUS
|
|
|
|
|
&& (XEXP (structure_value_addr, 0)
|
|
|
|
|
== virtual_stack_vars_rtx)
|
|
|
|
|
&& (GET_CODE (XEXP (structure_value_addr, 1))
|
|
|
|
|
== CONST_INT)))
|
|
|
|
|
{
|
|
|
|
|
SET_CONST_EQUIV_DATA (map, temp, structure_value_addr,
|
|
|
|
|
CONST_AGE_PARM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
temp = copy_rtx_and_substitute (loc, map, 1);
|
|
|
|
|
subst_constants (&temp, NULL_RTX, map, 0);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
apply_change_group ();
|
|
|
|
|
emit_move_insn (temp, structure_value_addr);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ignore)
|
|
|
|
|
/* We will ignore the result value, so don't look at its structure.
|
|
|
|
|
Note that preparations for an aggregate return value
|
|
|
|
|
do need to be made (above) even if it will be ignored. */
|
|
|
|
|
;
|
|
|
|
|
else if (GET_CODE (loc) == REG)
|
|
|
|
|
{
|
|
|
|
|
/* The function returns an object in a register and we use the return
|
|
|
|
|
value. Set up our target for remapping. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Machine mode function was declared to return. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
enum machine_mode departing_mode = TYPE_MODE (type);
|
|
|
|
|
/* (Possibly wider) machine mode it actually computes
|
1999-08-26 09:30:50 +00:00
|
|
|
|
(for the sake of callers that fail to declare it right).
|
|
|
|
|
We have to use the mode of the result's RTL, rather than
|
|
|
|
|
its type, since expand_function_start may have promoted it. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
enum machine_mode arriving_mode
|
1999-08-26 09:30:50 +00:00
|
|
|
|
= GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx reg_to_map;
|
|
|
|
|
|
|
|
|
|
/* Don't use MEMs as direct targets because on some machines
|
|
|
|
|
substituting a MEM for a REG makes invalid insns.
|
|
|
|
|
Let the combiner substitute the MEM if that is valid. */
|
|
|
|
|
if (target == 0 || GET_CODE (target) != REG
|
|
|
|
|
|| GET_MODE (target) != departing_mode)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
|
|
|
|
/* Don't make BLKmode registers. If this looks like
|
|
|
|
|
a BLKmode object being returned in a register, get
|
2002-02-01 18:16:02 +00:00
|
|
|
|
the mode from that, otherwise abort. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
if (departing_mode == BLKmode)
|
|
|
|
|
{
|
|
|
|
|
if (REG == GET_CODE (DECL_RTL (DECL_RESULT (fndecl))))
|
|
|
|
|
{
|
|
|
|
|
departing_mode = GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
|
|
|
|
|
arriving_mode = departing_mode;
|
|
|
|
|
}
|
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
abort ();
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
target = gen_reg_rtx (departing_mode);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* If function's value was promoted before return,
|
|
|
|
|
avoid machine mode mismatch when we substitute INLINE_TARGET.
|
|
|
|
|
But TARGET is what we will return to the caller. */
|
|
|
|
|
if (arriving_mode != departing_mode)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
|
|
|
|
/* Avoid creating a paradoxical subreg wider than
|
|
|
|
|
BITS_PER_WORD, since that is illegal. */
|
|
|
|
|
if (GET_MODE_BITSIZE (arriving_mode) > BITS_PER_WORD)
|
|
|
|
|
{
|
|
|
|
|
if (!TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (departing_mode),
|
|
|
|
|
GET_MODE_BITSIZE (arriving_mode)))
|
|
|
|
|
/* Maybe could be handled by using convert_move () ? */
|
|
|
|
|
abort ();
|
|
|
|
|
reg_to_map = gen_reg_rtx (arriving_mode);
|
|
|
|
|
target = gen_lowpart (departing_mode, reg_to_map);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reg_to_map = gen_rtx_SUBREG (arriving_mode, target, 0);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
|
|
|
|
reg_to_map = target;
|
|
|
|
|
|
|
|
|
|
/* Usually, the result value is the machine's return register.
|
|
|
|
|
Sometimes it may be a pseudo. Handle both cases. */
|
|
|
|
|
if (REG_FUNCTION_VALUE_P (loc))
|
|
|
|
|
map->inline_target = reg_to_map;
|
|
|
|
|
else
|
|
|
|
|
map->reg_map[REGNO (loc)] = reg_to_map;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (GET_CODE (loc) == CONCAT)
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode departing_mode = TYPE_MODE (type);
|
|
|
|
|
enum machine_mode arriving_mode
|
|
|
|
|
= GET_MODE (DECL_RTL (DECL_RESULT (fndecl)));
|
|
|
|
|
|
|
|
|
|
if (departing_mode != arriving_mode)
|
|
|
|
|
abort ();
|
|
|
|
|
if (GET_CODE (XEXP (loc, 0)) != REG
|
|
|
|
|
|| GET_CODE (XEXP (loc, 1)) != REG)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
/* Don't use MEMs as direct targets because on some machines
|
|
|
|
|
substituting a MEM for a REG makes invalid insns.
|
|
|
|
|
Let the combiner substitute the MEM if that is valid. */
|
|
|
|
|
if (target == 0 || GET_CODE (target) != REG
|
|
|
|
|
|| GET_MODE (target) != departing_mode)
|
|
|
|
|
target = gen_reg_rtx (departing_mode);
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (target) != CONCAT)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
map->reg_map[REGNO (XEXP (loc, 0))] = XEXP (target, 0);
|
|
|
|
|
map->reg_map[REGNO (XEXP (loc, 1))] = XEXP (target, 1);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
else
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Remap the exception handler data pointer from one to the other. */
|
|
|
|
|
temp = get_exception_pointer (inl_f);
|
|
|
|
|
if (temp)
|
|
|
|
|
map->reg_map[REGNO (temp)] = get_exception_pointer (cfun);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
/* Initialize label_map. get_label_from_map will actually make
|
|
|
|
|
the labels. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
memset ((char *) &map->label_map[min_labelno], 0,
|
1999-08-26 09:30:50 +00:00
|
|
|
|
(max_labelno - min_labelno) * sizeof (rtx));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Make copies of the decls of the symbols in the inline function, so that
|
|
|
|
|
the copies of the variables get declared in the current function. Set
|
|
|
|
|
up things so that lookup_static_chain knows that to interpret registers
|
|
|
|
|
in SAVE_EXPRs for TYPE_SIZEs as local. */
|
|
|
|
|
inline_function_decl = fndecl;
|
|
|
|
|
integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector);
|
|
|
|
|
block = integrate_decl_tree (inl_f->original_decl_initial, map);
|
|
|
|
|
BLOCK_ABSTRACT_ORIGIN (block) = DECL_ORIGIN (fndecl);
|
|
|
|
|
inline_function_decl = 0;
|
|
|
|
|
|
|
|
|
|
/* Make a fresh binding contour that we can easily remove. Do this after
|
|
|
|
|
expanding our arguments so cleanups are properly scoped. */
|
|
|
|
|
expand_start_bindings_and_block (0, block);
|
|
|
|
|
|
|
|
|
|
/* Sort the block-map so that it will be easy to find remapped
|
|
|
|
|
blocks later. */
|
|
|
|
|
qsort (&VARRAY_TREE (map->block_map, 0),
|
|
|
|
|
map->block_map->elements_used,
|
|
|
|
|
sizeof (tree),
|
|
|
|
|
compare_blocks);
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Perform postincrements before actually calling the function. */
|
|
|
|
|
emit_queue ();
|
|
|
|
|
|
|
|
|
|
/* Clean up stack so that variables might have smaller offsets. */
|
|
|
|
|
do_pending_stack_adjust ();
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
/* Save a copy of the location of const_equiv_varray for
|
|
|
|
|
mark_stores, called via note_stores. */
|
|
|
|
|
global_const_equiv_varray = map->const_equiv_varray;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* If the called function does an alloca, save and restore the
|
|
|
|
|
stack pointer around the call. This saves stack space, but
|
|
|
|
|
also is required if this inline is being done between two
|
|
|
|
|
pushes. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (inl_f->calls_alloca)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
emit_stack_save (SAVE_BLOCK, &stack_save, NULL_RTX);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Map pseudos used for initial hard reg values. */
|
|
|
|
|
setup_initial_hard_reg_value_integration (inl_f, map);
|
|
|
|
|
|
|
|
|
|
/* Now copy the insns one by one. */
|
|
|
|
|
copy_insn_list (insns, map, static_chain_value);
|
|
|
|
|
|
|
|
|
|
/* Duplicate the EH regions. This will create an offset from the
|
|
|
|
|
region numbers in the function we're inlining to the region
|
|
|
|
|
numbers in the calling function. This must wait until after
|
|
|
|
|
copy_insn_list, as we need the insn map to be complete. */
|
|
|
|
|
eh_region_offset = duplicate_eh_regions (inl_f, map);
|
|
|
|
|
|
|
|
|
|
/* Now copy the REG_NOTES for those insns. */
|
|
|
|
|
copy_insn_notes (insns, map, eh_region_offset);
|
|
|
|
|
|
|
|
|
|
/* If the insn sequence required one, emit the return label. */
|
|
|
|
|
if (map->local_return_label)
|
|
|
|
|
emit_label (map->local_return_label);
|
|
|
|
|
|
|
|
|
|
/* Restore the stack pointer if we saved it above. */
|
|
|
|
|
if (inl_f->calls_alloca)
|
|
|
|
|
emit_stack_restore (SAVE_BLOCK, stack_save, NULL_RTX);
|
|
|
|
|
|
|
|
|
|
if (! cfun->x_whole_function_mode_p)
|
|
|
|
|
/* In statement-at-a-time mode, we just tell the front-end to add
|
|
|
|
|
this block to the list of blocks at this binding level. We
|
|
|
|
|
can't do it the way it's done for function-at-a-time mode the
|
|
|
|
|
superblocks have not been created yet. */
|
|
|
|
|
insert_block (block);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BLOCK_CHAIN (block)
|
|
|
|
|
= BLOCK_CHAIN (DECL_INITIAL (current_function_decl));
|
|
|
|
|
BLOCK_CHAIN (DECL_INITIAL (current_function_decl)) = block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End the scope containing the copied formal parameter variables
|
|
|
|
|
and copied LABEL_DECLs. We pass NULL_TREE for the variables list
|
|
|
|
|
here so that expand_end_bindings will not check for unused
|
|
|
|
|
variables. That's already been checked for when the inlined
|
|
|
|
|
function was defined. */
|
|
|
|
|
expand_end_bindings (NULL_TREE, 1, 1);
|
|
|
|
|
|
|
|
|
|
/* Must mark the line number note after inlined functions as a repeat, so
|
|
|
|
|
that the test coverage code can avoid counting the call twice. This
|
|
|
|
|
just tells the code to ignore the immediately following line note, since
|
|
|
|
|
there already exists a copy of this note before the expanded inline call.
|
|
|
|
|
This line number note is still needed for debugging though, so we can't
|
|
|
|
|
delete it. */
|
|
|
|
|
if (flag_test_coverage)
|
|
|
|
|
emit_note (0, NOTE_INSN_REPEATED_LINE_NUMBER);
|
|
|
|
|
|
|
|
|
|
emit_line_note (input_filename, lineno);
|
|
|
|
|
|
|
|
|
|
/* If the function returns a BLKmode object in a register, copy it
|
|
|
|
|
out of the temp register into a BLKmode memory object. */
|
|
|
|
|
if (target
|
|
|
|
|
&& TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode
|
|
|
|
|
&& ! aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl))))
|
|
|
|
|
target = copy_blkmode_from_reg (0, target, TREE_TYPE (TREE_TYPE (fndecl)));
|
|
|
|
|
|
|
|
|
|
if (structure_value_addr)
|
|
|
|
|
{
|
|
|
|
|
target = gen_rtx_MEM (TYPE_MODE (type),
|
|
|
|
|
memory_address (TYPE_MODE (type),
|
|
|
|
|
structure_value_addr));
|
|
|
|
|
set_mem_attributes (target, type, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make sure we free the things we explicitly allocated with xmalloc. */
|
|
|
|
|
if (real_label_map)
|
|
|
|
|
free (real_label_map);
|
|
|
|
|
VARRAY_FREE (map->const_equiv_varray);
|
|
|
|
|
free (map->reg_map);
|
|
|
|
|
VARRAY_FREE (map->block_map);
|
|
|
|
|
free (map->insn_map);
|
|
|
|
|
free (map);
|
|
|
|
|
free (arg_vals);
|
|
|
|
|
free (arg_trees);
|
|
|
|
|
|
|
|
|
|
inlining = inlining_previous;
|
|
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make copies of each insn in the given list using the mapping
|
|
|
|
|
computed in expand_inline_function. This function may call itself for
|
|
|
|
|
insns containing sequences.
|
|
|
|
|
|
|
|
|
|
Copying is done in two passes, first the insns and then their REG_NOTES.
|
|
|
|
|
|
|
|
|
|
If static_chain_value is non-zero, it represents the context-pointer
|
|
|
|
|
register for the function. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
copy_insn_list (insns, map, static_chain_value)
|
|
|
|
|
rtx insns;
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
rtx static_chain_value;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
rtx insn;
|
|
|
|
|
rtx temp;
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
rtx cc0_insn = 0;
|
|
|
|
|
#endif
|
|
|
|
|
rtx static_chain_mem = 0;
|
|
|
|
|
|
|
|
|
|
/* Copy the insns one by one. Do this in two passes, first the insns and
|
|
|
|
|
then their REG_NOTES. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* This loop is very similar to the loop in copy_loop_body in unroll.c. */
|
|
|
|
|
|
|
|
|
|
for (insn = insns; insn; insn = NEXT_INSN (insn))
|
|
|
|
|
{
|
|
|
|
|
rtx copy, pattern, set;
|
|
|
|
|
|
|
|
|
|
map->orig_asm_operands_vector = 0;
|
|
|
|
|
|
|
|
|
|
switch (GET_CODE (insn))
|
|
|
|
|
{
|
|
|
|
|
case INSN:
|
|
|
|
|
pattern = PATTERN (insn);
|
|
|
|
|
set = single_set (insn);
|
|
|
|
|
copy = 0;
|
|
|
|
|
if (GET_CODE (pattern) == USE
|
|
|
|
|
&& GET_CODE (XEXP (pattern, 0)) == REG
|
|
|
|
|
&& REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
|
|
|
|
|
/* The (USE (REG n)) at return from the function should
|
|
|
|
|
be ignored since we are changing (REG n) into
|
|
|
|
|
inline_target. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Ignore setting a function value that we don't want to use. */
|
|
|
|
|
if (map->inline_target == 0
|
|
|
|
|
&& set != 0
|
|
|
|
|
&& GET_CODE (SET_DEST (set)) == REG
|
|
|
|
|
&& REG_FUNCTION_VALUE_P (SET_DEST (set)))
|
|
|
|
|
{
|
|
|
|
|
if (volatile_refs_p (SET_SRC (set)))
|
|
|
|
|
{
|
|
|
|
|
rtx new_set;
|
|
|
|
|
|
|
|
|
|
/* If we must not delete the source,
|
|
|
|
|
load it into a new temporary. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
new_set = single_set (copy);
|
|
|
|
|
if (new_set == 0)
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
SET_DEST (new_set)
|
|
|
|
|
= gen_reg_rtx (GET_MODE (SET_DEST (new_set)));
|
|
|
|
|
}
|
|
|
|
|
/* If the source and destination are the same and it
|
|
|
|
|
has a note on it, keep the insn. */
|
|
|
|
|
else if (rtx_equal_p (SET_DEST (set), SET_SRC (set))
|
|
|
|
|
&& REG_NOTES (insn) != 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Similarly if an ignored return value is clobbered. */
|
|
|
|
|
else if (map->inline_target == 0
|
|
|
|
|
&& GET_CODE (pattern) == CLOBBER
|
|
|
|
|
&& GET_CODE (XEXP (pattern, 0)) == REG
|
|
|
|
|
&& REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Look for the address of the static chain slot. The
|
|
|
|
|
rtx_equal_p comparisons against the
|
|
|
|
|
static_chain_incoming_rtx below may fail if the static
|
|
|
|
|
chain is in memory and the address specified is not
|
|
|
|
|
"legitimate". This happens on Xtensa where the static
|
|
|
|
|
chain is at a negative offset from argp and where only
|
|
|
|
|
positive offsets are legitimate. When the RTL is
|
|
|
|
|
generated, the address is "legitimized" by copying it
|
|
|
|
|
into a register, causing the rtx_equal_p comparisons to
|
|
|
|
|
fail. This workaround looks for code that sets a
|
|
|
|
|
register to the address of the static chain. Subsequent
|
|
|
|
|
memory references via that register can then be
|
|
|
|
|
identified as static chain references. We assume that
|
|
|
|
|
the register is only assigned once, and that the static
|
|
|
|
|
chain address is only live in one register at a time. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else if (static_chain_value != 0
|
|
|
|
|
&& set != 0
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& GET_CODE (static_chain_incoming_rtx) == MEM
|
1996-09-18 05:35:50 +00:00
|
|
|
|
&& GET_CODE (SET_DEST (set)) == REG
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& rtx_equal_p (SET_SRC (set),
|
|
|
|
|
XEXP (static_chain_incoming_rtx, 0)))
|
|
|
|
|
{
|
|
|
|
|
static_chain_mem =
|
|
|
|
|
gen_rtx_MEM (GET_MODE (static_chain_incoming_rtx),
|
|
|
|
|
SET_DEST (set));
|
|
|
|
|
|
|
|
|
|
/* emit the instruction in case it is used for something
|
|
|
|
|
other than setting the static chain; if it's not used,
|
|
|
|
|
it can always be removed as dead code */
|
|
|
|
|
copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is setting the static chain rtx, omit it. */
|
|
|
|
|
else if (static_chain_value != 0
|
|
|
|
|
&& set != 0
|
|
|
|
|
&& (rtx_equal_p (SET_DEST (set),
|
|
|
|
|
static_chain_incoming_rtx)
|
|
|
|
|
|| (static_chain_mem
|
|
|
|
|
&& rtx_equal_p (SET_DEST (set), static_chain_mem))))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If this is setting the static chain pseudo, set it from
|
|
|
|
|
the value we want to give it instead. */
|
|
|
|
|
else if (static_chain_value != 0
|
|
|
|
|
&& set != 0
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& (rtx_equal_p (SET_SRC (set),
|
|
|
|
|
static_chain_incoming_rtx)
|
|
|
|
|
|| (static_chain_mem
|
|
|
|
|
&& rtx_equal_p (SET_SRC (set), static_chain_mem))))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx newdest = copy_rtx_and_substitute (SET_DEST (set), map, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
copy = emit_move_insn (newdest, static_chain_value);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (GET_CODE (static_chain_incoming_rtx) != MEM)
|
|
|
|
|
static_chain_value = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is setting the virtual stack vars register, this must
|
|
|
|
|
be the code at the handler for a builtin longjmp. The value
|
|
|
|
|
saved in the setjmp buffer will be the address of the frame
|
|
|
|
|
we've made for this inlined instance within our frame. But we
|
|
|
|
|
know the offset of that value so we can use it to reconstruct
|
|
|
|
|
our virtual stack vars register from that value. If we are
|
|
|
|
|
copying it from the stack pointer, leave it unchanged. */
|
|
|
|
|
else if (set != 0
|
|
|
|
|
&& rtx_equal_p (SET_DEST (set), virtual_stack_vars_rtx))
|
|
|
|
|
{
|
|
|
|
|
HOST_WIDE_INT offset;
|
|
|
|
|
temp = map->reg_map[REGNO (SET_DEST (set))];
|
|
|
|
|
temp = VARRAY_CONST_EQUIV (map->const_equiv_varray,
|
|
|
|
|
REGNO (temp)).rtx;
|
|
|
|
|
|
|
|
|
|
if (rtx_equal_p (temp, virtual_stack_vars_rtx))
|
|
|
|
|
offset = 0;
|
|
|
|
|
else if (GET_CODE (temp) == PLUS
|
|
|
|
|
&& rtx_equal_p (XEXP (temp, 0), virtual_stack_vars_rtx)
|
|
|
|
|
&& GET_CODE (XEXP (temp, 1)) == CONST_INT)
|
|
|
|
|
offset = INTVAL (XEXP (temp, 1));
|
|
|
|
|
else
|
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
|
|
if (rtx_equal_p (SET_SRC (set), stack_pointer_rtx))
|
|
|
|
|
temp = SET_SRC (set);
|
|
|
|
|
else
|
|
|
|
|
temp = force_operand (plus_constant (SET_SRC (set),
|
|
|
|
|
- offset),
|
|
|
|
|
NULL_RTX);
|
|
|
|
|
|
|
|
|
|
copy = emit_move_insn (virtual_stack_vars_rtx, temp);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* REG_NOTES will be copied later. */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
/* If this insn is setting CC0, it may need to look at
|
|
|
|
|
the insn that uses CC0 to see what type of insn it is.
|
|
|
|
|
In that case, the call to recog via validate_change will
|
|
|
|
|
fail. So don't substitute constants here. Instead,
|
|
|
|
|
do it when we emit the following insn.
|
|
|
|
|
|
|
|
|
|
For example, see the pyr.md file. That machine has signed and
|
|
|
|
|
unsigned compares. The compare patterns must check the
|
|
|
|
|
following branch insn to see which what kind of compare to
|
|
|
|
|
emit.
|
|
|
|
|
|
|
|
|
|
If the previous insn set CC0, substitute constants on it as
|
|
|
|
|
well. */
|
|
|
|
|
if (sets_cc0_p (PATTERN (copy)) != 0)
|
|
|
|
|
cc0_insn = copy;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (cc0_insn)
|
|
|
|
|
try_constants (cc0_insn, map);
|
|
|
|
|
cc0_insn = 0;
|
|
|
|
|
try_constants (copy, map);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
try_constants (copy, map);
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JUMP_INSN:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (map->integrating && returnjump_p (insn))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (map->local_return_label == 0)
|
|
|
|
|
map->local_return_label = gen_label_rtx ();
|
|
|
|
|
pattern = gen_jump (map->local_return_label);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
copy = emit_jump_insn (pattern);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
if (cc0_insn)
|
|
|
|
|
try_constants (cc0_insn, map);
|
|
|
|
|
cc0_insn = 0;
|
|
|
|
|
#endif
|
|
|
|
|
try_constants (copy, map);
|
|
|
|
|
|
|
|
|
|
/* If this used to be a conditional jump insn but whose branch
|
|
|
|
|
direction is now know, we must do something special. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (any_condjump_p (insn) && onlyjump_p (insn) && map->last_pc_value)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_cc0
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If the previous insn set cc0 for us, delete it. */
|
|
|
|
|
if (only_sets_cc0_p (PREV_INSN (copy)))
|
|
|
|
|
delete_related_insns (PREV_INSN (copy));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* If this is now a no-op, delete it. */
|
|
|
|
|
if (map->last_pc_value == pc_rtx)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
delete_related_insns (copy);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
copy = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Otherwise, this is unconditional jump so we must put a
|
|
|
|
|
BARRIER after it. We could do some dead code elimination
|
|
|
|
|
here, but jump.c will do it just as well. */
|
|
|
|
|
emit_barrier ();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CALL_INSN:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If this is a CALL_PLACEHOLDER insn then we need to copy the
|
|
|
|
|
three attached sequences: normal call, sibling call and tail
|
|
|
|
|
recursion. */
|
|
|
|
|
if (GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
|
|
|
|
|
{
|
|
|
|
|
rtx sequence[3];
|
|
|
|
|
rtx tail_label;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
|
{
|
|
|
|
|
rtx seq;
|
|
|
|
|
|
|
|
|
|
sequence[i] = NULL_RTX;
|
|
|
|
|
seq = XEXP (PATTERN (insn), i);
|
|
|
|
|
if (seq)
|
|
|
|
|
{
|
|
|
|
|
start_sequence ();
|
|
|
|
|
copy_insn_list (seq, map, static_chain_value);
|
|
|
|
|
sequence[i] = get_insns ();
|
|
|
|
|
end_sequence ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the new tail recursion label.
|
|
|
|
|
It will already be substituted into sequence[2]. */
|
|
|
|
|
tail_label = copy_rtx_and_substitute (XEXP (PATTERN (insn), 3),
|
|
|
|
|
map, 0);
|
|
|
|
|
|
|
|
|
|
copy = emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode,
|
|
|
|
|
sequence[0],
|
|
|
|
|
sequence[1],
|
|
|
|
|
sequence[2],
|
|
|
|
|
tail_label));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
copy = emit_call_insn (pattern);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
SIBLING_CALL_P (copy) = SIBLING_CALL_P (insn);
|
|
|
|
|
CONST_OR_PURE_CALL_P (copy) = CONST_OR_PURE_CALL_P (insn);
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Because the USAGE information potentially contains objects other
|
|
|
|
|
than hard registers, we need to copy it. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
CALL_INSN_FUNCTION_USAGE (copy)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
= copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn),
|
|
|
|
|
map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
if (cc0_insn)
|
|
|
|
|
try_constants (cc0_insn, map);
|
|
|
|
|
cc0_insn = 0;
|
|
|
|
|
#endif
|
|
|
|
|
try_constants (copy, map);
|
|
|
|
|
|
|
|
|
|
/* Be lazy and assume CALL_INSNs clobber all hard registers. */
|
|
|
|
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
VARRAY_CONST_EQUIV (map->const_equiv_varray, i).rtx = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CODE_LABEL:
|
1999-08-26 09:30:50 +00:00
|
|
|
|
copy = emit_label (get_label_from_map (map,
|
|
|
|
|
CODE_LABEL_NUMBER (insn)));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
LABEL_NAME (copy) = LABEL_NAME (insn);
|
|
|
|
|
map->const_age++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BARRIER:
|
|
|
|
|
copy = emit_barrier ();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NOTE:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)
|
|
|
|
|
{
|
|
|
|
|
copy = emit_label (get_label_from_map (map,
|
|
|
|
|
CODE_LABEL_NUMBER (insn)));
|
|
|
|
|
LABEL_NAME (copy) = NOTE_SOURCE_FILE (insn);
|
|
|
|
|
map->const_age++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* NOTE_INSN_FUNCTION_END and NOTE_INSN_FUNCTION_BEG are
|
|
|
|
|
discarded because it is important to have only one of
|
|
|
|
|
each in the current function.
|
|
|
|
|
|
|
|
|
|
NOTE_INSN_DELETED notes aren't useful. */
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
|
|
|
|
|
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
|
|
|
|
|
&& NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
|
|
|
|
copy = emit_note (NOTE_SOURCE_FILE (insn),
|
|
|
|
|
NOTE_LINE_NUMBER (insn));
|
|
|
|
|
if (copy
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG
|
|
|
|
|
|| NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END)
|
|
|
|
|
&& NOTE_BLOCK (insn))
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree *mapped_block_p;
|
|
|
|
|
|
|
|
|
|
mapped_block_p
|
|
|
|
|
= (tree *) bsearch (NOTE_BLOCK (insn),
|
|
|
|
|
&VARRAY_TREE (map->block_map, 0),
|
|
|
|
|
map->block_map->elements_used,
|
|
|
|
|
sizeof (tree),
|
|
|
|
|
find_block);
|
|
|
|
|
|
|
|
|
|
if (!mapped_block_p)
|
|
|
|
|
abort ();
|
|
|
|
|
else
|
|
|
|
|
NOTE_BLOCK (copy) = *mapped_block_p;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (copy
|
|
|
|
|
&& NOTE_LINE_NUMBER (copy) == NOTE_INSN_EXPECTED_VALUE)
|
|
|
|
|
NOTE_EXPECTED_VALUE (copy)
|
|
|
|
|
= copy_rtx_and_substitute (NOTE_EXPECTED_VALUE (insn),
|
|
|
|
|
map, 0);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
else
|
|
|
|
|
copy = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (copy)
|
|
|
|
|
RTX_INTEGRATED_P (copy) = 1;
|
|
|
|
|
|
|
|
|
|
map->insn_map[INSN_UID (insn)] = copy;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Copy the REG_NOTES. Increment const_age, so that only constants
|
|
|
|
|
from parameters can be substituted in. These are the only ones
|
|
|
|
|
that are valid across the entire function. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
copy_insn_notes (insns, map, eh_region_offset)
|
|
|
|
|
rtx insns;
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
int eh_region_offset;
|
|
|
|
|
{
|
|
|
|
|
rtx insn, new_insn;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->const_age++;
|
|
|
|
|
for (insn = insns; insn; insn = NEXT_INSN (insn))
|
|
|
|
|
{
|
|
|
|
|
if (! INSN_P (insn))
|
|
|
|
|
continue;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
new_insn = map->insn_map[INSN_UID (insn)];
|
|
|
|
|
if (! new_insn)
|
|
|
|
|
continue;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (REG_NOTES (insn))
|
|
|
|
|
{
|
|
|
|
|
rtx next, note = copy_rtx_and_substitute (REG_NOTES (insn), map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* We must also do subst_constants, in case one of our parameters
|
|
|
|
|
has const type and constant value. */
|
|
|
|
|
subst_constants (¬e, NULL_RTX, map, 0);
|
|
|
|
|
apply_change_group ();
|
|
|
|
|
REG_NOTES (new_insn) = note;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Delete any REG_LABEL notes from the chain. Remap any
|
|
|
|
|
REG_EH_REGION notes. */
|
|
|
|
|
for (; note; note = next)
|
|
|
|
|
{
|
|
|
|
|
next = XEXP (note, 1);
|
|
|
|
|
if (REG_NOTE_KIND (note) == REG_LABEL)
|
|
|
|
|
remove_note (new_insn, note);
|
|
|
|
|
else if (REG_NOTE_KIND (note) == REG_EH_REGION
|
|
|
|
|
&& INTVAL (XEXP (note, 0)) > 0)
|
|
|
|
|
XEXP (note, 0) = GEN_INT (INTVAL (XEXP (note, 0))
|
|
|
|
|
+ eh_region_offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (GET_CODE (insn) == CALL_INSN
|
|
|
|
|
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
|
copy_insn_notes (XEXP (PATTERN (insn), i), map, eh_region_offset);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (GET_CODE (insn) == JUMP_INSN
|
|
|
|
|
&& GET_CODE (PATTERN (insn)) == RESX)
|
|
|
|
|
XINT (PATTERN (new_insn), 0) += eh_region_offset;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL,
|
|
|
|
|
push all of those decls and give each one the corresponding home. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
integrate_parm_decls (args, map, arg_vector)
|
|
|
|
|
tree args;
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
rtvec arg_vector;
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree tail;
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree decl = copy_decl_for_inlining (tail, map->fndecl,
|
|
|
|
|
current_function_decl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx new_decl_rtl
|
2002-02-01 18:16:02 +00:00
|
|
|
|
= copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* We really should be setting DECL_INCOMING_RTL to something reasonable
|
|
|
|
|
here, but that's going to require some more work. */
|
|
|
|
|
/* DECL_INCOMING_RTL (decl) = ?; */
|
|
|
|
|
/* Fully instantiate the address with the equivalent form so that the
|
|
|
|
|
debugging information contains the actual register, instead of the
|
|
|
|
|
virtual register. Do this by not passing an insn to
|
|
|
|
|
subst_constants. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&new_decl_rtl, NULL_RTX, map, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
apply_change_group ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
SET_DECL_RTL (decl, new_decl_rtl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a BLOCK node LET, push decls and levels so as to construct in the
|
|
|
|
|
current function a tree of contexts isomorphic to the one that is given.
|
|
|
|
|
|
|
|
|
|
MAP, if nonzero, is a pointer to an inline_remap map which indicates how
|
|
|
|
|
registers used in the DECL_RTL field should be remapped. If it is zero,
|
|
|
|
|
no mapping is necessary. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static tree
|
|
|
|
|
integrate_decl_tree (let, map)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
tree let;
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree t;
|
|
|
|
|
tree new_block;
|
|
|
|
|
tree *next;
|
|
|
|
|
|
|
|
|
|
new_block = make_node (BLOCK);
|
|
|
|
|
VARRAY_PUSH_TREE (map->block_map, new_block);
|
|
|
|
|
next = &BLOCK_VARS (new_block);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
|
|
|
|
|
{
|
|
|
|
|
tree d;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
d = copy_decl_for_inlining (t, map->fndecl, current_function_decl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (DECL_RTL_SET_P (t))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx r;
|
|
|
|
|
|
|
|
|
|
SET_DECL_RTL (d, copy_rtx_and_substitute (DECL_RTL (t), map, 1));
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* Fully instantiate the address with the equivalent form so that the
|
|
|
|
|
debugging information contains the actual register, instead of the
|
|
|
|
|
virtual register. Do this by not passing an insn to
|
|
|
|
|
subst_constants. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
r = DECL_RTL (d);
|
|
|
|
|
subst_constants (&r, NULL_RTX, map, 1);
|
|
|
|
|
SET_DECL_RTL (d, r);
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (r) == REG)
|
|
|
|
|
REGNO_DECL (REGNO (r)) = d;
|
|
|
|
|
else if (GET_CODE (r) == CONCAT)
|
|
|
|
|
{
|
|
|
|
|
REGNO_DECL (REGNO (XEXP (r, 0))) = d;
|
|
|
|
|
REGNO_DECL (REGNO (XEXP (r, 1))) = d;
|
|
|
|
|
}
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
apply_change_group ();
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Add this declaration to the list of variables in the new
|
|
|
|
|
block. */
|
|
|
|
|
*next = d;
|
|
|
|
|
next = &TREE_CHAIN (d);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
next = &BLOCK_SUBBLOCKS (new_block);
|
|
|
|
|
for (t = BLOCK_SUBBLOCKS (let); t; t = BLOCK_CHAIN (t))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
*next = integrate_decl_tree (t, map);
|
|
|
|
|
BLOCK_SUPERCONTEXT (*next) = new_block;
|
|
|
|
|
next = &BLOCK_CHAIN (*next);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
TREE_USED (new_block) = TREE_USED (let);
|
|
|
|
|
BLOCK_ABSTRACT_ORIGIN (new_block) = let;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return new_block;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Create a new copy of an rtx. Recursively copies the operands of the rtx,
|
1996-09-18 05:35:50 +00:00
|
|
|
|
except for those few rtx codes that are sharable.
|
|
|
|
|
|
|
|
|
|
We always return an rtx that is similar to that incoming rtx, with the
|
|
|
|
|
exception of possibly changing a REG to a SUBREG or vice versa. No
|
|
|
|
|
rtl is ever emitted.
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
If FOR_LHS is nonzero, if means we are processing something that will
|
|
|
|
|
be the LHS of a SET. In that case, we copy RTX_UNCHANGING_P even if
|
|
|
|
|
inlining since we need to be conservative in how it is set for
|
|
|
|
|
such cases.
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
Handle constants that need to be placed in the constant pool by
|
|
|
|
|
calling `force_const_mem'. */
|
|
|
|
|
|
|
|
|
|
rtx
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy_rtx_and_substitute (orig, map, for_lhs)
|
|
|
|
|
rtx orig;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
struct inline_remap *map;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int for_lhs;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx copy, temp;
|
|
|
|
|
int i, j;
|
|
|
|
|
RTX_CODE code;
|
|
|
|
|
enum machine_mode mode;
|
|
|
|
|
const char *format_ptr;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int regno;
|
|
|
|
|
|
|
|
|
|
if (orig == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
code = GET_CODE (orig);
|
|
|
|
|
mode = GET_MODE (orig);
|
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case REG:
|
|
|
|
|
/* If the stack pointer register shows up, it must be part of
|
|
|
|
|
stack-adjustments (*not* because we eliminated the frame pointer!).
|
|
|
|
|
Small hard registers are returned as-is. Pseudo-registers
|
|
|
|
|
go through their `reg_map'. */
|
|
|
|
|
regno = REGNO (orig);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (regno <= LAST_VIRTUAL_REGISTER
|
|
|
|
|
|| (map->integrating
|
|
|
|
|
&& DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer == orig))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
/* Some hard registers are also mapped,
|
|
|
|
|
but others are not translated. */
|
|
|
|
|
if (map->reg_map[regno] != 0)
|
|
|
|
|
return map->reg_map[regno];
|
|
|
|
|
|
|
|
|
|
/* If this is the virtual frame pointer, make space in current
|
|
|
|
|
function's stack frame for the stack frame of the inline function.
|
|
|
|
|
|
|
|
|
|
Copy the address of this area into a pseudo. Map
|
|
|
|
|
virtual_stack_vars_rtx to this pseudo and set up a constant
|
|
|
|
|
equivalence for it to be the address. This will substitute the
|
|
|
|
|
address into insns where it can be substituted and use the new
|
|
|
|
|
pseudo where it can't. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (regno == VIRTUAL_STACK_VARS_REGNUM)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
rtx loc, seq;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int size = get_func_frame_size (DECL_SAVED_INSNS (map->fndecl));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#ifdef FRAME_GROWS_DOWNWARD
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int alignment
|
|
|
|
|
= (DECL_SAVED_INSNS (map->fndecl)->stack_alignment_needed
|
|
|
|
|
/ BITS_PER_UNIT);
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* In this case, virtual_stack_vars_rtx points to one byte
|
|
|
|
|
higher than the top of the frame area. So make sure we
|
|
|
|
|
allocate a big enough chunk to keep the frame pointer
|
|
|
|
|
aligned like a real one. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (alignment)
|
|
|
|
|
size = CEIL_ROUND (size, alignment);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#endif
|
1996-09-18 05:35:50 +00:00
|
|
|
|
start_sequence ();
|
|
|
|
|
loc = assign_stack_temp (BLKmode, size, 1);
|
|
|
|
|
loc = XEXP (loc, 0);
|
|
|
|
|
#ifdef FRAME_GROWS_DOWNWARD
|
|
|
|
|
/* In this case, virtual_stack_vars_rtx points to one byte
|
|
|
|
|
higher than the top of the frame area. So compute the offset
|
1999-08-26 09:30:50 +00:00
|
|
|
|
to one byte higher than our substitute frame. */
|
|
|
|
|
loc = plus_constant (loc, size);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
map->reg_map[regno] = temp
|
|
|
|
|
= force_reg (Pmode, force_operand (loc, NULL_RTX));
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#ifdef STACK_BOUNDARY
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
seq = gen_sequence ();
|
|
|
|
|
end_sequence ();
|
|
|
|
|
emit_insn_after (seq, map->insns_at_start);
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM
|
|
|
|
|
|| (map->integrating
|
|
|
|
|
&& (DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer
|
|
|
|
|
== orig)))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
/* Do the same for a block to contain any arguments referenced
|
1999-08-26 09:30:50 +00:00
|
|
|
|
in memory. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx loc, seq;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int size = DECL_SAVED_INSNS (map->fndecl)->args_size;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
start_sequence ();
|
|
|
|
|
loc = assign_stack_temp (BLKmode, size, 1);
|
|
|
|
|
loc = XEXP (loc, 0);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* When arguments grow downward, the virtual incoming
|
1996-09-18 05:35:50 +00:00
|
|
|
|
args pointer points to the top of the argument block,
|
1999-08-26 09:30:50 +00:00
|
|
|
|
so the remapped location better do the same. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#ifdef ARGS_GROW_DOWNWARD
|
|
|
|
|
loc = plus_constant (loc, size);
|
|
|
|
|
#endif
|
|
|
|
|
map->reg_map[regno] = temp
|
|
|
|
|
= force_reg (Pmode, force_operand (loc, NULL_RTX));
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#ifdef STACK_BOUNDARY
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
seq = gen_sequence ();
|
|
|
|
|
end_sequence ();
|
|
|
|
|
emit_insn_after (seq, map->insns_at_start);
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
|
|
|
|
else if (REG_FUNCTION_VALUE_P (orig))
|
|
|
|
|
{
|
|
|
|
|
/* This is a reference to the function return value. If
|
|
|
|
|
the function doesn't have a return value, error. If the
|
1999-10-16 06:09:09 +00:00
|
|
|
|
mode doesn't agree, and it ain't BLKmode, make a SUBREG. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (map->inline_target == 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
if (rtx_equal_function_value_matters)
|
|
|
|
|
/* This is an ignored return value. We must not
|
|
|
|
|
leave it in with REG_FUNCTION_VALUE_P set, since
|
|
|
|
|
that would confuse subsequent inlining of the
|
|
|
|
|
current function into a later function. */
|
|
|
|
|
return gen_rtx_REG (GET_MODE (orig), regno);
|
|
|
|
|
else
|
|
|
|
|
/* Must be unrolling loops or replicating code if we
|
|
|
|
|
reach here, so return the register unchanged. */
|
|
|
|
|
return orig;
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
else if (GET_MODE (map->inline_target) != BLKmode
|
|
|
|
|
&& mode != GET_MODE (map->inline_target))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
return gen_lowpart (mode, map->inline_target);
|
|
|
|
|
else
|
|
|
|
|
return map->inline_target;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#if defined (LEAF_REGISTERS) && defined (LEAF_REG_REMAP)
|
|
|
|
|
/* If leaf_renumber_regs_insn() might remap this register to
|
|
|
|
|
some other number, make sure we don't share it with the
|
|
|
|
|
inlined function, otherwise delayed optimization of the
|
|
|
|
|
inlined function may change it in place, breaking our
|
|
|
|
|
reference to it. We may still shared it within the
|
|
|
|
|
function, so create an entry for this register in the
|
|
|
|
|
reg_map. */
|
|
|
|
|
if (map->integrating && regno < FIRST_PSEUDO_REGISTER
|
|
|
|
|
&& LEAF_REGISTERS[regno] && LEAF_REG_REMAP (regno) != regno)
|
|
|
|
|
{
|
|
|
|
|
if (!map->leaf_reg_map[regno][mode])
|
|
|
|
|
map->leaf_reg_map[regno][mode] = gen_rtx_REG (mode, regno);
|
|
|
|
|
return map->leaf_reg_map[regno][mode];
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
return orig;
|
|
|
|
|
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
if (map->reg_map[regno] == NULL)
|
|
|
|
|
{
|
|
|
|
|
map->reg_map[regno] = gen_reg_rtx (mode);
|
|
|
|
|
REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig);
|
|
|
|
|
REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig);
|
|
|
|
|
RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig);
|
|
|
|
|
/* A reg with REG_FUNCTION_VALUE_P true will never reach here. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (REG_POINTER (map->x_regno_reg_rtx[regno]))
|
1999-08-26 09:30:50 +00:00
|
|
|
|
mark_reg_pointer (map->reg_map[regno],
|
|
|
|
|
map->regno_pointer_align[regno]);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
return map->reg_map[regno];
|
|
|
|
|
|
|
|
|
|
case SUBREG:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = copy_rtx_and_substitute (SUBREG_REG (orig), map, for_lhs);
|
|
|
|
|
return simplify_gen_subreg (GET_MODE (orig), copy,
|
|
|
|
|
GET_MODE (SUBREG_REG (orig)),
|
|
|
|
|
SUBREG_BYTE (orig));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
case ADDRESSOF:
|
|
|
|
|
copy = gen_rtx_ADDRESSOF (mode,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy_rtx_and_substitute (XEXP (orig, 0),
|
|
|
|
|
map, for_lhs),
|
|
|
|
|
0, ADDRESSOF_DECL (orig));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
regno = ADDRESSOF_REGNO (orig);
|
|
|
|
|
if (map->reg_map[regno])
|
|
|
|
|
regno = REGNO (map->reg_map[regno]);
|
|
|
|
|
else if (regno > LAST_VIRTUAL_REGISTER)
|
|
|
|
|
{
|
|
|
|
|
temp = XEXP (orig, 0);
|
|
|
|
|
map->reg_map[regno] = gen_reg_rtx (GET_MODE (temp));
|
|
|
|
|
REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (temp);
|
|
|
|
|
REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (temp);
|
|
|
|
|
RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (temp);
|
|
|
|
|
/* A reg with REG_FUNCTION_VALUE_P true will never reach here. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (REG_POINTER (map->x_regno_reg_rtx[regno]))
|
1999-08-26 09:30:50 +00:00
|
|
|
|
mark_reg_pointer (map->reg_map[regno],
|
|
|
|
|
map->regno_pointer_align[regno]);
|
|
|
|
|
regno = REGNO (map->reg_map[regno]);
|
|
|
|
|
}
|
|
|
|
|
ADDRESSOF_REGNO (copy) = regno;
|
|
|
|
|
return copy;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case USE:
|
|
|
|
|
case CLOBBER:
|
|
|
|
|
/* USE and CLOBBER are ordinary, but we convert (use (subreg foo))
|
|
|
|
|
to (use foo) if the original insn didn't have a subreg.
|
|
|
|
|
Removing the subreg distorts the VAX movstrhi pattern
|
|
|
|
|
by changing the mode of an operand. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy = copy_rtx_and_substitute (XEXP (orig, 0), map, code == CLOBBER);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG)
|
|
|
|
|
copy = SUBREG_REG (copy);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return gen_rtx_fmt_e (code, VOIDmode, copy);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* We need to handle "deleted" labels that appear in the DECL_RTL
|
|
|
|
|
of a LABEL_DECL. */
|
|
|
|
|
case NOTE:
|
|
|
|
|
if (NOTE_LINE_NUMBER (orig) != NOTE_INSN_DELETED_LABEL)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* ... FALLTHRU ... */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
case CODE_LABEL:
|
1999-08-26 09:30:50 +00:00
|
|
|
|
LABEL_PRESERVE_P (get_label_from_map (map, CODE_LABEL_NUMBER (orig)))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
= LABEL_PRESERVE_P (orig);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return get_label_from_map (map, CODE_LABEL_NUMBER (orig));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case LABEL_REF:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy
|
|
|
|
|
= gen_rtx_LABEL_REF
|
|
|
|
|
(mode,
|
|
|
|
|
LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0)
|
|
|
|
|
: get_label_from_map (map, CODE_LABEL_NUMBER (XEXP (orig, 0))));
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig);
|
|
|
|
|
|
|
|
|
|
/* The fact that this label was previously nonlocal does not mean
|
|
|
|
|
it still is, so we must check if it is within the range of
|
|
|
|
|
this function's labels. */
|
|
|
|
|
LABEL_REF_NONLOCAL_P (copy)
|
|
|
|
|
= (LABEL_REF_NONLOCAL_P (orig)
|
|
|
|
|
&& ! (CODE_LABEL_NUMBER (XEXP (copy, 0)) >= get_first_label_num ()
|
|
|
|
|
&& CODE_LABEL_NUMBER (XEXP (copy, 0)) < max_label_num ()));
|
|
|
|
|
|
|
|
|
|
/* If we have made a nonlocal label local, it means that this
|
|
|
|
|
inlined call will be referring to our nonlocal goto handler.
|
|
|
|
|
So make sure we create one for this block; we normally would
|
|
|
|
|
not since this is not otherwise considered a "call". */
|
|
|
|
|
if (LABEL_REF_NONLOCAL_P (orig) && ! LABEL_REF_NONLOCAL_P (copy))
|
|
|
|
|
function_call_count++;
|
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
|
|
|
|
|
case PC:
|
|
|
|
|
case CC0:
|
|
|
|
|
case CONST_INT:
|
|
|
|
|
return orig;
|
|
|
|
|
|
|
|
|
|
case SYMBOL_REF:
|
|
|
|
|
/* Symbols which represent the address of a label stored in the constant
|
|
|
|
|
pool must be modified to point to a constant pool entry for the
|
|
|
|
|
remapped label. Otherwise, symbols are returned unchanged. */
|
|
|
|
|
if (CONSTANT_POOL_ADDRESS_P (orig))
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct function *f = inlining ? inlining : cfun;
|
|
|
|
|
rtx constant = get_pool_constant_for_function (f, orig);
|
|
|
|
|
enum machine_mode const_mode = get_pool_mode_for_function (f, orig);
|
|
|
|
|
if (inlining)
|
|
|
|
|
{
|
|
|
|
|
rtx temp = force_const_mem (const_mode,
|
1996-09-18 05:35:50 +00:00
|
|
|
|
copy_rtx_and_substitute (constant,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map, 0));
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Legitimizing the address here is incorrect.
|
|
|
|
|
|
|
|
|
|
Since we had a SYMBOL_REF before, we can assume it is valid
|
|
|
|
|
to have one in this position in the insn.
|
|
|
|
|
|
|
|
|
|
Also, change_address may create new registers. These
|
|
|
|
|
registers will not have valid reg_map entries. This can
|
|
|
|
|
cause try_constants() to fail because assumes that all
|
|
|
|
|
registers in the rtx have valid reg_map entries, and it may
|
|
|
|
|
end up replacing one of these new registers with junk. */
|
|
|
|
|
|
|
|
|
|
if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
|
|
|
|
|
temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
temp = XEXP (temp, 0);
|
|
|
|
|
|
|
|
|
|
#ifdef POINTERS_EXTEND_UNSIGNED
|
|
|
|
|
if (GET_MODE (temp) != GET_MODE (orig))
|
|
|
|
|
temp = convert_memory_address (GET_MODE (orig), temp);
|
|
|
|
|
#endif
|
|
|
|
|
return temp;
|
|
|
|
|
}
|
|
|
|
|
else if (GET_CODE (constant) == LABEL_REF)
|
|
|
|
|
return XEXP (force_const_mem
|
|
|
|
|
(GET_MODE (orig),
|
|
|
|
|
copy_rtx_and_substitute (constant, map, for_lhs)),
|
1996-09-18 05:35:50 +00:00
|
|
|
|
0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return orig;
|
|
|
|
|
|
|
|
|
|
case CONST_DOUBLE:
|
|
|
|
|
/* We have to make a new copy of this CONST_DOUBLE because don't want
|
|
|
|
|
to use the old value of CONST_DOUBLE_MEM. Also, this may be a
|
|
|
|
|
duplicate of a CONST_DOUBLE we have already seen. */
|
|
|
|
|
if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT)
|
|
|
|
|
{
|
|
|
|
|
REAL_VALUE_TYPE d;
|
|
|
|
|
|
|
|
|
|
REAL_VALUE_FROM_CONST_DOUBLE (d, orig);
|
|
|
|
|
return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (orig));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return immed_double_const (CONST_DOUBLE_LOW (orig),
|
|
|
|
|
CONST_DOUBLE_HIGH (orig), VOIDmode);
|
|
|
|
|
|
|
|
|
|
case CONST:
|
|
|
|
|
/* Make new constant pool entry for a constant
|
|
|
|
|
that was in the pool of the inline function. */
|
|
|
|
|
if (RTX_INTEGRATED_P (orig))
|
|
|
|
|
abort ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case ASM_OPERANDS:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If a single asm insn contains multiple output operands then
|
|
|
|
|
it contains multiple ASM_OPERANDS rtx's that share the input
|
|
|
|
|
and constraint vecs. We must make sure that the copied insn
|
|
|
|
|
continues to share it. */
|
|
|
|
|
if (map->orig_asm_operands_vector == ASM_OPERANDS_INPUT_VEC (orig))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
copy = rtx_alloc (ASM_OPERANDS);
|
|
|
|
|
copy->volatil = orig->volatil;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
PUT_MODE (copy, GET_MODE (orig));
|
|
|
|
|
ASM_OPERANDS_TEMPLATE (copy) = ASM_OPERANDS_TEMPLATE (orig);
|
|
|
|
|
ASM_OPERANDS_OUTPUT_CONSTRAINT (copy)
|
|
|
|
|
= ASM_OPERANDS_OUTPUT_CONSTRAINT (orig);
|
|
|
|
|
ASM_OPERANDS_OUTPUT_IDX (copy) = ASM_OPERANDS_OUTPUT_IDX (orig);
|
|
|
|
|
ASM_OPERANDS_INPUT_VEC (copy) = map->copy_asm_operands_vector;
|
|
|
|
|
ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy)
|
|
|
|
|
= map->copy_asm_constraints_vector;
|
|
|
|
|
ASM_OPERANDS_SOURCE_FILE (copy) = ASM_OPERANDS_SOURCE_FILE (orig);
|
|
|
|
|
ASM_OPERANDS_SOURCE_LINE (copy) = ASM_OPERANDS_SOURCE_LINE (orig);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CALL:
|
|
|
|
|
/* This is given special treatment because the first
|
|
|
|
|
operand of a CALL is a (MEM ...) which may get
|
|
|
|
|
forced into a register for cse. This is undesirable
|
|
|
|
|
if function-address cse isn't wanted or if we won't do cse. */
|
|
|
|
|
#ifndef NO_FUNCTION_CSE
|
|
|
|
|
if (! (optimize && ! flag_no_function_cse))
|
|
|
|
|
#endif
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
rtx copy
|
|
|
|
|
= gen_rtx_MEM (GET_MODE (XEXP (orig, 0)),
|
|
|
|
|
copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0),
|
|
|
|
|
map, 0));
|
|
|
|
|
|
|
|
|
|
MEM_COPY_ATTRIBUTES (copy, XEXP (orig, 0));
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
gen_rtx_CALL (GET_MODE (orig), copy,
|
|
|
|
|
copy_rtx_and_substitute (XEXP (orig, 1), map, 0));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Must be ifdefed out for loop unrolling to work. */
|
|
|
|
|
case RETURN:
|
|
|
|
|
abort ();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case SET:
|
|
|
|
|
/* If this is setting fp or ap, it means that we have a nonlocal goto.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
Adjust the setting by the offset of the area we made.
|
1996-09-18 05:35:50 +00:00
|
|
|
|
If the nonlocal goto is into the current function,
|
|
|
|
|
this will result in unnecessarily bad code, but should work. */
|
|
|
|
|
if (SET_DEST (orig) == virtual_stack_vars_rtx
|
|
|
|
|
|| SET_DEST (orig) == virtual_incoming_args_rtx)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* In case a translation hasn't occurred already, make one now. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
rtx equiv_reg;
|
|
|
|
|
rtx equiv_loc;
|
|
|
|
|
HOST_WIDE_INT loc_offset;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
copy_rtx_and_substitute (SET_DEST (orig), map, for_lhs);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
equiv_reg = map->reg_map[REGNO (SET_DEST (orig))];
|
2002-02-01 18:16:02 +00:00
|
|
|
|
equiv_loc = VARRAY_CONST_EQUIV (map->const_equiv_varray,
|
|
|
|
|
REGNO (equiv_reg)).rtx;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
loc_offset
|
|
|
|
|
= GET_CODE (equiv_loc) == REG ? 0 : INTVAL (XEXP (equiv_loc, 1));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return gen_rtx_SET (VOIDmode, SET_DEST (orig),
|
|
|
|
|
force_operand
|
|
|
|
|
(plus_constant
|
2002-02-01 18:16:02 +00:00
|
|
|
|
(copy_rtx_and_substitute (SET_SRC (orig),
|
|
|
|
|
map, 0),
|
1999-08-26 09:30:50 +00:00
|
|
|
|
- loc_offset),
|
|
|
|
|
NULL_RTX));
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
return gen_rtx_SET (VOIDmode,
|
|
|
|
|
copy_rtx_and_substitute (SET_DEST (orig), map, 1),
|
|
|
|
|
copy_rtx_and_substitute (SET_SRC (orig), map, 0));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MEM:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (inlining
|
|
|
|
|
&& GET_CODE (XEXP (orig, 0)) == SYMBOL_REF
|
|
|
|
|
&& CONSTANT_POOL_ADDRESS_P (XEXP (orig, 0)))
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode const_mode
|
|
|
|
|
= get_pool_mode_for_function (inlining, XEXP (orig, 0));
|
|
|
|
|
rtx constant
|
|
|
|
|
= get_pool_constant_for_function (inlining, XEXP (orig, 0));
|
|
|
|
|
|
|
|
|
|
constant = copy_rtx_and_substitute (constant, map, 0);
|
|
|
|
|
|
|
|
|
|
/* If this was an address of a constant pool entry that itself
|
|
|
|
|
had to be placed in the constant pool, it might not be a
|
|
|
|
|
valid address. So the recursive call might have turned it
|
|
|
|
|
into a register. In that case, it isn't a constant any
|
|
|
|
|
more, so return it. This has the potential of changing a
|
|
|
|
|
MEM into a REG, but we'll assume that it safe. */
|
|
|
|
|
if (! CONSTANT_P (constant))
|
|
|
|
|
return constant;
|
|
|
|
|
|
|
|
|
|
return validize_mem (force_const_mem (const_mode, constant));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy = gen_rtx_MEM (mode, copy_rtx_and_substitute (XEXP (orig, 0),
|
|
|
|
|
map, 0));
|
1999-10-16 06:09:09 +00:00
|
|
|
|
MEM_COPY_ATTRIBUTES (copy, orig);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* If inlining and this is not for the LHS, turn off RTX_UNCHANGING_P
|
|
|
|
|
since this may be an indirect reference to a parameter and the
|
|
|
|
|
actual may not be readonly. */
|
|
|
|
|
if (inlining && !for_lhs)
|
|
|
|
|
RTX_UNCHANGING_P (copy) = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
return copy;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy = rtx_alloc (code);
|
|
|
|
|
PUT_MODE (copy, mode);
|
|
|
|
|
copy->in_struct = orig->in_struct;
|
|
|
|
|
copy->volatil = orig->volatil;
|
|
|
|
|
copy->unchanging = orig->unchanging;
|
|
|
|
|
|
|
|
|
|
format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
|
|
|
|
|
{
|
|
|
|
|
switch (*format_ptr++)
|
|
|
|
|
{
|
|
|
|
|
case '0':
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Copy this through the wide int field; that's safest. */
|
|
|
|
|
X0WINT (copy, i) = X0WINT (orig, i);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'e':
|
2002-02-01 18:16:02 +00:00
|
|
|
|
XEXP (copy, i)
|
|
|
|
|
= copy_rtx_and_substitute (XEXP (orig, i), map, for_lhs);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
/* Change any references to old-insns to point to the
|
|
|
|
|
corresponding copied insns. */
|
|
|
|
|
XEXP (copy, i) = map->insn_map[INSN_UID (XEXP (orig, i))];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
XVEC (copy, i) = XVEC (orig, i);
|
|
|
|
|
if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
|
|
|
|
|
{
|
|
|
|
|
XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
|
|
|
|
|
for (j = 0; j < XVECLEN (copy, i); j++)
|
|
|
|
|
XVECEXP (copy, i, j)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
= copy_rtx_and_substitute (XVECEXP (orig, i, j),
|
|
|
|
|
map, for_lhs);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'w':
|
|
|
|
|
XWINT (copy, i) = XWINT (orig, i);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
|
XINT (copy, i) = XINT (orig, i);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
|
XSTR (copy, i) = XSTR (orig, i);
|
|
|
|
|
break;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 't':
|
|
|
|
|
XTREE (copy, i) = XTREE (orig, i);
|
|
|
|
|
break;
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
map->orig_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (orig);
|
|
|
|
|
map->copy_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (copy);
|
|
|
|
|
map->copy_asm_constraints_vector
|
|
|
|
|
= ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Substitute known constant values into INSN, if that is valid. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
try_constants (insn, map)
|
|
|
|
|
rtx insn;
|
|
|
|
|
struct inline_remap *map;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
map->num_sets = 0;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* First try just updating addresses, then other things. This is
|
|
|
|
|
important when we have something like the store of a constant
|
|
|
|
|
into memory and we can update the memory address but the machine
|
|
|
|
|
does not support a constant source. */
|
|
|
|
|
subst_constants (&PATTERN (insn), insn, map, 1);
|
|
|
|
|
apply_change_group ();
|
|
|
|
|
subst_constants (&PATTERN (insn), insn, map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
apply_change_group ();
|
|
|
|
|
|
|
|
|
|
/* Show we don't know the value of anything stored or clobbered. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
note_stores (PATTERN (insn), mark_stores, NULL);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->last_pc_value = 0;
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
map->last_cc0_value = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Set up any constant equivalences made in this insn. */
|
|
|
|
|
for (i = 0; i < map->num_sets; i++)
|
|
|
|
|
{
|
|
|
|
|
if (GET_CODE (map->equiv_sets[i].dest) == REG)
|
|
|
|
|
{
|
|
|
|
|
int regno = REGNO (map->equiv_sets[i].dest);
|
|
|
|
|
|
1999-10-16 06:09:09 +00:00
|
|
|
|
MAYBE_EXTEND_CONST_EQUIV_VARRAY (map, regno);
|
|
|
|
|
if (VARRAY_CONST_EQUIV (map->const_equiv_varray, regno).rtx == 0
|
|
|
|
|
/* Following clause is a hack to make case work where GNU C++
|
|
|
|
|
reassigns a variable to make cse work right. */
|
|
|
|
|
|| ! rtx_equal_p (VARRAY_CONST_EQUIV (map->const_equiv_varray,
|
|
|
|
|
regno).rtx,
|
|
|
|
|
map->equiv_sets[i].equiv))
|
|
|
|
|
SET_CONST_EQUIV_DATA (map, map->equiv_sets[i].dest,
|
|
|
|
|
map->equiv_sets[i].equiv, map->const_age);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
else if (map->equiv_sets[i].dest == pc_rtx)
|
|
|
|
|
map->last_pc_value = map->equiv_sets[i].equiv;
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
else if (map->equiv_sets[i].dest == cc0_rtx)
|
|
|
|
|
map->last_cc0_value = map->equiv_sets[i].equiv;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Substitute known constants for pseudo regs in the contents of LOC,
|
|
|
|
|
which are part of INSN.
|
|
|
|
|
If INSN is zero, the substitution should always be done (this is used to
|
|
|
|
|
update DECL_RTL).
|
|
|
|
|
These changes are taken out by try_constants if the result is not valid.
|
|
|
|
|
|
|
|
|
|
Note that we are more concerned with determining when the result of a SET
|
|
|
|
|
is a constant, for further propagation, than actually inserting constants
|
|
|
|
|
into insns; cse will do the latter task better.
|
|
|
|
|
|
|
|
|
|
This function is also used to adjust address of items previously addressed
|
2002-02-01 18:16:02 +00:00
|
|
|
|
via the virtual stack variable or virtual incoming arguments registers.
|
|
|
|
|
|
|
|
|
|
If MEMONLY is nonzero, only make changes inside a MEM. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (loc, insn, map, memonly)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx *loc;
|
|
|
|
|
rtx insn;
|
|
|
|
|
struct inline_remap *map;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int memonly;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
rtx x = *loc;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i, j;
|
|
|
|
|
enum rtx_code code;
|
|
|
|
|
const char *format_ptr;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
int num_changes = num_validated_changes ();
|
|
|
|
|
rtx new = 0;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
enum machine_mode op0_mode = MAX_MACHINE_MODE;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
code = GET_CODE (x);
|
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case PC:
|
|
|
|
|
case CONST_INT:
|
|
|
|
|
case CONST_DOUBLE:
|
|
|
|
|
case SYMBOL_REF:
|
|
|
|
|
case CONST:
|
|
|
|
|
case LABEL_REF:
|
|
|
|
|
case ADDRESS:
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
case CC0:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly)
|
|
|
|
|
validate_change (insn, loc, map->last_cc0_value, 1);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case USE:
|
|
|
|
|
case CLOBBER:
|
|
|
|
|
/* The only thing we can do with a USE or CLOBBER is possibly do
|
|
|
|
|
some substitutions in a MEM within it. */
|
|
|
|
|
if (GET_CODE (XEXP (x, 0)) == MEM)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&XEXP (XEXP (x, 0), 0), insn, map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case REG:
|
|
|
|
|
/* Substitute for parms and known constants. Don't replace
|
|
|
|
|
hard regs used as user variables with constants. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly)
|
|
|
|
|
{
|
|
|
|
|
int regno = REGNO (x);
|
|
|
|
|
struct const_equiv_data *p;
|
|
|
|
|
|
|
|
|
|
if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x))
|
|
|
|
|
&& (size_t) regno < VARRAY_SIZE (map->const_equiv_varray)
|
|
|
|
|
&& (p = &VARRAY_CONST_EQUIV (map->const_equiv_varray, regno),
|
|
|
|
|
p->rtx != 0)
|
|
|
|
|
&& p->age >= map->const_age)
|
|
|
|
|
validate_change (insn, loc, p->rtx, 1);
|
|
|
|
|
}
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
case SUBREG:
|
|
|
|
|
/* SUBREG applied to something other than a reg
|
|
|
|
|
should be treated as ordinary, since that must
|
|
|
|
|
be a special hack and we don't know how to treat it specially.
|
|
|
|
|
Consider for example mulsidi3 in m68k.md.
|
|
|
|
|
Ordinary SUBREG of a REG needs this special treatment. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly && GET_CODE (SUBREG_REG (x)) == REG)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
rtx inner = SUBREG_REG (x);
|
|
|
|
|
rtx new = 0;
|
|
|
|
|
|
|
|
|
|
/* We can't call subst_constants on &SUBREG_REG (x) because any
|
|
|
|
|
constant or SUBREG wouldn't be valid inside our SUBEG. Instead,
|
|
|
|
|
see what is inside, try to form the new SUBREG and see if that is
|
2002-02-01 18:16:02 +00:00
|
|
|
|
valid. We handle two cases: extracting a full word in an
|
1996-09-18 05:35:50 +00:00
|
|
|
|
integral mode and extracting the low part. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&inner, NULL_RTX, map, 0);
|
|
|
|
|
new = simplify_gen_subreg (GET_MODE (x), inner,
|
|
|
|
|
GET_MODE (SUBREG_REG (x)),
|
|
|
|
|
SUBREG_BYTE (x));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (new)
|
|
|
|
|
validate_change (insn, loc, new, 1);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
cancel_changes (num_changes);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MEM:
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&XEXP (x, 0), insn, map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* If a memory address got spoiled, change it back. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly && insn != 0 && num_validated_changes () != num_changes
|
|
|
|
|
&& ! memory_address_p (GET_MODE (x), XEXP (x, 0)))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
cancel_changes (num_changes);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case SET:
|
|
|
|
|
{
|
|
|
|
|
/* Substitute constants in our source, and in any arguments to a
|
|
|
|
|
complex (e..g, ZERO_EXTRACT) destination, but not in the destination
|
|
|
|
|
itself. */
|
|
|
|
|
rtx *dest_loc = &SET_DEST (x);
|
|
|
|
|
rtx dest = *dest_loc;
|
|
|
|
|
rtx src, tem;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
enum machine_mode compare_mode = VOIDmode;
|
|
|
|
|
|
|
|
|
|
/* If SET_SRC is a COMPARE which subst_constants would turn into
|
|
|
|
|
COMPARE of 2 VOIDmode constants, note the mode in which comparison
|
|
|
|
|
is to be done. */
|
|
|
|
|
if (GET_CODE (SET_SRC (x)) == COMPARE)
|
|
|
|
|
{
|
|
|
|
|
src = SET_SRC (x);
|
|
|
|
|
if (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
|| dest == cc0_rtx
|
|
|
|
|
#endif
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
compare_mode = GET_MODE (XEXP (src, 0));
|
|
|
|
|
if (compare_mode == VOIDmode)
|
|
|
|
|
compare_mode = GET_MODE (XEXP (src, 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&SET_SRC (x), insn, map, memonly);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
src = SET_SRC (x);
|
|
|
|
|
|
|
|
|
|
while (GET_CODE (*dest_loc) == ZERO_EXTRACT
|
|
|
|
|
|| GET_CODE (*dest_loc) == SUBREG
|
|
|
|
|
|| GET_CODE (*dest_loc) == STRICT_LOW_PART)
|
|
|
|
|
{
|
|
|
|
|
if (GET_CODE (*dest_loc) == ZERO_EXTRACT)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&XEXP (*dest_loc, 1), insn, map, memonly);
|
|
|
|
|
subst_constants (&XEXP (*dest_loc, 2), insn, map, memonly);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
dest_loc = &XEXP (*dest_loc, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do substitute in the address of a destination in memory. */
|
|
|
|
|
if (GET_CODE (*dest_loc) == MEM)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&XEXP (*dest_loc, 0), insn, map, 0);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* Check for the case of DEST a SUBREG, both it and the underlying
|
|
|
|
|
register are less than one word, and the SUBREG has the wider mode.
|
|
|
|
|
In the case, we are really setting the underlying register to the
|
|
|
|
|
source converted to the mode of DEST. So indicate that. */
|
|
|
|
|
if (GET_CODE (dest) == SUBREG
|
|
|
|
|
&& GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD
|
|
|
|
|
&& GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD
|
|
|
|
|
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
|
|
|
|
|
<= GET_MODE_SIZE (GET_MODE (dest)))
|
|
|
|
|
&& (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)),
|
|
|
|
|
src)))
|
|
|
|
|
src = tem, dest = SUBREG_REG (dest);
|
|
|
|
|
|
|
|
|
|
/* If storing a recognizable value save it for later recording. */
|
|
|
|
|
if ((map->num_sets < MAX_RECOG_OPERANDS)
|
|
|
|
|
&& (CONSTANT_P (src)
|
|
|
|
|
|| (GET_CODE (src) == REG
|
|
|
|
|
&& (REGNO (src) == VIRTUAL_INCOMING_ARGS_REGNUM
|
|
|
|
|
|| REGNO (src) == VIRTUAL_STACK_VARS_REGNUM))
|
|
|
|
|
|| (GET_CODE (src) == PLUS
|
|
|
|
|
&& GET_CODE (XEXP (src, 0)) == REG
|
|
|
|
|
&& (REGNO (XEXP (src, 0)) == VIRTUAL_INCOMING_ARGS_REGNUM
|
|
|
|
|
|| REGNO (XEXP (src, 0)) == VIRTUAL_STACK_VARS_REGNUM)
|
|
|
|
|
&& CONSTANT_P (XEXP (src, 1)))
|
|
|
|
|
|| GET_CODE (src) == COMPARE
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
|| dest == cc0_rtx
|
|
|
|
|
#endif
|
|
|
|
|
|| (dest == pc_rtx
|
|
|
|
|
&& (src == pc_rtx || GET_CODE (src) == RETURN
|
|
|
|
|
|| GET_CODE (src) == LABEL_REF))))
|
|
|
|
|
{
|
|
|
|
|
/* Normally, this copy won't do anything. But, if SRC is a COMPARE
|
|
|
|
|
it will cause us to save the COMPARE with any constants
|
|
|
|
|
substituted, which is what we want for later. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx src_copy = copy_rtx (src);
|
|
|
|
|
map->equiv_sets[map->num_sets].equiv = src_copy;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
map->equiv_sets[map->num_sets++].dest = dest;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (compare_mode != VOIDmode
|
|
|
|
|
&& GET_CODE (src) == COMPARE
|
|
|
|
|
&& (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC
|
|
|
|
|
#ifdef HAVE_cc0
|
|
|
|
|
|| dest == cc0_rtx
|
|
|
|
|
#endif
|
|
|
|
|
)
|
|
|
|
|
&& GET_MODE (XEXP (src, 0)) == VOIDmode
|
|
|
|
|
&& GET_MODE (XEXP (src, 1)) == VOIDmode)
|
|
|
|
|
{
|
|
|
|
|
map->compare_src = src_copy;
|
|
|
|
|
map->compare_mode = compare_mode;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
format_ptr = GET_RTX_FORMAT (code);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
/* If the first operand is an expression, save its mode for later. */
|
|
|
|
|
if (*format_ptr == 'e')
|
|
|
|
|
op0_mode = GET_MODE (XEXP (x, 0));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < GET_RTX_LENGTH (code); i++)
|
|
|
|
|
{
|
|
|
|
|
switch (*format_ptr++)
|
|
|
|
|
{
|
|
|
|
|
case '0':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
|
if (XEXP (x, i))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
subst_constants (&XEXP (x, i), insn, map, memonly);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
case 'i':
|
|
|
|
|
case 's':
|
|
|
|
|
case 'w':
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'n':
|
|
|
|
|
case 't':
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (j = 0; j < XVECLEN (x, i); j++)
|
|
|
|
|
subst_constants (&XVECEXP (x, i, j), insn, map, memonly);
|
|
|
|
|
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is a commutative operation, move a constant to the second
|
|
|
|
|
operand unless the second operand is already a CONST_INT. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly
|
|
|
|
|
&& (GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
&& CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT)
|
|
|
|
|
{
|
|
|
|
|
rtx tem = XEXP (x, 0);
|
|
|
|
|
validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
|
|
|
|
|
validate_change (insn, &XEXP (x, 1), tem, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Simplify the expression in case we put in some constants. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! memonly)
|
|
|
|
|
switch (GET_RTX_CLASS (code))
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case '1':
|
|
|
|
|
if (op0_mode == MAX_MACHINE_MODE)
|
|
|
|
|
abort ();
|
|
|
|
|
new = simplify_unary_operation (code, GET_MODE (x),
|
|
|
|
|
XEXP (x, 0), op0_mode);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode op_mode = GET_MODE (XEXP (x, 0));
|
|
|
|
|
|
|
|
|
|
if (op_mode == VOIDmode)
|
|
|
|
|
op_mode = GET_MODE (XEXP (x, 1));
|
|
|
|
|
new = simplify_relational_operation (code, op_mode,
|
|
|
|
|
XEXP (x, 0), XEXP (x, 1));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#ifdef FLOAT_STORE_FLAG_VALUE
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
|
|
|
|
{
|
|
|
|
|
enum machine_mode mode = GET_MODE (x);
|
|
|
|
|
if (new == const0_rtx)
|
|
|
|
|
new = CONST0_RTX (mode);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
REAL_VALUE_TYPE val;
|
|
|
|
|
|
|
|
|
|
/* Avoid automatic aggregate initialization. */
|
|
|
|
|
val = FLOAT_STORE_FLAG_VALUE (mode);
|
|
|
|
|
new = CONST_DOUBLE_FROM_REAL_VALUE (val, mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
#endif
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '2':
|
|
|
|
|
case 'c':
|
|
|
|
|
new = simplify_binary_operation (code, GET_MODE (x),
|
|
|
|
|
XEXP (x, 0), XEXP (x, 1));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case 'b':
|
|
|
|
|
case '3':
|
|
|
|
|
if (op0_mode == MAX_MACHINE_MODE)
|
|
|
|
|
abort ();
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (code == IF_THEN_ELSE)
|
|
|
|
|
{
|
|
|
|
|
rtx op0 = XEXP (x, 0);
|
|
|
|
|
|
|
|
|
|
if (GET_RTX_CLASS (GET_CODE (op0)) == '<'
|
|
|
|
|
&& GET_MODE (op0) == VOIDmode
|
|
|
|
|
&& ! side_effects_p (op0)
|
|
|
|
|
&& XEXP (op0, 0) == map->compare_src
|
|
|
|
|
&& GET_MODE (XEXP (op0, 1)) == VOIDmode)
|
|
|
|
|
{
|
|
|
|
|
/* We have compare of two VOIDmode constants for which
|
|
|
|
|
we recorded the comparison mode. */
|
|
|
|
|
rtx temp =
|
|
|
|
|
simplify_relational_operation (GET_CODE (op0),
|
|
|
|
|
map->compare_mode,
|
|
|
|
|
XEXP (op0, 0),
|
|
|
|
|
XEXP (op0, 1));
|
|
|
|
|
|
|
|
|
|
if (temp == const0_rtx)
|
|
|
|
|
new = XEXP (x, 2);
|
|
|
|
|
else if (temp == const1_rtx)
|
|
|
|
|
new = XEXP (x, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!new)
|
|
|
|
|
new = simplify_ternary_operation (code, GET_MODE (x), op0_mode,
|
|
|
|
|
XEXP (x, 0), XEXP (x, 1),
|
|
|
|
|
XEXP (x, 2));
|
|
|
|
|
break;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
if (new)
|
|
|
|
|
validate_change (insn, loc, new, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show that register modified no longer contain known constants. We are
|
|
|
|
|
called from note_stores with parts of the new insn. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
mark_stores (dest, x, data)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
rtx dest;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
rtx x ATTRIBUTE_UNUSED;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void *data ATTRIBUTE_UNUSED;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
int regno = -1;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
enum machine_mode mode = VOIDmode;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
/* DEST is always the innermost thing set, except in the case of
|
|
|
|
|
SUBREGs of hard registers. */
|
|
|
|
|
|
|
|
|
|
if (GET_CODE (dest) == REG)
|
|
|
|
|
regno = REGNO (dest), mode = GET_MODE (dest);
|
|
|
|
|
else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
regno = REGNO (SUBREG_REG (dest));
|
|
|
|
|
if (regno < FIRST_PSEUDO_REGISTER)
|
|
|
|
|
regno += subreg_regno_offset (REGNO (SUBREG_REG (dest)),
|
|
|
|
|
GET_MODE (SUBREG_REG (dest)),
|
|
|
|
|
SUBREG_BYTE (dest),
|
|
|
|
|
GET_MODE (dest));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
mode = GET_MODE (SUBREG_REG (dest));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (regno >= 0)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
unsigned int uregno = regno;
|
|
|
|
|
unsigned int last_reg = (uregno >= FIRST_PSEUDO_REGISTER ? uregno
|
|
|
|
|
: uregno + HARD_REGNO_NREGS (uregno, mode) - 1);
|
|
|
|
|
unsigned int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* Ignore virtual stack var or virtual arg register since those
|
|
|
|
|
are handled separately. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (uregno != VIRTUAL_INCOMING_ARGS_REGNUM
|
|
|
|
|
&& uregno != VIRTUAL_STACK_VARS_REGNUM)
|
|
|
|
|
for (i = uregno; i <= last_reg; i++)
|
|
|
|
|
if ((size_t) i < VARRAY_SIZE (global_const_equiv_varray))
|
1999-10-16 06:09:09 +00:00
|
|
|
|
VARRAY_CONST_EQUIV (global_const_equiv_varray, i).rtx = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the
|
|
|
|
|
given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so
|
|
|
|
|
that it points to the node itself, thus indicating that the node is its
|
|
|
|
|
own (abstract) origin. Additionally, if the BLOCK_ABSTRACT_ORIGIN for
|
|
|
|
|
the given node is NULL, recursively descend the decl/block tree which
|
|
|
|
|
it is the root of, and for each other ..._DECL or BLOCK node contained
|
|
|
|
|
therein whose DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also
|
|
|
|
|
still NULL, set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN
|
|
|
|
|
values to point to themselves. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_block_origin_self (stmt)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree stmt;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
BLOCK_ABSTRACT_ORIGIN (stmt) = stmt;
|
|
|
|
|
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree local_decl;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (local_decl = BLOCK_VARS (stmt);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
local_decl != NULL_TREE;
|
|
|
|
|
local_decl = TREE_CHAIN (local_decl))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
set_decl_origin_self (local_decl); /* Potential recursion. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree subblock;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (subblock = BLOCK_SUBBLOCKS (stmt);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
subblock != NULL_TREE;
|
|
|
|
|
subblock = BLOCK_CHAIN (subblock))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
set_block_origin_self (subblock); /* Recurse. */
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a pointer to some ..._DECL node, if the DECL_ABSTRACT_ORIGIN for
|
|
|
|
|
the given ..._DECL node is NULL, set the DECL_ABSTRACT_ORIGIN for the
|
|
|
|
|
node to so that it points to the node itself, thus indicating that the
|
|
|
|
|
node represents its own (abstract) origin. Additionally, if the
|
|
|
|
|
DECL_ABSTRACT_ORIGIN for the given node is NULL, recursively descend
|
|
|
|
|
the decl/block tree of which the given node is the root of, and for
|
|
|
|
|
each other ..._DECL or BLOCK node contained therein whose
|
|
|
|
|
DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also still NULL,
|
|
|
|
|
set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN values to
|
|
|
|
|
point to themselves. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
1996-09-18 05:35:50 +00:00
|
|
|
|
set_decl_origin_self (decl)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree decl;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
DECL_ABSTRACT_ORIGIN (decl) = decl;
|
|
|
|
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree arg;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
|
|
|
|
|
DECL_ABSTRACT_ORIGIN (arg) = arg;
|
|
|
|
|
if (DECL_INITIAL (decl) != NULL_TREE
|
|
|
|
|
&& DECL_INITIAL (decl) != error_mark_node)
|
|
|
|
|
set_block_origin_self (DECL_INITIAL (decl));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a pointer to some BLOCK node, and a boolean value to set the
|
|
|
|
|
"abstract" flags to, set that value into the BLOCK_ABSTRACT flag for
|
|
|
|
|
the given block, and for all local decls and all local sub-blocks
|
|
|
|
|
(recursively) which are contained therein. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_block_abstract_flags (stmt, setting)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree stmt;
|
|
|
|
|
int setting;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree local_decl;
|
|
|
|
|
tree subblock;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
BLOCK_ABSTRACT (stmt) = setting;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
for (local_decl = BLOCK_VARS (stmt);
|
|
|
|
|
local_decl != NULL_TREE;
|
|
|
|
|
local_decl = TREE_CHAIN (local_decl))
|
|
|
|
|
set_decl_abstract_flags (local_decl, setting);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
for (subblock = BLOCK_SUBBLOCKS (stmt);
|
|
|
|
|
subblock != NULL_TREE;
|
|
|
|
|
subblock = BLOCK_CHAIN (subblock))
|
|
|
|
|
set_block_abstract_flags (subblock, setting);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a pointer to some ..._DECL node, and a boolean value to set the
|
|
|
|
|
"abstract" flags to, set that value into the DECL_ABSTRACT flag for the
|
|
|
|
|
given decl, and (in the case where the decl is a FUNCTION_DECL) also
|
|
|
|
|
set the abstract flags for all of the parameters, local vars, local
|
|
|
|
|
blocks and sub-blocks (recursively) to the same setting. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
set_decl_abstract_flags (decl, setting)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree decl;
|
|
|
|
|
int setting;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
|
|
|
|
DECL_ABSTRACT (decl) = setting;
|
|
|
|
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree arg;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
|
|
|
|
|
DECL_ABSTRACT (arg) = setting;
|
|
|
|
|
if (DECL_INITIAL (decl) != NULL_TREE
|
|
|
|
|
&& DECL_INITIAL (decl) != error_mark_node)
|
|
|
|
|
set_block_abstract_flags (DECL_INITIAL (decl), setting);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output the assembly language code for the function FNDECL
|
|
|
|
|
from its DECL_SAVED_INSNS. Used for inline functions that are output
|
|
|
|
|
at end of compilation instead of where they came in the source. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
output_inline_function (fndecl)
|
|
|
|
|
tree fndecl;
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct function *old_cfun = cfun;
|
|
|
|
|
enum debug_info_type old_write_symbols = write_symbols;
|
|
|
|
|
struct gcc_debug_hooks *old_debug_hooks = debug_hooks;
|
|
|
|
|
struct function *f = DECL_SAVED_INSNS (fndecl);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun = f;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
current_function_decl = fndecl;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
clear_emit_caches ();
|
|
|
|
|
|
|
|
|
|
set_new_last_label_num (f->inl_max_label_num);
|
|
|
|
|
|
|
|
|
|
/* We're not deferring this any longer. */
|
|
|
|
|
DECL_DEFER_OUTPUT (fndecl) = 0;
|
|
|
|
|
|
|
|
|
|
/* If requested, suppress debugging information. */
|
|
|
|
|
if (f->no_debugging_symbols)
|
|
|
|
|
{
|
|
|
|
|
write_symbols = NO_DEBUG;
|
|
|
|
|
debug_hooks = &do_nothing_debug_hooks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compile this function all the way down to assembly code. As a
|
|
|
|
|
side effect this destroys the saved RTL representation, but
|
|
|
|
|
that's okay, because we don't need to inline this anymore. */
|
|
|
|
|
rest_of_compilation (fndecl);
|
|
|
|
|
DECL_INLINE (fndecl) = 0;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun = old_cfun;
|
|
|
|
|
current_function_decl = old_cfun ? old_cfun->decl : 0;
|
|
|
|
|
write_symbols = old_write_symbols;
|
|
|
|
|
debug_hooks = old_debug_hooks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Functions to keep track of the values hard regs had at the start of
|
|
|
|
|
the function. */
|
|
|
|
|
|
|
|
|
|
rtx
|
|
|
|
|
get_hard_reg_initial_reg (fun, reg)
|
|
|
|
|
struct function *fun;
|
|
|
|
|
rtx reg;
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return NULL_RTX;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < ivs->num_entries; i++)
|
|
|
|
|
if (rtx_equal_p (ivs->entries[i].pseudo, reg))
|
|
|
|
|
return ivs->entries[i].hard_reg;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return NULL_RTX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtx
|
|
|
|
|
has_func_hard_reg_initial_val (fun, reg)
|
|
|
|
|
struct function *fun;
|
|
|
|
|
rtx reg;
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return NULL_RTX;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < ivs->num_entries; i++)
|
|
|
|
|
if (rtx_equal_p (ivs->entries[i].hard_reg, reg))
|
|
|
|
|
return ivs->entries[i].pseudo;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return NULL_RTX;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx
|
|
|
|
|
get_func_hard_reg_initial_val (fun, reg)
|
|
|
|
|
struct function *fun;
|
|
|
|
|
rtx reg;
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
|
|
|
|
|
rtx rv = has_func_hard_reg_initial_val (fun, reg);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (rv)
|
|
|
|
|
return rv;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
{
|
|
|
|
|
fun->hard_reg_initial_vals = (void *) xmalloc (sizeof (initial_value_struct));
|
|
|
|
|
ivs = fun->hard_reg_initial_vals;
|
|
|
|
|
ivs->num_entries = 0;
|
|
|
|
|
ivs->max_entries = 5;
|
|
|
|
|
ivs->entries = (initial_value_pair *) xmalloc (5 * sizeof (initial_value_pair));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs->num_entries >= ivs->max_entries)
|
|
|
|
|
{
|
|
|
|
|
ivs->max_entries += 5;
|
|
|
|
|
ivs->entries =
|
|
|
|
|
(initial_value_pair *) xrealloc (ivs->entries,
|
|
|
|
|
ivs->max_entries
|
|
|
|
|
* sizeof (initial_value_pair));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
ivs->entries[ivs->num_entries].hard_reg = reg;
|
|
|
|
|
ivs->entries[ivs->num_entries].pseudo = gen_reg_rtx (GET_MODE (reg));
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return ivs->entries[ivs->num_entries++].pseudo;
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx
|
|
|
|
|
get_hard_reg_initial_val (mode, regno)
|
|
|
|
|
enum machine_mode mode;
|
|
|
|
|
int regno;
|
|
|
|
|
{
|
|
|
|
|
return get_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx
|
|
|
|
|
has_hard_reg_initial_val (mode, regno)
|
|
|
|
|
enum machine_mode mode;
|
|
|
|
|
int regno;
|
|
|
|
|
{
|
|
|
|
|
return has_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno));
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
|
|
|
|
mark_hard_reg_initial_vals (fun)
|
|
|
|
|
struct function *fun;
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = fun->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < ivs->num_entries; i ++)
|
1996-09-18 05:35:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
ggc_mark_rtx (ivs->entries[i].hard_reg);
|
|
|
|
|
ggc_mark_rtx (ivs->entries[i].pseudo);
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
|
|
|
|
setup_initial_hard_reg_value_integration (inl_f, remap)
|
|
|
|
|
struct function *inl_f;
|
|
|
|
|
struct inline_remap *remap;
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = inl_f->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < ivs->num_entries; i ++)
|
|
|
|
|
remap->reg_map[REGNO (ivs->entries[i].pseudo)]
|
|
|
|
|
= get_func_hard_reg_initial_val (cfun, ivs->entries[i].hard_reg);
|
|
|
|
|
}
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
|
|
|
|
emit_initial_value_sets ()
|
|
|
|
|
{
|
|
|
|
|
struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
|
|
|
|
rtx seq;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
start_sequence ();
|
|
|
|
|
for (i = 0; i < ivs->num_entries; i++)
|
|
|
|
|
emit_move_insn (ivs->entries[i].pseudo, ivs->entries[i].hard_reg);
|
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
|
|
|
|
|
|
|
|
|
emit_insns_after (seq, get_insns ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the backend knows where to allocate pseudos for hard
|
|
|
|
|
register initial values, register these allocations now. */
|
|
|
|
|
void
|
|
|
|
|
allocate_initial_values (reg_equiv_memory_loc)
|
|
|
|
|
rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED;
|
|
|
|
|
{
|
|
|
|
|
#ifdef ALLOCATE_INITIAL_VALUE
|
|
|
|
|
struct initial_value_struct *ivs = cfun->hard_reg_initial_vals;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (ivs == 0)
|
|
|
|
|
return;
|
1996-09-18 05:35:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < ivs->num_entries; i++)
|
|
|
|
|
{
|
|
|
|
|
int regno = REGNO (ivs->entries[i].pseudo);
|
|
|
|
|
rtx x = ALLOCATE_INITIAL_VALUE (ivs->entries[i].hard_reg);
|
|
|
|
|
|
|
|
|
|
if (x == NULL_RTX || REG_N_SETS (REGNO (ivs->entries[i].pseudo)) > 1)
|
|
|
|
|
; /* Do nothing. */
|
|
|
|
|
else if (GET_CODE (x) == MEM)
|
|
|
|
|
reg_equiv_memory_loc[regno] = x;
|
|
|
|
|
else if (GET_CODE (x) == REG)
|
|
|
|
|
{
|
|
|
|
|
reg_renumber[regno] = REGNO (x);
|
|
|
|
|
/* Poke the regno right into regno_reg_rtx
|
|
|
|
|
so that even fixed regs are accepted. */
|
|
|
|
|
REGNO (ivs->entries[i].pseudo) = REGNO (x);
|
|
|
|
|
}
|
|
|
|
|
else abort ();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
1996-09-18 05:35:50 +00:00
|
|
|
|
}
|