--- gcc/config/m88k.c 2018/04/24 17:51:36 1.1 +++ gcc/config/m88k.c 2018/04/24 18:05:39 1.1.1.4 @@ -25,6 +25,7 @@ the Free Software Foundation, 675 Mass A #include #include +#include "assert.h" #include "config.h" #include "rtl.h" #include "regs.h" @@ -38,7 +39,6 @@ the Free Software Foundation, 675 Mass A #include "tree.h" #include "c-tree.h" #include "expr.h" -#include "hard-reg-set.h" #include "flags.h" extern char *version_string; @@ -47,11 +47,13 @@ extern char *ctime (); extern int flag_traditional; extern FILE *asm_out_file; -static char out_sccs_id[] = "@(#)m88k.c 1.96.5.2 06 Feb 1992 10:02:20"; +static char out_sccs_id[] = "@(#)m88k.c 2.2.14.4 10/29/92 05:37:46"; static char tm_sccs_id [] = TM_SCCS_ID; char *m88k_pound_sign = ""; /* Either # for SVR4 or empty for SVR3 */ char *m88k_short_data; +char *m88k_version; +char m88k_volatile_code; int m88k_gp_threshold; int m88k_prologue_done = 0; /* Ln directives can now be emitted */ @@ -59,10 +61,13 @@ int m88k_function_number = 0; /* Counter int m88k_fp_offset = 0; /* offset of frame pointer if used */ int m88k_stack_size = 0; /* size of allocated stack (including frame) */ int m88k_case_index; +int m88k_version_0300; /* Version is at least 03.00 */ rtx m88k_compare_reg; /* cmp output pseudo register */ rtx m88k_compare_op0; /* cmpsi operand 0 */ rtx m88k_compare_op1; /* cmpsi operand 1 */ + +enum attr_cpu m88k_cpu; /* target cpu */ /* Determine what instructions are needed to manufacture the integer VALUE in the given MODE. */ @@ -92,6 +97,28 @@ classify_integer (mode, value) return m88k_oru_or; } +/* Return the bit number in a compare word corresponding to CONDITION. */ + +int +condition_value (condition) + rtx condition; +{ + switch (GET_CODE (condition)) + { + case EQ: return 2; + case NE: return 3; + case GT: return 4; + case LE: return 5; + case LT: return 6; + case GE: return 7; + case GTU: return 8; + case LEU: return 9; + case LTU: return 10; + case GEU: return 11; + default: abort (); + } +} + int integer_ok_for_set (value) register unsigned value; @@ -184,12 +211,16 @@ output_load_const_dimode (operands) Return 1 if we have written out everything that needs to be done to do the move. Otherwise, return 0 and the caller will emit the move - normally. */ + normally. + + SCRATCH if non zero can be used as a scratch register for the move + operation. It is provided by a SECONDARY_RELOAD_* macro if needed. */ int -emit_move_sequence (operands, mode) +emit_move_sequence (operands, mode, scratch) rtx *operands; enum machine_mode mode; + rtx scratch; { register rtx operand0 = operands[0]; register rtx operand1 = operands[1]; @@ -210,13 +241,14 @@ emit_move_sequence (operands, mode) } else if (GET_CODE (operand0) == MEM) { - if (register_operand (operand1, mode) || operand1 == const0_rtx) + if (register_operand (operand1, mode) + || (operand1 == const0_rtx && GET_MODE_SIZE (mode) <= UNITS_PER_WORD)) { /* Run this case quickly. */ emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); return 1; } - if (! reload_in_progress) + if (! reload_in_progress && ! reload_completed) { operands[0] = validize_mem (operand0); operands[1] = operand1 = force_reg (mode, operand1); @@ -229,10 +261,11 @@ emit_move_sequence (operands, mode) if (GET_CODE (operand1) != CONST_INT && GET_CODE (operand1) != CONST_DOUBLE) { - rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (Pmode); + rtx temp = ((reload_in_progress || reload_completed) + ? operand0 : 0); operands[1] = legitimize_address (flag_pic && symbolic_address_p (operand1), - operand1, temp); + operand1, temp, scratch); if (mode != SImode) operands[1] = gen_rtx (SUBREG, mode, operands[1], 0); } @@ -242,47 +275,61 @@ emit_move_sequence (operands, mode) return 0; } -/* Return a legitimate reference for ORIG (either an address or a MEM) using - the register REG. If PIC and the address is already position-independent, - use ORIG. */ +/* Return a legitimate reference for ORIG (either an address or a MEM) + using the register REG. If PIC and the address is already + position-independent, use ORIG. Newly generated position-independent + addresses go into a reg. This is REG if non zero, otherwise we + allocate register(s) as necessary. If this is called during reload, + and we need a second temp register, then we use SCRATCH, which is + provided via the SECONDARY_INPUT_RELOAD_CLASS mechanism. */ struct rtx_def * -legitimize_address (pic, orig, reg) +legitimize_address (pic, orig, reg, scratch) int pic; rtx orig; rtx reg; + rtx scratch; { rtx addr = (GET_CODE (orig) == MEM ? XEXP (orig, 0) : orig); rtx new = orig; - rtx temp; + rtx temp, insn; if (pic) { - if (GET_CODE (addr) == SYMBOL_REF - || GET_CODE (addr) == LABEL_REF) + if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) { - if (reg == 0) abort (); + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } if (flag_pic == 2) { + /* If not during reload, allocate another temp reg here for + loading in the address, so that these instructions can be + optimized properly. */ + temp = ((reload_in_progress || reload_completed) + ? reg : gen_reg_rtx (Pmode)); + emit_insn (gen_rtx (SET, VOIDmode, - reg, gen_rtx (HIGH, SImode, addr))); + temp, gen_rtx (HIGH, SImode, addr))); emit_insn (gen_rtx (SET, VOIDmode, - reg, gen_rtx (LO_SUM, SImode, reg, addr))); - addr = reg; + temp, gen_rtx (LO_SUM, SImode, temp, addr))); + addr = temp; } new = gen_rtx (MEM, Pmode, gen_rtx (PLUS, SImode, pic_offset_table_rtx, addr)); current_function_uses_pic_offset_table = 1; RTX_UNCHANGING_P (new) = 1; - { - rtx insn = emit_move_insn (reg, new); - /* Put a REG_EQUAL note on this insn, so that it can be optimized - by loop. */ - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, orig, - REG_NOTES (insn)); - } + insn = emit_move_insn (reg, new); + /* Put a REG_EQUAL note on this insn, so that it can be optimized + by loop. */ + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, orig, + REG_NOTES (insn)); new = reg; } else if (GET_CODE (addr) == CONST) @@ -294,23 +341,52 @@ legitimize_address (pic, orig, reg) return orig; if (reg == 0) - abort (); + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } if (GET_CODE (XEXP (addr, 0)) != PLUS) abort (); - base = legitimize_address (1, XEXP (XEXP (addr, 0), 0), reg); + base = legitimize_address (1, XEXP (XEXP (addr, 0), 0), reg, 0); addr = legitimize_address (1, XEXP (XEXP (addr, 0), 1), - base == reg ? 0 : reg); + base == reg ? 0 : reg, 0); if (GET_CODE (addr) == CONST_INT) - new = plus_constant_for_output (base, INTVAL (addr)); - else - new = gen_rtx (PLUS, SImode, base, addr); + { + if (SMALL_INT (addr)) + return plus_constant_for_output (base, INTVAL (addr)); + else if (! reload_in_progress && ! reload_completed) + addr = force_reg (Pmode, addr); + /* We can't create any new registers during reload, so use the + SCRATCH reg provided by the reload_insi pattern. */ + else if (scratch) + { + emit_move_insn (scratch, addr); + addr = scratch; + } + else + /* If we reach here, then the SECONDARY_INPUT_RELOAD_CLASS + macro needs to be adjusted so that a scratch reg is provided + for this address. */ + abort (); + } + new = gen_rtx (PLUS, SImode, base, addr); /* Should we set special REG_NOTEs here? */ } } else if (! SHORT_ADDRESS_P (addr, temp)) { + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } + emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (HIGH, SImode, addr))); new = gen_rtx (LO_SUM, SImode, reg, addr); @@ -341,22 +417,57 @@ legitimize_address (pic, orig, reg) #define MOVSTR_QI 16 /* __movstrQI16x16 .. __movstrQI16x2 */ #define MOVSTR_HI 48 /* __movstrHI48x48 .. __movstrHI48x4 */ #define MOVSTR_SI 96 /* __movstrSI96x96 .. __movstrSI96x8 */ +#define MOVSTR_DI 96 /* __movstrDI96x96 .. __movstrDI96x16 */ +#define MOVSTR_ODD_HI 16 /* __movstrHI15x15 .. __movstrHI15x5 */ #define MOVSTR_ODD_SI 48 /* __movstrSI47x47 .. __movstrSI47x11, __movstrSI46x46 .. __movstrSI46x10, __movstrSI45x45 .. __movstrSI45x9 */ -#define MOVSTR_ODD_HI 16 /* __movstrHI15x15 .. __movstrHI15x5 */ - -/* Break even points where memcpy will do just as well. */ -#define MOVSTR_QI_LIMIT 13 -#define MOVSTR_HI_LIMIT 38 -#define MOVSTR_SI_LIMIT MOVSTR_SI - -static enum machine_mode mode_from_bytes[] = - {VOIDmode, QImode, HImode, VOIDmode, SImode}; -static int max_from_bytes[] = {0, MOVSTR_QI, MOVSTR_HI, 0, MOVSTR_SI}; -static int all_from_bytes[] = {0, MOVSTR_QI, MOVSTR_ODD_HI, 0, MOVSTR_ODD_SI}; -static int best_from_bytes[] = - {0, MOVSTR_QI_LIMIT, MOVSTR_HI_LIMIT, 0, MOVSTR_SI_LIMIT}; +#define MOVSTR_ODD_DI 48 /* __movstrDI47x47 .. __movstrDI47x23, + __movstrDI46x46 .. __movstrDI46x22, + __movstrDI45x45 .. __movstrDI45x21, + __movstrDI44x44 .. __movstrDI44x20, + __movstrDI43x43 .. __movstrDI43x19, + __movstrDI42x42 .. __movstrDI42x18, + __movstrDI41x41 .. __movstrDI41x17 */ + +/* Limits for using the non-looping movstr functions. For the m88100 + processor, we assume the source and destination are word aligned. + The QImode and HImode limits are the break even points where memcpy + does just as well and beyond which memcpy does better. For the + m88110, we tend to assume double word alignment, but also analyze + the word aligned cases. The analysis is complicated because memcpy + may use the cache control instructions for better performance. */ + +#define MOVSTR_QI_LIMIT_88100 13 +#define MOVSTR_HI_LIMIT_88100 38 +#define MOVSTR_SI_LIMIT_88100 MOVSTR_SI +#define MOVSTR_DI_LIMIT_88100 MOVSTR_SI + +#define MOVSTR_QI_LIMIT_88000 16 +#define MOVSTR_HI_LIMIT_88000 38 +#define MOVSTR_SI_LIMIT_88000 72 +#define MOVSTR_DI_LIMIT_88000 72 + +#define MOVSTR_QI_LIMIT_88110 16 +#define MOVSTR_HI_LIMIT_88110 38 +#define MOVSTR_SI_LIMIT_88110 72 +#define MOVSTR_DI_LIMIT_88110 72 + +static enum machine_mode mode_from_align[] = + {VOIDmode, QImode, HImode, VOIDmode, SImode, + VOIDmode, VOIDmode, VOIDmode, DImode}; +static int max_from_align[] = {0, MOVSTR_QI, MOVSTR_HI, 0, MOVSTR_SI, + 0, 0, 0, MOVSTR_DI}; +static int all_from_align[] = {0, MOVSTR_QI, MOVSTR_ODD_HI, 0, MOVSTR_ODD_SI, + 0, 0, 0, MOVSTR_ODD_DI}; + +static int best_from_align[3][9] = + {0, MOVSTR_QI_LIMIT_88100, MOVSTR_HI_LIMIT_88100, 0, MOVSTR_SI_LIMIT_88100, + 0, 0, 0, MOVSTR_DI_LIMIT_88100, + 0, MOVSTR_QI_LIMIT_88110, MOVSTR_HI_LIMIT_88110, 0, MOVSTR_SI_LIMIT_88110, + 0, 0, 0, MOVSTR_DI_LIMIT_88110, + 0, MOVSTR_QI_LIMIT_88000, MOVSTR_HI_LIMIT_88000, 0, MOVSTR_SI_LIMIT_88000, + 0, 0, 0, MOVSTR_DI_LIMIT_88000}; static void block_move_loop (); static void block_move_no_loop (); @@ -378,12 +489,17 @@ expand_block_move (dest_mem, src_mem, op int align = INTVAL (operands[3]); int constp = (GET_CODE (operands[2]) == CONST_INT); int bytes = (constp ? INTVAL (operands[2]) : 0); + int target = (int) m88k_cpu; + + assert (CPU_M88100 == 0); + assert (CPU_M88110 == 1); + assert (CPU_M88000 == 2); if (constp && bytes <= 0) return; /* Determine machine mode to do move with. */ - if (align > 4) + if (align > 4 && !TARGET_88110) align = 4; else if (align <= 0 || align == 3) abort (); /* block move invalid alignment. */ @@ -392,11 +508,11 @@ expand_block_move (dest_mem, src_mem, op block_move_sequence (operands[0], dest_mem, operands[1], src_mem, bytes, align, 0); - else if (constp && bytes <= best_from_bytes[align]) + else if (constp && bytes <= best_from_align[target][align]) block_move_no_loop (operands[0], dest_mem, operands[1], src_mem, bytes, align); - else if (constp && align == 4) + else if (constp && align == 4 && TARGET_88100) block_move_loop (operands[0], dest_mem, operands[1], src_mem, bytes, align); @@ -460,7 +576,7 @@ block_move_loop (dest, dest_mem, src, sr remainder = size - count * MOVSTR_LOOP - units * align; - mode = mode_from_bytes[align]; + mode = mode_from_align[align]; sprintf (entry, "__movstr%s%dn%d", GET_MODE_NAME (mode), MOVSTR_LOOP, units * align); entry_name = get_identifier (entry); @@ -476,7 +592,7 @@ block_move_loop (dest, dest_mem, src, sr MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem); MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem); - emit_insn (gen_call_block_move_loop + emit_insn (gen_call_movstrsi_loop (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)), dest, src, offset_rtx, value_rtx, gen_rtx (REG, GET_MODE (value_rtx), ((units & 1) ? 4 : 5)), @@ -499,24 +615,24 @@ block_move_no_loop (dest, dest_mem, src, int size; int align; { - enum machine_mode mode = mode_from_bytes[align]; + enum machine_mode mode = mode_from_align[align]; int units = size / align; int remainder = size - units * align; int most; - int evenp; + int value_reg; rtx offset_rtx; rtx value_rtx; char entry[30]; tree entry_name; - if (remainder && size <= all_from_bytes[align]) + if (remainder && size <= all_from_align[align]) { - most = all_from_bytes[align] - (align - remainder); + most = all_from_align[align] - (align - remainder); remainder = 0; } else { - most = max_from_bytes[align]; + most = max_from_align[align]; } sprintf (entry, "__movstr%s%dx%d", @@ -533,12 +649,13 @@ block_move_no_loop (dest, dest_mem, src, MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem); MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem); - evenp = ((most - (size - remainder)) / align) & 1; + value_reg = ((((most - (size - remainder)) / align) & 1) == 0 + ? (align == 8 ? 6 : 5) : 4); emit_insn (gen_call_block_move (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)), dest, src, offset_rtx, value_rtx, - gen_rtx (REG, GET_MODE (value_rtx), (evenp ? 4 : 5)))); + gen_rtx (REG, GET_MODE (value_rtx), value_reg))); if (remainder) block_move_sequence (gen_rtx (REG, Pmode, 2), dest_mem, @@ -573,7 +690,7 @@ block_move_sequence (dest, dest_mem, src /* Establish parameters for the first load and for the second load if it is known to be the same mode as the first. */ amount[0] = amount[1] = align; - mode[0] = mode_from_bytes[align]; + mode[0] = mode_from_align[align]; temp[0] = gen_reg_rtx (mode[0]); if (size >= 2 * align) { @@ -592,8 +709,8 @@ block_move_sequence (dest, dest_mem, src /* Change modes as the sequence tails off. */ if (size < amount[next]) { - amount[next] = (size >= 2 ? 2 : 1); - mode[next] = mode_from_bytes[amount[next]]; + amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1)); + mode[next] = mode_from_align[amount[next]]; temp[next] = gen_reg_rtx (mode[next]); } size -= amount[next]; @@ -694,7 +811,7 @@ output_xor (operands) /* Output a call. Normally this is just bsr or jsr, but this also deals with accomplishing a branch after the call by incrementing r1. This requires - that various assembler bugs be accomodated. The 4.30 DG/UX assembler + that various assembler bugs be accommodated. The 4.30 DG/UX assembler requires that forward references not occur when computing the difference of two labels. The [version?] Motorola assembler computes a word difference. No doubt there's more to come! @@ -715,11 +832,14 @@ output_call (operands, addr) if (final_sequence) { rtx jump; + rtx seq_insn; /* This can be generalized, but there is currently no need. */ if (XVECLEN (final_sequence, 0) != 2) abort (); + /* The address of interior insns is not computed, so use the sequence. */ + seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); jump = XVECEXP (final_sequence, 0, 1); if (GET_CODE (jump) == JUMP_INSN) { @@ -727,7 +847,8 @@ output_call (operands, addr) char *last; rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0); int delta = 4 * (insn_addresses[INSN_UID (dest)] - - insn_addresses[INSN_UID (jump)]); + - insn_addresses[INSN_UID (seq_insn)] + - 2); #if (MONITOR_GCC & 0x2) /* How often do long branches happen? */ if ((unsigned) (delta + 0x8000) >= 0x10000) warning ("Internal gcc monitor: short-branch(%x)", delta); @@ -762,6 +883,12 @@ output_call (operands, addr) : (flag_pic ? "bsr.n %0#plt" : "bsr.n %0")), operands); +#ifdef USE_GAS + last = (delta < 0 + ? "subu %#r1,%#r1,.-%l0+4" + : "addu %#r1,%#r1,%l0-.-4"); + operands[0] = dest; +#else operands[0] = gen_label_rtx (); operands[1] = gen_label_rtx (); if (delta < 0) @@ -781,6 +908,7 @@ output_call (operands, addr) sb_name = gen_rtx (EXPR_LIST, VOIDmode, operands[0], sb_name); sb_high = gen_rtx (EXPR_LIST, VOIDmode, high, sb_high); sb_low = gen_rtx (EXPR_LIST, VOIDmode, low, sb_low); +#endif /* Don't USE_GAS */ return last; } @@ -808,56 +936,124 @@ output_short_branch_defs (stream) ASM_GENERATE_INTERNAL_LABEL (low, "L", CODE_LABEL_NUMBER (XEXP (sb_low, 0))); /* This will change as the assembler requirements become known. */ - fprintf (stream, "%s\t %s,%s-%s\n", - DEF_ASM_OP, &name[1], &high[1], &low[1]); + fprintf (stream, "\t%s\t %s,%s-%s\n", + SET_ASM_OP, &name[1], &high[1], &low[1]); } if (sb_name || sb_high || sb_low) abort (); } -/* Report errors on floating point, if we are given NaN's, or such. Leave - the number as is, though, since we output the number in hex, and the - assemble won't choak on it. */ +/* Return truth value of the statement that this conditional branch is likely + to fall through. CONDITION, is the condition that JUMP_INSN is testing. */ -void -check_float_value (mode, value) - enum machine_mode mode; - REAL_VALUE_TYPE value; +int +mostly_false_jump (jump_insn, condition) + rtx jump_insn, condition; { - union { - REAL_VALUE_TYPE d; - struct { - unsigned sign : 1; - unsigned exponent : 11; - unsigned mantissa1 : 20; - unsigned mantissa2; - } s; - } u; + rtx target_label = JUMP_LABEL (jump_insn); + rtx insnt, insnj; - if (mode == DFmode) - { - u.d = value; - if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0) - { - if (u.s.exponent == 0x7ff) /* Not a Number */ - warning ("floating point number is not valid for IEEE double precision"); - else if (u.s.exponent == 0) - warning ("denormalized double precision floating point number"); - } - } - else if (mode == SFmode) + /* Much of this isn't computed unless we're optimizing. */ + if (optimize == 0) + return 0; + + /* Determine if one path or the other leads to a return. */ + for (insnt = NEXT_INSN (target_label); + insnt; + insnt = NEXT_INSN (insnt)) + { + if (GET_CODE (insnt) == JUMP_INSN) + break; + else if (GET_CODE (insnt) == INSN + && GET_CODE (PATTERN (insnt)) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (insnt), 0, 0)) == JUMP_INSN) + { + insnt = XVECEXP (PATTERN (insnt), 0, 0); + break; + } + } + if (insnt + && (GET_CODE (PATTERN (insnt)) == RETURN + || (GET_CODE (PATTERN (insnt)) == SET + && GET_CODE (SET_SRC (PATTERN (insnt))) == REG + && REGNO (SET_SRC (PATTERN (insnt))) == 1))) + insnt = 0; + + for (insnj = NEXT_INSN (jump_insn); + insnj; + insnj = NEXT_INSN (insnj)) + { + if (GET_CODE (insnj) == JUMP_INSN) + break; + else if (GET_CODE (insnj) == INSN + && GET_CODE (PATTERN (insnj)) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (insnj), 0, 0)) == JUMP_INSN) + { + insnj = XVECEXP (PATTERN (insnj), 0, 0); + break; + } + } + if (insnj + && (GET_CODE (PATTERN (insnj)) == RETURN + || (GET_CODE (PATTERN (insnj)) == SET + && GET_CODE (SET_SRC (PATTERN (insnj))) == REG + && REGNO (SET_SRC (PATTERN (insnj))) == 1))) + insnj = 0; + + /* Predict to not return. */ + if ((insnt == 0) != (insnj == 0)) + return (insnt == 0); + + /* Predict loops to loop. */ + for (insnt = PREV_INSN (target_label); + insnt && GET_CODE (insnt) == NOTE; + insnt = PREV_INSN (insnt)) + if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_END) + return 1; + else if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_BEG) + return 0; + else if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_CONT) + return 0; + + /* Predict backward branches usually take. */ + if (final_sequence) + insnj = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); + else + insnj = jump_insn; + if (insn_addresses[INSN_UID (insnj)] + > insn_addresses[INSN_UID (target_label)]) + return 0; + + /* EQ tests are usually false and NE tests are usually true. Also, + most quantities are positive, so we can make the appropriate guesses + about signed comparisons against zero. Consider unsigned comparisons + to be a range check and assume quantities to be in range. */ + switch (GET_CODE (condition)) { - u.d = REAL_VALUE_TRUNCATE (mode, value); - if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0) - { - if (u.s.exponent == 0x7ff) /* Not a Number */ - warning ("floating point number is not valid for IEEE double precision"); - else if (u.s.exponent == 0) - warning ("denormalized single precision floating point number"); - } - else if (u.s.exponent == 0x7ff) /* Infinity */ - warning ("floating point number exceeds range of `float'"); + case CONST_INT: + /* Unconditional branch. */ + return 0; + case EQ: + return 1; + case NE: + return 0; + case LE: + case LT: + case GEU: + case GTU: /* Must get casesi right at least. */ + if (XEXP (condition, 1) == const0_rtx) + return 1; + break; + case GE: + case GT: + case LEU: + case LTU: + if (XEXP (condition, 1) == const0_rtx) + return 0; + break; } + + return 0; } /* Return true if the operand is a power of two and is a floating @@ -1257,6 +1453,8 @@ output_options (file, f_options, f_len, pos = output_option (file, sep, "-traditional", "", indent, pos, max); if (profile_flag) pos = output_option (file, sep, "-p", "", indent, pos, max); + if (profile_block_flag) + pos = output_option (file, sep, "-a", "", indent, pos, max); for (j = 0; j < f_len; j++) if (*f_options[j].variable == f_options[j].on_value) @@ -1308,7 +1506,7 @@ output_file_start (file, f_options, f_le char indent[256]; time_t now = time ((time_t *)0); - sprintf (indent, "]\"\n%s\t \"@(#)%s [", IDENT_ASM_OP, main_input_filename); + sprintf (indent, "]\"\n\t%s\t \"@(#)%s [", IDENT_ASM_OP, main_input_filename); fprintf (file, indent+3); pos = fprintf (file, "gcc %s, %.24s,", VERSION_STRING, ctime (&now)); output_options (file, f_options, f_len, W_options, W_len, @@ -1319,48 +1517,67 @@ output_file_start (file, f_options, f_le /* Output an ascii string. */ void -output_ascii (file, p, size) +output_ascii (file, opcode, max, p, size) FILE *file; + char *opcode; + int max; unsigned char *p; int size; { int i; + int in_escape = 0; register int num = 0; - fprintf (file, "%s\t \"", ASCII_DATA_ASM_OP); + fprintf (file, "\t%s\t \"", opcode); for (i = 0; i < size; i++) { register int c = p[i]; - if (num > 48) + if (num > max) { - fprintf (file, "\"\n%s\t \"", ASCII_DATA_ASM_OP); + fprintf (file, "\"\n\t%s\t \"", opcode); num = 0; } - + if (c == '\"' || c == '\\') { + escape: putc ('\\', file); - num++; + putc (c, file); + num += 2; + in_escape = 0; } - - if (c >= ' ' && c < 0177) + else if (in_escape && c >= '0' && c <= '9') + { + /* If a digit follows an octal-escape, the Vax assembler fails + to stop reading the escape after three digits. Continue to + output the values as an octal-escape until a non-digit is + found. */ + fprintf (file, "\\%03o", c); + num += 4; + } + else if (c >= ' ' && c < 0177) { putc (c, file); num++; + in_escape = 0; } else { + switch (c) + { + /* Some assemblers can't handle \a, \v, or \?. */ + case '\t': c = 't'; goto escape; + case '\f': c = 'f'; goto escape; + case '\b': c = 'b'; goto escape; + case '\r': c = 'r'; goto escape; + case '\n': c = 'n'; goto escape; + } + fprintf (file, "\\%03o", c); num += 4; - /* After an octal-escape, if a digit follows, - terminate one string constant and start another. - The Vax assembler fails to stop reading the escape - after three digits, so this is the only way we - can get it to parse the data properly. */ - if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9') - num = 32767; /* next pass will start a new string */ + in_escape = 1; } } fprintf (file, "\"\n"); @@ -1375,99 +1592,6 @@ output_label (label_number) { ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", label_number); } - -/* Handle a pragma directive. HANDLE_PRAGMA conspires to parse the input - following #pragma into tokens based on yylex. */ - -void -m88k_handle_pragma_token (string, token) - char *string; - tree token; -{ - static enum pragma_state - { - ps_start, - ps_done, - ps_bad, - ps_weak, - ps_name, - ps_equals, - ps_value - } state; - static char *name; - static char *value; - - if (HANDLE_PRAGMA_WEAK) - { - if (string == 0) - { - if (state == ps_name || state == ps_value) - { - fprintf (asm_out_file, "%s\t ", WEAK_ASM_OP); - ASM_OUTPUT_LABELREF (asm_out_file, name); - fputc ('\n', asm_out_file); - if (state == ps_value) - { - fprintf (asm_out_file, "%s\t ", DEF_ASM_OP); - ASM_OUTPUT_LABELREF (asm_out_file, name); - fputc (',', asm_out_file); - ASM_OUTPUT_LABELREF (asm_out_file, value); - fputc ('\n', asm_out_file); - } - } - else if (! (state == ps_done || state == ps_start)) - warning ("ignoring malformed #pragma weak symbol [=value]"); - state = ps_start; - } - else - switch (state) - { - case ps_start: - if (token - && TREE_CODE (token) == IDENTIFIER_NODE - && !strcmp (IDENTIFIER_POINTER (token), "weak")) - state = ps_weak; - else - state = ps_done; - break; - - case ps_weak: - if (token - && TREE_CODE (token) == IDENTIFIER_NODE) - { - name = IDENTIFIER_POINTER (token); - state = ps_name; - } - else - state = ps_bad; - break; - - case ps_name: - state = (strcmp (string, "=") ? ps_bad : ps_equals); - break; - - case ps_equals: - if (token - && TREE_CODE (token) == IDENTIFIER_NODE) - { - value = IDENTIFIER_POINTER (token); - state = ps_value; - } - else - state = ps_bad; - break; - - case ps_value: - state = ps_bad; - case ps_bad: - case ps_done: - break; - - default: - abort (); - } - } -} /* Generate the assembly code for function entry. @@ -1524,6 +1648,8 @@ m88k_handle_pragma_token (string, token) | [previous frame pointer (r30)] | |==============================================| <- fp | [preserved registers (r25..r14)] | + |----------------------------------------------| + | [preserved registers (x29..x22)] | |==============================================| | [dynamically allocated space (alloca)] | |==============================================| @@ -1540,15 +1666,19 @@ m88k_handle_pragma_token (string, token) variable space. */ -static void output_reg_adjust (); +static void emit_add (); static void preserve_registers (); +static void emit_ldst (); static void output_tdesc (); static int nregs; +static int nxregs; static char save_regs[FIRST_PSEUDO_REGISTER]; static int frame_laid_out; static int frame_size; static int variable_args_p; +static int epilogue_marked; +static int prologue_marked; extern char call_used_regs[]; extern int current_function_pretend_args_size; @@ -1558,6 +1688,9 @@ extern int frame_pointer_needed; #define FIRST_OCS_PRESERVE_REGISTER 14 #define LAST_OCS_PRESERVE_REGISTER 30 +#define FIRST_OCS_EXTENDED_PRESERVE_REGISTER (32 + 22) +#define LAST_OCS_EXTENDED_PRESERVE_REGISTER (32 + 31) + #define STACK_UNIT_BOUNDARY (STACK_BOUNDARY / BITS_PER_UNIT) #define ROUND_CALL_BLOCK_SIZE(BYTES) \ (((BYTES) + (STACK_UNIT_BOUNDARY - 1)) & ~(STACK_UNIT_BOUNDARY - 1)) @@ -1573,11 +1706,11 @@ m88k_layout_frame () frame_laid_out++; bzero ((char *) &save_regs[0], sizeof (save_regs)); - sp_size = nregs = 0; + sp_size = nregs = nxregs = 0; frame_size = get_frame_size (); /* Since profiling requires a call, make sure r1 is saved. */ - if (profile_flag) + if (profile_flag || profile_block_flag) save_regs[1] = 1; /* If we are producing debug information, store r1 and r30 where the @@ -1606,9 +1739,21 @@ m88k_layout_frame () /* If a frame is requested, save the previous FP, and the return address (r1), so that a traceback can be done without using tdesc - information. */ + information. Otherwise, simply save the FP if it is used as + a preserve register. */ if (frame_pointer_needed) save_regs[FRAME_POINTER_REGNUM] = save_regs[1] = 1; + else if (regs_ever_live[FRAME_POINTER_REGNUM]) + save_regs[FRAME_POINTER_REGNUM] = 1; + + /* Figure out which extended register(s) needs to be saved. */ + for (regno = FIRST_EXTENDED_REGISTER + 1; regno < FIRST_PSEUDO_REGISTER; + regno++) + if (regs_ever_live[regno] && ! call_used_regs[regno]) + { + save_regs[regno] = 1; + nxregs++; + } /* Figure out which normal register(s) needs to be saved. */ for (regno = 2; regno < FRAME_POINTER_REGNUM; regno++) @@ -1619,25 +1764,25 @@ m88k_layout_frame () } /* Achieve greatest use of double memory ops. Either we end up saving - r30 or we use that slot to align the regsters we do save. */ + r30 or we use that slot to align the registers we do save. */ if (nregs >= 2 && save_regs[1] && !save_regs[FRAME_POINTER_REGNUM]) sp_size += 4; nregs += save_regs[1] + save_regs[FRAME_POINTER_REGNUM]; + /* if we need to align extended registers, add a word */ + if (nxregs > 0 && (nregs & 1) != 0) + sp_size +=4; sp_size += 4 * nregs; + sp_size += 8 * nxregs; sp_size += current_function_outgoing_args_size; /* The first two saved registers are placed above the new frame pointer if any. In the only case this matters, they are r1 and r30. */ if (frame_pointer_needed || sp_size) - { - m88k_fp_offset = ROUND_CALL_BLOCK_SIZE (sp_size - STARTING_FRAME_OFFSET); - m88k_stack_size = m88k_fp_offset + STARTING_FRAME_OFFSET; - } + m88k_fp_offset = ROUND_CALL_BLOCK_SIZE (sp_size - STARTING_FRAME_OFFSET); else - { - m88k_stack_size = m88k_fp_offset = 0; - } + m88k_fp_offset = -STARTING_FRAME_OFFSET; + m88k_stack_size = m88k_fp_offset + STARTING_FRAME_OFFSET; /* First, combine m88k_stack_size and size. If m88k_stack_size is non-zero, align the frame size to 8 mod 16; otherwise align the @@ -1660,10 +1805,10 @@ m88k_layout_frame () } } -/* Return true if this function is known to have a null epilogue. */ +/* Return true if this function is known to have a null prologue. */ int -null_epilogue () +null_prologue () { if (! reload_completed) return 0; @@ -1671,101 +1816,10 @@ null_epilogue () m88k_layout_frame (); return (! frame_pointer_needed && nregs == 0 + && nxregs == 0 && m88k_stack_size == 0); } -/* Determine the number of instructions needed for the function epilogue. */ - -#define MAX_EPILOGUE_DELAY_INSNS 4 - -static char epilogue_dead_regs[FIRST_PSEUDO_REGISTER]; - -delay_slots_for_epilogue () -{ - register int insns = save_regs[1] + save_regs[FRAME_POINTER_REGNUM]; - register int regs = nregs - insns; - - if (regs > 3) - insns += 1 + (regs & 1); - else if (nregs == 4) - /* This is a special cases of ld/ld/ld.d which has no start-up delay. */ - return 0; - - if (insns) - { - bzero ((char *) &epilogue_dead_regs[0], sizeof (epilogue_dead_regs)); - epilogue_dead_regs[1] = save_regs[1]; - epilogue_dead_regs[STACK_POINTER_REGNUM] = frame_pointer_needed; - epilogue_dead_regs[TEMP_REGNUM] = ! ADD_INTVAL (m88k_fp_offset); - } - - return insns; -} - -/* Return 1 if X is safe to use as an epilogue insn. */ - -int -ok_for_epilogue_p (x) - rtx x; -{ - register char *fmt; - register int i, j; - - switch (GET_CODE (x)) - { - case REG: - for (i = REGNO (x), j = i + HARD_REGNO_NREGS (i, GET_MODE (x)); - i < j; - i++) - if (epilogue_dead_regs[i]) - return 0; - - case CONST_INT: - case CONST_DOUBLE: - case CONST: - case PC: - case CC0: - case LABEL_REF: - case SYMBOL_REF: - case CODE_LABEL: - return 1; - } - - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (!ok_for_epilogue_p (XEXP (x, i))) - return 0; - } - else if (fmt[i] == 'E') - { - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (!ok_for_epilogue_p (XVECEXP (x, i, j))) - return 0; - } - } - return 1; -} - -int -eligible_for_epilogue_delay (insn) - rtx insn; -{ - switch (get_attr_type (insn)) - { - case TYPE_STORE: - case TYPE_LOADA: - case TYPE_ARITH: - case TYPE_MARITH: - case TYPE_MSTORE: - return ok_for_epilogue_p (PATTERN (insn)); - default: - return 0; - } -} - /* Determine if the current function has any references to the arg pointer. This is done indirectly by examining the DECL_ARGUMENTS' DECL_RTL. It is OK to return TRUE if there are no references, but FALSE must be @@ -1797,75 +1851,80 @@ uses_arg_area_p () } void -m88k_output_prologue (stream, size) +m88k_begin_prologue (stream, size) FILE *stream; int size; { - int old_fp_offset = m88k_fp_offset; - int old_stack_size = m88k_stack_size; + m88k_prologue_done = 1; /* it's ok now to put out ln directives */ +} - m88k_layout_frame (); -#if (MONITOR_GCC & 0x8) /* Watch for suspicious register elimination changes. */ - if (frame_laid_out > 1) +void +m88k_end_prologue (stream) + FILE *stream; +{ + if (TARGET_OCS_DEBUG_INFO && !prologue_marked) { - if (old_fp_offset != m88k_fp_offset) - warning ("Internal gcc error: FP offset has changed by %d bytes", - m88k_fp_offset - old_fp_offset); - if (old_stack_size != m88k_stack_size) - warning ("Internal gcc error: stack size has changed by %d bytes", - m88k_stack_size - old_stack_size); + PUT_OCS_FUNCTION_START (stream); + prologue_marked = 1; + + /* If we've already passed the start of the epilogue, say that + it starts here. This marks the function as having a null body, + but at a point where the return address is in a known location. + + Originally, I thought this couldn't happen, but the pic prologue + for leaf functions ends with the instruction that restores the + return address from the temporary register. If the temporary + register is never used, that instruction can float all the way + to the end of the function. */ + if (epilogue_marked) + PUT_OCS_FUNCTION_END (stream); } -#endif - frame_laid_out = 0; +} + +void +m88k_expand_prologue () +{ + m88k_layout_frame (); if (TARGET_OPTIMIZE_ARG_AREA && m88k_stack_size && ! uses_arg_area_p ()) { /* The incoming argument area is used for stack space if it is not - used (or if -mno-use-arg-area is given). */ + used (or if -mno-optimize-arg-area is given). */ if ((m88k_stack_size -= REG_PARM_STACK_SPACE (0)) < 0) m88k_stack_size = 0; } if (m88k_stack_size) - output_reg_adjust (stream, 31, 31, -m88k_stack_size, 0); + emit_add (stack_pointer_rtx, stack_pointer_rtx, -m88k_stack_size); - if (nregs) - preserve_registers (stream, m88k_fp_offset + 4, 1); + if (nregs || nxregs) + preserve_registers (m88k_fp_offset + 4, 1); if (frame_pointer_needed) - output_reg_adjust (stream, 30, 31, m88k_fp_offset, 0); - - if (TARGET_OCS_DEBUG_INFO) - PUT_OCS_FUNCTION_START (stream); + emit_add (frame_pointer_rtx, stack_pointer_rtx, m88k_fp_offset); if (flag_pic && save_regs[PIC_OFFSET_TABLE_REGNUM]) { - char label[256]; + rtx return_reg = gen_rtx (REG, SImode, 1); + rtx label = gen_label_rtx (); + rtx temp_reg; if (! save_regs[1]) - fprintf (stream, "\tor\t %s,%s,0\n", - reg_names[TEMP_REGNUM], reg_names[1]); - ASM_GENERATE_INTERNAL_LABEL (label, "Lab", m88k_function_number); - fprintf (stream, "\tbsr.n\t %s\n", &label[1]); - fprintf (stream, "\tor.u\t %s,%s,%shi16(%s#abdiff)\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[0], - m88k_pound_sign, &label[1]); - ASM_OUTPUT_INTERNAL_LABEL (stream, "Lab", m88k_function_number); - fprintf (stream, "\tor\t %s,%s,%slo16(%s#abdiff)\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM], - m88k_pound_sign, &label[1]); - fprintf (stream, "\taddu\t %s,%s,%s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[1]); + { + temp_reg = gen_rtx (REG, SImode, TEMP_REGNUM); + emit_move_insn (temp_reg, return_reg); + } + emit_insn (gen_locate1 (pic_offset_table_rtx, label)); + emit_insn (gen_locate2 (pic_offset_table_rtx, label)); + emit_insn (gen_addsi3 (pic_offset_table_rtx, + pic_offset_table_rtx, return_reg)); if (! save_regs[1]) - fprintf (stream, "\tor\t %s,%s,0\n", - reg_names[1], reg_names[TEMP_REGNUM]); + emit_move_insn (return_reg, temp_reg); } - - m88k_prologue_done = 1; /* it's ok now to put out ln directives */ + if (profile_flag || profile_block_flag) + emit_insn (gen_blockage ()); } /* This function generates the assembly code for function exit, @@ -1877,39 +1936,34 @@ m88k_output_prologue (stream, size) omit stack adjustments before returning. */ void -m88k_output_epilogue (stream, size) +m88k_begin_epilogue (stream) + FILE *stream; +{ + if (TARGET_OCS_DEBUG_INFO && !epilogue_marked && prologue_marked) + { + PUT_OCS_FUNCTION_END (stream); + } + epilogue_marked = 1; +} + +void +m88k_end_epilogue (stream, size) FILE *stream; int size; { rtx insn = get_last_insn (); -#if (MONITOR_GCC & 0x4) /* What are interesting prologue/epiloge values? */ - fprintf (stream, "; size = %d, m88k_fp_offset = %d, m88k_stack_size = %d\n", - size, m88k_fp_offset, m88k_stack_size); -#endif - output_short_branch_defs (stream); - - if (TARGET_OCS_DEBUG_INFO) + if (TARGET_OCS_DEBUG_INFO && !epilogue_marked) PUT_OCS_FUNCTION_END (stream); - /* If the last insn was a BARRIER, we don't have to write any code. */ + /* If the last insn isn't a BARRIER, we must write a return insn. This + should only happen if the function has no prologe and no body. */ if (GET_CODE (insn) == NOTE) insn = prev_nonnote_insn (insn); - if (insn && GET_CODE (insn) == BARRIER) - { - if (current_function_epilogue_delay_list) - abort (); - } - else - { - if (frame_pointer_needed) - output_reg_adjust (stream, 31, 30, -m88k_fp_offset, 0); - - if (nregs) - preserve_registers (stream, m88k_fp_offset + 4, 0); + if (insn == 0 || GET_CODE (insn) != BARRIER) + fprintf (stream, "\tjmp\t %s\n", reg_names[1]); - output_reg_adjust (stream, 31, 31, m88k_stack_size, 1); - } + output_short_branch_defs (stream); fprintf (stream, "\n"); @@ -1919,69 +1973,58 @@ m88k_output_epilogue (stream, size) m88k_function_number++; m88k_prologue_done = 0; /* don't put out ln directives */ variable_args_p = 0; /* has variable args */ + frame_laid_out = 0; + epilogue_marked = 0; + prologue_marked = 0; } - -/* Output code to STREAM to set DSTREG to SRCREG + AMOUNT. Issue - a return instruction and use it's delay slot based on RETURN_P. */ -static void -output_reg_adjust (stream, dstreg, srcreg, amount, return_p) - FILE *stream; - int dstreg, srcreg, amount, return_p; +void +m88k_expand_epilogue () { - char *opname; - char incr[256]; +#if (MONITOR_GCC & 0x4) /* What are interesting prologue/epilogue values? */ + fprintf (stream, "; size = %d, m88k_fp_offset = %d, m88k_stack_size = %d\n", + size, m88k_fp_offset, m88k_stack_size); +#endif - if (amount < 0) - { - opname = "subu"; - amount = -amount; - } - else - opname = "addu"; + if (frame_pointer_needed) + emit_add (stack_pointer_rtx, frame_pointer_rtx, -m88k_fp_offset); - if (amount == 0 && dstreg == srcreg) - { - if (return_p) - fprintf (stream, "\tjmp\t %s\n", reg_names[1]); - return; - } - else if (SMALL_INTVAL (amount)) - sprintf (incr, "\t%s\t %s,%s,%d", opname, - reg_names[dstreg], reg_names[srcreg], amount); - else - { - rtx operands[2]; + if (nregs || nxregs) + preserve_registers (m88k_fp_offset + 4, 0); - operands[0] = gen_rtx (REG, SImode, TEMP_REGNUM); - operands[1] = gen_rtx (CONST_INT, VOIDmode, amount); - output_asm_insn (output_load_const_int (SImode, operands), - operands); - sprintf (incr, "\t%s\t %s,%s,%s", opname, - reg_names[dstreg], reg_names[srcreg], reg_names[TEMP_REGNUM]); - } + if (m88k_stack_size) + emit_add (stack_pointer_rtx, stack_pointer_rtx, m88k_stack_size); +} + +/* Emit insns to set DSTREG to SRCREG + AMOUNT during the prologue or + epilogue. */ - if (!return_p) - fprintf (stream, "%s\n", incr); - else if (flag_delayed_branch) - fprintf (stream, "\tjmp.n\t %s\n%s\n", reg_names[1], incr); - else - fprintf (stream, "%s\n\tjmp\t %s\n", incr, reg_names[1]); +static void +emit_add (dstreg, srcreg, amount) + rtx dstreg; + rtx srcreg; + int amount; +{ + rtx incr = gen_rtx (CONST_INT, VOIDmode, abs (amount)); + if (! ADD_INTVAL (amount)) + { + rtx temp = gen_rtx (REG, SImode, TEMP_REGNUM); + emit_move_insn (temp, incr); + incr = temp; + } + emit_insn ((amount < 0 ? gen_subsi3 : gen_addsi3) (dstreg, srcreg, incr)); } /* Save/restore the preserve registers. base is the highest offset from r31 at which a register is stored. store_p is true if stores are to - be done; otherwise loads. When loading, output the epilogue delay - insns. */ + be done; otherwise loads. */ static void -preserve_registers (stream, base, store_p) - FILE *stream; +preserve_registers (base, store_p) int base; int store_p; { int regno, offset; - char *fmt = (store_p ? "\tst%s\t %s,%s,%d\n" : "\tld%s\t %s,%s,%d\n"); struct mem_op { int regno; int nregs; @@ -2000,7 +2043,7 @@ preserve_registers (stream, base, store_ memory ops. */ if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM]) offset -= 4; - fprintf (stream, fmt, "", reg_names[1], reg_names[31], offset); + emit_ldst (store_p, 1, SImode, offset); offset -= 4; base = offset; } @@ -2044,71 +2087,49 @@ preserve_registers (stream, base, store_ offset -= 2*4; } } - mo_ptr->regno = 0; - - /* Output the delay insns interleaved with the memory operations. */ - if (! store_p && current_function_epilogue_delay_list) - { - rtx delay_insns = current_function_epilogue_delay_list; - rtx insn; - - /* The first delay insn goes after the restore of r1. */ - if (save_regs[1]) - { - final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1); - delay_insns = XEXP (delay_insns, 1); - } - while (delay_insns) - { - /* Find a memory operation that doesn't conflict with this insn. */ - for (mo_ptr = mem_op; mo_ptr->regno != 0; mo_ptr++) - { - if (mo_ptr->nregs) - { - rtx ok_insns = delay_insns; - int i; - - for (i = 0; i < mo_ptr->nregs; i++) - epilogue_dead_regs[mo_ptr->regno + i] = 1; + /* Walk the extended registers to record all memory operations. */ + /* Be sure the offset is double word aligned. */ + offset = (offset - 1) & ~7; + for (regno = FIRST_PSEUDO_REGISTER - 1; regno > FIRST_EXTENDED_REGISTER; + regno--) + if (save_regs[regno]) + { + mo_ptr->nregs = 2; + mo_ptr->regno = regno; + mo_ptr->offset = offset; + mo_ptr++; + offset -= 2*4; + } - while (ok_insns) - { - insn = XEXP (ok_insns, 0); - ok_insns = XEXP (ok_insns, 1); - - if (! ok_for_epilogue_p (PATTERN (insn))) - { - for (i = 0; i < mo_ptr->nregs; i++) - epilogue_dead_regs[mo_ptr->regno + i] = 0; - insn = 0; - break; /* foreach delay insn */ - } - } - if (insn) - { - fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "", - reg_names[mo_ptr->regno], reg_names[31], - mo_ptr->offset); - mo_ptr->nregs = 0; - break; /* foreach memory operation */ - } - } - } - final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1); - delay_insns = XEXP (delay_insns, 1); - } - } + mo_ptr->regno = 0; /* Output the memory operations. */ for (mo_ptr = mem_op; mo_ptr->regno; mo_ptr++) { if (mo_ptr->nregs) - fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "", - reg_names[mo_ptr->regno], reg_names[31], mo_ptr->offset); + emit_ldst (store_p, mo_ptr->regno, + (mo_ptr->nregs > 1 ? DImode : SImode), + mo_ptr->offset); } } +static void +emit_ldst (store_p, regno, mode, offset) + int store_p; + int regno; + enum machine_mode mode; + int offset; +{ + rtx reg = gen_rtx (REG, mode, regno); + rtx mem = gen_rtx (MEM, mode, plus_constant (stack_pointer_rtx, offset)); + + if (store_p) + emit_move_insn (mem, reg); + else + emit_move_insn (reg, mem); +} + /* Convert the address expression REG to a CFA offset. */ int @@ -2129,10 +2150,11 @@ m88k_debugger_offset (reg, offset) offset -= m88k_stack_size; else if (reg != arg_pointer_rtx) { +#if (MONITOR_GCC & 0x10) /* Watch for suspicious symbolic locations. */ if (! (GET_CODE (reg) == REG && REGNO (reg) >= FIRST_PSEUDO_REGISTER)) - /* @@ For now, I'd like to know if this happens. */ warning ("Internal gcc error: Can't express symbolic location"); +#endif return 0; } @@ -2142,27 +2164,30 @@ m88k_debugger_offset (reg, offset) /* Output the 88open OCS proscribed text description information. The information is: 0 8: zero - 0 22: info-byte-length (16 bytes) + 0 22: info-byte-length (16 or 20 bytes) 0 2: info-alignment (word 2) - 1 32: info-protocol (version 1) + 1 32: info-protocol (version 1 or 2(pic)) 2 32: starting-address (inclusive, not counting prologue) 3 32: ending-address (exclusive, not counting epilog) - 4 8: info-variant (version 1) + 4 8: info-variant (version 1 or 3(extended registers)) 4 17: register-save-mask (from register 14 to 30) 4 1: zero 4 1: return-address-info-discriminant 4 5: frame-address-register 5 32: frame-address-offset 6 32: return-address-info - 7 32: register-save-offset */ + 7 32: register-save-offset + 8 16: extended-register-save-mask (x16 - x31) + 8 16: extended-register-save-offset (WORDS from register-save-offset) */ static void output_tdesc (file, offset) FILE *file; int offset; { - int regno, i; + int regno, i, j; long mask, return_address_info, register_save_offset; + long xmask, xregister_save_offset; char buf[256]; for (mask = 0, i = 0, regno = FIRST_OCS_PRESERVE_REGISTER; @@ -2177,9 +2202,21 @@ output_tdesc (file, offset) } } + for (xmask = 0, j = 0, regno = FIRST_OCS_EXTENDED_PRESERVE_REGISTER; + regno <= LAST_OCS_EXTENDED_PRESERVE_REGISTER; + regno++) + { + xmask <<= 1; + if (save_regs[regno]) + { + xmask |= 1; + j++; + } + } + if (save_regs[1]) { - if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM]) + if ((nxregs > 0 || nregs > 2) && !save_regs[FRAME_POINTER_REGNUM]) offset -= 4; return_address_info = - m88k_stack_size + offset; register_save_offset = return_address_info - i*4; @@ -2190,32 +2227,37 @@ output_tdesc (file, offset) register_save_offset = - m88k_stack_size + offset + 4 - i*4; } + xregister_save_offset = - (j * 2 + ((register_save_offset >> 2) & 1)); + tdesc_section (); - fprintf (file, "%s\t %d", INT_ASM_OP, (16 << 2) | 2 /* 8:0,22:16,2:2 */); - fprintf (file, ",%d", flag_pic ? 2 : 1); + fprintf (file, "\t%s\t %d,%d", INT_ASM_OP, /* 8:0,22:(20 or 16),2:2 */ + (((xmask != 0) ? 20 : 16) << 2) | 2, + flag_pic ? 2 : 1); ASM_GENERATE_INTERNAL_LABEL (buf, OCS_START_PREFIX, m88k_function_number); fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); ASM_GENERATE_INTERNAL_LABEL (buf, OCS_END_PREFIX, m88k_function_number); fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); - fprintf (file, ",0x%x", /* 8:1,17:0x%.3x,1:0,1:%d,5:%d */ - (1 << (17+1+1+5)) | - (mask << (1+1+5)) | - ((!!save_regs[1]) << 5) | - ((frame_pointer_needed - ? FRAME_POINTER_REGNUM - : STACK_POINTER_REGNUM))); - - fprintf (file, ",0x%x", (m88k_stack_size - - (frame_pointer_needed ? m88k_fp_offset : 0))); - fprintf (file, ",0x%x", return_address_info); - fprintf (file, ",0x%x\n", register_save_offset); + fprintf (file, ",0x%x,0x%x,0x%x,0x%x", + /* 8:1,17:0x%.3x,1:0,1:%d,5:%d */ + (((xmask ? 3 : 1) << (17+1+1+5)) + | (mask << (1+1+5)) + | ((!!save_regs[1]) << 5) + | (frame_pointer_needed + ? FRAME_POINTER_REGNUM + : STACK_POINTER_REGNUM)), + (m88k_stack_size - (frame_pointer_needed ? m88k_fp_offset : 0)), + return_address_info, + register_save_offset); + if (xmask) + fprintf (file, ",0x%x%04x", xmask, (0xffff & xregister_save_offset)); + fputc ('\n', file); text_section (); } - + /* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. NAME is the mcount function name (varies), SAVEP indicates whether the parameter registers need to @@ -2232,6 +2274,8 @@ output_function_profiler (file, labelno, char dbi[256]; char *temp = (savep ? reg_names[2] : reg_names[10]); + /* Remember to update FUNCTION_PROFILER_LENGTH. */ + if (savep) { fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]); @@ -2291,6 +2335,8 @@ output_function_block_profiler (file, la char block[256]; char label[256]; + /* Remember to update FUNCTION_BLOCK_PROFILER_LENGTH. */ + ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0); ASM_GENERATE_INTERNAL_LABEL (label, "LPY", labelno); @@ -2302,11 +2348,21 @@ output_function_block_profiler (file, la m88k_pound_sign, &block[1]); fprintf (file, "\tbcnd\t %sne0,%s,%s\n", m88k_pound_sign, reg_names[26], &label[1]); + fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]); + fprintf (file, "\tst.d\t %s,%s,32\n", reg_names[2], reg_names[31]); + fprintf (file, "\tst.d\t %s,%s,40\n", reg_names[4], reg_names[31]); + fprintf (file, "\tst.d\t %s,%s,48\n", reg_names[6], reg_names[31]); + fprintf (file, "\tst.d\t %s,%s,56\n", reg_names[8], reg_names[31]); fputs ("\tbsr.n\t ", file); ASM_OUTPUT_LABELREF (file, "__bb_init_func"); putc ('\n', file); fprintf (file, "\tor\t %s,%s,%slo16(%s)\n", reg_names[2], reg_names[27], m88k_pound_sign, &block[1]); + fprintf (file, "\tld.d\t %s,%s,32\n", reg_names[2], reg_names[31]); + fprintf (file, "\tld.d\t %s,%s,40\n", reg_names[4], reg_names[31]); + fprintf (file, "\tld.d\t %s,%s,48\n", reg_names[6], reg_names[31]); + fprintf (file, "\tld.d\t %s,%s,56\n", reg_names[8], reg_names[31]); + fprintf (file, "\taddu\t %s,%s,64\n", reg_names[31], reg_names[31]); ASM_OUTPUT_INTERNAL_LABEL (file, "LPY", labelno); } @@ -2320,7 +2376,9 @@ output_block_profiler (file, blockno) { char block[256]; - ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0); + /* Remember to update BLOCK_PROFILER_LENGTH. */ + + ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 2); /* @@ Need to deal with PIC. I'm not sure what the requirements are on register usage, so I used r26/r27 to be safe. */ @@ -2440,7 +2498,7 @@ m88k_builtin_saveregs (arglist) } /* Allocate the va_list constructor */ - block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_UNIT); + block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_WORD); RTX_UNCHANGING_P (block) = 1; RTX_UNCHANGING_P (XEXP (block, 0)) = 1; @@ -2464,8 +2522,7 @@ m88k_builtin_saveregs (arglist) 2 * UNITS_PER_WORD)), copy_to_reg (XEXP (addr, 0))); - /* Now store the incoming registers and return the address of the - va_list constructor. */ + /* Now store the incoming registers. */ if (fixed < 8) move_block_from_reg (2 + fixed, @@ -2474,7 +2531,10 @@ m88k_builtin_saveregs (arglist) fixed * UNITS_PER_WORD)), 8 - fixed); - return copy_to_reg (XEXP (block, 0)); + /* Return the address of the va_list constructor, but don't put it in a + register. This fails when not optimizing and produces worse code when + optimizing. */ + return XEXP (block, 0); } /* If cmpsi has not been generated, emit code to do the test. Return the @@ -2581,6 +2641,45 @@ print_operand (file, x, code) case '#': /* SVR4 pound-sign syntax character (empty if SVR3) */ fputs (m88k_pound_sign, file); return; + case 'V': /* Output a serializing instruction as needed if the operand + (assumed to be a MEM) is a volatile load. */ + case 'v': /* ditto for a volatile store. */ + if (MEM_VOLATILE_P (x) && TARGET_SERIALIZE_VOLATILE) + { + /* The m88110 implements two FIFO queues, one for loads and + one for stores. These queues mean that loads complete in + their issue order as do stores. An interaction between the + history buffer and the store reservation station ensures + that a store will not bypass load. Finally, a load will not + bypass store, but only when they reference the same address. + + To avoid this reordering (a load bypassing a store) for + volatile references, a serializing instruction is output. + We choose the fldcr instruction as it does not serialize on + the m88100 so that -m88000 code will not be degraded. + + The mechanism below is completed by having CC_STATUS_INIT set + the code to the unknown value. */ + + static rtx last_addr = 0; + if (code == 'V' /* Only need to serialize before a load. */ + && m88k_volatile_code != 'V' /* Loads complete in FIFO order. */ + && !(m88k_volatile_code == 'v' + && GET_CODE (XEXP (x, 0)) == LO_SUM + && rtx_equal_p (XEXP (XEXP (x, 0), 1), last_addr))) + fprintf (file, +#ifdef AS_BUG_FLDCR + "fldcr\t %s,%scr63\n\t", +#else + "fldcr\t %s,%sfcr63\n\t", +#endif + reg_names[0], m88k_pound_sign); + m88k_volatile_code = code; + last_addr = (GET_CODE (XEXP (x, 0)) == LO_SUM + ? XEXP (XEXP (x, 0), 1) : 0); + } + return; + case 'X': /* print the upper 16 bits... */ value >>= 16; case 'x': /* print the lower 16 bits of the integer constant in hex */ @@ -2724,7 +2823,7 @@ print_operand (file, x, code) fputs (reg_names[REGNO (x) + 1], file); return; - case 'r': /* an immediate 0 should be repesented as `r0' */ + case 'r': /* an immediate 0 should be represented as `r0' */ if (x == const0_rtx) { fputs (reg_names[0], file);