diff options
author | Richard Kenner <kenner@gcc.gnu.org> | 1996-03-16 19:32:42 -0500 |
---|---|---|
committer | Richard Kenner <kenner@gcc.gnu.org> | 1996-03-16 19:32:42 -0500 |
commit | 6245e3dfd1f55063f4bea59d6a4b3618e26d915b (patch) | |
tree | 50c8c042f6a8126dfe2d7922b0d26b3c37809682 /gcc | |
parent | 2bf6230de9fe946ecc286c4d2543266a412d02b5 (diff) | |
download | gcc-6245e3dfd1f55063f4bea59d6a4b3618e26d915b.zip gcc-6245e3dfd1f55063f4bea59d6a4b3618e26d915b.tar.gz gcc-6245e3dfd1f55063f4bea59d6a4b3618e26d915b.tar.bz2 |
(alpha_tp, alpha_fprm, alpha_fptm): New variables.
(alpha_tp_string, alpha_fprm_string, alpha_fptm_string
(trap_pending): Likewise.
(override_options, summarize_insn, final_prescan_insn): New functions.
(print_operand): Handle cases '&', '\'', ')', and '+'.
(output_prolog): Emit ".eflag 48" if TARGET_IEEE_CONFORMANT.
(output_epilog): Call final_prescan_insn before emitting epilog.
From-SVN: r11550
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/config/alpha/alpha.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c index 739a0ff..5db3c58 100644 --- a/gcc/config/alpha/alpha.c +++ b/gcc/config/alpha/alpha.c @@ -38,6 +38,23 @@ Boston, MA 02111-1307, USA. */ #include "obstack.h" #include "tree.h" +/* 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; + +/* Strings decoded into the above options. */ +char *alpha_tp_string; /* -mtrap-precision=[p|s|i] */ +char *alpha_fprm_string; /* -mfp-rounding-mode=[n|m|c|d] */ +char *alpha_fptm_string; /* -mfp-trap-mode=[n|u|su|sui] */ + /* Save information from a "cmpxx" operation until the branch or scc is emitted. */ @@ -54,6 +71,10 @@ char *alpha_function_name; static int inside_function = FALSE; +/* Non-zero if an instruction that may cause a trap is pending. */ + +static int trap_pending = 0; + /* Nonzero if the current function needs gp. */ int alpha_function_needs_gp; @@ -65,6 +86,97 @@ extern int rtx_equal_function_value_matters; static void alpha_set_memflags_1 PROTO((rtx, int, int, int)); static void add_long_const PROTO((FILE *, HOST_WIDE_INT, int, int, int)); +/* Parse target option strings. */ + +void +override_options () +{ + alpha_tp = ALPHA_TP_PROG; + alpha_fprm = ALPHA_FPRM_NORM; + alpha_fptm = ALPHA_FPTM_N; + + if (TARGET_IEEE) + { + alpha_tp_string = "i"; + alpha_fptm_string = "su"; + target_flags |= MASK_IEEE_CONFORMANT; + } + + if (TARGET_IEEE_WITH_INEXACT) + { + alpha_tp_string = "i"; + alpha_fptm_string = "sui"; + target_flags |= MASK_IEEE_CONFORMANT; + } + + if (alpha_tp_string) + switch (alpha_tp_string[0]) + { + case 'p': + alpha_tp = ALPHA_TP_PROG; + break; + + case 'f': + alpha_tp = ALPHA_TP_FUNC; + break; + + case 'i': + alpha_tp = ALPHA_TP_INSN; + break; + + default: + error ("bad value (%s) for -mtrap-precision switch", + alpha_tp_string); + break; + } + + if (alpha_fprm_string) + switch (alpha_fprm_string[0]) + { + case 'n': + alpha_fprm = ALPHA_FPRM_NORM; + break; + + case 'm': + alpha_fprm = ALPHA_FPRM_MINF; + break; + + case 'c': + alpha_fprm = ALPHA_FPRM_CHOP; + break; + + case 'd': + alpha_fprm = ALPHA_FPRM_DYN; + break; + + default: + error ("bad value (%s) for -mfp-rounding-mode switch", + alpha_fprm_string); + break; + } + + 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 (%s) for -mfp-trap-mode switch", + alpha_fptm_string); + + /* Do some sanity checks on the above option. */ + + if (alpha_fptm >= ALPHA_FPTM_SU && alpha_tp != ALPHA_TP_INSN) + { + error ("fp software completion requires -mtrap-precision=i"); + alpha_tp = ALPHA_TP_INSN; + } +} + /* Returns 1 if VALUE is a mask that contains full bytes of zero or ones. */ int @@ -922,6 +1034,67 @@ print_operand (file, x, code) switch (code) { + case '&': + /* Generates fp-rounding mode suffix: nothing for normal, 'c' for + chopped, 'm' for minus-infinity, and 'd' for dynamic rounding + mode. alpha_fprm controls which suffix is generated. */ + switch (alpha_fprm) + { + case ALPHA_FPRM_NORM: + break; + case ALPHA_FPRM_MINF: + fputc ('m', file); + break; + case ALPHA_FPRM_CHOP: + fputc ('c', file); + break; + case ALPHA_FPRM_DYN: + fputc ('d', file); + break; + } + break; + + case '\'': + /* Generates trap-mode suffix for instructions that accept the su + suffix only (cmpt et al). */ + if (alpha_tp == ALPHA_TP_INSN) + fputs ("su", file); + break; + + case ')': + /* Generates trap-mode suffix for instructions that accept the u, su, + and sui suffix. This is the bulk of the IEEE floating point + instructions (addt et al). */ + switch (alpha_fptm) + { + case ALPHA_FPTM_N: + break; + case ALPHA_FPTM_U: + fputc ('u', file); + break; + case ALPHA_FPTM_SU: + fputs ("su", file); + break; + case ALPHA_FPTM_SUI: + fputs ("sui", file); + break; + } + break; + + case '+': + /* Generates trap-mode suffix for instructions that accept the sui + suffix (cvtqt and cvtqs). */ + switch (alpha_fptm) + { + case ALPHA_FPTM_N: case ALPHA_FPTM_U: + case ALPHA_FPTM_SU: /* cvtqt/cvtqs can't cause underflow */ + break; + case ALPHA_FPTM_SUI: + fputs ("sui", file); + break; + } + break; + case 'r': /* If this operand is the constant zero, write it as "$31". */ if (GET_CODE (x) == REG) @@ -1364,6 +1537,12 @@ output_prolog (file, size) ASM_OUTPUT_LABEL (file, alpha_function_name); inside_function = TRUE; + if (TARGET_IEEE_CONFORMANT) + /* Set flags in procedure descriptor to request IEEE-conformant + math-library routines. The value we set it to is PDSC_EXC_IEEE + (/usr/include/pdsc.h). */ + fprintf (file, "\t.eflag 48\n"); + /* Set up offsets to alpha virtual arg/local debugging pointer. */ alpha_auto_offset = -frame_size + current_function_pretend_args_size; @@ -1548,6 +1727,8 @@ output_epilog (file, size) { int fp_offset = 0; + final_prescan_insn (NULL_RTX, NULL_PTR, 0); + /* If we have a frame pointer, restore SP from it. */ if (frame_pointer_needed) fprintf (file, "\tbis $15,$15,$30\n"); @@ -1710,3 +1891,259 @@ alpha_output_lineno (stream, line) else fprintf (stream, "\n\t.loc\t%d %d\n", num_source_filenames, line); } + +/* Structure to show the current status of registers and memory. */ + +struct shadow_summary +{ + struct { + unsigned long i : 32; /* Mask of int regs */ + unsigned long fp : 32; /* Mask of fp regs */ + unsigned long mem : 1; /* mem == imem | fpmem */ + } used, defd; +}; + +/* Summary the effects of expression X on the machine. Update SUM, a pointer + to the summary structure. SET is nonzero if the insn is setting the + object, otherwise zero. */ + +static void +summarize_insn (x, sum, set) + rtx x; + struct shadow_summary *sum; + int set; +{ + char *format_ptr; + int i, j; + + if (x == 0) + return; + + switch (GET_CODE (x)) + { + /* ??? Note that this case would be incorrect if the Alpha had a + ZERO_EXTRACT in SET_DEST. */ + case SET: + summarize_insn (SET_SRC (x), sum, 0); + summarize_insn (SET_DEST (x), sum, 1); + break; + + case CLOBBER: + summarize_insn (XEXP (x, 0), sum, 1); + break; + + case USE: + summarize_insn (XEXP (x, 0), sum, 0); + break; + + case PARALLEL: + for (i = XVECLEN (x, 0); i >= 0; i--) + summarize_insn (XVECEXP (x, 0, i), sum, 0); + break; + + case REG: + { + int regno = REGNO (x); + unsigned long mask = 1UL << (regno % 32); + + if (regno == 31 || regno == 63) + break; + + if (set) + { + if (regno < 32) + sum->defd.i |= mask; + else + sum->defd.fp |= mask; + } + else + { + if (regno < 32) + sum->used.i |= mask; + else + sum->used.fp |= mask; + } + } + break; + + case MEM: + if (set) + sum->defd.mem = 1; + else + sum->used.mem = 1; + + /* Find the regs used in memory address computation: */ + summarize_insn (XEXP (x, 0), sum, 0); + break; + + /* Handle common unary and binary ops for efficiency. */ + case COMPARE: case PLUS: case MINUS: case MULT: case DIV: + case MOD: case UDIV: case UMOD: case AND: case IOR: + case XOR: case ASHIFT: case ROTATE: case ASHIFTRT: case LSHIFTRT: + case ROTATERT: case SMIN: case SMAX: case UMIN: case UMAX: + case NE: case EQ: case GE: case GT: case LE: + case LT: case GEU: case GTU: case LEU: case LTU: + summarize_insn (XEXP (x, 0), sum, 0); + summarize_insn (XEXP (x, 1), sum, 0); + break; + + case NEG: case NOT: case SIGN_EXTEND: case ZERO_EXTEND: + case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE: case FLOAT: + case FIX: case UNSIGNED_FLOAT: case UNSIGNED_FIX: case ABS: + case SQRT: case FFS: + summarize_insn (XEXP (x, 0), sum, 0); + break; + + default: + format_ptr = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)); i >= 0; i--) + switch (*format_ptr++) + { + case 'e': + summarize_insn (XEXP (x, i), sum, 0); + break; + + case 'E': + for (j = XVECLEN (x, i); j >= 0; j--) + summarize_insn (XVECEXP (x, i, j), sum, 0); + break; + + default: + abort (); + } + } +} + +/* This function is executed just prior to the output of assembler code for + INSN to modify the extracted operands so they will be output differently. + + OPVEC is the vector containing the operands extracted from INSN, and + NOPERANDS is the number of elements of the vector which contain meaningful + data for this insn. The contents of this vector are what will be used to + convert the insn template into assembler code, so you can change the + assembler output by changing the contents of the vector. + + We use this function to ensure a sufficient number of `trapb' instructions + are in the code when the user requests code with a trap precision of + functions or instructions. + + In naive mode, when the user requests a trap-precision of "instruction", a + trapb is needed after every instruction that may generate a trap (and after + jsr/bsr instructions, because called functions may import a trap from the + caller). This ensures that the code is resumption safe but it is also slow. + + When optimizations are turned on, we delay issuing a trapb as long as + possible. In this context, a trap shadow is the sequence of instructions + that starts with a (potentially) trap generating instruction and extends to + the next trapb or call_pal instruction (but GCC never generates call_pal by + itself). We can delay (and therefore sometimes omit) a trapb subject to the + following conditions: + + (a) On entry to the trap shadow, if any Alpha register or memory location + contains a value that is used as an operand value by some instruction in + the trap shadow (live on entry), then no instruction in the trap shadow + may modify the register or memory location. + + (b) Within the trap shadow, the computation of the base register for a + memory load or store instruction may not involve using the result + of an instruction that might generate an UNPREDICTABLE result. + + (c) Within the trap shadow, no register may be used more than once as a + destination register. (This is to make life easier for the trap-handler.) + + (d) The trap shadow may not include any branch instructions. + + */ + +void +final_prescan_insn (insn, opvec, noperands) + rtx insn; + rtx *opvec; + int noperands; +{ + static struct shadow_summary shadow = {0, 0, 0, 0, 0}; + +#define CLOSE_SHADOW \ + do \ + { \ + fputs ("\ttrapb\n", asm_out_file); \ + trap_pending = 0; \ + bzero ((char *) &shadow, sizeof shadow); \ + } \ + while (0) + + if (alpha_tp == ALPHA_TP_PROG) + return; + + if (trap_pending) + switch (alpha_tp) + { + case ALPHA_TP_FUNC: + /* Generate one trapb before epilogue (indicated by INSN==0) */ + if (insn == 0) + CLOSE_SHADOW; + break; + + case ALPHA_TP_INSN: + if (optimize && insn != 0) + { + struct shadow_summary sum = {0, 0, 0}; + + switch (GET_CODE(insn)) + { + case INSN: + summarize_insn (PATTERN (insn), &sum, 0); + + if ((sum.defd.i & shadow.defd.i) + || (sum.defd.fp & shadow.defd.fp)) + { + /* (c) would be violated */ + CLOSE_SHADOW; + break; + } + + /* Combine shadow with summary of current insn: */ + shadow.used.i |= sum.used.i; + shadow.used.fp |= sum.used.fp; + shadow.used.mem |= sum.used.mem; + shadow.defd.i |= sum.defd.i; + shadow.defd.fp |= sum.defd.fp; + shadow.defd.mem |= sum.defd.mem; + + if ((sum.defd.i & shadow.used.i) + || (sum.defd.fp & shadow.used.fp) + || (sum.defd.mem & shadow.used.mem)) + { + /* (a) would be violated (also takes care of (b)). */ + if (get_attr_trap (insn) == TRAP_YES + && ((sum.defd.i & sum.used.i) + || (sum.defd.fp & sum.used.fp))) + abort (); + + CLOSE_SHADOW; + break; + } + break; + + case JUMP_INSN: + case CALL_INSN: + case CODE_LABEL: + CLOSE_SHADOW; + break; + + default: + abort (); + } + } + else + CLOSE_SHADOW; + break; + } + + if (insn != 0 && get_attr_trap (insn) == TRAP_YES) + { + if (optimize && !trap_pending && GET_CODE (insn) == INSN) + summarize_insn (PATTERN (insn), &shadow, 0); + trap_pending = 1; + } +} |