/mingw-w64-v2.0.999/gcc/src/gcc/config/alpha/alpha.c
C | 9837 lines | 7066 code | 1485 blank | 1286 comment | 1909 complexity | cec2b962a92c448b30d79200c3f77735 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-1.0, LGPL-3.0, Unlicense, GPL-2.0, LGPL-2.0, BSD-3-Clause, GPL-3.0
Large files files are truncated, but you can click here to view the full file
- /* Subroutines used for code generation on the DEC Alpha.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
- Free Software Foundation, Inc.
- Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- GCC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "rtl.h"
- #include "tree.h"
- #include "regs.h"
- #include "hard-reg-set.h"
- #include "insn-config.h"
- #include "conditions.h"
- #include "output.h"
- #include "insn-attr.h"
- #include "flags.h"
- #include "recog.h"
- #include "expr.h"
- #include "optabs.h"
- #include "reload.h"
- #include "obstack.h"
- #include "except.h"
- #include "function.h"
- #include "diagnostic-core.h"
- #include "ggc.h"
- #include "tm_p.h"
- #include "target.h"
- #include "target-def.h"
- #include "common/common-target.h"
- #include "debug.h"
- #include "langhooks.h"
- #include "splay-tree.h"
- #include "gimple.h"
- #include "tree-flow.h"
- #include "tree-stdarg.h"
- #include "tm-constrs.h"
- #include "df.h"
- #include "libfuncs.h"
- #include "opts.h"
- #include "params.h"
- /* Specify which cpu to schedule for. */
- enum processor_type alpha_tune;
- /* Which cpu we're generating code for. */
- enum processor_type alpha_cpu;
- static const char * const alpha_cpu_name[] =
- {
- "ev4", "ev5", "ev6"
- };
- /* Specify how accurate floating-point traps need to be. */
- enum alpha_trap_precision alpha_tp;
- /* Specify the floating-point rounding mode. */
- enum alpha_fp_rounding_mode alpha_fprm;
- /* Specify which things cause traps. */
- enum alpha_fp_trap_mode alpha_fptm;
- /* Nonzero if inside of a function, because the Alpha asm can't
- handle .files inside of functions. */
- static int inside_function = FALSE;
- /* The number of cycles of latency we should assume on memory reads. */
- int alpha_memory_latency = 3;
- /* Whether the function needs the GP. */
- static int alpha_function_needs_gp;
- /* The assembler name of the current function. */
- static const char *alpha_fnname;
- /* The next explicit relocation sequence number. */
- extern GTY(()) int alpha_next_sequence_number;
- int alpha_next_sequence_number = 1;
- /* The literal and gpdisp sequence numbers for this insn, as printed
- by %# and %* respectively. */
- extern GTY(()) int alpha_this_literal_sequence_number;
- extern GTY(()) int alpha_this_gpdisp_sequence_number;
- int alpha_this_literal_sequence_number;
- int alpha_this_gpdisp_sequence_number;
- /* Costs of various operations on the different architectures. */
- struct alpha_rtx_cost_data
- {
- unsigned char fp_add;
- unsigned char fp_mult;
- unsigned char fp_div_sf;
- unsigned char fp_div_df;
- unsigned char int_mult_si;
- unsigned char int_mult_di;
- unsigned char int_shift;
- unsigned char int_cmov;
- unsigned short int_div;
- };
- static struct alpha_rtx_cost_data const alpha_rtx_cost_data[PROCESSOR_MAX] =
- {
- { /* EV4 */
- COSTS_N_INSNS (6), /* fp_add */
- COSTS_N_INSNS (6), /* fp_mult */
- COSTS_N_INSNS (34), /* fp_div_sf */
- COSTS_N_INSNS (63), /* fp_div_df */
- COSTS_N_INSNS (23), /* int_mult_si */
- COSTS_N_INSNS (23), /* int_mult_di */
- COSTS_N_INSNS (2), /* int_shift */
- COSTS_N_INSNS (2), /* int_cmov */
- COSTS_N_INSNS (97), /* int_div */
- },
- { /* EV5 */
- COSTS_N_INSNS (4), /* fp_add */
- COSTS_N_INSNS (4), /* fp_mult */
- COSTS_N_INSNS (15), /* fp_div_sf */
- COSTS_N_INSNS (22), /* fp_div_df */
- COSTS_N_INSNS (8), /* int_mult_si */
- COSTS_N_INSNS (12), /* int_mult_di */
- COSTS_N_INSNS (1) + 1, /* int_shift */
- COSTS_N_INSNS (1), /* int_cmov */
- COSTS_N_INSNS (83), /* int_div */
- },
- { /* EV6 */
- COSTS_N_INSNS (4), /* fp_add */
- COSTS_N_INSNS (4), /* fp_mult */
- COSTS_N_INSNS (12), /* fp_div_sf */
- COSTS_N_INSNS (15), /* fp_div_df */
- COSTS_N_INSNS (7), /* int_mult_si */
- COSTS_N_INSNS (7), /* int_mult_di */
- COSTS_N_INSNS (1), /* int_shift */
- COSTS_N_INSNS (2), /* int_cmov */
- COSTS_N_INSNS (86), /* int_div */
- },
- };
- /* Similar but tuned for code size instead of execution latency. The
- extra +N is fractional cost tuning based on latency. It's used to
- encourage use of cheaper insns like shift, but only if there's just
- one of them. */
- static struct alpha_rtx_cost_data const alpha_rtx_cost_size =
- {
- COSTS_N_INSNS (1), /* fp_add */
- COSTS_N_INSNS (1), /* fp_mult */
- COSTS_N_INSNS (1), /* fp_div_sf */
- COSTS_N_INSNS (1) + 1, /* fp_div_df */
- COSTS_N_INSNS (1) + 1, /* int_mult_si */
- COSTS_N_INSNS (1) + 2, /* int_mult_di */
- COSTS_N_INSNS (1), /* int_shift */
- COSTS_N_INSNS (1), /* int_cmov */
- COSTS_N_INSNS (6), /* int_div */
- };
- /* Get the number of args of a function in one of two ways. */
- #if TARGET_ABI_OPEN_VMS
- #define NUM_ARGS crtl->args.info.num_args
- #else
- #define NUM_ARGS crtl->args.info
- #endif
- #define REG_PV 27
- #define REG_RA 26
- /* Declarations of static functions. */
- static struct machine_function *alpha_init_machine_status (void);
- static rtx alpha_emit_xfloating_compare (enum rtx_code *, rtx, rtx);
- #if TARGET_ABI_OPEN_VMS
- static void alpha_write_linkage (FILE *, const char *);
- static bool vms_valid_pointer_mode (enum machine_mode);
- #else
- #define vms_patch_builtins() gcc_unreachable()
- #endif
- #ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
- /* Implement TARGET_MANGLE_TYPE. */
- static const char *
- alpha_mangle_type (const_tree type)
- {
- if (TYPE_MAIN_VARIANT (type) == long_double_type_node
- && TARGET_LONG_DOUBLE_128)
- return "g";
- /* For all other types, use normal C++ mangling. */
- return NULL;
- }
- #endif
- /* Parse target option strings. */
- static void
- alpha_option_override (void)
- {
- static const struct cpu_table {
- const char *const name;
- const enum processor_type processor;
- const int flags;
- const unsigned short line_size; /* in bytes */
- const unsigned short l1_size; /* in kb. */
- const unsigned short l2_size; /* in kb. */
- } cpu_table[] = {
- /* EV4/LCA45 had 8k L1 caches; EV45 had 16k L1 caches.
- EV4/EV45 had 128k to 16M 32-byte direct Bcache. LCA45
- had 64k to 8M 8-byte direct Bcache. */
- { "ev4", PROCESSOR_EV4, 0, 32, 8, 8*1024 },
- { "21064", PROCESSOR_EV4, 0, 32, 8, 8*1024 },
- { "ev45", PROCESSOR_EV4, 0, 32, 16, 16*1024 },
- /* EV5 or EV56 had 8k 32 byte L1, 96k 32 or 64 byte L2,
- and 1M to 16M 64 byte L3 (not modeled).
- PCA56 had 16k 64-byte cache; PCA57 had 32k Icache.
- PCA56 had 8k 64-byte cache; PCA57 had 16k Dcache. */
- { "ev5", PROCESSOR_EV5, 0, 32, 8, 96 },
- { "21164", PROCESSOR_EV5, 0, 32, 8, 96 },
- { "ev56", PROCESSOR_EV5, MASK_BWX, 32, 8, 96 },
- { "21164a", PROCESSOR_EV5, MASK_BWX, 32, 8, 96 },
- { "pca56", PROCESSOR_EV5, MASK_BWX|MASK_MAX, 64, 16, 4*1024 },
- { "21164PC",PROCESSOR_EV5, MASK_BWX|MASK_MAX, 64, 16, 4*1024 },
- { "21164pc",PROCESSOR_EV5, MASK_BWX|MASK_MAX, 64, 16, 4*1024 },
- /* EV6 had 64k 64 byte L1, 1M to 16M Bcache. */
- { "ev6", PROCESSOR_EV6, MASK_BWX|MASK_MAX|MASK_FIX, 64, 64, 16*1024 },
- { "21264", PROCESSOR_EV6, MASK_BWX|MASK_MAX|MASK_FIX, 64, 64, 16*1024 },
- { "ev67", PROCESSOR_EV6, MASK_BWX|MASK_MAX|MASK_FIX|MASK_CIX,
- 64, 64, 16*1024 },
- { "21264a", PROCESSOR_EV6, MASK_BWX|MASK_MAX|MASK_FIX|MASK_CIX,
- 64, 64, 16*1024 }
- };
- int const ct_size = ARRAY_SIZE (cpu_table);
- int line_size = 0, l1_size = 0, l2_size = 0;
- int i;
- #ifdef SUBTARGET_OVERRIDE_OPTIONS
- SUBTARGET_OVERRIDE_OPTIONS;
- #endif
- /* Default to full IEEE compliance mode for Go language. */
- if (strcmp (lang_hooks.name, "GNU Go") == 0
- && !(target_flags_explicit & MASK_IEEE))
- target_flags |= MASK_IEEE;
- alpha_fprm = ALPHA_FPRM_NORM;
- alpha_tp = ALPHA_TP_PROG;
- alpha_fptm = ALPHA_FPTM_N;
- if (TARGET_IEEE)
- {
- alpha_tp = ALPHA_TP_INSN;
- alpha_fptm = ALPHA_FPTM_SU;
- }
- if (TARGET_IEEE_WITH_INEXACT)
- {
- alpha_tp = ALPHA_TP_INSN;
- alpha_fptm = ALPHA_FPTM_SUI;
- }
- if (alpha_tp_string)
- {
- if (! strcmp (alpha_tp_string, "p"))
- alpha_tp = ALPHA_TP_PROG;
- else if (! strcmp (alpha_tp_string, "f"))
- alpha_tp = ALPHA_TP_FUNC;
- else if (! strcmp (alpha_tp_string, "i"))
- alpha_tp = ALPHA_TP_INSN;
- else
- error ("bad value %qs for -mtrap-precision switch", alpha_tp_string);
- }
- if (alpha_fprm_string)
- {
- if (! strcmp (alpha_fprm_string, "n"))
- alpha_fprm = ALPHA_FPRM_NORM;
- else if (! strcmp (alpha_fprm_string, "m"))
- alpha_fprm = ALPHA_FPRM_MINF;
- else if (! strcmp (alpha_fprm_string, "c"))
- alpha_fprm = ALPHA_FPRM_CHOP;
- else if (! strcmp (alpha_fprm_string,"d"))
- alpha_fprm = ALPHA_FPRM_DYN;
- else
- error ("bad value %qs for -mfp-rounding-mode switch",
- alpha_fprm_string);
- }
- if (alpha_fptm_string)
- {
- if (strcmp (alpha_fptm_string, "n") == 0)
- alpha_fptm = ALPHA_FPTM_N;
- else if (strcmp (alpha_fptm_string, "u") == 0)
- alpha_fptm = ALPHA_FPTM_U;
- else if (strcmp (alpha_fptm_string, "su") == 0)
- alpha_fptm = ALPHA_FPTM_SU;
- else if (strcmp (alpha_fptm_string, "sui") == 0)
- alpha_fptm = ALPHA_FPTM_SUI;
- else
- error ("bad value %qs for -mfp-trap-mode switch", alpha_fptm_string);
- }
- if (alpha_cpu_string)
- {
- for (i = 0; i < ct_size; i++)
- if (! strcmp (alpha_cpu_string, cpu_table [i].name))
- {
- alpha_tune = alpha_cpu = cpu_table[i].processor;
- line_size = cpu_table[i].line_size;
- l1_size = cpu_table[i].l1_size;
- l2_size = cpu_table[i].l2_size;
- target_flags &= ~ (MASK_BWX | MASK_MAX | MASK_FIX | MASK_CIX);
- target_flags |= cpu_table[i].flags;
- break;
- }
- if (i == ct_size)
- error ("bad value %qs for -mcpu switch", alpha_cpu_string);
- }
- if (alpha_tune_string)
- {
- for (i = 0; i < ct_size; i++)
- if (! strcmp (alpha_tune_string, cpu_table [i].name))
- {
- alpha_tune = cpu_table[i].processor;
- line_size = cpu_table[i].line_size;
- l1_size = cpu_table[i].l1_size;
- l2_size = cpu_table[i].l2_size;
- break;
- }
- if (i == ct_size)
- error ("bad value %qs for -mtune switch", alpha_tune_string);
- }
- if (line_size)
- maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE, line_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
- if (l1_size)
- maybe_set_param_value (PARAM_L1_CACHE_SIZE, l1_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
- if (l2_size)
- maybe_set_param_value (PARAM_L2_CACHE_SIZE, l2_size,
- global_options.x_param_values,
- global_options_set.x_param_values);
- /* 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)
- {
- warning (0, "fp software completion requires -mtrap-precision=i");
- alpha_tp = ALPHA_TP_INSN;
- }
- if (alpha_cpu == PROCESSOR_EV6)
- {
- /* Except for EV6 pass 1 (not released), we always have precise
- arithmetic traps. Which means we can do software completion
- without minding trap shadows. */
- alpha_tp = ALPHA_TP_PROG;
- }
- if (TARGET_FLOAT_VAX)
- {
- if (alpha_fprm == ALPHA_FPRM_MINF || alpha_fprm == ALPHA_FPRM_DYN)
- {
- warning (0, "rounding mode not supported for VAX floats");
- alpha_fprm = ALPHA_FPRM_NORM;
- }
- if (alpha_fptm == ALPHA_FPTM_SUI)
- {
- warning (0, "trap mode not supported for VAX floats");
- alpha_fptm = ALPHA_FPTM_SU;
- }
- if (target_flags_explicit & MASK_LONG_DOUBLE_128)
- warning (0, "128-bit long double not supported for VAX floats");
- target_flags &= ~MASK_LONG_DOUBLE_128;
- }
- {
- char *end;
- int lat;
- if (!alpha_mlat_string)
- alpha_mlat_string = "L1";
- if (ISDIGIT ((unsigned char)alpha_mlat_string[0])
- && (lat = strtol (alpha_mlat_string, &end, 10), *end == '\0'))
- ;
- else if ((alpha_mlat_string[0] == 'L' || alpha_mlat_string[0] == 'l')
- && ISDIGIT ((unsigned char)alpha_mlat_string[1])
- && alpha_mlat_string[2] == '\0')
- {
- static int const cache_latency[][4] =
- {
- { 3, 30, -1 }, /* ev4 -- Bcache is a guess */
- { 2, 12, 38 }, /* ev5 -- Bcache from PC164 LMbench numbers */
- { 3, 12, 30 }, /* ev6 -- Bcache from DS20 LMbench. */
- };
- lat = alpha_mlat_string[1] - '0';
- if (lat <= 0 || lat > 3 || cache_latency[alpha_tune][lat-1] == -1)
- {
- warning (0, "L%d cache latency unknown for %s",
- lat, alpha_cpu_name[alpha_tune]);
- lat = 3;
- }
- else
- lat = cache_latency[alpha_tune][lat-1];
- }
- else if (! strcmp (alpha_mlat_string, "main"))
- {
- /* Most current memories have about 370ns latency. This is
- a reasonable guess for a fast cpu. */
- lat = 150;
- }
- else
- {
- warning (0, "bad value %qs for -mmemory-latency", alpha_mlat_string);
- lat = 3;
- }
- alpha_memory_latency = lat;
- }
- /* Default the definition of "small data" to 8 bytes. */
- if (!global_options_set.x_g_switch_value)
- g_switch_value = 8;
- /* Infer TARGET_SMALL_DATA from -fpic/-fPIC. */
- if (flag_pic == 1)
- target_flags |= MASK_SMALL_DATA;
- else if (flag_pic == 2)
- target_flags &= ~MASK_SMALL_DATA;
- /* Align labels and loops for optimal branching. */
- /* ??? Kludge these by not doing anything if we don't optimize. */
- if (optimize > 0)
- {
- if (align_loops <= 0)
- align_loops = 16;
- if (align_jumps <= 0)
- align_jumps = 16;
- }
- if (align_functions <= 0)
- align_functions = 16;
- /* Register variables and functions with the garbage collector. */
- /* Set up function hooks. */
- init_machine_status = alpha_init_machine_status;
- /* Tell the compiler when we're using VAX floating point. */
- if (TARGET_FLOAT_VAX)
- {
- REAL_MODE_FORMAT (SFmode) = &vax_f_format;
- REAL_MODE_FORMAT (DFmode) = &vax_g_format;
- REAL_MODE_FORMAT (TFmode) = NULL;
- }
- #ifdef TARGET_DEFAULT_LONG_DOUBLE_128
- if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
- target_flags |= MASK_LONG_DOUBLE_128;
- #endif
- }
- /* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */
- int
- zap_mask (HOST_WIDE_INT value)
- {
- int i;
- for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR;
- i++, value >>= 8)
- if ((value & 0xff) != 0 && (value & 0xff) != 0xff)
- return 0;
- return 1;
- }
- /* Return true if OP is valid for a particular TLS relocation.
- We are already guaranteed that OP is a CONST. */
- int
- tls_symbolic_operand_1 (rtx op, int size, int unspec)
- {
- op = XEXP (op, 0);
- if (GET_CODE (op) != UNSPEC || XINT (op, 1) != unspec)
- return 0;
- op = XVECEXP (op, 0, 0);
- if (GET_CODE (op) != SYMBOL_REF)
- return 0;
- switch (SYMBOL_REF_TLS_MODEL (op))
- {
- case TLS_MODEL_LOCAL_DYNAMIC:
- return unspec == UNSPEC_DTPREL && size == alpha_tls_size;
- case TLS_MODEL_INITIAL_EXEC:
- return unspec == UNSPEC_TPREL && size == 64;
- case TLS_MODEL_LOCAL_EXEC:
- return unspec == UNSPEC_TPREL && size == alpha_tls_size;
- default:
- gcc_unreachable ();
- }
- }
- /* Used by aligned_memory_operand and unaligned_memory_operand to
- resolve what reload is going to do with OP if it's a register. */
- rtx
- resolve_reload_operand (rtx op)
- {
- if (reload_in_progress)
- {
- rtx tmp = op;
- if (GET_CODE (tmp) == SUBREG)
- tmp = SUBREG_REG (tmp);
- if (REG_P (tmp)
- && REGNO (tmp) >= FIRST_PSEUDO_REGISTER)
- {
- op = reg_equiv_memory_loc (REGNO (tmp));
- if (op == 0)
- return 0;
- }
- }
- return op;
- }
- /* The scalar modes supported differs from the default check-what-c-supports
- version in that sometimes TFmode is available even when long double
- indicates only DFmode. */
- static bool
- alpha_scalar_mode_supported_p (enum machine_mode mode)
- {
- switch (mode)
- {
- case QImode:
- case HImode:
- case SImode:
- case DImode:
- case TImode: /* via optabs.c */
- return true;
- case SFmode:
- case DFmode:
- return true;
- case TFmode:
- return TARGET_HAS_XFLOATING_LIBS;
- default:
- return false;
- }
- }
- /* Alpha implements a couple of integer vector mode operations when
- TARGET_MAX is enabled. We do not check TARGET_MAX here, however,
- which allows the vectorizer to operate on e.g. move instructions,
- or when expand_vector_operations can do something useful. */
- static bool
- alpha_vector_mode_supported_p (enum machine_mode mode)
- {
- return mode == V8QImode || mode == V4HImode || mode == V2SImode;
- }
- /* Return 1 if this function can directly return via $26. */
- int
- direct_return (void)
- {
- return (TARGET_ABI_OSF
- && reload_completed
- && alpha_sa_size () == 0
- && get_frame_size () == 0
- && crtl->outgoing_args_size == 0
- && crtl->args.pretend_args_size == 0);
- }
- /* Return the TLS model to use for SYMBOL. */
- static enum tls_model
- tls_symbolic_operand_type (rtx symbol)
- {
- enum tls_model model;
- if (GET_CODE (symbol) != SYMBOL_REF)
- return TLS_MODEL_NONE;
- model = SYMBOL_REF_TLS_MODEL (symbol);
- /* Local-exec with a 64-bit size is the same code as initial-exec. */
- if (model == TLS_MODEL_LOCAL_EXEC && alpha_tls_size == 64)
- model = TLS_MODEL_INITIAL_EXEC;
- return model;
- }
- /* Return true if the function DECL will share the same GP as any
- function in the current unit of translation. */
- static bool
- decl_has_samegp (const_tree decl)
- {
- /* Functions that are not local can be overridden, and thus may
- not share the same gp. */
- if (!(*targetm.binds_local_p) (decl))
- return false;
- /* If -msmall-data is in effect, assume that there is only one GP
- for the module, and so any local symbol has this property. We
- need explicit relocations to be able to enforce this for symbols
- not defined in this unit of translation, however. */
- if (TARGET_EXPLICIT_RELOCS && TARGET_SMALL_DATA)
- return true;
- /* Functions that are not external are defined in this UoT. */
- /* ??? Irritatingly, static functions not yet emitted are still
- marked "external". Apply this to non-static functions only. */
- return !TREE_PUBLIC (decl) || !DECL_EXTERNAL (decl);
- }
- /* Return true if EXP should be placed in the small data section. */
- static bool
- alpha_in_small_data_p (const_tree exp)
- {
- /* We want to merge strings, so we never consider them small data. */
- if (TREE_CODE (exp) == STRING_CST)
- return false;
- /* Functions are never in the small data area. Duh. */
- if (TREE_CODE (exp) == FUNCTION_DECL)
- return false;
- if (TREE_CODE (exp) == VAR_DECL && DECL_SECTION_NAME (exp))
- {
- const char *section = TREE_STRING_POINTER (DECL_SECTION_NAME (exp));
- if (strcmp (section, ".sdata") == 0
- || strcmp (section, ".sbss") == 0)
- return true;
- }
- else
- {
- HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (exp));
- /* If this is an incomplete type with size 0, then we can't put it
- in sdata because it might be too big when completed. */
- if (size > 0 && size <= g_switch_value)
- return true;
- }
- return false;
- }
- #if TARGET_ABI_OPEN_VMS
- static bool
- vms_valid_pointer_mode (enum machine_mode mode)
- {
- return (mode == SImode || mode == DImode);
- }
- static bool
- alpha_linkage_symbol_p (const char *symname)
- {
- int symlen = strlen (symname);
- if (symlen > 4)
- return strcmp (&symname [symlen - 4], "..lk") == 0;
- return false;
- }
- #define LINKAGE_SYMBOL_REF_P(X) \
- ((GET_CODE (X) == SYMBOL_REF \
- && alpha_linkage_symbol_p (XSTR (X, 0))) \
- || (GET_CODE (X) == CONST \
- && GET_CODE (XEXP (X, 0)) == PLUS \
- && GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF \
- && alpha_linkage_symbol_p (XSTR (XEXP (XEXP (X, 0), 0), 0))))
- #endif
- /* legitimate_address_p recognizes an RTL expression that is a valid
- memory address for an instruction. The MODE argument is the
- machine mode for the MEM expression that wants to use this address.
- For Alpha, we have either a constant address or the sum of a
- register and a constant address, or just a register. For DImode,
- any of those forms can be surrounded with an AND that clear the
- low-order three bits; this is an "unaligned" access. */
- static bool
- alpha_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
- {
- /* If this is an ldq_u type address, discard the outer AND. */
- if (mode == DImode
- && GET_CODE (x) == AND
- && CONST_INT_P (XEXP (x, 1))
- && INTVAL (XEXP (x, 1)) == -8)
- x = XEXP (x, 0);
- /* Discard non-paradoxical subregs. */
- if (GET_CODE (x) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
- x = SUBREG_REG (x);
- /* Unadorned general registers are valid. */
- if (REG_P (x)
- && (strict
- ? STRICT_REG_OK_FOR_BASE_P (x)
- : NONSTRICT_REG_OK_FOR_BASE_P (x)))
- return true;
- /* Constant addresses (i.e. +/- 32k) are valid. */
- if (CONSTANT_ADDRESS_P (x))
- return true;
- #if TARGET_ABI_OPEN_VMS
- if (LINKAGE_SYMBOL_REF_P (x))
- return true;
- #endif
- /* Register plus a small constant offset is valid. */
- if (GET_CODE (x) == PLUS)
- {
- rtx ofs = XEXP (x, 1);
- x = XEXP (x, 0);
- /* Discard non-paradoxical subregs. */
- if (GET_CODE (x) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
- x = SUBREG_REG (x);
- if (REG_P (x))
- {
- if (! strict
- && NONSTRICT_REG_OK_FP_BASE_P (x)
- && CONST_INT_P (ofs))
- return true;
- if ((strict
- ? STRICT_REG_OK_FOR_BASE_P (x)
- : NONSTRICT_REG_OK_FOR_BASE_P (x))
- && CONSTANT_ADDRESS_P (ofs))
- return true;
- }
- }
- /* If we're managing explicit relocations, LO_SUM is valid, as are small
- data symbols. Avoid explicit relocations of modes larger than word
- mode since i.e. $LC0+8($1) can fold around +/- 32k offset. */
- else if (TARGET_EXPLICIT_RELOCS
- && GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
- {
- if (small_symbolic_operand (x, Pmode))
- return true;
- if (GET_CODE (x) == LO_SUM)
- {
- rtx ofs = XEXP (x, 1);
- x = XEXP (x, 0);
- /* Discard non-paradoxical subregs. */
- if (GET_CODE (x) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
- x = SUBREG_REG (x);
- /* Must have a valid base register. */
- if (! (REG_P (x)
- && (strict
- ? STRICT_REG_OK_FOR_BASE_P (x)
- : NONSTRICT_REG_OK_FOR_BASE_P (x))))
- return false;
- /* The symbol must be local. */
- if (local_symbolic_operand (ofs, Pmode)
- || dtp32_symbolic_operand (ofs, Pmode)
- || tp32_symbolic_operand (ofs, Pmode))
- return true;
- }
- }
- return false;
- }
- /* Build the SYMBOL_REF for __tls_get_addr. */
- static GTY(()) rtx tls_get_addr_libfunc;
- static rtx
- get_tls_get_addr (void)
- {
- if (!tls_get_addr_libfunc)
- tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
- return tls_get_addr_libfunc;
- }
- /* Try machine-dependent ways of modifying an illegitimate address
- to be legitimate. If we find one, return the new, valid address. */
- static rtx
- alpha_legitimize_address_1 (rtx x, rtx scratch, enum machine_mode mode)
- {
- HOST_WIDE_INT addend;
- /* If the address is (plus reg const_int) and the CONST_INT is not a
- valid offset, compute the high part of the constant and add it to
- the register. Then our address is (plus temp low-part-const). */
- if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && CONST_INT_P (XEXP (x, 1))
- && ! CONSTANT_ADDRESS_P (XEXP (x, 1)))
- {
- addend = INTVAL (XEXP (x, 1));
- x = XEXP (x, 0);
- goto split_addend;
- }
- /* If the address is (const (plus FOO const_int)), find the low-order
- part of the CONST_INT. Then load FOO plus any high-order part of the
- CONST_INT into a register. Our address is (plus reg low-part-const).
- This is done to reduce the number of GOT entries. */
- if (can_create_pseudo_p ()
- && GET_CODE (x) == CONST
- && GET_CODE (XEXP (x, 0)) == PLUS
- && CONST_INT_P (XEXP (XEXP (x, 0), 1)))
- {
- addend = INTVAL (XEXP (XEXP (x, 0), 1));
- x = force_reg (Pmode, XEXP (XEXP (x, 0), 0));
- goto split_addend;
- }
- /* If we have a (plus reg const), emit the load as in (2), then add
- the two registers, and finally generate (plus reg low-part-const) as
- our address. */
- if (can_create_pseudo_p ()
- && GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == CONST
- && GET_CODE (XEXP (XEXP (x, 1), 0)) == PLUS
- && CONST_INT_P (XEXP (XEXP (XEXP (x, 1), 0), 1)))
- {
- addend = INTVAL (XEXP (XEXP (XEXP (x, 1), 0), 1));
- x = expand_simple_binop (Pmode, PLUS, XEXP (x, 0),
- XEXP (XEXP (XEXP (x, 1), 0), 0),
- NULL_RTX, 1, OPTAB_LIB_WIDEN);
- goto split_addend;
- }
- /* If this is a local symbol, split the address into HIGH/LO_SUM parts.
- Avoid modes larger than word mode since i.e. $LC0+8($1) can fold
- around +/- 32k offset. */
- if (TARGET_EXPLICIT_RELOCS
- && GET_MODE_SIZE (mode) <= UNITS_PER_WORD
- && symbolic_operand (x, Pmode))
- {
- rtx r0, r16, eqv, tga, tp, insn, dest, seq;
- switch (tls_symbolic_operand_type (x))
- {
- case TLS_MODEL_NONE:
- break;
- case TLS_MODEL_GLOBAL_DYNAMIC:
- start_sequence ();
- r0 = gen_rtx_REG (Pmode, 0);
- r16 = gen_rtx_REG (Pmode, 16);
- tga = get_tls_get_addr ();
- dest = gen_reg_rtx (Pmode);
- seq = GEN_INT (alpha_next_sequence_number++);
- emit_insn (gen_movdi_er_tlsgd (r16, pic_offset_table_rtx, x, seq));
- insn = gen_call_value_osf_tlsgd (r0, tga, seq);
- insn = emit_call_insn (insn);
- RTL_CONST_CALL_P (insn) = 1;
- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16);
- insn = get_insns ();
- end_sequence ();
- emit_libcall_block (insn, dest, r0, x);
- return dest;
- case TLS_MODEL_LOCAL_DYNAMIC:
- start_sequence ();
- r0 = gen_rtx_REG (Pmode, 0);
- r16 = gen_rtx_REG (Pmode, 16);
- tga = get_tls_get_addr ();
- scratch = gen_reg_rtx (Pmode);
- seq = GEN_INT (alpha_next_sequence_number++);
- emit_insn (gen_movdi_er_tlsldm (r16, pic_offset_table_rtx, seq));
- insn = gen_call_value_osf_tlsldm (r0, tga, seq);
- insn = emit_call_insn (insn);
- RTL_CONST_CALL_P (insn) = 1;
- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r16);
- insn = get_insns ();
- end_sequence ();
- eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
- UNSPEC_TLSLDM_CALL);
- emit_libcall_block (insn, scratch, r0, eqv);
- eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_DTPREL);
- eqv = gen_rtx_CONST (Pmode, eqv);
- if (alpha_tls_size == 64)
- {
- dest = gen_reg_rtx (Pmode);
- emit_insn (gen_rtx_SET (VOIDmode, dest, eqv));
- emit_insn (gen_adddi3 (dest, dest, scratch));
- return dest;
- }
- if (alpha_tls_size == 32)
- {
- insn = gen_rtx_HIGH (Pmode, eqv);
- insn = gen_rtx_PLUS (Pmode, scratch, insn);
- scratch = gen_reg_rtx (Pmode);
- emit_insn (gen_rtx_SET (VOIDmode, scratch, insn));
- }
- return gen_rtx_LO_SUM (Pmode, scratch, eqv);
- case TLS_MODEL_INITIAL_EXEC:
- eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL);
- eqv = gen_rtx_CONST (Pmode, eqv);
- tp = gen_reg_rtx (Pmode);
- scratch = gen_reg_rtx (Pmode);
- dest = gen_reg_rtx (Pmode);
- emit_insn (gen_load_tp (tp));
- emit_insn (gen_rtx_SET (VOIDmode, scratch, eqv));
- emit_insn (gen_adddi3 (dest, tp, scratch));
- return dest;
- case TLS_MODEL_LOCAL_EXEC:
- eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, x), UNSPEC_TPREL);
- eqv = gen_rtx_CONST (Pmode, eqv);
- tp = gen_reg_rtx (Pmode);
- emit_insn (gen_load_tp (tp));
- if (alpha_tls_size == 32)
- {
- insn = gen_rtx_HIGH (Pmode, eqv);
- insn = gen_rtx_PLUS (Pmode, tp, insn);
- tp = gen_reg_rtx (Pmode);
- emit_insn (gen_rtx_SET (VOIDmode, tp, insn));
- }
- return gen_rtx_LO_SUM (Pmode, tp, eqv);
- default:
- gcc_unreachable ();
- }
- if (local_symbolic_operand (x, Pmode))
- {
- if (small_symbolic_operand (x, Pmode))
- return x;
- else
- {
- if (can_create_pseudo_p ())
- scratch = gen_reg_rtx (Pmode);
- emit_insn (gen_rtx_SET (VOIDmode, scratch,
- gen_rtx_HIGH (Pmode, x)));
- return gen_rtx_LO_SUM (Pmode, scratch, x);
- }
- }
- }
- return NULL;
- split_addend:
- {
- HOST_WIDE_INT low, high;
- low = ((addend & 0xffff) ^ 0x8000) - 0x8000;
- addend -= low;
- high = ((addend & 0xffffffff) ^ 0x80000000) - 0x80000000;
- addend -= high;
- if (addend)
- x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (addend),
- (!can_create_pseudo_p () ? scratch : NULL_RTX),
- 1, OPTAB_LIB_WIDEN);
- if (high)
- x = expand_simple_binop (Pmode, PLUS, x, GEN_INT (high),
- (!can_create_pseudo_p () ? scratch : NULL_RTX),
- 1, OPTAB_LIB_WIDEN);
- return plus_constant (Pmode, x, low);
- }
- }
- /* Try machine-dependent ways of modifying an illegitimate address
- to be legitimate. Return X or the new, valid address. */
- static rtx
- alpha_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
- enum machine_mode mode)
- {
- rtx new_x = alpha_legitimize_address_1 (x, NULL_RTX, mode);
- return new_x ? new_x : x;
- }
- /* Primarily this is required for TLS symbols, but given that our move
- patterns *ought* to be able to handle any symbol at any time, we
- should never be spilling symbolic operands to the constant pool, ever. */
- static bool
- alpha_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
- {
- enum rtx_code code = GET_CODE (x);
- return code == SYMBOL_REF || code == LABEL_REF || code == CONST;
- }
- /* We do not allow indirect calls to be optimized into sibling calls, nor
- can we allow a call to a function with a different GP to be optimized
- into a sibcall. */
- static bool
- alpha_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
- {
- /* Can't do indirect tail calls, since we don't know if the target
- uses the same GP. */
- if (!decl)
- return false;
- /* Otherwise, we can make a tail call if the target function shares
- the same GP. */
- return decl_has_samegp (decl);
- }
- int
- some_small_symbolic_operand_int (rtx *px, void *data ATTRIBUTE_UNUSED)
- {
- rtx x = *px;
- /* Don't re-split. */
- if (GET_CODE (x) == LO_SUM)
- return -1;
- return small_symbolic_operand (x, Pmode) != 0;
- }
- static int
- split_small_symbolic_operand_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
- {
- rtx x = *px;
- /* Don't re-split. */
- if (GET_CODE (x) == LO_SUM)
- return -1;
- if (small_symbolic_operand (x, Pmode))
- {
- x = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, x);
- *px = x;
- return -1;
- }
- return 0;
- }
- rtx
- split_small_symbolic_operand (rtx x)
- {
- x = copy_insn (x);
- for_each_rtx (&x, split_small_symbolic_operand_1, NULL);
- return x;
- }
- /* Indicate that INSN cannot be duplicated. This is true for any insn
- that we've marked with gpdisp relocs, since those have to stay in
- 1-1 correspondence with one another.
- Technically we could copy them if we could set up a mapping from one
- sequence number to another, across the set of insns to be duplicated.
- This seems overly complicated and error-prone since interblock motion
- from sched-ebb could move one of the pair of insns to a different block.
- Also cannot allow jsr insns to be duplicated. If they throw exceptions,
- then they'll be in a different block from their ldgp. Which could lead
- the bb reorder code to think that it would be ok to copy just the block
- containing the call and branch to the block containing the ldgp. */
- static bool
- alpha_cannot_copy_insn_p (rtx insn)
- {
- if (!reload_completed || !TARGET_EXPLICIT_RELOCS)
- return false;
- if (recog_memoized (insn) >= 0)
- return get_attr_cannot_copy (insn);
- else
- return false;
- }
- /* Try a machine-dependent way of reloading an illegitimate address
- operand. If we find one, push the reload and return the new rtx. */
- rtx
- alpha_legitimize_reload_address (rtx x,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- int opnum, int type,
- int ind_levels ATTRIBUTE_UNUSED)
- {
- /* We must recognize output that we have already generated ourselves. */
- if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == PLUS
- && REG_P (XEXP (XEXP (x, 0), 0))
- && CONST_INT_P (XEXP (XEXP (x, 0), 1))
- && CONST_INT_P (XEXP (x, 1)))
- {
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- return x;
- }
- /* We wish to handle large displacements off a base register by
- splitting the addend across an ldah and the mem insn. This
- cuts number of extra insns needed from 3 to 1. */
- if (GET_CODE (x) == PLUS
- && REG_P (XEXP (x, 0))
- && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
- && REGNO_OK_FOR_BASE_P (REGNO (XEXP (x, 0)))
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
- HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000;
- HOST_WIDE_INT high
- = (((val - low) & 0xffffffff) ^ 0x80000000) - 0x80000000;
- /* Check for 32-bit overflow. */
- if (high + low != val)
- return NULL_RTX;
- /* Reload the high part into a base reg; leave the low part
- in the mem directly. */
- x = gen_rtx_PLUS (GET_MODE (x),
- gen_rtx_PLUS (GET_MODE (x), XEXP (x, 0),
- GEN_INT (high)),
- GEN_INT (low));
- push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
- BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
- opnum, (enum reload_type) type);
- return x;
- }
- return NULL_RTX;
- }
- /* Compute a (partial) cost for rtx X. Return true if the complete
- cost has been computed, and false if subexpressions should be
- scanned. In either case, *TOTAL contains the cost result. */
- static bool
- alpha_rtx_costs (rtx x, int code, int outer_code, int opno, int *total,
- bool speed)
- {
- enum machine_mode mode = GET_MODE (x);
- bool float_mode_p = FLOAT_MODE_P (mode);
- const struct alpha_rtx_cost_data *cost_data;
- if (!speed)
- cost_data = &alpha_rtx_cost_size;
- else
- cost_data = &alpha_rtx_cost_data[alpha_tune];
- switch (code)
- {
- case CONST_INT:
- /* If this is an 8-bit constant, return zero since it can be used
- nearly anywhere with no cost. If it is a valid operand for an
- ADD or AND, likewise return 0 if we know it will be used in that
- context. Otherwise, return 2 since it might be used there later.
- All other constants take at least two insns. */
- if (INTVAL (x) >= 0 && INTVAL (x) < 256)
- {
- *total = 0;
- return true;
- }
- /* FALLTHRU */
- case CONST_DOUBLE:
- if (x == CONST0_RTX (mode))
- *total = 0;
- else if ((outer_code == PLUS && add_operand (x, VOIDmode))
- || (outer_code == AND && and_operand (x, VOIDmode)))
- *total = 0;
- else if (add_operand (x, VOIDmode) || and_operand (x, VOIDmode))
- *total = 2;
- else
- *total = COSTS_N_INSNS (2);
- return true;
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- if (TARGET_EXPLICIT_RELOCS && small_symbolic_operand (x, VOIDmode))
- *total = COSTS_N_INSNS (outer_code != MEM);
- else if (TARGET_EXPLICIT_RELOCS && local_symbolic_operand (x, VOIDmode))
- *total = COSTS_N_INSNS (1 + (outer_code != MEM));
- else if (tls_symbolic_operand_type (x))
- /* Estimate of cost for call_pal rduniq. */
- /* ??? How many insns do we emit here? More than one... */
- *total = COSTS_N_INSNS (15);
- else
- /* Otherwise we do a load from the GOT. */
- *total = COSTS_N_INSNS (!speed ? 1 : alpha_memory_latency);
- return true;
- case HIGH:
- /* This is effectively an add_operand. */
- *total = 2;
- return true;
- case PLUS:
- case MINUS:
- if (float_mode_p)
- *total = cost_data->fp_add;
- else if (GET_CODE (XEXP (x, 0)) == MULT
- && const48_operand (XEXP (XEXP (x, 0), 1), VOIDmode))
- {
- *total = (rtx_cost (XEXP (XEXP (x, 0), 0),
- (enum rtx_code) outer_code, opno, speed)
- + rtx_cost (XEXP (x, 1),
- (enum rtx_code) outer_code, opno, speed)
- + COSTS_N_INSNS (1));
- return true;
- }
- return false;
- case MULT:
- if (float_mode_p)
- *total = cost_data->fp_mult;
- else if (mode == DImode)
- *total = cost_data->int_mult_di;
- else
- *total = cost_data->int_mult_si;
- return false;
- case ASHIFT:
- if (CONST_INT_P (XEXP (x, 1))
- && INTVAL (XEXP (x, 1)) <= 3)
- {
- *total = COSTS_N_INSNS (1);
- return false;
- }
- /* FALLTHRU */
- case ASHIFTRT:
- case LSHIFTRT:
- *total = cost_data->int_shift;
- return false;
- case IF_THEN_ELSE:
- if (float_mode_p)
- *total = cost_data->fp_add;
- else
- *total = cost_data->int_cmov;
- return false;
- case DIV:
- case UDIV:
- case MOD:
- case UMOD:
- if (!float_mode_p)
- *total = cost_data->int_div;
- else if (mode == SFmode)
- *total = cost_data->fp_div_sf;
- else
- *total = cost_data->fp_div_df;
- return false;
- case MEM:
- *total = COSTS_N_INSNS (!speed ? 1 : alpha_memory_latency);
- return true;
- case NEG:
- if (! float_mode_p)
- {
- *total = COSTS_N_INSNS (1);
- return false;
- }
- /* FALLTHRU */
- case ABS:
- if (! float_mode_p)
- {
- *total = COSTS_N_INSNS (1) + cost_data->int_cmov;
- return false;
- }
- /* FALLTHRU */
- case FLOAT:
- case UNSIGNED_FLOAT:
- case FIX:
- case UNSIGNED_FIX:
- case FLOAT_TRUNCATE:
- *total = cost_data->fp_add;
- return false;
- case FLOAT_EXTEND:
- if (MEM_P (XEXP (x, 0)))
- *total = 0;
- else
- *total = cost_data->fp_add;
- return false;
- default:
- return false;
- }
- }
- /* REF is an alignable memory location. Place an aligned SImode
- reference into *PALIGNED_MEM and the number of bits to shift into
- *PBITNUM. SCRATCH is a free register for use in reloading out
- of range stack slots. */
- void
- get_aligned_mem (rtx ref, rtx *paligned_mem, rtx *pbitnum)
- {
- rtx base;
- HOST_WIDE_INT disp, offset;
- gcc_assert (MEM_P (ref));
- if (reload_in_progress
- && ! memory_address_p (GET_MODE (ref), XEXP (ref, 0)))
- {
- base = find_replacement (&XEXP (ref, 0));
- gcc_assert (memory_address_p (GET_MODE (ref), base));
- }
- else
- base = XEXP (ref, 0);
- if (GET_CODE (base) == PLUS)
- disp = INTVAL (XEXP (base, 1)), base = XEXP (base, 0);
- else
- disp = 0;
- /* Find the byte offset within an aligned word. If the memory itself is
- claimed to be aligned, believe it. Otherwise, aligned_memory_operand
- will have examined the base register and determined it is aligned, and
- thus displacements from it are naturally alignable. */
- if (MEM_ALIGN (ref) >= 32)
- offset = 0;
- else
- offset = disp & 3;
- /* The location should not cross aligned word boundary. */
- gcc_assert (offset + GET_MODE_SIZE (GET_MODE (ref))
- <= GET_MODE_SIZE (SImode));
- /* Access the entire aligned word. */
- *paligned_mem = widen_memory_access (ref, SImode, -offset);
- /* Convert the byte offset within the word to a bit offset. */
- offset *= BITS_PER_UNIT;
- *pbitnum = GEN_INT (offset);
- }
- /* Similar, but just get the address. Handle the two reload cases.
- Add EXTRA_OFFSET to the address we return. */
- rtx
- get_unaligned_address (rtx ref)
- {
- rtx base;
- HOST_WIDE_INT offset = 0;
- gcc_assert (MEM_P (ref));
- if (reload_in_progress
- && ! memory_address_p (GET_MODE (ref), XEXP (ref, 0)))
- {
- base = find_replacement (&XEXP (ref, 0));
- gcc_assert (memory_address_p (GET_MODE (ref), base));
- }
- else
- base = XEXP (ref, 0);
- if (GET_CODE (base) == PLUS)
- offset += INTVAL (XEXP (base, 1)), base = XEXP (base, 0);
- return plus_constant (Pmode, base, offset);
- }
- /* Compute a value X, such that X & 7 == (ADDR + OFS) & 7.
- X is always returned in a register. */
- rtx
- get_unaligned_offset (rtx addr, HOST_WIDE_INT ofs)
- {
- if (GET_CODE (addr) == PLUS)
- {
- ofs += INTVAL (XEXP (addr, 1));
- addr = XEXP (addr, 0);
- }
- return expand_simple_binop (Pmode, PLUS, addr, GEN_INT (ofs & 7),
- NULL_RTX, 1, OPTAB_LIB_WIDEN);
- }
- /* On the Alpha, all (non-symbolic) constants except zero go into
- a floating-point register via memory. Note that we cannot
- return anything that is not a subset of RCLASS, and that some
- symbolic constants cannot be dropped to memory. */
- enum reg_class
- alpha_preferred_reload_class(rtx x, enum reg_class rclass)
- {
- /* Zero is present in any register class. */
- if (x == CONST0_RTX (GET_MODE (x)))
- return rclass;
- /* These sorts of constants we can easily drop to memory. */
- if (CONST_INT_P (x)
- || GET_CODE (x) == CONST_DOUBLE
- || GET_CODE (x) == CONST_VECTOR)
- {
- if (rclass == FLOAT_REGS)
- return NO_REGS;
- if (rclass == ALL_REGS)
- return GENERAL_REGS;
- return rclass;
- }
- /* All other kinds of constants should not (and in the case of HIGH
- cannot) be dropped to memory -- instead we use a GENERAL_REGS
- secondary reload. */
- if (CONSTANT_P (x))
- return (rclass == ALL_REGS ? GENERAL_REGS : rclass);
- return rclass;
- }
- /* Inform reload about cases where moving X with a mode MODE to a register in
- RCLASS requires an extra scratch or immediate register. Return the class
- needed for the immediate register. */
- static reg_class_t
- alpha_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
- enum machine_mode mode, secondary_reload_info *sri)
- {
- enum reg_class rclass = (enum reg_class) rclass_i;
- /* Loading and storing HImode or QImode values to and from memory
- usually requires a scratch register. */
- if (!TARGET_BWX && (mode == QImode || mode == HImode || mode == CQImode))
- {
- if (any_memory_operand (x, mode))
- {
- if (in_p)
- {
- if (!aligned_memory_operand (x, mode))
- sri->icode = direct_optab_handler (reload_in_optab, mode);
- }
- else
- sri->icode = direct_optab_handler (reload_out_optab, mode);
- return NO_REGS;
- }
- }
- /* We also cannot do integral arithmetic into FP regs, as might result
- from register elimination into a DImode fp register. */
- if (rclass == FLOAT_REGS)
- {
- if (MEM_P (x) && GET_CODE (XEXP (x, 0)) == AND)
- return GENERAL_REGS;
- if (in_p && INTEGRAL_MODE_P (mode)
- && !MEM_P (x) && !REG_P (x) && !CONST_INT_P (x))
- return GENERAL_REGS;
- }
- return NO_REGS;
- }
- /* Subfunction of the following function. Update the flags of any MEM
- found in part of X. */
- static int
- alpha_set_memflags_1 (rtx *xp, void *data)
- {
- rtx x = *xp, orig = (rtx) data;
- if (!MEM_P (x))
- return 0;
- MEM_VOLATILE_P (x) = MEM_VOLATILE_P (orig);
- MEM_NOTRAP_P (x) = MEM_NOTRAP_P (orig);
- MEM_READONLY_P (x) = MEM_READONLY_P (orig);
- /* Sadly, we cannot use alias sets because the extra aliasing
- produced by the AND interferes. Given that two-byte quantities
- are the only thing we would be able to differentiate anyway,
- there does not seem to be any point in convoluting the early
- out of the alias check. */
- return -1;
- }
- /* Given SEQ, which is an INSN list, look for any MEMs in either
- a SET_DEST or a SET_SRC and copy the in-struct, unchanging, and
- volatile flags from REF into each of the MEMs found. If REF is not
- a MEM, don't do anything. */
- void
- alpha_set_memflags (rtx seq, rtx ref)
- {
- rtx insn;
- if (!MEM_P (ref))
- return;
- /* This is only called from alpha.md, after having had something
- generated from one of the insn patterns. So if everything is
- zero, the pattern is already up-to-date. */
- if (!MEM_VOLATILE_P (ref)
- && !MEM_NOTRAP_P (ref)
- && !MEM_READONLY_P (ref))
- return;
- for (insn = seq; insn; insn = NEXT_INSN (insn))
- if (INSN_P (insn))
- for_each_rtx (&PATTERN (insn), alpha_set_memflags_1, (void *) ref);
- else
- gcc_unreachable ();
- }
- static rtx alpha_emit_set_const (rtx, enum machine_mode, HOST_WIDE_INT,
- int, bool);
- /* Internal routine for alpha_emit_set_const to check for N or below insns.
- If NO_OUTPUT is true, then we only check to see if N insns are possible,
- and return pc_rtx if successful. */
- static rtx
- alpha_emit_set_const_1 (rtx target, enum machine_mode mode,
- HOST_WIDE_INT c, int n, bool no_output)
- {
- HOST_WIDE_INT new_const;
- int i, bits;
- /* Use a pseudo if highly optimizing and still generating RTL. */
- rtx subtarget
- = (flag_expensive_optimizations && can_create_pseudo_p () ? 0 : target);
- rtx temp, insn;
- /* If this is a sign-extended 32-bit constant, we can do this in at most
- three insns, so do it if we have enough insns left. We always have
- a sign-extended 32-bit constant when compiling on a narrow machine. */
- if (HOST_BITS_PER_WIDE_INT != 64
- || c >> 31 == -1 || c >> 31 == 0)
- {
- HOST_WIDE_INT low = ((c & 0xffff) ^ 0x8000) - 0x8000;
- HOST_WIDE_INT tmp1 = c - low;
- HOST_WIDE_INT high = (((tmp1 >> 16) & 0xffff) ^ 0x8000) - 0x8000;
- HOST_WIDE_INT extra = 0;
- /* If HIGH will be interpreted as negative but the constant is
- positive, we must adjust it to do two ldha insns. */
- if ((high & 0x8000) != 0 && c >= 0)
- {
- extra = 0x4000;
- tmp1 -= 0x40000000;
- high = ((tmp1 >> 16) & 0xffff) - 2 * ((tmp1 >> 16) & 0x8000);
- }
- if (c == low || (low == 0 && extra == 0))
- {
- /* We used to use copy_to_suggested_reg (GEN_INT (c), target, mode)
- but that meant that we can't handle INT_MIN on 32-bit machines
- (like NT/Alpha), because we recurse indefinitely through
- emit_move_insn to gen_movdi. So instead, since we know exactly
- what we want, create it explicitly. */
- if (no_output)
- return pc_rtx;
- if (target == NULL)
- target = gen_reg_rtx (mode);
- emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (c)));
- return target;
- }
- else if (n >= 2 + (extra != 0))
- {
- if (no_output)
- return pc_rtx;
- if (!can_create_pseudo_p ())
- {
- emit_insn (gen_rtx_SET (VOIDmode, target, GEN_INT (high << 16)));
- temp = target;
- }
- else
- temp = copy_to_suggested_reg (GEN_INT (high << 16),
- subtarget, mode);
- /* As of 2002-02-23, addsi3 is only available when not optimizing.
- This means that if we go through expand_binop, we'll try to
- generate extensions, etc, which will require new pseudos, which
- will fail during some split phases. The SImode add patterns
- still exist, but are not named. So build the insns by hand. */
- if (extra != 0)
- {
- if (! subtarget)
- subtarget = gen_reg_rtx (mode);
- insn = gen_rtx_PLUS (mode, temp, GEN_INT (extra << 16));
- insn = gen_rtx_SET (VOIDmode, subtarget, insn);
- emit_insn (insn);
- temp = subtarget;
- }
- if (target == NULL)
- target = gen_reg_rtx (mode);
- insn = gen_rtx_PLUS (mode, temp, GEN_INT (low));
- insn = gen_rtx_SET (VOIDmode, target, insn);
- emit_insn (insn);
- return target;
- }
- }
- /* If we couldn't do it that way, try some other methods. But if we have
- no instructions left, don't bother. Likewise, if this is SImode and
- we can't make pseudos, we can't do anything since the expand_binop
- and expand_unop calls will widen and try to make pseudos. */
- if (n == 1 || (mode == SImode && !can_create_pseudo_p ()))
- return 0;
- /* Next, see if we can load a related constant and then shift and possibly
- negate it to get the constant we want. Try this once each increasing
- numbers of insns. */
- for (i = 1; i < n; i++)
- {
- /* First, see if minus some low bits, we've an easy load of
- high bits. */
- new_const = ((c & 0xffff) ^ 0x8000) - 0x8000;
- if (new_const != 0)
- {
- temp = alpha_emit_set_const (subtarget, mode, c - new_const, i, no_output);
- if (temp)
- {
- if (no_output)
- return temp;
- return expand_binop (mode, add_optab, temp, GEN_INT (new_const),
- target, 0, OPTAB_WIDEN);
- }
- }
- /* Next try complementing. */
- temp = alpha_emit_set_const (subtarget, mode, ~c, i, no_output);
- if (temp)
- {
- if (no_output)
- return temp;
- return expand_unop (mode, one_cmpl_optab, temp, target, 0);
- }
- /* Next try to form a constant and do a left shift. We can do this
- if some low-order bits are zero; the exact_log2 call below tells
- us that information. The bits we are shifting out could be any
- value, but here we'll just try the 0- and sign-extended forms of
- the constant. To try to increase the chance of having the same
- constant in more than one insn, start at the highest number of
- bits to shift, but try all possibilities in case a ZAPNOT will
- be useful. */
- bits = exact_log2 (c & -c);
- if (bits > 0)
- for (; bits > 0; bits--)
- {
- new_const = c >> bits;
- temp = alpha_emit_set_const (subtarget, mode, new_const, i, no_output);
- if (!temp && c < 0)
- {
- new_const = (unsigned HOST_WIDE_INT)c >> bits;
- temp = alpha_emit_set_const (subtarget, mode, new_const,
- i, no_output);
- }
- if (temp)
- {
- if (no_output)
- return temp;
- return expand_binop (mode, ashl_optab, temp, GEN_INT (bits),
- target, 0, OPTAB_WIDEN);
- }
- }
- /* Now try high-order zero bits. Here we try the shifted-in bits as
- all zero and all ones. Be careful to avoid shifting outside the
- mode and to avoid shifting outside the host wide int…
Large files files are truncated, but you can click here to view the full file