diff options
Diffstat (limited to 'gcc/config/m32c/m32c.c')
-rw-r--r-- | gcc/config/m32c/m32c.c | 561 |
1 files changed, 540 insertions, 21 deletions
diff --git a/gcc/config/m32c/m32c.c b/gcc/config/m32c/m32c.c index 73d75d6..75fc3bc 100644 --- a/gcc/config/m32c/m32c.c +++ b/gcc/config/m32c/m32c.c @@ -412,7 +412,7 @@ m32c_override_options (void) error ("invalid target memregs value '%d'", target_memregs); } else - target_memregs = "16"; + target_memregs = 16; } /* Defining data structures for per-function information */ @@ -490,7 +490,6 @@ static struct void m32c_conditional_register_usage (void) { - int memregs; int i; if (0 <= target_memregs && target_memregs <= 16) @@ -564,8 +563,10 @@ m32c_modes_tieable_p (enum machine_mode m1, enum machine_mode m2) if (GET_MODE_SIZE (m1) == GET_MODE_SIZE (m2)) return 1; +#if 0 if (m1 == QImode || m2 == QImode) return 0; +#endif return 1; } @@ -615,10 +616,10 @@ m32c_reg_class_from_constraint (char c ATTRIBUTE_UNUSED, const char *s) return FB_REGS; if (memcmp (s, "Rsb", 3) == 0) return SB_REGS; - if (memcmp (s, "Rcr", 3) == 0 && TARGET_A16) - return CR_REGS; - if (memcmp (s, "Rcl", 3) == 0 && TARGET_A24) - return CR_REGS; + if (memcmp (s, "Rcr", 3) == 0) + return TARGET_A16 ? CR_REGS : NO_REGS; + if (memcmp (s, "Rcl", 3) == 0) + return TARGET_A24 ? CR_REGS : NO_REGS; if (memcmp (s, "R0w", 3) == 0) return R0_REGS; if (memcmp (s, "R1w", 3) == 0) @@ -637,12 +638,16 @@ m32c_reg_class_from_constraint (char c ATTRIBUTE_UNUSED, const char *s) return HL_REGS; if (memcmp (s, "R23", 3) == 0) return R23_REGS; + if (memcmp (s, "Ra0", 3) == 0) + return A0_REGS; + if (memcmp (s, "Ra1", 3) == 0) + return A1_REGS; if (memcmp (s, "Raa", 3) == 0) return A_REGS; - if (memcmp (s, "Raw", 3) == 0 && TARGET_A16) - return A_REGS; - if (memcmp (s, "Ral", 3) == 0 && TARGET_A24) - return A_REGS; + if (memcmp (s, "Raw", 3) == 0) + return TARGET_A16 ? A_REGS : NO_REGS; + if (memcmp (s, "Ral", 3) == 0) + return TARGET_A24 ? A_REGS : NO_REGS; if (memcmp (s, "Rqi", 3) == 0) return QI_REGS; if (memcmp (s, "Rad", 3) == 0) @@ -677,6 +682,12 @@ m32c_reg_class_from_constraint (char c ATTRIBUTE_UNUSED, const char *s) if (memcmp (s, "Rpa", 3) == 0) return NO_REGS; + if (*s == 'R') + { + fprintf(stderr, "unrecognized R constraint: %.3s\n", s); + gcc_unreachable(); + } + return NO_REGS; } @@ -914,11 +925,25 @@ m32c_const_ok_for_constraint_p (HOST_WIDE_INT value, int b = exact_log2 (value); return (b >= 1 && b <= 8); } + if (memcmp (str, "Imb", 3) == 0) + { + int b = exact_log2 ((value ^ 0xff) & 0xff); + return (b >= 1 && b <= 8); + } if (memcmp (str, "Ilw", 3) == 0) { int b = exact_log2 (value); return (b >= 1 && b <= 16); } + if (memcmp (str, "Imw", 3) == 0) + { + int b = exact_log2 ((value ^ 0xffff) & 0xffff); + return (b >= 1 && b <= 16); + } + if (memcmp (str, "I00", 3) == 0) + { + return (value == 0); + } return 0; } @@ -937,6 +962,12 @@ m32c_extra_constraint_p2 (rtx value, char c ATTRIBUTE_UNUSED, const char *str) return 1; if (RTX_IS ("ms") || RTX_IS ("m+si")) return 1; + if (RTX_IS ("m++rii")) + { + if (REGNO (patternr[3]) == FB_REGNO + && INTVAL (patternr[4]) == 0) + return 1; + } if (RTX_IS ("mr")) r = patternr[1]; else if (RTX_IS ("m+ri") || RTX_IS ("m+rs") || RTX_IS ("m+r+si")) @@ -980,6 +1011,12 @@ m32c_extra_constraint_p2 (rtx value, char c ATTRIBUTE_UNUSED, const char *str) && (IS_REG (patternr[1], SB_REGNO))) || (RTX_IS ("m+ri") && (IS_REG (patternr[2], SB_REGNO)))); } + else if (memcmp (str, "Sp", 2) == 0) + { + /* Absolute addresses 0..0x1fff used for bit addressing (I/O ports) */ + return (RTX_IS ("mi") + && !(INTVAL (patternr[1]) & ~0x1fff)); + } else if (memcmp (str, "S1", 2) == 0) { return r1h_operand (value, QImode); @@ -1683,6 +1720,32 @@ m32c_initialize_trampoline (rtx tramp, rtx function, rtx chainval) #undef A0 } +/* Implicit Calls to Library Routines */ + +#undef TARGET_INIT_LIBFUNCS +#define TARGET_INIT_LIBFUNCS m32c_init_libfuncs +static void +m32c_init_libfuncs (void) +{ + if (TARGET_A24) + { + /* We do this because the M32C has an HImode operand, but the + M16C has an 8 bit operand. Since gcc looks at the match data + and not the expanded rtl, we have to reset the array so that + the right modes are found. */ + setcc_gen_code[EQ] = CODE_FOR_seq_24; + setcc_gen_code[NE] = CODE_FOR_sne_24; + setcc_gen_code[GT] = CODE_FOR_sgt_24; + setcc_gen_code[GE] = CODE_FOR_sge_24; + setcc_gen_code[LT] = CODE_FOR_slt_24; + setcc_gen_code[LE] = CODE_FOR_sle_24; + setcc_gen_code[GTU] = CODE_FOR_sgtu_24; + setcc_gen_code[GEU] = CODE_FOR_sgeu_24; + setcc_gen_code[LTU] = CODE_FOR_sltu_24; + setcc_gen_code[LEU] = CODE_FOR_sleu_24; + } +} + /* Addressing Modes */ /* Used by GO_IF_LEGITIMATE_ADDRESS. The r8c/m32c family supports a @@ -2030,6 +2093,107 @@ m32c_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, return COSTS_N_INSNS (10); } +/* Here we try to describe when we use multiple opcodes for one RTX so + that gcc knows when to use them. */ +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS m32c_rtx_costs +static bool +m32c_rtx_costs (rtx x, int code, int outer_code, int *total) +{ + switch (code) + { + case REG: + if (REGNO (x) >= MEM0_REGNO && REGNO (x) <= MEM7_REGNO) + *total += COSTS_N_INSNS (500); + else + *total += COSTS_N_INSNS (1); + return true; + + case ASHIFT: + case LSHIFTRT: + case ASHIFTRT: + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + { + /* mov.b r1l, r1h */ + *total += COSTS_N_INSNS (1); + return true; + } + if (INTVAL (XEXP (x, 1)) > 8 + || INTVAL (XEXP (x, 1)) < -8) + { + /* mov.b #N, r1l */ + /* mov.b r1l, r1h */ + *total += COSTS_N_INSNS (2); + return true; + } + return true; + + case LE: + case LEU: + case LT: + case LTU: + case GT: + case GTU: + case GE: + case GEU: + case NE: + case EQ: + if (outer_code == SET) + { + *total += COSTS_N_INSNS (2); + return true; + } + break; + + case ZERO_EXTRACT: + { + rtx dest = XEXP (x, 0); + rtx addr = XEXP (dest, 0); + switch (GET_CODE (addr)) + { + case CONST_INT: + *total += COSTS_N_INSNS (1); + break; + case SYMBOL_REF: + *total += COSTS_N_INSNS (3); + break; + default: + *total += COSTS_N_INSNS (2); + break; + } + return true; + } + break; + + default: + /* Reasonable default. */ + if (TARGET_A16 && GET_MODE(x) == SImode) + *total += COSTS_N_INSNS (2); + break; + } + return false; +} + +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST m32c_address_cost +static int +m32c_address_cost (rtx addr) +{ + /* fprintf(stderr, "\naddress_cost\n"); + debug_rtx(addr);*/ + switch (GET_CODE (addr)) + { + case CONST_INT: + return COSTS_N_INSNS(1); + case SYMBOL_REF: + return COSTS_N_INSNS(3); + case REG: + return COSTS_N_INSNS(2); + default: + return 0; + } +} + /* Defining the Output Assembler Language */ /* The Overall Framework of an Assembler File */ @@ -2111,6 +2275,7 @@ const conversions[] = { { 'X', "i", "#0" }, { 'm', "i", "#0" }, { 'b', "i", "#0" }, + { 'B', "i", "0" }, { 'p', "i", "0" }, { 0, 0, 0 } @@ -2253,6 +2418,39 @@ m32c_print_operand (FILE * file, rtx x, int code) x = m32c_subreg (HImode, x, SImode, 2); code = 0; } + if (code == 'h' && GET_MODE (x) == HImode) + { + x = m32c_subreg (QImode, x, HImode, 0); + code = 0; + } + if (code == 'H' && GET_MODE (x) == HImode) + { + /* We can't actually represent this as an rtx. Do it here. */ + if (GET_CODE (x) == REG) + { + switch (REGNO (x)) + { + case R0_REGNO: + fputs ("r0h", file); + return; + case R1_REGNO: + fputs ("r1h", file); + return; + default: + gcc_unreachable(); + } + } + /* This should be a MEM. */ + x = m32c_subreg (QImode, x, HImode, 1); + code = 0; + } + /* This is for BMcond, which always wants word register names. */ + if (code == 'h' && GET_MODE (x) == QImode) + { + if (GET_CODE (x) == REG) + x = gen_rtx_REG (HImode, REGNO (x)); + code = 0; + } /* 'x' and 'X' need to be ignored for non-immediates. */ if ((code == 'x' || code == 'X') && GET_CODE (x) != CONST_INT) code = 0; @@ -2284,8 +2482,17 @@ m32c_print_operand (FILE * file, rtx x, int code) switch (code) { case 'b': - /* Bit position. */ - fprintf (file, "%d", (int) exact_log2 (INTVAL (r))); + case 'B': + { + int v = INTVAL (r); + int i = (int) exact_log2 (v); + if (i == -1) + i = (int) exact_log2 ((v ^ 0xffff) & 0xffff); + if (i == -1) + i = (int) exact_log2 ((v ^ 0xff) & 0xff); + /* Bit position. */ + fprintf (file, "%d", i); + } break; case 'x': /* Unsigned byte. */ @@ -2838,6 +3045,184 @@ m32c_split_move (rtx * operands, enum machine_mode mode, int split_all) return rv; } +/* The m32c has a number of opcodes that act like memcpy, strcmp, and + the like. For the R8C they expect one of the addresses to be in + R1L:An so we need to arrange for that. Otherwise, it's just a + matter of picking out the operands we want and emitting the right + pattern for them. All these expanders, which correspond to + patterns in blkmov.md, must return nonzero if they expand the insn, + or zero if they should FAIL. */ + +/* This is a memset() opcode. All operands are implied, so we need to + arrange for them to be in the right registers. The opcode wants + addresses, not [mem] syntax. $0 is the destination (MEM:BLK), $1 + the count (HI), and $2 the value (QI). */ +int +m32c_expand_setmemhi(rtx *operands) +{ + rtx desta, count, val; + rtx desto, counto; + + desta = XEXP (operands[0], 0); + count = operands[1]; + val = operands[2]; + + desto = gen_reg_rtx (Pmode); + counto = gen_reg_rtx (HImode); + + if (GET_CODE (desta) != REG + || REGNO (desta) < FIRST_PSEUDO_REGISTER) + desta = copy_to_mode_reg (Pmode, desta); + + /* This looks like an arbitrary restriction, but this is by far the + most common case. For counts 8..14 this actually results in + smaller code with no speed penalty because the half-sized + constant can be loaded with a shorter opcode. */ + if (GET_CODE (count) == CONST_INT + && GET_CODE (val) == CONST_INT + && ! (INTVAL (count) & 1) + && (INTVAL (count) > 1) + && (INTVAL (val) <= 7 && INTVAL (val) >= -8)) + { + unsigned v = INTVAL (val) & 0xff; + v = v | (v << 8); + count = copy_to_mode_reg (HImode, GEN_INT (INTVAL (count) / 2)); + val = copy_to_mode_reg (HImode, GEN_INT (v)); + if (TARGET_A16) + emit_insn (gen_setmemhi_whi_op (desto, counto, val, desta, count)); + else + emit_insn (gen_setmemhi_wpsi_op (desto, counto, val, desta, count)); + return 1; + } + + /* This is the generalized memset() case. */ + if (GET_CODE (val) != REG + || REGNO (val) < FIRST_PSEUDO_REGISTER) + val = copy_to_mode_reg (QImode, val); + + if (GET_CODE (count) != REG + || REGNO (count) < FIRST_PSEUDO_REGISTER) + count = copy_to_mode_reg (HImode, count); + + if (TARGET_A16) + emit_insn (gen_setmemhi_bhi_op (desto, counto, val, desta, count)); + else + emit_insn (gen_setmemhi_bpsi_op (desto, counto, val, desta, count)); + + return 1; +} + +/* This is a memcpy() opcode. All operands are implied, so we need to + arrange for them to be in the right registers. The opcode wants + addresses, not [mem] syntax. $0 is the destination (MEM:BLK), $1 + is the source (MEM:BLK), and $2 the count (HI). */ +int +m32c_expand_movmemhi(rtx *operands) +{ + rtx desta, srca, count; + rtx desto, srco, counto; + + desta = XEXP (operands[0], 0); + srca = XEXP (operands[1], 0); + count = operands[2]; + + desto = gen_reg_rtx (Pmode); + srco = gen_reg_rtx (Pmode); + counto = gen_reg_rtx (HImode); + + if (GET_CODE (desta) != REG + || REGNO (desta) < FIRST_PSEUDO_REGISTER) + desta = copy_to_mode_reg (Pmode, desta); + + if (GET_CODE (srca) != REG + || REGNO (srca) < FIRST_PSEUDO_REGISTER) + srca = copy_to_mode_reg (Pmode, srca); + + /* Similar to setmem, but we don't need to check the value. */ + if (GET_CODE (count) == CONST_INT + && ! (INTVAL (count) & 1) + && (INTVAL (count) > 1)) + { + count = copy_to_mode_reg (HImode, GEN_INT (INTVAL (count) / 2)); + if (TARGET_A16) + emit_insn (gen_movmemhi_whi_op (desto, srco, counto, desta, srca, count)); + else + emit_insn (gen_movmemhi_wpsi_op (desto, srco, counto, desta, srca, count)); + return 1; + } + + /* This is the generalized memset() case. */ + if (GET_CODE (count) != REG + || REGNO (count) < FIRST_PSEUDO_REGISTER) + count = copy_to_mode_reg (HImode, count); + + if (TARGET_A16) + emit_insn (gen_movmemhi_bhi_op (desto, srco, counto, desta, srca, count)); + else + emit_insn (gen_movmemhi_bpsi_op (desto, srco, counto, desta, srca, count)); + + return 1; +} + +/* This is a stpcpy() opcode. $0 is the destination (MEM:BLK) after + the copy, which should point to the NUL at the end of the string, + $1 is the destination (MEM:BLK), and $2 is the source (MEM:BLK). + Since our opcode leaves the destination pointing *after* the NUL, + we must emit an adjustment. */ +int +m32c_expand_movstr(rtx *operands) +{ + rtx desta, srca; + rtx desto, srco; + + desta = XEXP (operands[1], 0); + srca = XEXP (operands[2], 0); + + desto = gen_reg_rtx (Pmode); + srco = gen_reg_rtx (Pmode); + + if (GET_CODE (desta) != REG + || REGNO (desta) < FIRST_PSEUDO_REGISTER) + desta = copy_to_mode_reg (Pmode, desta); + + if (GET_CODE (srca) != REG + || REGNO (srca) < FIRST_PSEUDO_REGISTER) + srca = copy_to_mode_reg (Pmode, srca); + + emit_insn (gen_movstr_op (desto, srco, desta, srca)); + /* desto ends up being a1, which allows this type of add through MOVA. */ + emit_insn (gen_addpsi3 (operands[0], desto, GEN_INT (-1))); + + return 1; +} + +/* This is a strcmp() opcode. $0 is the destination (HI) which holds + <=>0 depending on the comparison, $1 is one string (MEM:BLK), and + $2 is the other (MEM:BLK). We must do the comparison, and then + convert the flags to a signed integer result. */ +int +m32c_expand_cmpstr(rtx *operands) +{ + rtx src1a, src2a; + + src1a = XEXP (operands[1], 0); + src2a = XEXP (operands[2], 0); + + if (GET_CODE (src1a) != REG + || REGNO (src1a) < FIRST_PSEUDO_REGISTER) + src1a = copy_to_mode_reg (Pmode, src1a); + + if (GET_CODE (src2a) != REG + || REGNO (src2a) < FIRST_PSEUDO_REGISTER) + src2a = copy_to_mode_reg (Pmode, src2a); + + emit_insn (gen_cmpstrhi_op (src1a, src2a, src1a, src2a)); + emit_insn (gen_cond_to_int (operands[0])); + + return 1; +} + + typedef rtx (*shift_gen_func)(rtx, rtx, rtx); static shift_gen_func @@ -2857,11 +3242,14 @@ shift_gen_func_for (int mode, int code) GFF(SImode, ASHIFTRT, TARGET_A16 ? gen_ashrsi3_16 : gen_ashrsi3_24); GFF(SImode, LSHIFTRT, TARGET_A16 ? gen_lshrsi3_16 : gen_lshrsi3_24); #undef GFF + gcc_unreachable (); } /* The m32c only has one shift, but it takes a signed count. GCC doesn't want this, so we fake it by negating any shift count when - we're pretending to shift the other way. */ + we're pretending to shift the other way. Also, the shift count is + limited to -8..8. It's slightly better to use two shifts for 9..15 + than to load the count into r1h, so we do that too. */ int m32c_prepare_shift (rtx * operands, int scale, int shift_code) { @@ -2971,23 +3359,154 @@ m32c_expand_neg_mulpsi3 (rtx * operands) { /* operands: a = b * i */ rtx temp1; /* b as SI */ - rtx temp2; /* -b as SI */ - rtx temp3; /* -b as PSI */ - rtx scale; + rtx scale /* i as SI */; + rtx temp2; /* a*b as SI */ temp1 = gen_reg_rtx (SImode); temp2 = gen_reg_rtx (SImode); - temp3 = gen_reg_rtx (PSImode); - scale = GEN_INT (- INTVAL (operands[2])); + if (GET_CODE (operands[2]) != CONST_INT) + { + scale = gen_reg_rtx (SImode); + emit_insn (gen_zero_extendpsisi2 (scale, operands[2])); + } + else + scale = copy_to_mode_reg (SImode, operands[2]); emit_insn (gen_zero_extendpsisi2 (temp1, operands[1])); - emit_insn (gen_negsi2 (temp2, temp1)); - emit_insn (gen_truncsipsi2 (temp3, temp2)); - emit_insn (gen_mulpsi3 (operands[0], temp3, scale)); + temp2 = expand_simple_binop (SImode, MULT, temp1, scale, temp2, 1, OPTAB_LIB); + emit_insn (gen_truncsipsi2 (operands[0], temp2)); } /* Pattern Output Functions */ +/* Returns a (OP (reg:CC FLG_REGNO) (const_int 0)) from some other + match_operand rtx's OP. */ +rtx +m32c_cmp_flg_0 (rtx cmp) +{ + return gen_rtx_fmt_ee (GET_CODE (cmp), + GET_MODE (cmp), + gen_rtx_REG (CCmode, FLG_REGNO), + GEN_INT (0)); +} + +int +m32c_expand_movcc (rtx *operands) +{ + rtx rel = operands[1]; + if (GET_CODE (rel) != EQ && GET_CODE (rel) != NE) + return 1; + if (GET_CODE (operands[2]) != CONST_INT + || GET_CODE (operands[3]) != CONST_INT) + return 1; + emit_insn (gen_cmpqi(XEXP (rel, 0), XEXP (rel, 1))); + if (GET_CODE (rel) == NE) + { + rtx tmp = operands[2]; + operands[2] = operands[3]; + operands[3] = tmp; + } + if (TARGET_A16) + emit_insn (gen_stzx_16 (operands[0], operands[2], operands[3])); + else if (GET_MODE (operands[0]) == QImode) + emit_insn (gen_stzx_24_qi (operands[0], operands[2], operands[3])); + else + emit_insn (gen_stzx_24_hi (operands[0], operands[2], operands[3])); + return 0; +} + +/* Used for the "insv" pattern. Return nonzero to fail, else done. */ +int +m32c_expand_insv (rtx *operands) +{ + rtx op0, src0, p; + int mask; + + if (INTVAL (operands[1]) != 1) + return 1; + + mask = 1 << INTVAL (operands[2]); + + op0 = operands[0]; + if (GET_CODE (op0) == SUBREG + && SUBREG_BYTE (op0) == 0) + { + rtx sub = SUBREG_REG (op0); + if (GET_MODE (sub) == HImode || GET_MODE (sub) == QImode) + op0 = sub; + } + + if (no_new_pseudos + || (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0))) + src0 = op0; + else + { + src0 = gen_reg_rtx (GET_MODE (op0)); + emit_move_insn (src0, op0); + } + + if (GET_MODE (op0) == HImode + && INTVAL (operands[2]) >= 8 + && GET_MODE (op0) == MEM) + { + /* We are little endian. */ + rtx new_mem = gen_rtx_MEM (QImode, plus_constant (XEXP (op0, 0), 1)); + MEM_COPY_ATTRIBUTES (new_mem, op0); + mask >>= 8; + } + + if (INTVAL (operands[3])) + { + if (GET_MODE (op0) == HImode) + mask ^= 0xffff; + else + mask ^= 0xff; + } + if (GET_MODE (op0) == HImode) + { + if (mask & 0x8000) + mask -= 0x10000; + } + else + { + if (mask & 0x80) + mask -= 0x100; + } + + switch ( (INTVAL (operands[3]) ? 4 : 0) + + ((GET_MODE (op0) == HImode) ? 2 : 0) + + (TARGET_A24 ? 1 : 0)) + { + case 0: p = gen_andqi3_16 (op0, src0, GEN_INT (mask)); break; + case 1: p = gen_andqi3_24 (op0, src0, GEN_INT (mask)); break; + case 2: p = gen_andhi3_16 (op0, src0, GEN_INT (mask)); break; + case 3: p = gen_andhi3_24 (op0, src0, GEN_INT (mask)); break; + case 4: p = gen_iorqi3_16 (op0, src0, GEN_INT (mask)); break; + case 5: p = gen_iorqi3_24 (op0, src0, GEN_INT (mask)); break; + case 6: p = gen_iorhi3_16 (op0, src0, GEN_INT (mask)); break; + case 7: p = gen_iorhi3_24 (op0, src0, GEN_INT (mask)); break; + } + + emit_insn (p); + return 0; +} + +const char * +m32c_scc_pattern(rtx *operands, RTX_CODE code) +{ + static char buf[30]; + if (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) == R0_REGNO) + { + if (code == EQ) + return "stzx\t#1,#0,r0l"; + if (code == NE) + return "stzx\t#0,#1,r0l"; + } + sprintf(buf, "bm%s\t0,%%h0\n\tand.b\t#1,%%0", GET_RTX_NAME (code)); + return buf; +} + /* Returns TRUE if the current function is a leaf, and thus we can determine which registers an interrupt function really needs to save. The logic below is mostly about finding the insn sequence |