/* Subroutines used for code generation on the DEC Alpha.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000 Free Software Foundation, Inc.
+ 2000, 2001 Free Software Foundation, Inc.
Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
This file is part of GNU CC.
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
-#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
/* Strings decoded into the above options. */
const char *alpha_cpu_string; /* -mcpu= */
+const char *alpha_tune_string; /* -mtune= */
const char *alpha_tp_string; /* -mtrap-precision=[p|s|i] */
const char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */
const char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */
PARAMS ((struct function *p));
static void alpha_mark_machine_status
PARAMS ((struct function *p));
+static void alpha_free_machine_status
+ PARAMS ((struct function *p));
static int alpha_ra_ever_killed
PARAMS ((void));
static rtx set_frame_related_p
void
override_options ()
{
+ int i;
+ static struct cpu_table {
+ const char *name;
+ enum processor_type processor;
+ int flags;
+ } cpu_table[] = {
+#define EV5_MASK (MASK_CPU_EV5)
+#define EV6_MASK (MASK_CPU_EV6|MASK_BWX|MASK_MAX|MASK_FIX)
+ { "ev4", PROCESSOR_EV4, 0 },
+ { "ev45", PROCESSOR_EV4, 0 },
+ { "21064", PROCESSOR_EV4, 0 },
+ { "ev5", PROCESSOR_EV5, EV5_MASK },
+ { "21164", PROCESSOR_EV5, EV5_MASK },
+ { "ev56", PROCESSOR_EV5, EV5_MASK|MASK_BWX },
+ { "21164a", PROCESSOR_EV5, EV5_MASK|MASK_BWX },
+ { "pca56", PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX },
+ { "21164PC",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX },
+ { "21164pc",PROCESSOR_EV5, EV5_MASK|MASK_BWX|MASK_MAX },
+ { "ev6", PROCESSOR_EV6, EV6_MASK },
+ { "21264", PROCESSOR_EV6, EV6_MASK },
+ { "ev67", PROCESSOR_EV6, EV6_MASK|MASK_CIX },
+ { "21264a", PROCESSOR_EV6, EV6_MASK|MASK_CIX },
+ { 0, 0, 0 }
+ };
+
alpha_tp = ALPHA_TP_PROG;
alpha_fprm = ALPHA_FPRM_NORM;
alpha_fptm = ALPHA_FPTM_N;
if (alpha_cpu_string)
{
- if (! strcmp (alpha_cpu_string, "ev4")
- || ! strcmp (alpha_cpu_string, "ev45")
- || ! strcmp (alpha_cpu_string, "21064"))
- {
- alpha_cpu = PROCESSOR_EV4;
- target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX);
- }
- else if (! strcmp (alpha_cpu_string, "ev5")
- || ! strcmp (alpha_cpu_string, "21164"))
- {
- alpha_cpu = PROCESSOR_EV5;
- target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX);
- }
- else if (! strcmp (alpha_cpu_string, "ev56")
- || ! strcmp (alpha_cpu_string, "21164a"))
- {
- alpha_cpu = PROCESSOR_EV5;
- target_flags |= MASK_BWX;
- target_flags &= ~ (MASK_MAX | MASK_FIX | MASK_CIX);
- }
- else if (! strcmp (alpha_cpu_string, "pca56")
- || ! strcmp (alpha_cpu_string, "21164PC")
- || ! strcmp (alpha_cpu_string, "21164pc"))
- {
- alpha_cpu = PROCESSOR_EV5;
- target_flags |= MASK_BWX | MASK_MAX;
- target_flags &= ~ (MASK_FIX | MASK_CIX);
- }
- else if (! strcmp (alpha_cpu_string, "ev6")
- || ! strcmp (alpha_cpu_string, "21264"))
- {
- alpha_cpu = PROCESSOR_EV6;
- target_flags |= MASK_BWX | MASK_MAX | MASK_FIX;
- target_flags &= ~ (MASK_CIX);
- }
- else if (! strcmp (alpha_cpu_string, "ev67")
- || ! strcmp (alpha_cpu_string, "21264a"))
- {
- alpha_cpu = PROCESSOR_EV6;
- target_flags |= MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX;
- }
- else
+ for (i = 0; cpu_table [i].name; i++)
+ if (! strcmp (alpha_cpu_string, cpu_table [i].name))
+ {
+ alpha_cpu = cpu_table [i].processor;
+ target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX
+ | MASK_CPU_EV5 | MASK_CPU_EV6);
+ target_flags |= cpu_table [i].flags;
+ break;
+ }
+ if (! cpu_table [i].name)
error ("bad value `%s' for -mcpu switch", alpha_cpu_string);
}
+ if (alpha_tune_string)
+ {
+ for (i = 0; cpu_table [i].name; i++)
+ if (! strcmp (alpha_tune_string, cpu_table [i].name))
+ {
+ alpha_cpu = cpu_table [i].processor;
+ break;
+ }
+ if (! cpu_table [i].name)
+ error ("bad value `%s' for -mcpu switch", alpha_tune_string);
+ }
+
/* Do some sanity checks on the above options. */
if ((alpha_fptm == ALPHA_FPTM_SU || alpha_fptm == ALPHA_FPTM_SUI)
- && alpha_tp != ALPHA_TP_INSN && alpha_cpu != PROCESSOR_EV6)
+ && alpha_tp != ALPHA_TP_INSN && ! TARGET_CPU_EV6)
{
warning ("fp software completion requires -mtrap-precision=i");
alpha_tp = ALPHA_TP_INSN;
}
- if (alpha_cpu == PROCESSOR_EV6)
+ if (TARGET_CPU_EV6)
{
/* Except for EV6 pass 1 (not released), we always have precise
arithmetic traps. Which means we can do software completion
{
{ 3, 30, -1 }, /* ev4 -- Bcache is a guess */
{ 2, 12, 38 }, /* ev5 -- Bcache from PC164 LMbench numbers */
- { 3, 13, -1 }, /* ev6 -- Ho hum, doesn't exist yet */
+ { 3, 12, 30 }, /* ev6 -- Bcache from DS20 LMbench. */
};
lat = alpha_mlat_string[1] - '0';
- if (lat < 0 || lat > 3 || cache_latency[alpha_cpu][lat-1] == -1)
+ if (lat <= 0 || lat > 3 || cache_latency[alpha_cpu][lat-1] == -1)
{
warning ("L%d cache latency unknown for %s",
lat, alpha_cpu_name[alpha_cpu]);
if (!g_switch_set)
g_switch_value = 8;
+ /* Align labels and loops for optimal branching. */
+ /* ??? Kludge these by not doing anything if we don't optimize and also if
+ we are writing ECOFF symbols to work around a bug in DEC's assembler. */
+ if (optimize > 0 && write_symbols != SDB_DEBUG)
+ {
+ if (align_loops <= 0)
+ align_loops = 16;
+ if (align_jumps <= 0)
+ align_jumps = 16;
+ }
+ if (align_functions <= 0)
+ align_functions = 16;
+
/* Acquire a unique set number for our register saves and restores. */
alpha_sr_alias_set = new_alias_set ();
/* Set up function hooks. */
init_machine_status = alpha_init_machine_status;
mark_machine_status = alpha_mark_machine_status;
+ free_machine_status = alpha_free_machine_status;
}
\f
/* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */
1 true
Convert the compare against the raw return value. */
- op0 = alpha_emit_xfloating_compare (code, op0, op1);
+ if (code == UNORDERED || code == ORDERED)
+ cmp_code = EQ;
+ else
+ cmp_code = code;
+
+ op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1);
op1 = const0_rtx;
alpha_compare.fp_p = 0;
- code = GT;
+
+ if (code == UNORDERED)
+ code = LT;
+ else if (code == ORDERED)
+ code = GE;
+ else
+ code = GT;
}
/* The general case: fold the comparison code to the types of compares
if (alpha_compare.fp_p)
{
cmp_mode = DFmode;
- if (flag_fast_math)
+ if (flag_unsafe_math_optimizations)
{
/* When we are not as concerned about non-finite values, and we
are comparing against zero, we can branch directly. */
}
}
}
- }
- /* Force op0 into a register. */
- if (GET_CODE (op0) != REG)
- op0 = force_reg (cmp_mode, op0);
+ if (!reg_or_0_operand (op0, DImode))
+ op0 = force_reg (DImode, op0);
+ if (cmp_code != PLUS && !reg_or_8bit_operand (op1, DImode))
+ op1 = force_reg (DImode, op1);
+ }
/* Emit an initial compare instruction, if necessary. */
tem = op0;
return gen_rtx_fmt_ee (branch_code, branch_mode, tem, CONST0_RTX (cmp_mode));
}
+/* Certain simplifications can be done to make invalid setcc operations
+ valid. Return the final comparison, or NULL if we can't work. */
+
+rtx
+alpha_emit_setcc (code)
+ enum rtx_code code;
+{
+ enum rtx_code cmp_code;
+ rtx op0 = alpha_compare.op0, op1 = alpha_compare.op1;
+ int fp_p = alpha_compare.fp_p;
+ rtx tmp;
+
+ /* Zero the operands. */
+ memset (&alpha_compare, 0, sizeof (alpha_compare));
+
+ if (fp_p && GET_MODE (op0) == TFmode)
+ {
+ if (! TARGET_HAS_XFLOATING_LIBS)
+ abort ();
+
+ /* X_floating library comparison functions return
+ -1 unordered
+ 0 false
+ 1 true
+ Convert the compare against the raw return value. */
+
+ if (code == UNORDERED || code == ORDERED)
+ cmp_code = EQ;
+ else
+ cmp_code = code;
+
+ op0 = alpha_emit_xfloating_compare (cmp_code, op0, op1);
+ op1 = const0_rtx;
+ fp_p = 0;
+
+ if (code == UNORDERED)
+ code = LT;
+ else if (code == ORDERED)
+ code = GE;
+ else
+ code = GT;
+ }
+
+ if (fp_p && !TARGET_FIX)
+ return NULL_RTX;
+
+ /* The general case: fold the comparison code to the types of compares
+ that we have, choosing the branch as necessary. */
+
+ cmp_code = NIL;
+ switch (code)
+ {
+ case EQ: case LE: case LT: case LEU: case LTU:
+ case UNORDERED:
+ /* We have these compares. */
+ if (fp_p)
+ cmp_code = code, code = NE;
+ break;
+
+ case NE:
+ if (!fp_p && op1 == const0_rtx)
+ break;
+ /* FALLTHRU */
+
+ case ORDERED:
+ cmp_code = reverse_condition (code);
+ code = EQ;
+ break;
+
+ case GE: case GT: case GEU: case GTU:
+ /* These are normally need swapping, but for integer zero we have
+ special patterns that recognize swapped operands. */
+ if (!fp_p && op1 == const0_rtx)
+ break;
+ code = swap_condition (code);
+ if (fp_p)
+ cmp_code = code, code = NE;
+ tmp = op0, op0 = op1, op1 = tmp;
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (!fp_p)
+ {
+ if (!register_operand (op0, DImode))
+ op0 = force_reg (DImode, op0);
+ if (!reg_or_8bit_operand (op1, DImode))
+ op1 = force_reg (DImode, op1);
+ }
+
+ /* Emit an initial compare instruction, if necessary. */
+ if (cmp_code != NIL)
+ {
+ enum machine_mode mode = fp_p ? DFmode : DImode;
+
+ tmp = gen_reg_rtx (mode);
+ emit_insn (gen_rtx_SET (VOIDmode, tmp,
+ gen_rtx_fmt_ee (cmp_code, mode, op0, op1)));
+
+ op0 = fp_p ? gen_lowpart (DImode, tmp) : tmp;
+ op1 = const0_rtx;
+ }
+
+ /* Return the setcc comparison. */
+ return gen_rtx_fmt_ee (code, DImode, op0, op1);
+}
+
/* Rewrite a comparison against zero CMP of the form
(CODE (cc0) (const_int 0)) so it can be written validly in
= (GET_MODE (op0) == VOIDmode ? DImode : GET_MODE (op0));
enum machine_mode cmp_op_mode = fp_p ? DFmode : DImode;
enum machine_mode cmov_mode = VOIDmode;
- int local_fast_math = flag_fast_math;
+ int local_fast_math = flag_unsafe_math_optimizations;
rtx tem;
/* Zero the operands. */
case GE: case GT: case GEU: case GTU:
/* These must be swapped. */
- cmp_code = swap_condition (code);
- code = NE;
- tem = op0, op0 = op1, op1 = tem;
+ if (op1 == CONST0_RTX (cmp_mode))
+ cmp_code = code, code = NE;
+ else
+ {
+ cmp_code = swap_condition (code);
+ code = NE;
+ tem = op0, op0 = op1, op1 = tem;
+ }
break;
default:
break;
case GE: case GT: case GEU: case GTU:
- /* These must be swapped. Make sure the new first operand is in
- a register. */
- code = swap_condition (code);
- tem = op0, op0 = op1, op1 = tem;
- op0 = force_reg (cmp_mode, op0);
+ /* These must be swapped. */
+ if (op1 != CONST0_RTX (cmp_mode))
+ {
+ code = swap_condition (code);
+ tem = op0, op0 = op1, op1 = tem;
+ }
break;
default:
abort ();
}
+ if (!fp_p)
+ {
+ if (!reg_or_0_operand (op0, DImode))
+ op0 = force_reg (DImode, op0);
+ if (!reg_or_8bit_operand (op1, DImode))
+ op1 = force_reg (DImode, op1);
+ }
+
/* ??? We mark the branch mode to be CCmode to prevent the compare
and cmov from being combined, since the compare insn follows IEEE
rules that the cmov does not. */
operands[1]));
}
+/* Split a TFmode OP[1] into DImode OP[2,3] and likewise for
+ OP[0] into OP[0,1]. Naturally, output operand ordering is
+ little-endian. */
+
void
alpha_split_tfmode_pair (operands)
rtx operands[4];
else
abort ();
}
+
+/* Implement negtf2 or abstf2. Op0 is destination, op1 is source,
+ op2 is a register containing the sign bit, operation is the
+ logical operation to be performed. */
+
+void
+alpha_split_tfmode_frobsign (operands, operation)
+ rtx operands[3];
+ rtx (*operation) PARAMS ((rtx, rtx, rtx));
+{
+ rtx high_bit = operands[2];
+ rtx scratch;
+ int move;
+
+ alpha_split_tfmode_pair (operands);
+
+ /* Detect three flavours of operand overlap. */
+ move = 1;
+ if (rtx_equal_p (operands[0], operands[2]))
+ move = 0;
+ else if (rtx_equal_p (operands[1], operands[2]))
+ {
+ if (rtx_equal_p (operands[0], high_bit))
+ move = 2;
+ else
+ move = -1;
+ }
+
+ if (move < 0)
+ emit_move_insn (operands[0], operands[2]);
+
+ /* ??? If the destination overlaps both source tf and high_bit, then
+ assume source tf is dead in its entirety and use the other half
+ for a scratch register. Otherwise "scratch" is just the proper
+ destination register. */
+ scratch = operands[move < 2 ? 1 : 3];
+
+ emit_insn ((*operation) (scratch, high_bit, operands[3]));
+
+ if (move > 0)
+ {
+ emit_move_insn (operands[0], operands[2]);
+ if (move > 1)
+ emit_move_insn (operands[1], scratch);
+ }
+}
\f
/* Use ext[wlq][lh] as the Architecture Handbook describes for extracting
unaligned data:
HOST_WIDE_INT size, ofs;
int sign;
{
- rtx meml, memh, addr, extl, exth;
+ rtx meml, memh, addr, extl, exth, tmp;
enum machine_mode mode;
meml = gen_reg_rtx (DImode);
extl = gen_reg_rtx (DImode);
exth = gen_reg_rtx (DImode);
- emit_move_insn (meml,
- change_address (mem, DImode,
- gen_rtx_AND (DImode,
- plus_constant (XEXP (mem, 0),
- ofs),
- GEN_INT (-8))));
-
- emit_move_insn (memh,
- change_address (mem, DImode,
- gen_rtx_AND (DImode,
- plus_constant (XEXP (mem, 0),
- ofs + size - 1),
- GEN_INT (-8))));
+ /* AND addresses cannot be in any alias set, since they may implicitly
+ alias surrounding code. Ideally we'd have some alias set that
+ covered all types except those with alignment 8 or higher. */
+
+ tmp = change_address (mem, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant (XEXP (mem, 0), ofs),
+ GEN_INT (-8)));
+ MEM_ALIAS_SET (tmp) = 0;
+ emit_move_insn (meml, tmp);
+
+ tmp = change_address (mem, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant (XEXP (mem, 0),
+ ofs + size - 1),
+ GEN_INT (-8)));
+ MEM_ALIAS_SET (tmp) = 0;
+ emit_move_insn (memh, tmp);
if (sign && size == 2)
{
insl = gen_reg_rtx (DImode);
insh = gen_reg_rtx (DImode);
+ /* AND addresses cannot be in any alias set, since they may implicitly
+ alias surrounding code. Ideally we'd have some alias set that
+ covered all types except those with alignment 8 or higher. */
+
meml = change_address (dst, DImode,
gen_rtx_AND (DImode,
plus_constant (XEXP (dst, 0), ofs),
GEN_INT (-8)));
+ MEM_ALIAS_SET (meml) = 0;
+
memh = change_address (dst, DImode,
gen_rtx_AND (DImode,
plus_constant (XEXP (dst, 0),
ofs+size-1),
GEN_INT (-8)));
+ MEM_ALIAS_SET (memh) = 0;
emit_move_insn (dsth, memh);
emit_move_insn (dstl, meml);
rtx const im8 = GEN_INT (-8);
rtx const i64 = GEN_INT (64);
rtx ext_tmps[MAX_MOVE_WORDS], data_regs[MAX_MOVE_WORDS+1];
- rtx sreg, areg;
+ rtx sreg, areg, tmp;
HOST_WIDE_INT i;
/* Generate all the tmp registers we need. */
/* Load up all of the source data. */
for (i = 0; i < words; ++i)
{
- emit_move_insn (data_regs[i],
- change_address (smem, DImode,
- gen_rtx_AND (DImode,
- plus_constant (XEXP(smem,0),
- 8*i),
- im8)));
+ tmp = change_address (smem, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant (XEXP(smem,0), 8*i),
+ im8));
+ MEM_ALIAS_SET (tmp) = 0;
+ emit_move_insn (data_regs[i], tmp);
}
- emit_move_insn (data_regs[words],
- change_address (smem, DImode,
- gen_rtx_AND (DImode,
- plus_constant (XEXP(smem,0),
- 8*words - 1),
- im8)));
+
+ tmp = change_address (smem, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant (XEXP(smem,0), 8*words - 1),
+ im8));
+ MEM_ALIAS_SET (tmp) = 0;
+ emit_move_insn (data_regs[words], tmp);
/* Extract the half-word fragments. Unfortunately DEC decided to make
extxh with offset zero a noop instead of zeroing the register, so
plus_constant (XEXP(dmem,0),
words*8 - 1),
im8));
+ MEM_ALIAS_SET (st_addr_2) = 0;
+
st_addr_1 = change_address (dmem, DImode,
gen_rtx_AND (DImode,
XEXP (dmem, 0),
im8));
+ MEM_ALIAS_SET (st_addr_1) = 0;
/* Load up the destination end bits. */
emit_move_insn (st_tmp_2, st_addr_2);
emit_move_insn (st_addr_2, st_tmp_2);
for (i = words-1; i > 0; --i)
{
- emit_move_insn (change_address (dmem, DImode,
- gen_rtx_AND (DImode,
- plus_constant(XEXP (dmem,0),
- i*8),
- im8)),
- data_regs ? ins_tmps[i-1] : const0_rtx);
+ rtx tmp = change_address (dmem, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant(XEXP (dmem,0), i*8),
+ im8));
+ MEM_ALIAS_SET (tmp) = 0;
+ emit_move_insn (tmp, data_regs ? ins_tmps[i-1] : const0_rtx);
}
emit_move_insn (st_addr_1, st_tmp_1);
}
rtx bytes_rtx = operands[2];
rtx align_rtx = operands[3];
HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx);
- unsigned HOST_WIDE_INT bytes = orig_bytes;
- unsigned HOST_WIDE_INT src_align = INTVAL (align_rtx) * BITS_PER_UNIT;
- unsigned HOST_WIDE_INT dst_align = src_align;
+ HOST_WIDE_INT bytes = orig_bytes;
+ HOST_WIDE_INT src_align = INTVAL (align_rtx) * BITS_PER_UNIT;
+ HOST_WIDE_INT dst_align = src_align;
rtx orig_src = operands[1];
rtx orig_dst = operands[0];
rtx data_regs[2 * MAX_MOVE_WORDS + 16];
rtx tmp;
- unsigned int i, words, ofs, nregs = 0;
+ int i, words, ofs, nregs = 0;
if (orig_bytes <= 0)
return 1;
- else if (bytes > MAX_MOVE_WORDS * BITS_PER_UNIT)
+ else if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD)
return 0;
/* Look for additional alignment information from recorded register info. */
/* No appropriate mode; fall back on memory. */
orig_src = change_address (orig_src, GET_MODE (orig_src),
copy_addr_to_reg (XEXP (orig_src, 0)));
+ src_align = GET_MODE_BITSIZE (GET_MODE (tmp));
}
ofs = 0;
ofs += words * 4;
}
- if (bytes >= 16)
+ if (bytes >= 8)
{
words = bytes / 8;
ofs += words * 8;
}
- if (! TARGET_BWX && bytes >= 8)
- {
- data_regs[nregs++] = tmp = gen_reg_rtx (DImode);
- alpha_expand_unaligned_load (tmp, orig_src, 8, ofs, 0);
- bytes -= 8;
- ofs += 8;
- }
-
if (! TARGET_BWX && bytes >= 4)
{
data_regs[nregs++] = tmp = gen_reg_rtx (SImode);
ofs += 2;
} while (bytes >= 2);
}
-
else if (! TARGET_BWX)
{
data_regs[nregs++] = tmp = gen_reg_rtx (HImode);
up by recognizing extra alignment information. */
orig_dst = change_address (orig_dst, GET_MODE (orig_dst),
copy_addr_to_reg (XEXP (orig_dst, 0)));
- dst_align = GET_MODE_SIZE (GET_MODE (tmp));
+ dst_align = GET_MODE_BITSIZE (GET_MODE (tmp));
}
/* Write out the data in whatever chunks reading the source allowed. */
rtx bytes_rtx = operands[1];
rtx align_rtx = operands[2];
HOST_WIDE_INT orig_bytes = INTVAL (bytes_rtx);
- unsigned HOST_WIDE_INT bytes = orig_bytes;
- unsigned HOST_WIDE_INT align = INTVAL (align_rtx);
+ HOST_WIDE_INT bytes = orig_bytes;
+ HOST_WIDE_INT align = INTVAL (align_rtx) * BITS_PER_UNIT;
+ HOST_WIDE_INT alignofs = 0;
rtx orig_dst = operands[0];
rtx tmp;
- unsigned HOST_WIDE_INT i, words, ofs = 0;
+ int i, words, ofs = 0;
if (orig_bytes <= 0)
return 1;
- if (bytes > MAX_MOVE_WORDS*8)
+ if (orig_bytes > MAX_MOVE_WORDS * UNITS_PER_WORD)
return 0;
/* Look for stricter alignment. */
&& GET_CODE (XEXP (tmp, 0)) == REG
&& GET_CODE (XEXP (tmp, 1)) == CONST_INT)
{
- unsigned HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1));
- unsigned int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0)));
+ HOST_WIDE_INT c = INTVAL (XEXP (tmp, 1));
+ int a = REGNO_POINTER_ALIGN (REGNO (XEXP (tmp, 0)));
if (a > align)
{
- if (a >= 64 && c % 8 == 0)
- align = 64;
- else if (a >= 32 && c % 4 == 0)
- align = 32;
- else if (a >= 16 && c % 2 == 0)
- align = 16;
+ if (a >= 64)
+ align = a, alignofs = 8 - c % 8;
+ else if (a >= 32)
+ align = a, alignofs = 4 - c % 4;
+ else if (a >= 16)
+ align = a, alignofs = 2 - c % 2;
}
}
-
else if (GET_CODE (tmp) == ADDRESSOF)
{
enum machine_mode mode;
/* No appropriate mode; fall back on memory. */
orig_dst = change_address (orig_dst, GET_MODE (orig_dst),
copy_addr_to_reg (tmp));
- align = GET_MODE_SIZE (GET_MODE (XEXP (tmp, 0)));
+ align = GET_MODE_BITSIZE (GET_MODE (XEXP (tmp, 0)));
}
- /* Handle a block of contiguous words first. */
+ /* Handle an unaligned prefix first. */
+
+ if (alignofs > 0)
+ {
+#if HOST_BITS_PER_WIDE_INT >= 64
+ /* Given that alignofs is bounded by align, the only time BWX could
+ generate three stores is for a 7 byte fill. Prefer two individual
+ stores over a load/mask/store sequence. */
+ if ((!TARGET_BWX || alignofs == 7)
+ && align >= 32
+ && !(alignofs == 4 && bytes >= 4))
+ {
+ enum machine_mode mode = (align >= 64 ? DImode : SImode);
+ int inv_alignofs = (align >= 64 ? 8 : 4) - alignofs;
+ rtx mem, tmp;
+ HOST_WIDE_INT mask;
+
+ mem = change_address (orig_dst, mode,
+ plus_constant (XEXP (orig_dst, 0),
+ ofs - inv_alignofs));
+ MEM_ALIAS_SET (mem) = 0;
+
+ mask = ~(~(HOST_WIDE_INT)0 << (inv_alignofs * 8));
+ if (bytes < alignofs)
+ {
+ mask |= ~(HOST_WIDE_INT)0 << ((inv_alignofs + bytes) * 8);
+ ofs += bytes;
+ bytes = 0;
+ }
+ else
+ {
+ bytes -= alignofs;
+ ofs += alignofs;
+ }
+ alignofs = 0;
+
+ tmp = expand_binop (mode, and_optab, mem, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_WIDEN);
+
+ emit_move_insn (mem, tmp);
+ }
+#endif
+
+ if (TARGET_BWX && (alignofs & 1) && bytes >= 1)
+ {
+ emit_move_insn (change_address (orig_dst, QImode,
+ plus_constant (XEXP (orig_dst, 0),
+ ofs)),
+ const0_rtx);
+ bytes -= 1;
+ ofs += 1;
+ alignofs -= 1;
+ }
+ if (TARGET_BWX && align >= 16 && (alignofs & 3) == 2 && bytes >= 2)
+ {
+ emit_move_insn (change_address (orig_dst, HImode,
+ plus_constant (XEXP (orig_dst, 0),
+ ofs)),
+ const0_rtx);
+ bytes -= 2;
+ ofs += 2;
+ alignofs -= 2;
+ }
+ if (alignofs == 4 && bytes >= 4)
+ {
+ emit_move_insn (change_address (orig_dst, SImode,
+ plus_constant (XEXP (orig_dst, 0),
+ ofs)),
+ const0_rtx);
+ bytes -= 4;
+ ofs += 4;
+ alignofs = 0;
+ }
+
+ /* If we've not used the extra lead alignment information by now,
+ we won't be able to. Downgrade align to match what's left over. */
+ if (alignofs > 0)
+ {
+ alignofs = alignofs & -alignofs;
+ align = MIN (align, alignofs * BITS_PER_UNIT);
+ }
+ }
+
+ /* Handle a block of contiguous long-words. */
if (align >= 64 && bytes >= 8)
{
ofs += words * 8;
}
- if (align >= 16 && bytes >= 4)
+ /* If the block is large and appropriately aligned, emit a single
+ store followed by a sequence of stq_u insns. */
+
+ if (align >= 32 && bytes > 16)
+ {
+ emit_move_insn (change_address (orig_dst, SImode,
+ plus_constant (XEXP (orig_dst, 0), ofs)),
+ const0_rtx);
+ bytes -= 4;
+ ofs += 4;
+
+ words = bytes / 8;
+ for (i = 0; i < words; ++i)
+ {
+ rtx mem;
+ mem = change_address (orig_dst, DImode,
+ gen_rtx_AND (DImode,
+ plus_constant (XEXP (orig_dst, 0),
+ ofs + i*8),
+ GEN_INT (-8)));
+ MEM_ALIAS_SET (mem) = 0;
+ emit_move_insn (mem, const0_rtx);
+ }
+
+ /* Depending on the alignment, the first stq_u may have overlapped
+ with the initial stl, which means that the last stq_u didn't
+ write as much as it would appear. Leave those questionable bytes
+ unaccounted for. */
+ bytes -= words * 8 - 4;
+ ofs += words * 8 - 4;
+ }
+
+ /* Handle a smaller block of aligned words. */
+
+ if ((align >= 64 && bytes == 4)
+ || (align == 32 && bytes >= 4))
{
words = bytes / 4;
ofs += words * 4;
}
- if (bytes >= 16)
+ /* An unaligned block uses stq_u stores for as many as possible. */
+
+ if (bytes >= 8)
{
words = bytes / 8;
ofs += words * 8;
}
- /* Next clean up any trailing pieces. We know from the contiguous
- block move that there are no aligned SImode or DImode hunks left. */
+ /* Next clean up any trailing pieces. */
- if (! TARGET_BWX && bytes >= 8)
- {
- alpha_expand_unaligned_store (orig_dst, const0_rtx, 8, ofs);
- bytes -= 8;
- ofs += 8;
+#if HOST_BITS_PER_WIDE_INT >= 64
+ /* Count the number of bits in BYTES for which aligned stores could
+ be emitted. */
+ words = 0;
+ for (i = (TARGET_BWX ? 1 : 4); i * BITS_PER_UNIT <= align ; i <<= 1)
+ if (bytes & i)
+ words += 1;
+
+ /* If we have appropriate alignment (and it wouldn't take too many
+ instructions otherwise), mask out the bytes we need. */
+ if (TARGET_BWX ? words > 2 : bytes > 0)
+ {
+ if (align >= 64)
+ {
+ rtx mem, tmp;
+ HOST_WIDE_INT mask;
+
+ mem = change_address (orig_dst, DImode,
+ plus_constant (XEXP (orig_dst, 0), ofs));
+ MEM_ALIAS_SET (mem) = 0;
+
+ mask = ~(HOST_WIDE_INT)0 << (bytes * 8);
+
+ tmp = expand_binop (DImode, and_optab, mem, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_WIDEN);
+
+ emit_move_insn (mem, tmp);
+ return 1;
+ }
+ else if (align >= 32 && bytes < 4)
+ {
+ rtx mem, tmp;
+ HOST_WIDE_INT mask;
+
+ mem = change_address (orig_dst, SImode,
+ plus_constant (XEXP (orig_dst, 0), ofs));
+ MEM_ALIAS_SET (mem) = 0;
+
+ mask = ~(HOST_WIDE_INT)0 << (bytes * 8);
+
+ tmp = expand_binop (SImode, and_optab, mem, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_WIDEN);
+
+ emit_move_insn (mem, tmp);
+ return 1;
+ }
}
+#endif
if (!TARGET_BWX && bytes >= 4)
{
{
struct machine_function *machine = p->machine;
- ggc_mark_rtx (machine->eh_epilogue_sp_ofs);
- ggc_mark_rtx (machine->ra_rtx);
+ if (machine)
+ {
+ ggc_mark_rtx (machine->ra_rtx);
+ ggc_mark_rtx (machine->gp_save_rtx);
+ }
+}
+
+static void
+alpha_free_machine_status (p)
+ struct function *p;
+{
+ free (p->machine);
+ p->machine = NULL;
}
/* Start the ball rolling with RETURN_ADDR_RTX. */
return reg;
}
+/* Return or create a pseudo containing the gp value for the current
+ function. Needed only if TARGET_LD_BUGGY_LDGP. */
+
+rtx
+alpha_gp_save_rtx ()
+{
+ rtx init, reg;
+
+ reg = cfun->machine->gp_save_rtx;
+ if (reg == NULL)
+ {
+ reg = gen_reg_rtx (DImode);
+ cfun->machine->gp_save_rtx = reg;
+ init = gen_rtx_SET (VOIDmode, reg, gen_rtx_REG (DImode, 29));
+
+ push_topmost_sequence ();
+ emit_insn_after (init, get_insns ());
+ pop_topmost_sequence ();
+ }
+
+ return reg;
+}
+
static int
alpha_ra_ever_killed ()
{
if (GET_RTX_CLASS (c) != '<')
output_operand_lossage ("invalid %%C value");
- if (code == 'D')
+ else if (code == 'D')
c = reverse_condition (c);
else if (code == 'c')
c = swap_condition (c);
basereg = REGNO (addr);
else if (GET_CODE (addr) == SUBREG
&& GET_CODE (SUBREG_REG (addr)) == REG)
- basereg = REGNO (SUBREG_REG (addr)) + SUBREG_WORD (addr);
+ basereg = REGNO (SUBREG_REG (addr))
+ + SUBREG_BYTE (addr) / GET_MODE_SIZE (GET_MODE (addr));
else if (GET_CODE (addr) == CONST_INT)
offset = INTVAL (addr);
else
tree t;
tree offset_field, base_field, addr_tree, addend;
tree wide_type, wide_ofs;
+ int indirect = 0;
if (TARGET_OPEN_VMS)
return std_expand_builtin_va_arg (valist, type);
wide_ofs = save_expr (build1 (CONVERT_EXPR, wide_type, offset_field));
addend = wide_ofs;
- if (FLOAT_TYPE_P (type))
+
+ if (TYPE_MODE (type) == TFmode || TYPE_MODE (type) == TCmode)
+ {
+ indirect = 1;
+ tsize = UNITS_PER_WORD;
+ }
+ else if (FLOAT_TYPE_P (type))
{
tree fpaddend, cond;
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ if (indirect)
+ {
+ addr = force_reg (Pmode, addr);
+ addr = gen_rtx_MEM (Pmode, addr);
+ }
+
return addr;
}
\f
fmask |= (1L << (i - 32));
}
+ /* We need to restore these for the handler. */
+ if (current_function_calls_eh_return)
+ {
+ for (i = 0; ; ++i)
+ {
+ unsigned regno = EH_RETURN_DATA_REGNO (i);
+ if (regno == INVALID_REGNUM)
+ break;
+ imask |= 1L << regno;
+ }
+ }
+
if (imask || fmask || alpha_ra_ever_killed ())
imask |= (1L << REG_RA);
}
}
if (frame_size != 0)
- {
- FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (-frame_size))));
- }
+ FRP (emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-frame_size))));
}
else
{
if (TARGET_OPEN_VMS)
{
if (!vms_is_stack_procedure)
- {
- /* Register frame procedures fave the fp. */
- FRP (emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno),
- hard_frame_pointer_rtx));
- }
+ /* Register frame procedures fave the fp. */
+ FRP (emit_move_insn (gen_rtx_REG (DImode, vms_save_fp_regno),
+ hard_frame_pointer_rtx));
if (vms_base_regno != REG_PV)
FRP (emit_move_insn (gen_rtx_REG (DImode, vms_base_regno),
gen_rtx_REG (DImode, REG_PV)));
if (vms_unwind_regno == HARD_FRAME_POINTER_REGNUM)
- {
- FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
- }
+ FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
/* If we have to allocate space for outgoing args, do it now. */
if (current_function_outgoing_args_size != 0)
- {
- FRP (emit_move_insn (stack_pointer_rtx,
- plus_constant (hard_frame_pointer_rtx,
- - ALPHA_ROUND (current_function_outgoing_args_size))));
- }
+ FRP (emit_move_insn
+ (stack_pointer_rtx,
+ plus_constant (hard_frame_pointer_rtx,
+ - (ALPHA_ROUND
+ (current_function_outgoing_args_size)))));
}
else
{
if (TARGET_CAN_FAULT_IN_PROLOGUE)
FRP (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
else
- {
- /* This must always be the last instruction in the
- prologue, thus we emit a special move + clobber. */
+ /* This must always be the last instruction in the
+ prologue, thus we emit a special move + clobber. */
FRP (emit_insn (gen_init_fp (hard_frame_pointer_rtx,
stack_pointer_rtx, sa_reg)));
- }
}
}
fp_offset = 0;
sa_reg = stack_pointer_rtx;
- eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
+ if (current_function_calls_eh_return)
+ eh_ofs = EH_RETURN_STACKADJ_RTX;
+ else
+ eh_ofs = NULL_RTX;
+
if (sa_size)
{
/* If we have a frame pointer, restore SP from it. */
if ((TARGET_OPEN_VMS
&& vms_unwind_regno == HARD_FRAME_POINTER_REGNUM)
|| (!TARGET_OPEN_VMS && frame_pointer_needed))
- {
- FRP (emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx));
- }
+ FRP (emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx));
/* Cope with very large offsets to the register save area. */
if (reg_offset + sa_size > 0x8000)
/* Restore registers in order, excepting a true frame pointer. */
+ mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, reg_offset));
if (! eh_ofs)
- {
- mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, reg_offset));
- MEM_ALIAS_SET (mem) = alpha_sr_alias_set;
- FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem));
- }
+ MEM_ALIAS_SET (mem) = alpha_sr_alias_set;
+ FRP (emit_move_insn (gen_rtx_REG (DImode, REG_RA), mem));
+
reg_offset += 8;
imask &= ~(1L << REG_RA);
if (fp_is_frame_pointer)
{
emit_insn (gen_blockage ());
- mem = gen_rtx_MEM (DImode, plus_constant(sa_reg, fp_offset));
+ mem = gen_rtx_MEM (DImode, plus_constant (sa_reg, fp_offset));
MEM_ALIAS_SET (mem) = alpha_sr_alias_set;
FRP (emit_move_insn (hard_frame_pointer_rtx, mem));
}
else if (write_symbols == DBX_DEBUG)
{
ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
- fprintf (stream, "%s ", ASM_STABS_OP);
+ fprintf (stream, "%s", ASM_STABS_OP);
output_quoted_string (stream, name);
fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]);
}
{
/* mips-tfile doesn't understand .stabd directives. */
++sym_lineno;
- fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n",
+ fprintf (stream, "$LM%d:\n%s%d,0,%d,$LM%d\n",
sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno);
}
else
case REG:
{
int regno = REGNO (x);
- unsigned long mask = 1UL << (regno % 32);
+ unsigned long mask = ((unsigned long) 1) << (regno % 32);
if (regno == 31 || regno == 63)
break;
else
fvptr = &float_values[4];
- bcopy ((char *) d, (char *) &r, sizeof (REAL_VALUE_TYPE));
+ memcpy (&r, d, sizeof (REAL_VALUE_TYPE));
if (REAL_VALUES_LESS (fvptr[0], r))
{
- bcopy ((char *) &fvptr[0], (char *) d,
- sizeof (REAL_VALUE_TYPE));
+ memcpy (d, &fvptr[0], sizeof (REAL_VALUE_TYPE));
return 1;
}
else if (REAL_VALUES_LESS (r, fvptr[1]))
{
- bcopy ((char *) &fvptr[1], (char *) d,
- sizeof (REAL_VALUE_TYPE));
+ memcpy (d, &fvptr[1], sizeof (REAL_VALUE_TYPE));
return 1;
}
else if (REAL_VALUES_LESS (dconst0, r)
&& REAL_VALUES_LESS (r, fvptr[2]))
{
- bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE));
+ memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE));
return 1;
}
else if (REAL_VALUES_LESS (r, dconst0)
&& REAL_VALUES_LESS (fvptr[3], r))
{
- bcopy ((char *) &dconst0, (char *) d, sizeof (REAL_VALUE_TYPE));
+ memcpy (d, &dconst0, sizeof (REAL_VALUE_TYPE));
return 1;
}
}
/* Construct a SYMBOL_REF for us to call. */
{
size_t name_len = strlen (name);
- char *linksym = ggc_alloc_string (NULL, name_len + 6);
-
+ char *linksym = alloca (name_len + 6);
linksym[0] = '$';
memcpy (linksym + 1, name, name_len);
memcpy (linksym + 1 + name_len, "..lk", 5);
- al->linkage = gen_rtx_SYMBOL_REF (Pmode, linksym);
+ al->linkage = gen_rtx_SYMBOL_REF (Pmode,
+ ggc_alloc_string (linksym, name_len + 5));
}
splay_tree_insert (alpha_links, (splay_tree_key) name,