/* Emit RTL for the GCC expander.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
/* Middle-to-low level generation of rtx code and insns.
This file contains support functions for creating rtl expressions
and manipulating them in the doubly-linked chain of insns.
The patterns of the insns are created by machine-dependent
routines in insn-emit.c, which is generated automatically from
the machine description. These routines make the individual rtx's
of the pattern with `gen_rtx_fmt_ee' and others in genrtl.[ch],
which are automatically generated from rtl.def; what is machine
dependent is the kind of rtx's they make and what arguments they
use. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "flags.h"
#include "function.h"
#include "expr.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hashtab.h"
#include "insn-config.h"
#include "recog.h"
#include "real.h"
#include "bitmap.h"
#include "basic-block.h"
#include "ggc.h"
#include "debug.h"
#include "langhooks.h"
#include "tree-pass.h"
/* Commonly used modes. */
enum machine_mode byte_mode; /* Mode whose width is BITS_PER_UNIT. */
enum machine_mode word_mode; /* Mode whose width is BITS_PER_WORD. */
enum machine_mode double_mode; /* Mode whose width is DOUBLE_TYPE_SIZE. */
enum machine_mode ptr_mode; /* Mode whose width is POINTER_SIZE. */
/* This is *not* reset after each function. It gives each CODE_LABEL
in the entire compilation a unique label number. */
static GTY(()) int label_num = 1;
/* Nonzero means do not generate NOTEs for source line numbers. */
static int no_line_numbers;
/* Commonly used rtx's, so that we only need space for one copy.
These are initialized once for the entire compilation.
All of these are unique; no other rtx-object will be equal to any
of these. */
rtx global_rtl[GR_MAX];
/* Commonly used RTL for hard registers. These objects are not necessarily
unique, so we allocate them separately from global_rtl. They are
initialized once per compilation unit, then copied into regno_reg_rtx
at the beginning of each function. */
static GTY(()) rtx static_regno_reg_rtx[FIRST_PSEUDO_REGISTER];
/* We record floating-point CONST_DOUBLEs in each floating-point mode for
the values of 0, 1, and 2. For the integer entries and VOIDmode, we
record a copy of const[012]_rtx. */
rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE];
rtx const_true_rtx;
REAL_VALUE_TYPE dconst0;
REAL_VALUE_TYPE dconst1;
REAL_VALUE_TYPE dconst2;
REAL_VALUE_TYPE dconst3;
REAL_VALUE_TYPE dconst10;
REAL_VALUE_TYPE dconstm1;
REAL_VALUE_TYPE dconstm2;
REAL_VALUE_TYPE dconsthalf;
REAL_VALUE_TYPE dconstthird;
REAL_VALUE_TYPE dconstpi;
REAL_VALUE_TYPE dconste;
/* All references to the following fixed hard registers go through
these unique rtl objects. On machines where the frame-pointer and
arg-pointer are the same register, they use the same unique object.
After register allocation, other rtl objects which used to be pseudo-regs
may be clobbered to refer to the frame-pointer register.
But references that were originally to the frame-pointer can be
distinguished from the others because they contain frame_pointer_rtx.
When to use frame_pointer_rtx and hard_frame_pointer_rtx is a little
tricky: until register elimination has taken place hard_frame_pointer_rtx
should be used if it is being set, and frame_pointer_rtx otherwise. After
register elimination hard_frame_pointer_rtx should always be used.
On machines where the two registers are same (most) then these are the
same.
In an inline procedure, the stack and frame pointer rtxs may not be
used for anything else. */
rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */
rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */
rtx pic_offset_table_rtx; /* (REG:Pmode PIC_OFFSET_TABLE_REGNUM) */
/* This is used to implement __builtin_return_address for some machines.
See for instance the MIPS port. */
rtx return_address_pointer_rtx; /* (REG:Pmode RETURN_ADDRESS_POINTER_REGNUM) */
/* We make one copy of (const_int C) where C is in
[- MAX_SAVED_CONST_INT, MAX_SAVED_CONST_INT]
to save space during the compilation and simplify comparisons of
integers. */
rtx const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1];
/* A hash table storing CONST_INTs whose absolute value is greater
than MAX_SAVED_CONST_INT. */
static GTY ((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
htab_t const_int_htab;
/* A hash table storing memory attribute structures. */
static GTY ((if_marked ("ggc_marked_p"), param_is (struct mem_attrs)))
htab_t mem_attrs_htab;
/* A hash table storing register attribute structures. */
static GTY ((if_marked ("ggc_marked_p"), param_is (struct reg_attrs)))
htab_t reg_attrs_htab;
/* A hash table storing all CONST_DOUBLEs. */
static GTY ((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
htab_t const_double_htab;
#define first_insn (cfun->emit->x_first_insn)
#define last_insn (cfun->emit->x_last_insn)
#define cur_insn_uid (cfun->emit->x_cur_insn_uid)
#define last_location (cfun->emit->x_last_location)
#define first_label_num (cfun->emit->x_first_label_num)
static rtx make_call_insn_raw (rtx);
static rtx find_line_note (rtx);
static rtx change_address_1 (rtx, enum machine_mode, rtx, int);
static void unshare_all_decls (tree);
static void reset_used_decls (tree);
static void mark_label_nuses (rtx);
static hashval_t const_int_htab_hash (const void *);
static int const_int_htab_eq (const void *, const void *);
static hashval_t const_double_htab_hash (const void *);
static int const_double_htab_eq (const void *, const void *);
static rtx lookup_const_double (rtx);
static hashval_t mem_attrs_htab_hash (const void *);
static int mem_attrs_htab_eq (const void *, const void *);
static mem_attrs *get_mem_attrs (HOST_WIDE_INT, tree, rtx, rtx, unsigned int,
enum machine_mode);
static hashval_t reg_attrs_htab_hash (const void *);
static int reg_attrs_htab_eq (const void *, const void *);
static reg_attrs *get_reg_attrs (tree, int);
static tree component_ref_for_mem_expr (tree);
static rtx gen_const_vector (enum machine_mode, int);
static void copy_rtx_if_shared_1 (rtx *orig);
/* Probability of the conditional branch currently proceeded by try_split.
Set to -1 otherwise. */
int split_branch_probability = -1;
/* Returns a hash code for X (which is a really a CONST_INT). */
static hashval_t
const_int_htab_hash (const void *x)
{
return (hashval_t) INTVAL ((rtx) x);
}
/* Returns nonzero if the value represented by X (which is really a
CONST_INT) is the same as that given by Y (which is really a
HOST_WIDE_INT *). */
static int
const_int_htab_eq (const void *x, const void *y)
{
return (INTVAL ((rtx) x) == *((const HOST_WIDE_INT *) y));
}
/* Returns a hash code for X (which is really a CONST_DOUBLE). */
static hashval_t
const_double_htab_hash (const void *x)
{
rtx value = (rtx) x;
hashval_t h;
if (GET_MODE (value) == VOIDmode)
h = CONST_DOUBLE_LOW (value) ^ CONST_DOUBLE_HIGH (value);
else
{
h = real_hash (CONST_DOUBLE_REAL_VALUE (value));
/* MODE is used in the comparison, so it should be in the hash. */
h ^= GET_MODE (value);
}
return h;
}
/* Returns nonzero if the value represented by X (really a ...)
is the same as that represented by Y (really a ...) */
static int
const_double_htab_eq (const void *x, const void *y)
{
rtx a = (rtx)x, b = (rtx)y;
if (GET_MODE (a) != GET_MODE (b))
return 0;
if (GET_MODE (a) == VOIDmode)
return (CONST_DOUBLE_LOW (a) == CONST_DOUBLE_LOW (b)
&& CONST_DOUBLE_HIGH (a) == CONST_DOUBLE_HIGH (b));
else
return real_identical (CONST_DOUBLE_REAL_VALUE (a),
CONST_DOUBLE_REAL_VALUE (b));
}
/* Returns a hash code for X (which is a really a mem_attrs *). */
static hashval_t
mem_attrs_htab_hash (const void *x)
{
mem_attrs *p = (mem_attrs *) x;
return (p->alias ^ (p->align * 1000)
^ ((p->offset ? INTVAL (p->offset) : 0) * 50000)
^ ((p->size ? INTVAL (p->size) : 0) * 2500000)
^ (size_t) iterative_hash_expr (p->expr, 0));
}
/* Returns nonzero if the value represented by X (which is really a
mem_attrs *) is the same as that given by Y (which is also really a
mem_attrs *). */
static int
mem_attrs_htab_eq (const void *x, const void *y)
{
mem_attrs *p = (mem_attrs *) x;
mem_attrs *q = (mem_attrs *) y;
return (p->alias == q->alias && p->offset == q->offset
&& p->size == q->size && p->align == q->align
&& (p->expr == q->expr
|| (p->expr != NULL_TREE && q->expr != NULL_TREE
&& operand_equal_p (p->expr, q->expr, 0))));
}
/* Allocate a new mem_attrs structure and insert it into the hash table if
one identical to it is not already in the table. We are doing this for
MEM of mode MODE. */
static mem_attrs *
get_mem_attrs (HOST_WIDE_INT alias, tree expr, rtx offset, rtx size,
unsigned int align, enum machine_mode mode)
{
mem_attrs attrs;
void **slot;
/* If everything is the default, we can just return zero.
This must match what the corresponding MEM_* macros return when the
field is not present. */
if (alias == 0 && expr == 0 && offset == 0
&& (size == 0
|| (mode != BLKmode && GET_MODE_SIZE (mode) == INTVAL (size)))
&& (STRICT_ALIGNMENT && mode != BLKmode
? align == GET_MODE_ALIGNMENT (mode) : align == BITS_PER_UNIT))
return 0;
attrs.alias = alias;
attrs.expr = expr;
attrs.offset = offset;
attrs.size = size;
attrs.align = align;
slot = htab_find_slot (mem_attrs_htab, &attrs, INSERT);
if (*slot == 0)
{
*slot = ggc_alloc (sizeof (mem_attrs));
memcpy (*slot, &attrs, sizeof (mem_attrs));
}
return *slot;
}
/* Returns a hash code for X (which is a really a reg_attrs *). */
static hashval_t
reg_attrs_htab_hash (const void *x)
{
reg_attrs *p = (reg_attrs *) x;
return ((p->offset * 1000) ^ (long) p->decl);
}
/* Returns nonzero if the value represented by X (which is really a
reg_attrs *) is the same as that given by Y (which is also really a
reg_attrs *). */
static int
reg_attrs_htab_eq (const void *x, const void *y)
{
reg_attrs *p = (reg_attrs *) x;
reg_attrs *q = (reg_attrs *) y;
return (p->decl == q->decl && p->offset == q->offset);
}
/* Allocate a new reg_attrs structure and insert it into the hash table if
one identical to it is not already in the table. We are doing this for
MEM of mode MODE. */
static reg_attrs *
get_reg_attrs (tree decl, int offset)
{
reg_attrs attrs;
void **slot;
/* If everything is the default, we can just return zero. */
if (decl == 0 && offset == 0)
return 0;
attrs.decl = decl;
attrs.offset = offset;
slot = htab_find_slot (reg_attrs_htab, &attrs, INSERT);
if (*slot == 0)
{
*slot = ggc_alloc (sizeof (reg_attrs));
memcpy (*slot, &attrs, sizeof (reg_attrs));
}
return *slot;
}
/* Generate a new REG rtx. Make sure ORIGINAL_REGNO is set properly, and
don't attempt to share with the various global pieces of rtl (such as
frame_pointer_rtx). */
rtx
gen_raw_REG (enum machine_mode mode, int regno)
{
rtx x = gen_rtx_raw_REG (mode, regno);
ORIGINAL_REGNO (x) = regno;
return x;
}
/* There are some RTL codes that require special attention; the generation
functions do the raw handling. If you add to this list, modify
special_rtx in gengenrtl.c as well. */
rtx
gen_rtx_CONST_INT (enum machine_mode mode ATTRIBUTE_UNUSED, HOST_WIDE_INT arg)
{
void **slot;
if (arg >= - MAX_SAVED_CONST_INT && arg <= MAX_SAVED_CONST_INT)
return const_int_rtx[arg + MAX_SAVED_CONST_INT];
#if STORE_FLAG_VALUE != 1 && STORE_FLAG_VALUE != -1
if (const_true_rtx && arg == STORE_FLAG_VALUE)
return const_true_rtx;
#endif
/* Look up the CONST_INT in the hash table. */
slot = htab_find_slot_with_hash (const_int_htab, &arg,
(hashval_t) arg, INSERT);
if (*slot == 0)
*slot = gen_rtx_raw_CONST_INT (VOIDmode, arg);
return (rtx) *slot;
}
rtx
gen_int_mode (HOST_WIDE_INT c, enum machine_mode mode)
{
return GEN_INT (trunc_int_for_mode (c, mode));
}
/* CONST_DOUBLEs might be created from pairs of integers, or from
REAL_VALUE_TYPEs. Also, their length is known only at run time,
so we cannot use gen_rtx_raw_CONST_DOUBLE. */
/* Determine whether REAL, a CONST_DOUBLE, already exists in the
hash table. If so, return its counterpart; otherwise add it
to the hash table and return it. */
static rtx
lookup_const_double (rtx real)
{
void **slot = htab_find_slot (const_double_htab, real, INSERT);
if (*slot == 0)
*slot = real;
return (rtx) *slot;
}
/* Return a CONST_DOUBLE rtx for a floating-point value specified by
VALUE in mode MODE. */
rtx
const_double_from_real_value (REAL_VALUE_TYPE value, enum machine_mode mode)
{
rtx real = rtx_alloc (CONST_DOUBLE);
PUT_MODE (real, mode);
real->u.rv = value;
return lookup_const_double (real);
}
/* Return a CONST_DOUBLE or CONST_INT for a value specified as a pair
of ints: I0 is the low-order word and I1 is the high-order word.
Do not use this routine for non-integer modes; convert to
REAL_VALUE_TYPE and use CONST_DOUBLE_FROM_REAL_VALUE. */
rtx
immed_double_const (HOST_WIDE_INT i0, HOST_WIDE_INT i1, enum machine_mode mode)
{
rtx value;
unsigned int i;
/* There are the following cases (note that there are no modes with
HOST_BITS_PER_WIDE_INT < GET_MODE_BITSIZE (mode) < 2 * HOST_BITS_PER_WIDE_INT):
1) If GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT, then we use
gen_int_mode.
2) GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT, but the value of
the integer fits into HOST_WIDE_INT anyway (i.e., i1 consists only
from copies of the sign bit, and sign of i0 and i1 are the same), then
we return a CONST_INT for i0.
3) Otherwise, we create a CONST_DOUBLE for i0 and i1. */
if (mode != VOIDmode)
{
gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
|| GET_MODE_CLASS (mode) == MODE_PARTIAL_INT
/* We can get a 0 for an error mark. */
|| GET_MODE_CLASS (mode) == MODE_VECTOR_INT
|| GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT);
if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
return gen_int_mode (i0, mode);
gcc_assert (GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT);
}
/* If this integer fits in one word, return a CONST_INT. */
if ((i1 == 0 && i0 >= 0) || (i1 == ~0 && i0 < 0))
return GEN_INT (i0);
/* We use VOIDmode for integers. */
value = rtx_alloc (CONST_DOUBLE);
PUT_MODE (value, VOIDmode);
CONST_DOUBLE_LOW (value) = i0;
CONST_DOUBLE_HIGH (value) = i1;
for (i = 2; i < (sizeof CONST_DOUBLE_FORMAT - 1); i++)
XWINT (value, i) = 0;
return lookup_const_double (value);
}
rtx
gen_rtx_REG (enum machine_mode mode, unsigned int regno)
{
/* In case the MD file explicitly references the frame pointer, have
all such references point to the same frame pointer. This is
used during frame pointer elimination to distinguish the explicit
references to these registers from pseudos that happened to be
assigned to them.
If we have eliminated the frame pointer or arg pointer, we will
be using it as a normal register, for example as a spill
register. In such cases, we might be accessing it in a mode that
is not Pmode and therefore cannot use the pre-allocated rtx.
Also don't do this when we are making new REGs in reload, since
we don't want to get confused with the real pointers. */
if (mode == Pmode && !reload_in_progress)
{
if (regno == FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed))
return frame_pointer_rtx;
#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
if (regno == HARD_FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed))
return hard_frame_pointer_rtx;
#endif
#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM && HARD_FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
if (regno == ARG_POINTER_REGNUM)
return arg_pointer_rtx;
#endif
#ifdef RETURN_ADDRESS_POINTER_REGNUM
if (regno == RETURN_ADDRESS_POINTER_REGNUM)
return return_address_pointer_rtx;
#endif
if (regno == (unsigned) PIC_OFFSET_TABLE_REGNUM
&& fixed_regs[PIC_OFFSET_TABLE_REGNUM])
return pic_offset_table_rtx;
if (regno == STACK_POINTER_REGNUM)
return stack_pointer_rtx;
}
#if 0
/* If the per-function register table has been set up, try to re-use
an existing entry in that table to avoid useless generation of RTL.
This code is disabled for now until we can fix the various backends
which depend on having non-shared hard registers in some cases. Long
term we want to re-enable this code as it can significantly cut down
on the amount of useless RTL that gets generated.
We'll also need to fix some code that runs after reload that wants to
set ORIGINAL_REGNO. */
if (cfun
&& cfun->emit
&& regno_reg_rtx
&& regno < FIRST_PSEUDO_REGISTER
&& reg_raw_mode[regno] == mode)
return regno_reg_rtx[regno];
#endif
return gen_raw_REG (mode, regno);
}
rtx
gen_rtx_MEM (enum machine_mode mode, rtx addr)
{
rtx rt = gen_rtx_raw_MEM (mode, addr);
/* This field is not cleared by the mere allocation of the rtx, so
we clear it here. */
MEM_ATTRS (rt) = 0;
return rt;
}
/* Generate a memory referring to non-trapping constant memory. */
rtx
gen_const_mem (enum machine_mode mode, rtx addr)
{
rtx mem = gen_rtx_MEM (mode, addr);
MEM_READONLY_P (mem) = 1;
MEM_NOTRAP_P (mem) = 1;
return mem;
}
/* Generate a MEM referring to fixed portions of the frame, e.g., register
save areas. */
rtx
gen_frame_mem (enum machine_mode mode, rtx addr)
{
rtx mem = gen_rtx_MEM (mode, addr);
MEM_NOTRAP_P (mem) = 1;
set_mem_alias_set (mem, get_frame_alias_set ());
return mem;
}
/* Generate a MEM referring to a temporary use of the stack, not part
of the fixed stack frame. For example, something which is pushed
by a target splitter. */
rtx
gen_tmp_stack_mem (enum machine_mode mode, rtx addr)
{
rtx mem = gen_rtx_MEM (mode, addr);
MEM_NOTRAP_P (mem) = 1;
if (!current_function_calls_alloca)
set_mem_alias_set (mem, get_frame_alias_set ());
return mem;
}
/* We want to create (subreg:OMODE (obj:IMODE) OFFSET). Return true if
this construct would be valid, and false otherwise. */
bool
validate_subreg (enum machine_mode omode, enum machine_mode imode,
rtx reg, unsigned int offset)
{
unsigned int isize = GET_MODE_SIZE (imode);
unsigned int osize = GET_MODE_SIZE (omode);
/* All subregs must be aligned. */
if (offset % osize != 0)
return false;
/* The subreg offset cannot be outside the inner object. */
if (offset >= isize)
return false;
/* ??? This should not be here. Temporarily continue to allow word_mode
subregs of anything. The most common offender is (subreg:SI (reg:DF)).
Generally, backends are doing something sketchy but it'll take time to
fix them all. */
if (omode == word_mode)
;
/* ??? Similarly, e.g. with (subreg:DF (reg:TI)). Though store_bit_field
is the culprit here, and not the backends. */
else if (osize >= UNITS_PER_WORD && isize >= osize)
;
/* Allow component subregs of complex and vector. Though given the below
extraction rules, it's not always clear what that means. */
else if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
&& GET_MODE_INNER (imode) == omode)
;
/* ??? x86 sse code makes heavy use of *paradoxical* vector subregs,
i.e. (subreg:V4SF (reg:SF) 0). This surely isn't the cleanest way to
represent this. It's questionable if this ought to be represented at
all -- why can't this all be hidden in post-reload splitters that make
arbitrarily mode changes to the registers themselves. */
else if (VECTOR_MODE_P (omode) && GET_MODE_INNER (omode) == imode)
;
/* Subregs involving floating point modes are not allowed to
change size. Therefore (subreg:DI (reg:DF) 0) is fine, but
(subreg:SI (reg:DF) 0) isn't. */
else if (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))
{
if (isize != osize)
return false;
}
/* Paradoxical subregs must have offset zero. */
if (osize > isize)
return offset == 0;
/* This is a normal subreg. Verify that the offset is representable. */
/* For hard registers, we already have most of these rules collected in
subreg_offset_representable_p. */
if (reg && REG_P (reg) && HARD_REGISTER_P (reg))
{
unsigned int regno = REGNO (reg);
#ifdef CANNOT_CHANGE_MODE_CLASS
if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
&& GET_MODE_INNER (imode) == omode)
;
else if (REG_CANNOT_CHANGE_MODE_P (regno, imode, omode))
return false;
#endif
return subreg_offset_representable_p (regno, imode, offset, omode);
}
/* For pseudo registers, we want most of the same checks. Namely:
If the register no larger than a word, the subreg must be lowpart.
If the register is larger than a word, the subreg must be the lowpart
of a subword. A subreg does *not* perform arbitrary bit extraction.
Given that we've already checked mode/offset alignment, we only have
to check subword subregs here. */
if (osize < UNITS_PER_WORD)
{
enum machine_mode wmode = isize > UNITS_PER_WORD ? word_mode : imode;
unsigned int low_off = subreg_lowpart_offset (omode, wmode);
if (offset % UNITS_PER_WORD != low_off)
return false;
}
return true;
}
rtx
gen_rtx_SUBREG (enum machine_mode mode, rtx reg, int offset)
{
gcc_assert (validate_subreg (mode, GET_MODE (reg), reg, offset));
return gen_rtx_raw_SUBREG (mode, reg, offset);
}
/* Generate a SUBREG representing the least-significant part of REG if MODE
is smaller than mode of REG, otherwise paradoxical SUBREG. */
rtx
gen_lowpart_SUBREG (enum machine_mode mode, rtx reg)
{
enum machine_mode inmode;
inmode = GET_MODE (reg);
if (inmode == VOIDmode)
inmode = mode;
return gen_rtx_SUBREG (mode, reg,
subreg_lowpart_offset (mode, inmode));
}
/* gen_rtvec (n, [rt1, ..., rtn])
**
** This routine creates an rtvec and stores within it the
** pointers to rtx's which are its arguments.
*/
/*VARARGS1*/
rtvec
gen_rtvec (int n, ...)
{
int i, save_n;
rtx *vector;
va_list p;
va_start (p, n);
if (n == 0)
return NULL_RTVEC; /* Don't allocate an empty rtvec... */
vector = alloca (n * sizeof (rtx));
for (i = 0; i < n; i++)
vector[i] = va_arg (p, rtx);
/* The definition of VA_* in K&R C causes `n' to go out of scope. */
save_n = n;
va_end (p);
return gen_rtvec_v (save_n, vector);
}
rtvec
gen_rtvec_v (int n, rtx *argp)
{
int i;
rtvec rt_val;
if (n == 0)
return NULL_RTVEC; /* Don't allocate an empty rtvec... */
rt_val = rtvec_alloc (n); /* Allocate an rtvec... */
for (i = 0; i < n; i++)
rt_val->elem[i] = *argp++;
return rt_val;
}
/* Generate a REG rtx for a new pseudo register of mode MODE.
This pseudo is assigned the next sequential register number. */
rtx
gen_reg_rtx (enum machine_mode mode)
{
struct function *f = cfun;
rtx val;
/* Don't let anything called after initial flow analysis create new
registers. */
gcc_assert (!no_new_pseudos);
if (generating_concat_p
&& (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
|| GET_MODE_CLASS (mode) == MODE_COMPLEX_INT))
{
/* For complex modes, don't make a single pseudo.
Instead, make a CONCAT of two pseudos.
This allows noncontiguous allocation of the real and imaginary parts,
which makes much better code. Besides, allocating DCmode
pseudos overstrains reload on some machines like the 386. */
rtx realpart, imagpart;
enum machine_mode partmode = GET_MODE_INNER (mode);
realpart = gen_reg_rtx (partmode);
imagpart = gen_reg_rtx (partmode);
return gen_rtx_CONCAT (mode, realpart, imagpart);
}
/* Make sure regno_pointer_align, and regno_reg_rtx are large
enough to have an element for this pseudo reg number. */
if (reg_rtx_no == f->emit->regno_pointer_align_length)
{
int old_size = f->emit->regno_pointer_align_length;
char *new;
rtx *new1;
new = ggc_realloc (f->emit->regno_pointer_align, old_size * 2);
memset (new + old_size, 0, old_size);
f->emit->regno_pointer_align = (unsigned char *) new;
new1 = ggc_realloc (f->emit->x_regno_reg_rtx,
old_size * 2 * sizeof (rtx));
memset (new1 + old_size, 0, old_size * sizeof (rtx));
regno_reg_rtx = new1;
f->emit->regno_pointer_align_length = old_size * 2;
}
val = gen_raw_REG (mode, reg_rtx_no);
regno_reg_rtx[reg_rtx_no++] = val;
return val;
}
/* Generate a register with same attributes as REG, but offsetted by OFFSET.
Do the big endian correction if needed. */
rtx
gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno, int offset)
{
rtx new = gen_rtx_REG (mode, regno);
tree decl;
HOST_WIDE_INT var_size;
/* PR middle-end/14084
The problem appears when a variable is stored in a larger register
and later it is used in the original mode or some mode in between
or some part of variable is accessed.
On little endian machines there is no problem because
the REG_OFFSET of the start of the variable is the same when
accessed in any mode (it is 0).
However, this is not true on big endian machines.
The offset of the start of the variable is different when accessed
in different modes.
When we are taking a part of the REG we have to change the OFFSET
from offset WRT size of mode of REG to offset WRT size of variable.
If we would not do the big endian correction the resulting REG_OFFSET
would be larger than the size of the DECL.
Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
REG.mode MODE DECL size old offset new offset description
DI SI 4 4 0 int32 in SImode
DI SI 1 4 0 char in SImode
DI QI 1 7 0 char in QImode
DI QI 4 5 1 1st element in QImode
of char[4]
DI HI 4 6 2 1st element in HImode
of int16[2]
If the size of DECL is equal or greater than the size of REG
we can't do this correction because the register holds the
whole variable or a part of the variable and thus the REG_OFFSET
is already correct. */
decl = REG_EXPR (reg);
if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
&& decl != NULL
&& offset > 0
&& GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (mode)
&& ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
&& var_size < GET_MODE_SIZE (GET_MODE (reg))))
{
int offset_le;
/* Convert machine endian to little endian WRT size of mode of REG. */
if (WORDS_BIG_ENDIAN)
offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
/ UNITS_PER_WORD) * UNITS_PER_WORD;
else
offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
% UNITS_PER_WORD);
else
offset_le += offset % UNITS_PER_WORD;
if (offset_le >= var_size)
{
/* MODE is wider than the variable so the new reg will cover
the whole variable so the resulting OFFSET should be 0. */
offset = 0;
}
else
{
/* Convert little endian to machine endian WRT size of variable. */
if (WORDS_BIG_ENDIAN)
offset = ((var_size - 1 - offset_le)
/ UNITS_PER_WORD) * UNITS_PER_WORD;
else
offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
if (BYTES_BIG_ENDIAN)
offset += ((var_size - 1 - offset_le)
% UNITS_PER_WORD);
else
offset += offset_le % UNITS_PER_WORD;
}
}
REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
REG_OFFSET (reg) + offset);
return new;
}
/* Set the decl for MEM to DECL. */
void
set_reg_attrs_from_mem (rtx reg, rtx mem)
{
if (MEM_OFFSET (mem) && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
REG_ATTRS (reg)
= get_reg_attrs (MEM_EXPR (mem), INTVAL (MEM_OFFSET (mem)));
}
/* Set the register attributes for registers contained in PARM_RTX.
Use needed values from memory attributes of MEM. */
void
set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
{
if (REG_P (parm_rtx))
set_reg_attrs_from_mem (parm_rtx, mem);
else if (GET_CODE (parm_rtx) == PARALLEL)
{
/* Check for a NULL entry in the first slot, used to indicate that the
parameter goes both on the stack and in registers. */
int i = XEXP (XVECEXP (parm_rtx, 0, 0), 0) ? 0 : 1;
for (; i < XVECLEN (parm_rtx, 0); i++)
{
rtx x = XVECEXP (parm_rtx, 0, i);
if (REG_P (XEXP (x, 0)))
REG_ATTRS (XEXP (x, 0))
= get_reg_attrs (MEM_EXPR (mem),
INTVAL (XEXP (x, 1)));
}
}
}
/* Assign the RTX X to declaration T. */
void
set_decl_rtl (tree t, rtx x)
{
DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
if (!x)
return;
/* For register, we maintain the reverse information too. */
if (REG_P (x))
REG_ATTRS (x) = get_reg_attrs (t, 0);
else if (GET_CODE (x) == SUBREG)
REG_ATTRS (SUBREG_REG (x))
= get_reg_attrs (t, -SUBREG_BYTE (x));
if (GET_CODE (x) == CONCAT)
{
if (REG_P (XEXP (x, 0)))
REG_ATTRS (XEXP (x, 0)) = get_reg_attrs (t, 0);
if (REG_P (XEXP (x, 1)))
REG_ATTRS (XEXP (x, 1))
= get_reg_attrs (t, GET_MODE_UNIT_SIZE (GET_MODE (XEXP (x, 0))));
}
if (GET_CODE (x) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (x, 0); i++)
{
rtx y = XVECEXP (x, 0, i);
if (REG_P (XEXP (y, 0)))
REG_ATTRS (XEXP (y, 0)) = get_reg_attrs (t, INTVAL (XEXP (y, 1)));
}
}
}
/* Assign the RTX X to parameter declaration T. */
void
set_decl_incoming_rtl (tree t, rtx x)
{
DECL_INCOMING_RTL (t) = x;
if (!x)
return;
/* For register, we maintain the reverse information too. */
if (REG_P (x))
REG_ATTRS (x) = get_reg_attrs (t, 0);
else if (GET_CODE (x) == SUBREG)
REG_ATTRS (SUBREG_REG (x))
= get_reg_attrs (t, -SUBREG_BYTE (x));
if (GET_CODE (x) == CONCAT)
{
if (REG_P (XEXP (x, 0)))
REG_ATTRS (XEXP (x, 0)) = get_reg_attrs (t, 0);
if (REG_P (XEXP (x, 1)))
REG_ATTRS (XEXP (x, 1))
= get_reg_attrs (t, GET_MODE_UNIT_SIZE (GET_MODE (XEXP (x, 0))));
}
if (GET_CODE (x) == PARALLEL)
{
int i, start;
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
if (XEXP (XVECEXP (x, 0, 0), 0))
start = 0;
else
start = 1;
for (i = start; i < XVECLEN (x, 0); i++)
{
rtx y = XVECEXP (x, 0, i);
if (REG_P (XEXP (y, 0)))
REG_ATTRS (XEXP (y, 0)) = get_reg_attrs (t, INTVAL (XEXP (y, 1)));
}
}
}
/* Identify REG (which may be a CONCAT) as a user register. */
void
mark_user_reg (rtx reg)
{
if (GET_CODE (reg) == CONCAT)
{
REG_USERVAR_P (XEXP (reg, 0)) = 1;
REG_USERVAR_P (XEXP (reg, 1)) = 1;
}
else
{
gcc_assert (REG_P (reg));
REG_USERVAR_P (reg) = 1;
}
}
/* Identify REG as a probable pointer register and show its alignment
as ALIGN, if nonzero. */
void
mark_reg_pointer (rtx reg, int align)
{
if (! REG_POINTER (reg))
{
REG_POINTER (reg) = 1;
if (align)
REGNO_POINTER_ALIGN (REGNO (reg)) = align;
}
else if (align && align < REGNO_POINTER_ALIGN (REGNO (reg)))
/* We can no-longer be sure just how aligned this pointer is. */
REGNO_POINTER_ALIGN (REGNO (reg)) = align;
}
/* Return 1 plus largest pseudo reg number used in the current function. */
int
max_reg_num (void)
{
return reg_rtx_no;
}
/* Return 1 + the largest label number used so far in the current function. */
int
max_label_num (void)
{
return label_num;
}
/* Return first label number used in this function (if any were used). */
int
get_first_label_num (void)
{
return first_label_num;
}
/* If the rtx for label was created during the expansion of a nested
function, then first_label_num won't include this label number.
Fix this now so that array indicies work later. */
void
maybe_set_first_label_num (rtx x)
{
if (CODE_LABEL_NUMBER (x) < first_label_num)
first_label_num = CODE_LABEL_NUMBER (x);
}
/* Return a value representing some low-order bits of X, where the number
of low-order bits is given by MODE. Note that no conversion is done
between floating-point and fixed-point values, rather, the bit
representation is returned.
This function handles the cases in common between gen_lowpart, below,
and two variants in cse.c and combine.c. These are the cases that can
be safely handled at all points in the compilation.
If this is not a case we can handle, return 0. */
rtx
gen_lowpart_common (enum machine_mode mode, rtx x)
{
int msize = GET_MODE_SIZE (mode);
int xsize;
int offset = 0;
enum machine_mode innermode;
/* Unfortunately, this routine doesn't take a parameter for the mode of X,
so we have to make one up. Yuk. */
innermode = GET_MODE (x);
if (GET_CODE (x) == CONST_INT
&& msize * BITS_PER_UNIT <= HOST_BITS_PER_WIDE_INT)
innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
else if (innermode == VOIDmode)
|