This change converts the pdp11 back end to use CCmode rather than cc0. It is functional and the testsuite compile sections runs at least as well as before.
There is additional work left to be done; for example, the compare elimination pass, which this target uses, does not know how to deal with a machine that has two condition code registers. Committed. paul ChangeLog: 2018-06-27 Paul Koning <n...@arrl.net> * common/config/pdp11/pdp11-common.c (pdp11_handle_option): Handle mutually exclusive options. * config/pdp11/constraints.md (h): New constraint. (O): Update definition to match shift code generation. (D): New constraint. * config/pdp11/pdp11-modes.def (CCNZ): Define mode. (CCFP): Remove. * config/pdp11/pdp11-protos.h (int_no_side_effect_operand): New function. (output_jump): Change arguments. (pdp11_fixed_cc_regs): New function. (pdp11_cc_mode): Ditto. (pdp11_expand_shift): Ditto. (pdp11_assemble_shift): Ditto. (pdp11_small_shift): Ditto. (pdp11_branch_cost): Remove. * config/pdp11/pdp11.c (pdp11_assemble_integer): Remove comments from output. (pdp11_register_move_cost): Update for CC registers. (pdp11_rtx_costs): Add case for LSHIFTRT. (pdp11_output_jump): Add CCNZ mode conditional branches. (notice_update_cc_on_set): Remove. (pdp11_cc_mode): New function. (simple_memory_operand): Correct pre/post decrement case. (no_side_effect_operand): New function. (pdp11_regno_reg_class): Add CC_REGS class. (pdp11_fixed_cc_regs): New function. (pdp11_small_shift): New function. (pdp11_expand_shift): New function to expand shift insns. (pdp11_assemble_shift): New function to output shifts. (pdp11_branch_cost): Remove. (pdp11_modes_tieable_p): Make QI/HI modes tieable. * config/pdp11/pdp11.h (SIZE_TYPE): Ensure 16-bit type. (WCHAR_TYPE): Ditto. (PTRDIFF_TYPE): Ditto. (ADJUST_INSN_LENGTH): New macro. (FIXED_REGISTERS): Add CC registers. (CALL_USED_REGISTERS): Ditto. (reg_class): Ditto. (REG_CLASS_NAMES): Ditto. (REG_CLASS_CONTENTS): Ditto. (SELECT_CC_MODE): Use new function. (TARGET_FLAGS_REGNUM): New macro. (TARGET_FIXED_CONDITION_CODE_REGS): Ditto. (cc0_reg_rtx): Remove. (CC_STATUS_MDEP): Remove. (CC_STATUS_MDEFP_INIT): Remove. (CC_IN_FPU): Remove. (NOTICE_UPDATE_CC): Remove. (REGISTER_NAMES): Add CC registers. (BRANCH_COST): Change to constant 1. * config/pdp11/pdp11.md: Rewrite for CCmode condition code handling. * config/pdp11/pdp11.opt (mbcopy): Remove. (mbcopy-builtin): Remove. (mbranch-cheap): Remove. (mbranch-expensive): Remove. * config/pdp11/predicates.md (expand_shift_operand): Update to match shift code generation. (ccnz_operator): New predicate. * doc/invoke.texi (PDP-11 Options): Remove deleted options -mbcopy, -mbcopy-builtin, -mbranch-cheap, -mbranch-expensive. Remove non-existent option -mabshi, -mno-abshi. Document mutually exclusive options. * doc/md.texi (PDP-11): Document new D and h constraints. Update description of O constraint. Index: common/config/pdp11/pdp11-common.c =================================================================== --- common/config/pdp11/pdp11-common.c (revision 262195) +++ common/config/pdp11/pdp11-common.c (working copy) @@ -39,9 +39,27 @@ pdp11_handle_option (struct gcc_options *opts, switch (code) { case OPT_m10: - opts->x_target_flags &= ~(MASK_40 | MASK_45); + opts->x_target_flags &= ~(MASK_40 | MASK_45 | MASK_FPU | MASK_AC0 | MASK_SPLIT); return true; + case OPT_m40: + opts->x_target_flags &= ~(MASK_45 | MASK_FPU | MASK_AC0 | MASK_SPLIT); + return true; + + case OPT_mfpu: + opts->x_target_flags &= ~MASK_40; + opts->x_target_flags |= MASK_45; + return true; + + case OPT_msoft_float: + opts->x_target_flags &= ~MASK_AC0; + return true; + + case OPT_msplit: + opts->x_target_flags &= ~MASK_40; + opts->x_target_flags |= MASK_45; + return true; + default: return true; } Index: config/pdp11/constraints.md =================================================================== --- config/pdp11/constraints.md (revision 262195) +++ config/pdp11/constraints.md (working copy) @@ -18,11 +18,14 @@ ;; along with GCC; see the file COPYING3. If not see ;; <http://www.gnu.org/licenses/>. +(define_register_constraint "a" "LOAD_FPU_REGS" + "FPU register that can be directly loaded from memory") + (define_register_constraint "f" "FPU_REGS" "Any FPU register") -(define_register_constraint "a" "LOAD_FPU_REGS" - "FPU register that can be directly loaded from memory") +(define_register_constraint "h" "NO_LOAD_FPU_REGS" + "FPU register that cannot be directly loaded from memory") (define_register_constraint "d" "MUL_REGS" "General register that can be used for 16-bit multiply (odd numbered)") @@ -60,7 +63,7 @@ (define_constraint "O" "Integer constant for which several individual shifts are better than one big one" (and (match_code "const_int") - (match_test "abs (ival) > 1 && abs (ival) <= 4"))) + (match_test "pdp11_small_shift (ival)"))) (define_constraint "G" "Defines a real zero constant." @@ -79,3 +82,9 @@ (match_test "memory_address_p (GET_MODE (op), XEXP (op, 0)) && simple_memory_operand (op, GET_MODE (op))"))) +(define_constraint "D" + "Memory reference that is encoded within the opcode, and not push or pop" + (and (match_code "mem") + (match_test "memory_address_p (GET_MODE (op), XEXP (op, 0)) + && no_side_effect_operand (op, GET_MODE (op))"))) + Index: config/pdp11/pdp11-modes.def =================================================================== --- config/pdp11/pdp11-modes.def (revision 262195) +++ config/pdp11/pdp11-modes.def (working copy) @@ -19,8 +19,26 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ /* Add any extra modes needed to represent the condition code. - CCFPmode is used for FPU, but should we use a separate reg? */ -CC_MODE (CCFP); + The default CCmode is the CPU condition codes, as set by compare; + all conditional branches are valid with this. + + CCNZmode is the CPU condition code as a side effect of arithmetic + or logic operations where N and Z reflect sign and zero status of + the result, but the V bit is not meaningful. Unsigned conditional + branches don't apply then (no such thing when comparing with zero) + and signed branches that use V need to clear V first if they are to + be used. CCNZ mode appears in side effects (implicit compare with + zero) if V is not forced to 0 by the instruction. In such cases, V + often reflects signed overflow of the operation, which means a + signed branch will get the sign backwards. This applies both to + some float and integer operations. + + These modes are used both in the FPU and the CPU, since they have + the same meaning, and also because the FPU condition codes are + copied to the CPU before being used in conditional branches. */ + +CC_MODE (CCNZ); + RESET_FLOAT_FORMAT (SF, pdp11_f_format); RESET_FLOAT_FORMAT (DF, pdp11_d_format); Index: config/pdp11/pdp11-protos.h =================================================================== --- config/pdp11/pdp11-protos.h (revision 262195) +++ config/pdp11/pdp11-protos.h (working copy) @@ -21,21 +21,27 @@ along with GCC; see the file COPYING3. If not see /* declarations */ #ifdef RTX_CODE extern int simple_memory_operand (rtx, machine_mode); - +extern int no_side_effect_operand (rtx, machine_mode); extern int legitimate_const_double_p (rtx); extern void notice_update_cc_on_set (rtx, rtx); extern void output_addr_const_pdp11 (FILE *, rtx); extern const char *output_move_multiple (rtx *); extern const char *output_block_move (rtx *); -extern const char *output_jump (enum rtx_code, int, int); +extern const char *output_jump (rtx *, int, int); extern void print_operand_address (FILE *, rtx); typedef enum { no_action, dec_before, inc_after } pdp11_action; typedef enum { little, either, big } pdp11_partorder; -extern bool pdp11_expand_operands (rtx *, rtx [][2], int, +extern bool pdp11_expand_operands (rtx *, rtx [][2], int, pdp11_action *, pdp11_partorder); extern int pdp11_sp_frame_offset (void); extern int pdp11_initial_elimination_offset (int, int); extern enum reg_class pdp11_regno_reg_class (int); +extern bool pdp11_fixed_cc_regs (unsigned int *, unsigned int *); +extern machine_mode pdp11_cc_mode (enum rtx_code, rtx, rtx); +extern bool pdp11_expand_shift (rtx *, rtx (*) (rtx, rtx, rtx), + rtx (*) (rtx, rtx, rtx)); +extern const char * pdp11_assemble_shift (rtx *, machine_mode, int); +extern bool pdp11_small_shift (int); #endif /* RTX_CODE */ @@ -43,5 +49,4 @@ extern void output_ascii (FILE *, const char *, in extern void pdp11_asm_output_var (FILE *, const char *, int, int, bool); extern void pdp11_expand_prologue (void); extern void pdp11_expand_epilogue (void); -extern int pdp11_branch_cost (void); extern poly_int64 pdp11_push_rounding (poly_int64); Index: config/pdp11/pdp11.c =================================================================== --- config/pdp11/pdp11.c (revision 262195) +++ config/pdp11/pdp11.c (working copy) @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "expr.h" #include "builtins.h" #include "dbxout.h" +#include "expmed.h" /* This file should be included last. */ #include "target-def.h" @@ -146,9 +147,6 @@ decode_pdp11_d (const struct real_format *fmt ATTR (*vax_d_format.decode) (fmt, r, tbuf); } -/* This is where the condition code register lives. */ -/* rtx cc0_reg_rtx; - no longer needed? */ - static const char *singlemove_string (rtx *); static bool pdp11_assemble_integer (rtx, unsigned int, int); static bool pdp11_rtx_costs (rtx, machine_mode, int, int, int *, bool); @@ -384,7 +382,7 @@ pdp11_expand_epilogue (void) emit_move_insn (reg, x); else { - emit_move_insn (via_ac, x); + emit_move_insn (via_ac, x); emit_move_insn (reg, via_ac); } } @@ -872,14 +870,13 @@ pdp11_assemble_integer (rtx x, unsigned int size, case 1: fprintf (asm_out_file, "\t.byte\t"); output_addr_const_pdp11 (asm_out_file, GEN_INT (INTVAL (x) & 0xff)); -; - fprintf (asm_out_file, " /* char */\n"); + fputs ("\n", asm_out_file); return true; case 2: fprintf (asm_out_file, TARGET_UNIX_ASM ? "\t" : "\t.word\t"); output_addr_const_pdp11 (asm_out_file, x); - fprintf (asm_out_file, " /* short */\n"); + fputs ("\n", asm_out_file); return true; } return default_assemble_integer (x, size, aligned_p); @@ -886,34 +883,27 @@ pdp11_assemble_integer (rtx x, unsigned int size, } -/* register move costs, indexed by regs */ - -static const int move_costs[N_REG_CLASSES][N_REG_CLASSES] = -{ - /* NO MUL GEN LFPU NLFPU FPU ALL */ - -/* NO */ { 0, 0, 0, 0, 0, 0, 0}, -/* MUL */ { 0, 2, 2, 22, 22, 22, 22}, -/* GEN */ { 0, 2, 2, 22, 22, 22, 22}, -/* LFPU */ { 0, 22, 22, 2, 2, 2, 22}, -/* NLFPU */ { 0, 22, 22, 2, 10, 10, 22}, -/* FPU */ { 0, 22, 22, 2, 10, 10, 22}, -/* ALL */ { 0, 22, 22, 22, 22, 22, 22} -} ; - - -/* -- note that some moves are tremendously expensive, - because they require lots of tricks! do we have to - charge the costs incurred by secondary reload class - -- as we do here with 10 -- or not ? */ - +/* Register to register moves are cheap if both are general registers. + The same is true for FPU, but there we return cost of 3 rather than + 2 to make reload look at the constraints. The raeson is that + load/store double require extra care since load touches condition + codes and store doesn't, which is (partly anyway) described by + constraints. */ static int pdp11_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED, reg_class_t c1, reg_class_t c2) { - return move_costs[(int)c1][(int)c2]; + if (((c1 == MUL_REGS || c1 == GENERAL_REGS) && + (c2 == MUL_REGS || c2 == GENERAL_REGS))) + return 2; + else if ((c1 >= LOAD_FPU_REGS && c1 <= FPU_REGS && c2 == LOAD_FPU_REGS) || + (c2 >= LOAD_FPU_REGS && c2 <= FPU_REGS && c1 == LOAD_FPU_REGS)) + return 3; + else + return 22; } + static bool pdp11_rtx_costs (rtx x, machine_mode mode, int outer_code ATTRIBUTE_UNUSED, int opno ATTRIBUTE_UNUSED, int *total, @@ -988,7 +978,6 @@ pdp11_rtx_costs (rtx x, machine_mode mode, int out return false; case ASHIFT: - case LSHIFTRT: case ASHIFTRT: if (optimize_size) *total = COSTS_N_INSNS (1); @@ -1020,6 +1009,37 @@ pdp11_rtx_costs (rtx x, machine_mode mode, int out } return false; + case LSHIFTRT: + if (optimize_size) + *total = COSTS_N_INSNS (2); + else if (mode == QImode) + { + if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total = COSTS_N_INSNS (12); /* worst case */ + else + *total = COSTS_N_INSNS (1 + INTVAL (XEXP (x, 1))); + } + else if (mode == HImode) + { + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + if (abs (INTVAL (XEXP (x, 1))) == 1) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (3.5 + 0.5 * INTVAL (XEXP (x, 1))); + } + else + *total = COSTS_N_INSNS (12); /* worst case */ + } + else if (mode == SImode) + { + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + *total = COSTS_N_INSNS (3.5 + 0.5 * INTVAL (XEXP (x, 1))); + else /* worst case */ + *total = COSTS_N_INSNS (20); + } + return false; + default: return false; } @@ -1026,108 +1046,97 @@ pdp11_rtx_costs (rtx x, machine_mode mode, int out } const char * -output_jump (enum rtx_code code, int inv, int length) +output_jump (rtx *operands, int ccnz, int length) { - static int x = 0; - - static char buf[1000]; - const char *pos, *neg; + rtx tmpop[1]; + static char buf[100]; + const char *pos, *neg; + enum rtx_code code = GET_CODE (operands[0]); - if (cc_prev_status.flags & CC_NO_OVERFLOW) - { - switch (code) - { - case GTU: code = GT; break; - case LTU: code = LT; break; - case GEU: code = GE; break; - case LEU: code = LE; break; - default: ; - } - } - switch (code) - { - case EQ: pos = "beq", neg = "bne"; break; - case NE: pos = "bne", neg = "beq"; break; - case GT: pos = "bgt", neg = "ble"; break; - case GTU: pos = "bhi", neg = "blos"; break; - case LT: pos = "blt", neg = "bge"; break; - case LTU: pos = "blo", neg = "bhis"; break; - case GE: pos = "bge", neg = "blt"; break; - case GEU: pos = "bhis", neg = "blo"; break; - case LE: pos = "ble", neg = "bgt"; break; - case LEU: pos = "blos", neg = "bhi"; break; - default: gcc_unreachable (); - } - -#if 0 -/* currently we don't need this, because the tstdf and cmpdf - copy the condition code immediately, and other float operations are not - yet recognized as changing the FCC - if so, then the length-cost of all - jump insns increases by one, because we have to potentially copy the - FCC! */ - if (cc_status.flags & CC_IN_FPU) - output_asm_insn("cfcc", NULL); -#endif - - switch (length) + if (ccnz) { - case 2: - - sprintf(buf, "%s %%l1", inv ? neg : pos); - - return buf; - - case 6: - - sprintf(buf, "%s JMP_%d\n\tjmp %%l1\nJMP_%d:", inv ? pos : neg, x, x); - - x++; - - return buf; - - default: - - gcc_unreachable (); + /* These are the branches valid for CCNZmode, i.e., a comparison + with zero where the V bit is not set to zero. These cases + occur when CC or FCC are set as a side effect of some data + manipulation, such as the ADD instruction. */ + switch (code) + { + case EQ: pos = "beq", neg = "bne"; break; + case NE: pos = "bne", neg = "beq"; break; + case LT: pos = "bmi", neg = "bpl"; break; + case GE: pos = "bpl", neg = "bmi"; break; + default: gcc_unreachable (); + } } - + else + { + switch (code) + { + case EQ: pos = "beq", neg = "bne"; break; + case NE: pos = "bne", neg = "beq"; break; + case GT: pos = "bgt", neg = "ble"; break; + case GTU: pos = "bhi", neg = "blos"; break; + case LT: pos = "blt", neg = "bge"; break; + case LTU: pos = "blo", neg = "bhis"; break; + case GE: pos = "bge", neg = "blt"; break; + case GEU: pos = "bhis", neg = "blo"; break; + case LE: pos = "ble", neg = "bgt"; break; + case LEU: pos = "blos", neg = "bhi"; break; + default: gcc_unreachable (); + } + } + switch (length) + { + case 2: + sprintf (buf, "%s %%l1", pos); + return buf; + case 6: + tmpop[0] = gen_label_rtx (); + sprintf (buf, "%s %%l0", neg); + output_asm_insn (buf, tmpop); + output_asm_insn ("jmp %l1", operands); + output_asm_label (tmpop[0]); + fputs (":\n", asm_out_file); + return ""; + default: + gcc_unreachable (); + } } -void -notice_update_cc_on_set(rtx exp, rtx insn ATTRIBUTE_UNUSED) +/* Select the CC mode to be used for the side effect compare with + zero, given the compare operation code in op and the compare + operands in x in and y. */ +machine_mode +pdp11_cc_mode (enum rtx_code op, rtx x, rtx y) { - if (GET_CODE (SET_DEST (exp)) == CC0) - { - cc_status.flags = 0; - cc_status.value1 = SET_DEST (exp); - cc_status.value2 = SET_SRC (exp); - } - else if (GET_CODE (SET_SRC (exp)) == CALL) - { - CC_STATUS_INIT; + if (FLOAT_MODE_P (GET_MODE (x))) + { + switch (GET_CODE (x)) + { + case ABS: + case NEG: + case REG: + case MEM: + return CCmode; + default: + return CCNZmode; + } } - else if (SET_DEST(exp) == pc_rtx) - { - /* jump */ - } - else if (GET_MODE (SET_DEST(exp)) == HImode - || GET_MODE (SET_DEST(exp)) == QImode) - { - cc_status.flags = GET_CODE (SET_SRC(exp)) == MINUS ? 0 : CC_NO_OVERFLOW; - cc_status.value1 = SET_SRC (exp); - cc_status.value2 = SET_DEST (exp); - - if (cc_status.value1 && GET_CODE (cc_status.value1) == REG - && cc_status.value2 - && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) - cc_status.value2 = 0; - if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM - && cc_status.value2 - && GET_CODE (cc_status.value2) == MEM) - cc_status.value2 = 0; - } - else - { - CC_STATUS_INIT; + else + { + switch (GET_CODE (x)) + { + case XOR: + case AND: + case IOR: + case MULT: + case NOT: + case REG: + case MEM: + return CCmode; + default: + return CCNZmode; + } } } @@ -1135,62 +1144,115 @@ const char * int simple_memory_operand(rtx op, machine_mode mode ATTRIBUTE_UNUSED) { - rtx addr; + rtx addr; - /* Eliminate non-memory operations */ - if (GET_CODE (op) != MEM) - return FALSE; + /* Eliminate non-memory operations */ + if (GET_CODE (op) != MEM) + return FALSE; -#if 0 - /* dword operations really put out 2 instructions, so eliminate them. */ - if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) - return FALSE; -#endif + /* Decode the address now. */ - /* Decode the address now. */ - - indirection: + indirection: - addr = XEXP (op, 0); + addr = XEXP (op, 0); - switch (GET_CODE (addr)) + switch (GET_CODE (addr)) { - case REG: - /* (R0) - no extra cost */ - return 1; + case REG: + /* (R0) - no extra cost */ + return 1; - case PRE_DEC: - case POST_INC: - /* -(R0), (R0)+ - cheap! */ + case PRE_DEC: + case POST_INC: + case PRE_MODIFY: + case POST_MODIFY: + /* -(R0), (R0)+ - cheap! */ + return 1; + + case MEM: + /* cheap - is encoded in addressing mode info! + + -- except for @(R0), which has to be @0(R0) !!! */ + + if (GET_CODE (XEXP (addr, 0)) == REG) return 0; - case MEM: - /* cheap - is encoded in addressing mode info! + op=addr; + goto indirection; + + case CONST_INT: + case LABEL_REF: + case CONST: + case SYMBOL_REF: + /* @#address - extra cost */ + return 0; - -- except for @(R0), which has to be @0(R0) !!! */ + case PLUS: + /* X(R0) - extra cost */ + return 0; - if (GET_CODE (XEXP (addr, 0)) == REG) - return 0; + default: + break; + } + + return FALSE; +} + +/* Similar to simple_memory_operand but doesn't match push/pop. */ +int +no_side_effect_operand(rtx op, machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx addr; + + /* Eliminate non-memory operations */ + if (GET_CODE (op) != MEM) + return FALSE; + + /* Decode the address now. */ + + indirection: + + addr = XEXP (op, 0); + + switch (GET_CODE (addr)) + { + case REG: + /* (R0) - no extra cost */ + return 1; - op=addr; - goto indirection; + case PRE_DEC: + case POST_INC: + case PRE_MODIFY: + case POST_MODIFY: + return 0; - case CONST_INT: - case LABEL_REF: - case CONST: - case SYMBOL_REF: - /* @#address - extra cost */ - return 0; + case MEM: + /* cheap - is encoded in addressing mode info! - case PLUS: - /* X(R0) - extra cost */ + -- except for @(R0), which has to be @0(R0) !!! */ + + if (GET_CODE (XEXP (addr, 0)) == REG) return 0; + + op=addr; + goto indirection; + + case CONST_INT: + case LABEL_REF: + case CONST: + case SYMBOL_REF: + /* @#address - extra cost */ + return 0; - default: - break; + case PLUS: + /* X(R0) - extra cost */ + return 0; + + default: + break; } - return FALSE; + return FALSE; } @@ -1446,7 +1508,7 @@ pdp11_preferred_output_reload_class (rtx x, reg_cl FPU registers AC4 and AC5 (class NO_LOAD_FPU_REGS) require an intermediate register (AC0-AC3: LOAD_FPU_REGS). Everything else - can be loade/stored directly. */ + can be loaded/stored directly. */ static reg_class_t pdp11_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x, @@ -1463,9 +1525,8 @@ pdp11_secondary_reload (bool in_p ATTRIBUTE_UNUSED /* Implement TARGET_SECONDARY_MEMORY_NEEDED. - The answer is yes if we're going between general register and FPU - registers. The mode doesn't matter in making this check. -*/ + The answer is yes if we're going between general register and FPU + registers. The mode doesn't matter in making this check. */ static bool pdp11_secondary_memory_needed (machine_mode, reg_class_t c1, reg_class_t c2) { @@ -1594,6 +1655,8 @@ pdp11_regno_reg_class (int regno) { if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM) return GENERAL_REGS; + else if (regno == CC_REGNUM || regno == FCC_REGNUM) + return CC_REGS; else if (regno > AC3_REGNUM) return NO_LOAD_FPU_REGS; else if (regno >= AC0_REGNUM) @@ -1604,6 +1667,14 @@ pdp11_regno_reg_class (int regno) return GENERAL_REGS; } +/* Return the regnums of the CC registers. */ +bool +pdp11_fixed_cc_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = CC_REGNUM; + *p2 = FCC_REGNUM; + return true; +} int pdp11_sp_frame_offset (void) @@ -1804,6 +1875,151 @@ pdp11_function_value_regno_p (const unsigned int r return (regno == RETVAL_REGNUM) || (TARGET_AC0 && (regno == AC0_REGNUM)); } +/* Used for O constraint, matches if shift count is "small". */ +bool +pdp11_small_shift (int n) +{ + return (unsigned) n < 4; +} + +/* Expand a shift insn. Returns true if the expansion was done, + false if it needs to be handled by the caller. */ +bool +pdp11_expand_shift (rtx *operands, rtx (*shift_sc) (rtx, rtx, rtx), + rtx (*shift_base) (rtx, rtx, rtx)) +{ + rtx dest, n, r, test; + rtx_code_label *lb, *lb2; + + if (CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]))) + emit_insn ((*shift_sc) (operands[0], operands[1], operands[2])); + else if (TARGET_40_PLUS) + return false; + else + { + lb = gen_label_rtx (); + r = gen_reg_rtx (HImode); + emit_move_insn (operands[0], operands[1]); + emit_move_insn (r, operands[2]); + if (!CONSTANT_P (operands[2])) + { + test = gen_rtx_LE (HImode, r, const0_rtx); + emit_jump_insn (gen_cbranchhi4 (test, r, const0_rtx, lb)); + } + /* It would be nice to expand the loop here, but that's not + possible because shifts may be generated by the loop unroll + optimizer and it doesn't appreciate flow changes happening + while it's doing things. */ + emit_insn ((*shift_base) (operands[0], operands[1], r)); + if (!CONSTANT_P (operands[2])) + { + emit_label (lb); + + /* Allow REG_NOTES to be set on last insn (labels don't have enough + fields, and can't be used for REG_NOTES anyway). */ + emit_use (stack_pointer_rtx); + } + } + return true; +} + +/* Emit the instructions needed to produce a shift by a small constant + amount (unrolled), or a shift made from a loop for the base machine + case. */ +const char * +pdp11_assemble_shift (rtx *operands, machine_mode m, int code) +{ + int i, n; + rtx exops[4][2]; + rtx lb[1]; + pdp11_action action[2]; + const bool small = CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])); + + gcc_assert (small || !TARGET_40_PLUS); + + if (m == E_SImode) + pdp11_expand_operands (operands, exops, 1, action, either); + + if (!small) + { + /* Loop case, generate the top of loop label. */ + lb[0] = gen_label_rtx (); + output_asm_label (lb[0]); + fputs (":\n", asm_out_file); + n = 1; + } + else + n = INTVAL (operands[2]); + if (code == LSHIFTRT) + { + output_asm_insn ("clc", NULL); + switch (m) + { + case E_QImode: + output_asm_insn ("rorb %0", operands); + break; + case E_HImode: + output_asm_insn ("ror %0", operands); + break; + case E_SImode: + output_asm_insn ("ror %0", exops[0]); + output_asm_insn ("ror %0", exops[1]); + break; + default: + gcc_unreachable (); + } + n--; + } + for (i = 0; i < n; i++) + { + switch (code) + { + case LSHIFTRT: + case ASHIFTRT: + switch (m) + { + case E_QImode: + output_asm_insn ("asrb %0", operands); + break; + case E_HImode: + output_asm_insn ("asr %0", operands); + break; + case E_SImode: + output_asm_insn ("asr %0", exops[0]); + output_asm_insn ("ror %0", exops[1]); + break; + default: + gcc_unreachable (); + } + break; + case ASHIFT: + switch (m) + { + case E_QImode: + output_asm_insn ("aslb %0", operands); + break; + case E_HImode: + output_asm_insn ("asl %0", operands); + break; + case E_SImode: + output_asm_insn ("asl %0", exops[1]); + output_asm_insn ("rol %0", exops[0]); + break; + default: + gcc_unreachable (); + } + break; + } + } + if (!small) + { + /* Loop case, emit the count-down and branch if not done. */ + output_asm_insn ("dec %2", operands); + output_asm_insn ("bne %l0", lb); + } + return ""; +} + /* Worker function for TARGET_TRAMPOLINE_INIT. trampoline - how should i do it in separate i+d ? @@ -1814,7 +2030,6 @@ pdp11_function_value_regno_p (const unsigned int r MOV #STATIC, $4 01270Y 0x0000 <- STATIC; Y = STATIC_CHAIN_REGNUM JMP @#FUNCTION 000137 0x0000 <- FUNCTION */ - static void pdp11_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) { @@ -1934,12 +2149,6 @@ pdp11_scalar_mode_supported_p (scalar_mode mode) return default_scalar_mode_supported_p (mode); } -int -pdp11_branch_cost () -{ - return (TARGET_BRANCH_CHEAP ? 0 : 1); -} - /* Implement TARGET_HARD_REGNO_NREGS. */ static unsigned int @@ -1972,9 +2181,9 @@ pdp11_hard_regno_mode_ok (unsigned int regno, mach /* Implement TARGET_MODES_TIEABLE_P. */ static bool -pdp11_modes_tieable_p (machine_mode, machine_mode) +pdp11_modes_tieable_p (machine_mode mode1, machine_mode mode2) { - return false; + return mode1 == HImode && mode2 == QImode; } /* Implement PUSH_ROUNDING. On the pdp11, the stack is on an even Index: config/pdp11/pdp11.h =================================================================== --- config/pdp11/pdp11.h (revision 262195) +++ config/pdp11/pdp11.h (working copy) @@ -65,11 +65,11 @@ along with GCC; see the file COPYING3. If not see #define LONG_DOUBLE_TYPE_SIZE 64 /* machine types from ansi */ -#define SIZE_TYPE "unsigned int" /* definition of size_t */ -#define WCHAR_TYPE "int" /* or long int???? */ +#define SIZE_TYPE "short unsigned int" /* definition of size_t */ +#define WCHAR_TYPE "short int" /* or long int???? */ #define WCHAR_TYPE_SIZE 16 -#define PTRDIFF_TYPE "int" +#define PTRDIFF_TYPE "short int" /* target machine storage layout */ @@ -99,8 +99,7 @@ along with GCC; see the file COPYING3. If not see extern const struct real_format pdp11_f_format; extern const struct real_format pdp11_d_format; -/* Maximum sized of reasonable data type - DImode or Dfmode ...*/ +/* Maximum sized of reasonable data type -- DImode ...*/ #define MAX_FIXED_MODE_SIZE 64 /* Allocation boundary (in *bits*) for storing pointers in memory. */ @@ -124,6 +123,22 @@ extern const struct real_format pdp11_d_format; /* Define this if move instructions will actually fail to work when given unaligned data. */ #define STRICT_ALIGNMENT 1 + +/* Adjust the length of shifts by small constant amounts. The base + value (in "length" on input) is the length of a shift by one, not + including the CLC in logical shifts. */ +#define ADJUST_INSN_LENGTH(insn, length) \ + if ((GET_CODE (insn) == ASHIFT || \ + GET_CODE (insn) == ASHIFTRT || \ + GET_CODE (insn) == LSHIFTRT) && \ + GET_CODE (XEXP (insn, 2)) == CONST_INT && \ + pdp11_small_shift (XINT (insn, 2))) \ + { \ + if (GET_CODE (insn) == LSHIFTRT) \ + length = (length * XINT (insn, 2)) + 2; \ + else \ + length *= XINT (insn, 2); \ + } /* Standard register usage. */ @@ -147,7 +162,8 @@ extern const struct real_format pdp11_d_format; #define FIXED_REGISTERS \ {0, 0, 0, 0, 0, 0, 1, 1, \ - 0, 0, 0, 0, 0, 0, 1, 1 } + 0, 0, 0, 0, 0, 0, 1, 1, \ + 1, 1 } @@ -161,7 +177,8 @@ extern const struct real_format pdp11_d_format; /* don't know about fp */ #define CALL_USED_REGISTERS \ {1, 1, 0, 0, 0, 0, 1, 1, \ - 0, 0, 0, 0, 0, 0, 1, 1 } + 0, 0, 0, 0, 0, 0, 1, 1, \ + 1, 1 } /* Specify the registers used for certain standard purposes. @@ -203,27 +220,47 @@ NO_LOAD_FPU_REGS is ac4 and ac5, currently - diffi FPU_REGS is all fpu regs */ -enum reg_class { NO_REGS, MUL_REGS, GENERAL_REGS, LOAD_FPU_REGS, NO_LOAD_FPU_REGS, FPU_REGS, ALL_REGS, LIM_REG_CLASSES }; +enum reg_class + { NO_REGS, + MUL_REGS, + GENERAL_REGS, + LOAD_FPU_REGS, + NO_LOAD_FPU_REGS, + FPU_REGS, + CC_REGS, + ALL_REGS, + LIM_REG_CLASSES }; -#define N_REG_CLASSES (int) LIM_REG_CLASSES +#define N_REG_CLASSES ((int) LIM_REG_CLASSES) /* have to allow this till cmpsi/tstsi are fixed in a better way !! */ #define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P hook_bool_mode_true -/* Since GENERAL_REGS is the same class as ALL_REGS, - don't give it a different class number; just make it an alias. */ - -/* #define GENERAL_REGS ALL_REGS */ - /* Give names of register classes as strings for dump file. */ -#define REG_CLASS_NAMES {"NO_REGS", "MUL_REGS", "GENERAL_REGS", "LOAD_FPU_REGS", "NO_LOAD_FPU_REGS", "FPU_REGS", "ALL_REGS" } +#define REG_CLASS_NAMES \ + { "NO_REGS", \ + "MUL_REGS", \ + "GENERAL_REGS", \ + "LOAD_FPU_REGS", \ + "NO_LOAD_FPU_REGS", \ + "FPU_REGS", \ + "CC_REGS", \ + "ALL_REGS" } /* Define which registers fit in which classes. This is an initializer for a vector of HARD_REG_SET of length N_REG_CLASSES. */ -#define REG_CLASS_CONTENTS {{0}, {0x00aa}, {0xc0ff}, {0x0f00}, {0x3000}, {0x3f00}, {0xffff}} +#define REG_CLASS_CONTENTS \ + { {0x00000}, /* NO_REGS */ \ + {0x000aa}, /* MUL_REGS */ \ + {0x0c0ff}, /* GENERAL_REGS */ \ + {0x00f00}, /* LOAD_FPU_REGS */ \ + {0x03000}, /* NO_LOAD_FPU_REGS */ \ + {0x03f00}, /* FPU_REGS */ \ + {0x30000}, /* CC_REGS */ \ + {0x3ffff}} /* ALL_REGS */ /* The same information, inverted: Return the class number of the smallest class containing @@ -331,7 +368,7 @@ extern int may_call_alloca; {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ - { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}} \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}} #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ ((OFFSET) = pdp11_initial_elimination_offset ((FROM), (TO))) @@ -424,12 +461,19 @@ extern int may_call_alloca; #define DBX_CONTIN_LENGTH 0 /* Give a comparison code (EQ, NE etc) and the first operand of a COMPARE, - return the mode to be used for the comparison. For floating-point, CCFPmode - should be used. */ + return the mode to be used for the comparison. */ -#define SELECT_CC_MODE(OP,X,Y) \ -(GET_MODE_CLASS(GET_MODE(X)) == MODE_FLOAT? CCFPmode : CCmode) +#define SELECT_CC_MODE(OP,X,Y) pdp11_cc_mode (OP, X, Y) +/* Enable compare elimination pass. + FIXME: how can this be enabled for two registers? */ +#undef TARGET_FLAGS_REGNUM +#define TARGET_FLAGS_REGNUM CC_REGNUM + +/* Specify the CC registers. TODO: is this for "type 1" CC handling only? */ +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS pdp11_fixed_cc_regs + /* Specify the machine mode that pointers have. After generation of rtl, the compiler makes no further distinction between pointers and any other objects of this machine mode. */ @@ -447,54 +491,6 @@ extern int may_call_alloca; /* #define NO_FUNCTION_CSE */ -/* Tell emit-rtl.c how to initialize special values on a per-function base. */ -extern rtx cc0_reg_rtx; - -#define CC_STATUS_MDEP rtx - -#define CC_STATUS_MDEP_INIT (cc_status.mdep = 0) - -/* Tell final.c how to eliminate redundant test instructions. */ - -/* Here we define machine-dependent flags and fields in cc_status - (see `conditions.h'). */ - -#define CC_IN_FPU 04000 - -/* Do UPDATE_CC if EXP is a set, used in - NOTICE_UPDATE_CC - - floats only do compare correctly, else nullify ... - - get cc0 out soon ... -*/ - -/* Store in cc_status the expressions - that the condition codes will describe - after execution of an instruction whose pattern is EXP. - Do not alter them if the instruction would not alter the cc's. */ - -#define NOTICE_UPDATE_CC(EXP, INSN) \ -{ if (GET_CODE (EXP) == SET) \ - { \ - notice_update_cc_on_set(EXP, INSN); \ - } \ - else if (GET_CODE (EXP) == PARALLEL \ - && GET_CODE (XVECEXP (EXP, 0, 0)) == SET) \ - { \ - notice_update_cc_on_set(XVECEXP (EXP, 0, 0), INSN); \ - } \ - else if (GET_CODE (EXP) == CALL) \ - { /* all bets are off */ CC_STATUS_INIT; } \ - if (cc_status.value1 && GET_CODE (cc_status.value1) == REG \ - && cc_status.value2 \ - && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) \ - { \ - printf ("here!\n"); \ - cc_status.value2 = 0; \ - } \ -} - /* Control the assembler format that we output. */ /* Output to assembler file text saying following lines @@ -520,7 +516,8 @@ extern int may_call_alloca; #define REGISTER_NAMES \ {"r0", "r1", "r2", "r3", "r4", "r5", "sp", "pc", \ - "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "fp", "ap" } + "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "fp", "ap", \ + "cc", "fcc" } /* Globalizing directive for a label. */ #define GLOBAL_ASM_OP "\t.globl " @@ -603,10 +600,7 @@ extern int may_call_alloca; #define TRAMPOLINE_SIZE 8 #define TRAMPOLINE_ALIGNMENT 16 -/* there is no point in avoiding branches on a pdp, - since branches are really cheap - I just want to find out - how much difference the BRANCH_COST macro makes in code */ -#define BRANCH_COST(speed_p, predictable_p) pdp11_branch_cost () +#define BRANCH_COST(speed_p, predictable_p) 1 #define COMPARE_FLAG_MODE HImode Index: config/pdp11/pdp11.md =================================================================== --- config/pdp11/pdp11.md (revision 262195) +++ config/pdp11/pdp11.md (working copy) @@ -44,7 +44,12 @@ ;; arguments. (FRAME_POINTER_REGNUM 14) (ARG_POINTER_REGNUM 15) - (FIRST_PSEUDO_REGISTER 16) + ;; Condition code registers + (CC_REGNUM 16) + (FCC_REGNUM 17) + ;; End of hard registers + (FIRST_PSEUDO_REGISTER 18) + ;; Branch offset limits, as byte offsets from instruction address (MIN_BRANCH -254) (MAX_BRANCH 256) @@ -51,21 +56,68 @@ (MIN_SOB -126) (MAX_SOB 0)]) +;; DF is 64 bit +;; SF is 32 bit +;; SI is 32 bit ;; HI is 16 bit ;; QI is 8 bit ;; Integer modes supported on the PDP11, with a mapping from machine mode -;; to mnemonic suffix. SImode and DImode always are special cases. +;; to mnemonic suffix. SImode and DImode are usually special cases. (define_mode_iterator PDPint [QI HI]) (define_mode_attr isfx [(QI "b") (HI "")]) +(define_mode_attr mname [(QI "QImode") (HI "HImode") (SI "SImode") (DI "DImode")]) +(define_mode_attr e_mname [(QI "E_QImode") (HI "E_HImode") (SI "E_SImode") (DI "E_DImode")]) +(define_mode_attr hmode [(QI "hi") (HI "hi") (SI "si") (DI "di")]) +;; These are analogous for use in splitters and expanders. +(define_mode_iterator HSint [HI SI]) +(define_mode_iterator QHSint [QI HI SI]) +(define_mode_iterator QHSDint [QI HI SI DI]) + +(define_code_iterator SHF [ashift ashiftrt lshiftrt]) + +;; Substitution to turn a CC clobber into a CC setter. We have four of +;; these: for CCmode vs. CCNZmode, and for CC_REGNUM vs. FCC_REGNUM. +(define_subst "cc_cc" + [(set (match_operand 0 "") (match_operand 1 "")) + (clobber (reg CC_REGNUM))] + "" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_dup 1) (const_int 0))) + (set (match_dup 0) (match_dup 1))]) + +(define_subst "cc_ccnz" + [(set (match_operand 0 "") (match_operand 1 "")) + (clobber (reg CC_REGNUM))] + "" + [(set (reg:CCNZ CC_REGNUM) + (compare:CCNZ (match_dup 1) (const_int 0))) + (set (match_dup 0) (match_dup 1))]) + +(define_subst "fcc_cc" + [(set (match_operand 0 "") (match_operand 1 "")) + (clobber (reg FCC_REGNUM))] + "" + [(set (reg:CC FCC_REGNUM) + (compare:CC (match_dup 1) (const_int 0))) + (set (match_dup 0) (match_dup 1))]) + +(define_subst "fcc_ccnz" + [(set (match_operand 0 "") (match_operand 1 "")) + (clobber (reg FCC_REGNUM))] + "" + [(set (reg:CCNZ FCC_REGNUM) + (compare:CCNZ (match_dup 1) (const_int 0))) + (set (match_dup 0) (match_dup 1))]) + +(define_subst_attr "cc_cc" "cc_cc" "_nocc" "_cc") +(define_subst_attr "fcc_cc" "fcc_cc" "_nocc" "_cc") +(define_subst_attr "cc_ccnz" "cc_ccnz" "_nocc" "_cc") +(define_subst_attr "fcc_ccnz" "fcc_ccnz" "_nocc" "_cc") + ;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. -;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code -;;- updates for most instructions. - -;;- Operand classes for the register allocator: - ;; Compare instructions. ;; currently we only support df floats, which saves us quite some @@ -82,16 +134,6 @@ ;; and ucmp_optab for mode SImode, because we don't have that!!! ;; - yet since no libfunc is there, we abort () -;; The only thing that remains to be done then is output -;; the floats in a way the assembler can handle it (and -;; if you're really into it, use a PDP11 float emulation -;; library to do floating point constant folding - but -;; I guess you'll get reasonable results even when not -;; doing this) -;; the last thing to do is fix the UPDATE_CC macro to check -;; for floating point condition codes, and set cc_status -;; properly, also setting the CC_IN_FCCR flag. - ;; define attributes ;; currently type is only fpu or arith or unknown, maybe branch later ? ;; default is arith @@ -163,24 +205,31 @@ ;; compare (define_insn "*cmpdf" - [(set (cc0) - (compare (match_operand:DF 0 "general_operand" "fR,fR,Q,QF") - (match_operand:DF 1 "register_or_const0_operand" "G,a,G,a")))] - "TARGET_FPU" + [(set (reg:CC FCC_REGNUM) + (compare:CC (match_operand:DF 0 "general_operand" "fR,fR,Q,QF") + (match_operand:DF 1 "register_or_const0_operand" "G,a,G,a")))] + "TARGET_FPU && reload_completed" "* { - cc_status.flags = CC_IN_FPU; if (which_alternative == 0 || which_alternative == 2) - return \"{tstd|tstf} %0\;cfcc\"; + return \"{tstd|tstf} %0\"; else - return \"{cmpd|cmpf} %0, %1\;cfcc\"; + return \"{cmpd|cmpf} %0, %1\"; }" - [(set_attr "length" "4,4,6,6")]) + [(set_attr "length" "2,2,4,4") + (set_attr "type" "fp")]) -(define_insn "*cmp<mode>" - [(set (cc0) - (compare (match_operand:PDPint 0 "general_operand" "rR,rR,rR,Q,Qi,Qi") - (match_operand:PDPint 1 "general_operand" "N,rR,Qi,N,rR,Qi")))] +;; Copy floating point processor condition code register to main CPU +;; condition code register. +(define_insn "*cfcc" + [(set (reg CC_REGNUM) (reg FCC_REGNUM))] + "TARGET_FPU && reload_completed" + "cfcc") + +(define_insn "cmp<mode>" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_operand:PDPint 0 "general_operand" "rR,rR,rR,Q,Qi,Qi") + (match_operand:PDPint 1 "general_operand" "N,rR,Qi,N,rR,Qi")))] "" "@ tst<PDPint:isfx> %0 @@ -191,8 +240,7 @@ cmp<PDPint:isfx> %0,%1" [(set_attr "length" "2,2,4,4,4,6")]) -;; sob instruction - we need an assembler which can make this instruction -;; valid under _all_ circumstances! +;; sob instruction - FIXME: this doesn't do anything, need to use doloop_end. (define_insn "" [(set (pc) @@ -208,22 +256,17 @@ "TARGET_40_PLUS" "* { - static int labelcount = 0; - static char buf[1000]; - if (get_attr_length (insn) == 2) return \"sob %0, %l1\"; /* emulate sob */ + operands[2] = gen_label_rtx (); output_asm_insn (\"dec %0\", operands); - - sprintf (buf, \"bge LONG_SOB%d\", labelcount); - output_asm_insn (buf, NULL); - + output_asm_insn (\"beq %l2\", operands); output_asm_insn (\"jmp %l1\", operands); - sprintf (buf, \"LONG_SOB%d:\", labelcount++); - output_asm_insn (buf, NULL); + output_asm_label (operands[2]); + fputs (\":\\n\", asm_out_file); return \"\"; }" @@ -238,46 +281,73 @@ ;; These control RTL generation for conditional jump insns ;; and match them for register allocation. - -(define_expand "cbranchdf4" - [(set (cc0) - (compare (match_operand:DF 1 "general_operand") - (match_operand:DF 2 "register_or_const0_operand"))) - (set (pc) +;; Post reload these get expanded into insns that actually +;; manipulate the condition code registers. We can't do that before +;; because instructions generated by reload clobber condition codes (new +;; CC design, type #2). +(define_insn_and_split "cbranchdf4" + [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(cc0) (const_int 0)]) + [(match_operand:DF 1 "general_operand" "fg") + (match_operand:DF 2 "general_operand" "a")]) (label_ref (match_operand 3 "" "")) (pc)))] "TARGET_FPU" + "#" + "&& reload_completed" + [(set (reg:CC FCC_REGNUM) + (compare:CC (match_dup 1) (match_dup 2))) + (set (pc) + (if_then_else (match_op_dup 0 + [(reg:CC FCC_REGNUM) (const_int 0)]) + (label_ref (match_dup 3)) + (pc)))] "") -(define_expand "cbranch<mode>4" - [(set (cc0) - (compare (match_operand:PDPint 1 "general_operand") - (match_operand:PDPint 2 "general_operand"))) - (set (pc) +(define_insn_and_split "cbranch<mode>4" + [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(cc0) (const_int 0)]) + [(match_operand:PDPint 1 "general_operand" "g") + (match_operand:PDPint 2 "general_operand" "g")]) (label_ref (match_operand 3 "" "")) (pc)))] "" + "#" + "reload_completed" + [(set (reg:CC CC_REGNUM) + (compare:CC (match_dup 1) (match_dup 2))) + (set (pc) + (if_then_else (match_op_dup 0 + [(reg:CC CC_REGNUM) (const_int 0)]) + (label_ref (match_dup 3)) + (pc)))] "") -;; problem with too short jump distance! we need an assembler which can -;; make this valid for all jump distances! -;; e.g. gas! +;; This splitter turns a branch on float condition into a branch on +;; CPU condition, by adding a CFCC. +(define_split + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(reg:CC FCC_REGNUM) (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_FPU && reload_completed" + [(set (reg:CC CC_REGNUM) (reg:CC FCC_REGNUM)) + (set (pc) + (if_then_else (match_op_dup 0 + [(reg:CC CC_REGNUM) (const_int 0)]) + (label_ref (match_dup 1)) + (pc)))] + "") -;; these must be changed to check for CC_IN_FCCR if float is to be -;; enabled - -(define_insn "*branch" +(define_insn "cond_branch" [(set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" - [(cc0) (const_int 0)]) + [(reg:CC CC_REGNUM) (const_int 0)]) (label_ref (match_operand 1 "" "")) (pc)))] - "" - "* return output_jump(GET_CODE (operands[0]), 0, get_attr_length(insn));" + "reload_completed" + "* return output_jump (operands, 0, get_attr_length (insn));" [(set (attr "length") (if_then_else (ior (lt (minus (match_dup 1) (pc)) (const_int MIN_BRANCH)) @@ -287,17 +357,14 @@ (const_int 6) (const_int 2)))]) - -;; These match inverted jump insns for register allocation. - -(define_insn "*branch_inverted" +(define_insn "*branch" [(set (pc) - (if_then_else (match_operator 0 "ordered_comparison_operator" - [(cc0) (const_int 0)]) - (pc) - (label_ref (match_operand 1 "" ""))))] - "" - "* return output_jump(GET_CODE (operands[0]), 1, get_attr_length(insn));" + (if_then_else (match_operator 0 "ccnz_operator" + [(reg:CCNZ CC_REGNUM) (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "reload_completed" + "* return output_jump (operands, 1, get_attr_length (insn));" [(set (attr "length") (if_then_else (ior (lt (minus (match_dup 1) (pc)) (const_int MIN_BRANCH)) @@ -306,6 +373,7 @@ (const_int MAX_BRANCH))) (const_int 6) (const_int 2)))]) + ;; Move instructions @@ -313,6 +381,14 @@ [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g") (match_operand:DI 1 "general_operand" "rN,g"))] "" + "") + + +(define_insn "*movdi_nocc" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g") + (match_operand:DI 1 "general_operand" "rN,g")) + (clobber (reg:CC CC_REGNUM))] + "" "* return output_move_multiple (operands);" [(set_attr "length" "16,32")]) @@ -320,6 +396,13 @@ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g") (match_operand:SI 1 "general_operand" "rN,IJ,IJ,g"))] "" + "") + +(define_insn "*movsi_nocc" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g") + (match_operand:SI 1 "general_operand" "rN,IJ,IJ,g")) + (clobber (reg:CC CC_REGNUM))] + "" "* return output_move_multiple (operands);" [(set_attr "length" "4,6,8,16")]) @@ -327,6 +410,25 @@ [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") (match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))] "" + "") + +;; This splits all the integer moves: DI and SI modes as well as +;; the simple machine operations. +(define_split + [(set (match_operand:QHSDint 0 "nonimmediate_operand" "") + (match_operand:QHSDint 1 "general_operand" ""))] + "reload_completed" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; MOV clears V +(define_insn "*mov<mode>_<cc_cc>" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") + (match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi")) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { if (operands[1] == const0_rtx) @@ -336,32 +438,88 @@ }" [(set_attr "length" "2,4,4,6")]) -(define_insn "movdf" - [(set (match_operand:DF 0 "float_nonimm_operand" "=a,fR,a,Q,g") - (match_operand:DF 1 "float_operand" "fR,a,FQ,a,g"))] +;; movdf has unusually complicated condition code handling, because +;; load (into float register) updates the FCC, while store (from +;; float register) leaves it untouched. +;; +;; 1. Loads are: ac4, ac5, or non-register into load-register +;; 2. Stores are: load-register to non-register, ac4, or ac5 +;; 3. Moves from ac0-ac3 to another ac0-ac3 can be handled +;; either as loads or as stores. + +(define_expand "movdf" + [(set (match_operand:DF 0 "float_nonimm_operand" "") + (match_operand:DF 1 "float_operand" ""))] "TARGET_FPU" - "* if (which_alternative ==0 || which_alternative == 2) - return \"ldd %1, %0\"; - else if (which_alternative == 1 || which_alternative == 3) - return \"std %1, %0\"; - else - return output_move_multiple (operands); " -;; last one is worst-case - [(set_attr "length" "2,2,4,4,24")]) + "") -(define_insn "movsf" - [(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q,g") - (match_operand:SF 1 "float_operand" "fR,a,FQ,a,g"))] +;; Splitter for all these cases. Store is the first two +;; alternatives, which are not split. Note that case 3 +;; is treated as a store, i.e., not split. +(define_insn_and_split "movdf_split" + [(set (match_operand:DF 0 "float_nonimm_operand" "=fR,FQ,a,a,a") + (match_operand:DF 1 "float_operand" "a,a,hR,FQ,G"))] "TARGET_FPU" - "* if (which_alternative ==0 || which_alternative == 2) - return \"{ldcfd|movof} %1, %0\"; - else if (which_alternative == 1 || which_alternative == 3) - return \"{stcdf|movfo} %1, %0\"; - else - return output_move_multiple (operands); " -;; last one is worst-case - [(set_attr "length" "2,2,4,4,12")]) + "* + gcc_assert (which_alternative < 2); + return \"std %1, %0\"; + " + "&& reload_completed" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (reg:CC FCC_REGNUM))])] + "{ + if (GET_CODE (operands[1]) == REG && + REGNO_REG_CLASS (REGNO (operands[1])) == LOAD_FPU_REGS) + FAIL; + }" + [(set_attr "length" "2,4,0,0,0")]) +;; Loads (case 1). +(define_insn "*ldd<fcc_cc>" + [(set (match_operand:DF 0 "float_nonimm_operand" "=a,a,a") + (match_operand:DF 1 "float_operand" "hR,FQ,G")) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" + "@ + ldd %1, %0 + ldd %1, %0 + clrd %0" + [(set_attr "length" "2,4,2")]) + +;; SFmode is easier because that uses convert load/store, which +;; always change condition codes. +;; Note that these insns are cheating a bit. We actually have +;; DFmode operands in the FPU registers, which is why the +;; ldcfd and stcdf instructions appear. But GCC likes to think +;; of these as SFmode loads and does the conversion once in the +;; register, at least in many cases. So we pretend to do this, +;; but then extend and truncate register-to-register are NOP and +;; generate no code. +(define_insn_and_split "movsf" + [(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q") + (match_operand:SF 1 "float_operand" "fRG,a,FQ,a"))] + "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "*movsf<fcc_ccnz>" + [(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q,a") + (match_operand:SF 1 "float_operand" "fR,a,FQ,a,G")) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" + "@ + {ldcfd|movof} %1, %0 + {stcdf|movfo} %1, %0 + {ldcfd|movof} %1, %0 + {stcdf|movfo} %1, %0 + clrf %0" + [(set_attr "length" "2,2,4,4,2")]) + ;; maybe fiddle a bit with move_ratio, then ;; let constraints only accept a register ... @@ -374,7 +532,7 @@ (clobber (match_dup 4)) (clobber (match_dup 5)) (clobber (match_dup 2))])] - "(TARGET_BCOPY_BUILTIN)" + "" " { operands[0] @@ -389,7 +547,7 @@ }") -(define_insn "movmemhi1" +(define_insn "*movmemhi1" [(set (mem:BLK (match_operand:HI 0 "register_operand" "r,r")) (mem:BLK (match_operand:HI 1 "register_operand" "r,r"))) (use (match_operand:HI 2 "general_operand" "n,r")) @@ -398,7 +556,7 @@ (clobber (match_dup 0)) (clobber (match_dup 1)) (clobber (match_dup 2))] - "(TARGET_BCOPY_BUILTIN)" + "" "* return output_block_move (operands);" ;;; just a guess [(set_attr "length" "80")]) @@ -407,29 +565,56 @@ ;;- truncation instructions -(define_insn "truncdfsf2" +;; We sometimes end up doing a register to register truncate, +;; which isn't right because we actually load registers always +;; with a DFmode value. But even with PROMOTE the compiler +;; doesn't always get that (so we don't use it). That means +;; a register to register truncate is a NOP. +(define_insn_and_split "truncdfsf2" [(set (match_operand:SF 0 "float_nonimm_operand" "=f,R,Q") - (float_truncate:SF (match_operand:DF 1 "register_operand" "f,a,a")))] + (float_truncate:SF (match_operand:DF 1 "register_operand" "0,a,a")))] "TARGET_FPU" - "* if (which_alternative ==0) - { - return \"\"; - } - else if (which_alternative == 1) - return \"{stcdf|movfo} %1, %0\"; - else - return \"{stcdf|movfo} %1, %0\"; - " - [(set_attr "length" "0,2,4")]) + { + gcc_assert (which_alternative == 0); + return ""; + } + "&& reload_completed" + [(parallel [(set (match_dup 0) (float_truncate:SF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "{ + if (GET_CODE (operands[0]) == REG && + GET_CODE (operands[1]) == REG && + REGNO (operands[0]) == REGNO (operands[1])) + FAIL; + }" + [(set_attr "length" "0,0,0")]) +(define_insn "*truncdfsf2_<fcc_cc>" + [(set (match_operand:SF 0 "float_nonimm_operand" "=R,Q") + (float_truncate:SF (match_operand:DF 1 "register_operand" "a,a"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" + "{stcdf|movfo} %1, %0" + [(set_attr "length" "2,4")]) ;;- zero extension instructions -(define_insn "zero_extendqihi2" +(define_insn_and_split "zero_extendqihi2" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") (zero_extend:HI (match_operand:QI 1 "general_operand" "0,0")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (zero_extend:HI (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*zero_extendqihi2<cc_cc>" + [(parallel [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") + (zero_extend:HI (match_operand:QI 1 "general_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))])] + "reload_completed" "bic $0177400, %0" [(set_attr "length" "4,6")]) @@ -448,56 +633,73 @@ ;;- sign extension instructions -(define_insn "extendsfdf2" +;; We sometimes end up doing a register to register extend, +;; which isn't right because we actually load registers always +;; with a DFmode value. But even with PROMOTE the compiler +;; doesn't always get that (so we don't use it). That means +;; a register to register truncate is a NOP. +(define_insn_and_split "extendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f,a,a") - (float_extend:DF (match_operand:SF 1 "float_operand" "f,R,Q")))] + (float_extend:DF (match_operand:SF 1 "float_operand" "0,R,Q")))] "TARGET_FPU" - "@ - /* nothing */ - {ldcfd|movof} %1, %0 - {ldcfd|movof} %1, %0" - [(set_attr "length" "0,2,4")]) + { + gcc_assert (which_alternative == 0); + return ""; + } + "&& reload_completed" + [(parallel [(set (match_dup 0) (float_extend:DF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "{ + if (GET_CODE (operands[0]) == REG && + GET_CODE (operands[1]) == REG && + REGNO (operands[0]) == REGNO (operands[1])) + FAIL; + }" + [(set_attr "length" "0,0,0")]) -;; does movb sign extend in register-to-register move? -(define_insn "extendqihi2" +(define_insn "*extendsfdf2_<fcc_cc>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (float_extend:DF (match_operand:SF 1 "float_operand" "R,Q"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" + "{ldcfd|movof} %1, %0" + [(set_attr "length" "2,4")]) + +;; movb sign extends if destination is a register +(define_insn_and_split "extendqihi2" [(set (match_operand:HI 0 "register_operand" "=r,r") (sign_extend:HI (match_operand:QI 1 "general_operand" "rR,Q")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (sign_extend:HI (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; MOVB clears V +(define_insn "*extendqihi2<cc_cc>" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (sign_extend:HI (match_operand:QI 1 "general_operand" "rR,Q"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "movb %1, %0" [(set_attr "length" "2,4")]) -(define_insn "extendqisi2" - [(set (match_operand:SI 0 "register_operand" "=r,r") - (sign_extend:SI (match_operand:QI 1 "general_operand" "rR,Q")))] +(define_insn_and_split "extendhisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=o,<,r") + (sign_extend:SI (match_operand:HI 1 "general_operand" "g,g,g")))] "TARGET_40_PLUS" - "* -{ - rtx latehalf[2]; + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (sign_extend:SI (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") - /* make register pair available */ - latehalf[0] = operands[0]; - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0])+ 1); - - output_asm_insn(\"movb %1, %0\", operands); - output_asm_insn(\"sxt %0\", latehalf); - - return \"\"; -}" - [(set_attr "length" "4,6")]) - -;; maybe we have to use define_expand to say that we have the instruction, -;; unconditionally, and then match dependent on CPU type: - -(define_expand "extendhisi2" - [(set (match_operand:SI 0 "nonimmediate_operand" "=g") - (sign_extend:SI (match_operand:HI 1 "general_operand" "g")))] - "" - "") - -(define_insn "" ; "extendhisi2" +(define_insn "*extendhisi2_nocc" [(set (match_operand:SI 0 "nonimmediate_operand" "=o,<,r") - (sign_extend:SI (match_operand:HI 1 "general_operand" "g,g,g")))] - "TARGET_40_PLUS" + (sign_extend:SI (match_operand:HI 1 "general_operand" "g,g,g"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_40_PLUS && reload_completed" "* { rtx latehalf[2]; @@ -542,52 +744,33 @@ }" [(set_attr "length" "10,6,6")]) - -(define_insn "" - [(set (match_operand:SI 0 "register_operand" "=r") - (sign_extend:SI (match_operand:HI 1 "general_operand" "0")))] - "(! TARGET_40_PLUS)" - "* -{ - static int count = 0; - char buf[100]; - rtx lateoperands[2]; - - lateoperands[0] = operands[0]; - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); - - output_asm_insn(\"tst %0\", operands); - sprintf(buf, \"bge extendhisi%d\", count); - output_asm_insn(buf, NULL); - output_asm_insn(\"mov -1, %0\", lateoperands); - sprintf(buf, \"bne extendhisi%d\", count+1); - output_asm_insn(buf, NULL); - sprintf(buf, \"\\nextendhisi%d:\", count); - output_asm_insn(buf, NULL); - output_asm_insn(\"clr %0\", lateoperands); - sprintf(buf, \"\\nextendhisi%d:\", count+1); - output_asm_insn(buf, NULL); - - count += 2; - - return \"\"; -}" - [(set_attr "length" "12")]) - ;; make float to int and vice versa -;; using the cc_status.flag field we could probably cut down -;; on seti and setl ;; assume that we are normally in double and integer mode - ;; what do pdp library routines do to fpu mode ? -(define_insn "floatsidf2" +;; Note: the hardware treats register source as +;; a 16-bit (high order only) source, which isn't +;; what we want. But we do need to support register +;; dest because gcc asks for it. +(define_insn_and_split "floatsidf2" [(set (match_operand:DF 0 "register_operand" "=a,a,a") (float:DF (match_operand:SI 1 "general_operand" "r,R,Q")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (float:DF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "*floatsidf2<fcc_cc>" + [(set (match_operand:DF 0 "register_operand" "=a,a,a") + (float:DF (match_operand:SI 1 "general_operand" "r,R,Q"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "* if (which_alternative ==0) { rtx latehalf[2]; - + latehalf[0] = NULL; latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1); output_asm_insn(\"mov %1, -(sp)\", latehalf); @@ -598,25 +781,53 @@ output_asm_insn(\"seti\", operands); return \"\"; } - else if (which_alternative == 1) - return \"setl\;{ldcld|movif} %1, %0\;seti\"; else return \"setl\;{ldcld|movif} %1, %0\;seti\"; " [(set_attr "length" "10,6,8")]) -(define_insn "floathidf2" +(define_insn_and_split "floathidf2" [(set (match_operand:DF 0 "register_operand" "=a,a") (float:DF (match_operand:HI 1 "general_operand" "rR,Qi")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (float:DF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "*floathidf2<fcc_cc>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (float:DF (match_operand:HI 1 "general_operand" "rR,Qi"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{ldcid|movif} %1, %0" [(set_attr "length" "2,4")]) ;; cut float to int -(define_insn "fix_truncdfsi2" + +;; Note: the hardware treats register destination as +;; a 16-bit (high order only) destination, which isn't +;; what we want. But we do need to support register +;; dest because gcc asks for it. +(define_insn_and_split "fix_truncdfsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "=r,R,Q") (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "a,a,a"))))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (fix:SI (fix:DF (match_dup 1)))) + (clobber (reg:CC CC_REGNUM)) + (clobber (reg:CC FCC_REGNUM))])] + "") + +;; Note: this clobbers both sets of condition codes! +(define_insn "*fix_truncdfsi2_nocc" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,R,Q") + (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "a,a,a")))) + (clobber (reg:CC CC_REGNUM)) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "* if (which_alternative ==0) { output_asm_insn(\"setl\", operands); @@ -627,17 +838,29 @@ output_asm_insn(\"mov (sp)+, %0\", operands); return \"\"; } - else if (which_alternative == 1) - return \"setl\;{stcdl|movfi} %1, %0\;seti\"; else return \"setl\;{stcdl|movfi} %1, %0\;seti\"; " [(set_attr "length" "10,6,8")]) -(define_insn "fix_truncdfhi2" +(define_insn_and_split "fix_truncdfhi2" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") (fix:HI (fix:DF (match_operand:DF 1 "register_operand" "a,a"))))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (fix:HI (fix:DF (match_dup 1)))) + (clobber (reg:CC CC_REGNUM)) + (clobber (reg:CC FCC_REGNUM))])] + "") + +;; Note: this clobbers both sets of condition codes! +(define_insn "*fix_truncdfhi2_nocc" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") + (fix:HI (fix:DF (match_operand:DF 1 "register_operand" "a,a")))) + (clobber (reg:CC CC_REGNUM)) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{stcdi|movfi} %1, %0" [(set_attr "length" "2,4")]) @@ -645,19 +868,45 @@ ;;- arithmetic instructions ;;- add instructions -(define_insn "adddf3" +(define_insn_and_split "adddf3" [(set (match_operand:DF 0 "register_operand" "=a,a") (plus:DF (match_operand:DF 1 "register_operand" "%0,0") (match_operand:DF 2 "general_operand" "fR,QF")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (plus:DF (match_dup 1) (match_dup 2))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +;; Float add sets V if overflow from add +(define_insn "*adddf3<fcc_ccnz>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (plus:DF (match_operand:DF 1 "register_operand" "%0,0") + (match_operand:DF 2 "general_operand" "fR,QF"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{addd|addf} %2, %0" [(set_attr "length" "2,4")]) -(define_insn "adddi3" +(define_insn_and_split "adddi3" [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o") (plus:DI (match_operand:DI 1 "general_operand" "%0,0,0,0") (match_operand:DI 2 "general_operand" "r,on,r,on")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*adddi3_nocc" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o") + (plus:DI (match_operand:DI 1 "general_operand" "%0,0,0,0") + (match_operand:DI 2 "general_operand" "r,on,r,on"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { rtx inops[2]; @@ -701,11 +950,23 @@ ;; high word is added at the end, so the adding of the high parts ;; will always used the original high part and not a high part ;; modified by carry (which would amount to double carry). -(define_insn "addsi3" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o") +(define_insn_and_split "addsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o") (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0") (match_operand:SI 2 "general_operand" "r,on,r,on")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*addsi3_nocc" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o") + (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0") + (match_operand:SI 2 "general_operand" "r,on,r,on"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { rtx inops[2]; @@ -727,11 +988,25 @@ }" [(set_attr "length" "6,10,12,16")]) -(define_insn "addhi3" +(define_insn_and_split "addhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q") (plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0") (match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (plus:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; Add sets V if overflow from the add +(define_insn "*addhi3<cc_ccnz>" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q") + (plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0") + (match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { if (GET_CODE (operands[2]) == CONST_INT) @@ -752,19 +1027,44 @@ ;; args, since they are canonical plus:xx now! ;; also for minus:DF ?? -(define_insn "subdf3" +(define_insn_and_split "subdf3" [(set (match_operand:DF 0 "register_operand" "=a,a") (minus:DF (match_operand:DF 1 "register_operand" "0,0") (match_operand:DF 2 "general_operand" "fR,Q")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (minus:DF (match_dup 1) (match_dup 2))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "*subdf3<fcc_ccnz>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (minus:DF (match_operand:DF 1 "register_operand" "0,0") + (match_operand:DF 2 "general_operand" "fR,QF"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{subd|subf} %2, %0" [(set_attr "length" "2,4")]) -(define_insn "subdi3" +(define_insn_and_split "subdi3" [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o") (minus:DI (match_operand:DI 1 "general_operand" "0,0,0,0") (match_operand:DI 2 "general_operand" "r,on,r,on")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (minus:DI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*subdi3_nocc" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o") + (minus:DI (match_operand:DI 1 "general_operand" "0,0,0,0") + (match_operand:DI 2 "general_operand" "r,on,r,on"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { rtx inops[2]; @@ -799,11 +1099,23 @@ }" [(set_attr "length" "20,28,40,48")]) -(define_insn "subsi3" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o") +(define_insn_and_split "subsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o") (minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0") (match_operand:SI 2 "general_operand" "r,on,r,on")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*subsi3_nocc" + [(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o") + (minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0") + (match_operand:SI 2 "general_operand" "r,on,r,on"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { rtx inops[2]; @@ -825,14 +1137,37 @@ }" [(set_attr "length" "6,10,12,16")]) -(define_insn "subhi3" +(define_insn_and_split "subhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q") (minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0") - (match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi")))] + (match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (minus:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; Note: the manual says that (minus m (const_int n)) is converted +;; to (plus m (const_int -n)) but that does not appear to be +;; the case when it's wrapped in a PARALLEL. So instead we handle +;; that case here, which is easy enough. +(define_insn "*subhi3<cc_ccnz>" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q") + (minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0") + (match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "* { - gcc_assert (GET_CODE (operands[2]) != CONST_INT); + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL(operands[2]) == 1) + return \"dec %0\"; + else if (INTVAL(operands[2]) == -1) + return \"inc %0\"; + } return \"sub %2, %0\"; }" @@ -867,121 +1202,149 @@ operands[1] = expand_unop (<MODE>mode, one_cmpl_optab, op1, 0, 1); }") -(define_insn "*bic<mode>" +(define_insn_and_split "*bic<mode>" [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") (and:PDPint (not: PDPint (match_operand:PDPint 1 "general_operand" "rR,Qi,rR,Qi")) (match_operand:PDPint 2 "general_operand" "0,0,0,0")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (and:PDPint (not:PDPint (match_dup 1)) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*bic<mode><cc_cc>" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") + (and:PDPint + (not: PDPint (match_operand:PDPint 1 "general_operand" "rR,Qi,rR,Qi")) + (match_operand:PDPint 2 "general_operand" "0,0,0,0"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "bic<PDPint:isfx> %1, %0" [(set_attr "length" "2,4,4,6")]) ;;- Bit set (inclusive or) instructions -(define_insn "ior<mode>3" +(define_insn_and_split "ior<mode>3" [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") (ior:PDPint (match_operand:PDPint 1 "general_operand" "%0,0,0,0") - (match_operand:PDPint 2 "general_operand" "rR,Qi,rR,Qi")))] + (match_operand:PDPint 2 "general_operand" "rR,Qi,rR,Qi")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (ior:PDPint (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "ior<mode>3<cc_cc>" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") + (ior:PDPint (match_operand:PDPint 1 "general_operand" "%0,0,0,0") + (match_operand:PDPint 2 "general_operand" "rR,Qi,rR,Qi"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "bis<PDPint:isfx> %2, %0" [(set_attr "length" "2,4,4,6")]) ;;- xor instructions -(define_insn "xorhi3" +(define_insn_and_split "xorhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") (xor:HI (match_operand:HI 1 "general_operand" "%0,0") (match_operand:HI 2 "register_operand" "r,r")))] "TARGET_40_PLUS" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (xor:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*xorhi3<cc_cc>" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") + (xor:HI (match_operand:HI 1 "general_operand" "%0,0") + (match_operand:HI 2 "register_operand" "r,r"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_40_PLUS && reload_completed" "xor %2, %0" [(set_attr "length" "2,4")]) ;;- one complement instructions -(define_insn "one_cmpl<mode>2" +(define_insn_and_split "one_cmpl<mode>2" [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q") (not:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (not:PDPint (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "*one_cmpl<mode>2<cc_cc>" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q") + (not:PDPint (match_operand:PDPint 1 "general_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" "com<PDPint:isfx> %0" [(set_attr "length" "2,4")]) ;;- arithmetic shift instructions -(define_insn "ashlsi3" - [(set (match_operand:SI 0 "register_operand" "=r,r") - (ashift:SI (match_operand:SI 1 "register_operand" "0,0") - (match_operand:HI 2 "general_operand" "rR,Qi")))] - "TARGET_40_PLUS" - "ashc %2,%0" +;; +;; There is a fair amount of complexity here because with -m10 +;; (pdp-11/10, /20) we only have shift by one bit. Iterators are +;; used to reduce the amount of very similar code. +;; +;; First the insns used for small constant shifts. +; +;; The "length" attribute values are modified by the ADJUST_INSN_LENGTH +;; macro for the small constant shift case (first two alternatives). +;; For those, the value coded in the length attribute is the cost of just +;; the shift for a single shift. +(define_insn "<code><mode>_sc" + [(set (match_operand:QHSint 0 "nonimmediate_operand" "=rD,Q") + (SHF:QHSint (match_operand:QHSint 1 "general_operand" "0,0") + (match_operand:HI 2 "expand_shift_operand" "O,O")))] + "" + "* return pdp11_assemble_shift (operands, <QHSint:mname>, <CODE>);" [(set_attr "length" "2,4")]) -;; Arithmetic right shift on the pdp works by negating the shift count. -(define_expand "ashrsi3" - [(set (match_operand:SI 0 "register_operand" "=r") - (ashift:SI (match_operand:SI 1 "register_operand" "0") - (match_operand:HI 2 "general_operand" "g")))] +;; Next, shifts that are done as a loop on base (11/10 class) machines. +;; This applies to shift counts too large to unroll, or variable shift +;; counts. The check for count <= 0 is done before we get here. +(define_insn "<code><mode>_base" + [(set (match_operand:QHSint 0 "nonimmediate_operand" "=rD,Q") + (SHF:QHSint (match_operand:QHSint 1 "general_operand" "0,0") + (match_operand:HI 2 "register_operand" "r,r"))) + (clobber (match_dup 2))] "" - " -{ - operands[2] = negate_rtx (HImode, operands[2]); -}") - -;; define asl aslb asr asrb - ashc missing! - -;; asl -(define_insn "" - [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") - (ashift:HI (match_operand:HI 1 "general_operand" "0,0") - (const_int 1)))] - "" - "asl %0" + "* return pdp11_assemble_shift (operands, <QHSint:mname>, <CODE>);" [(set_attr "length" "2,4")]) -;; and another possibility for asr is << -1 -;; might cause problems since -1 can also be encoded as 65535! -;; not in gcc2 ??? - -;; asr -(define_insn "" - [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") +;; Next the insns that use the extended instructions ash and ashc. +;; Note that these are just left shifts, and HI/SI only. (Right shifts +;; are done by shifting by a negative amount.) +(define_insn "aslhi_op" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r") (ashift:HI (match_operand:HI 1 "general_operand" "0,0") - (const_int -1)))] - "" - "asr %0" + (match_operand:HI 2 "general_operand" "rR,Q")))] + "TARGET_40_PLUS" + "ash %2, %0" [(set_attr "length" "2,4")]) -;; lsr -(define_insn "lsrhi1" - [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") - (lshiftrt:HI (match_operand:HI 1 "general_operand" "0,0") - (const_int 1)))] - "" - "clc\;ror %0" +(define_insn "aslsi_op" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r") + (ashift:SI (match_operand:SI 1 "general_operand" "0,0") + (match_operand:HI 2 "general_operand" "rR,Q")))] + "TARGET_40_PLUS" + "ashc %2, %0" [(set_attr "length" "2,4")]) -(define_insn "lsrsi1" - [(set (match_operand:SI 0 "register_operand" "=r") - (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") - (const_int 1)))] - "" -{ - - rtx lateoperands[2]; - - lateoperands[0] = operands[0]; - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); - - lateoperands[1] = operands[1]; - operands[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1); - - output_asm_insn (\"clc\", operands); - output_asm_insn (\"ror %0\", lateoperands); - output_asm_insn (\"ror %0\", operands); - - return \"\"; -} - [(set_attr "length" "10")]) - -(define_expand "lshrsi3" - [(match_operand:SI 0 "register_operand" "") - (match_operand:SI 1 "register_operand" "0") +;; Now the expanders that produce the insns defined above. +(define_expand "ashl<mode>3" + [(match_operand:QHSint 0 "nonimmediate_operand" "") + (match_operand:QHSint 1 "general_operand" "") (match_operand:HI 2 "general_operand" "")] "" " @@ -988,196 +1351,139 @@ { rtx r; - if (!TARGET_40_PLUS && - (GET_CODE (operands[2]) != CONST_INT || - (unsigned) INTVAL (operands[2]) > 3)) - FAIL; - emit_insn (gen_lsrsi1 (operands[0], operands[1])); - if (GET_CODE (operands[2]) != CONST_INT) + if (!pdp11_expand_shift (operands, gen_ashift<mode>_sc, gen_ashift<mode>_base)) { - r = gen_reg_rtx (HImode); - emit_insn (gen_addhi3 (r, operands [2], GEN_INT (-1))); - emit_insn (gen_ashrsi3 (operands[0], operands[0], r)); + if (<QHSint:e_mname> == E_QImode) + { + r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1])); + emit_insn (gen_aslhi_op (r, r, operands[2])); + emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0))); + } + else + { + emit_insn (gen_asl<QHSint:hmode>_op (operands[0], operands[1], operands[2])); + } } - else if ((unsigned) INTVAL (operands[2]) != 1) - { - emit_insn (gen_ashlsi3 (operands[0], operands[0], - GEN_INT (1 - INTVAL (operands[2])))); - } DONE; -} -" -) +}") -;; shift is by arbitrary count is expensive, -;; shift by one cheap - so let's do that, if -;; space doesn't matter -(define_insn "" - [(set (match_operand:HI 0 "nonimmediate_operand" "=r") - (ashift:HI (match_operand:HI 1 "general_operand" "0") - (match_operand:HI 2 "expand_shift_operand" "O")))] - "! optimize_size" - "* +(define_expand "ashr<mode>3" + [(match_operand:QHSint 0 "nonimmediate_operand" "") + (match_operand:QHSint 1 "general_operand" "") + (match_operand:HI 2 "general_operand" "")] + "" + " { - register int i; + rtx r; - for (i = 1; i <= abs(INTVAL(operands[2])); i++) - if (INTVAL(operands[2]) < 0) - output_asm_insn(\"asr %0\", operands); - else - output_asm_insn(\"asl %0\", operands); - - return \"\"; -}" -;; longest is 4 - [(set (attr "length") (const_int 8))]) - -;; aslb -(define_insn "" - [(set (match_operand:QI 0 "nonimmediate_operand" "=r,o") - (ashift:QI (match_operand:QI 1 "general_operand" "0,0") - (match_operand:HI 2 "const_int_operand" "n,n")))] - "" - "* -{ /* allowing predec or post_inc is possible, but hairy! */ - int i, cnt; - - cnt = INTVAL(operands[2]) & 0x0007; - - for (i=0 ; i < cnt ; i++) - output_asm_insn(\"aslb %0\", operands); - - return \"\"; -}" -;; set attribute length ( match_dup 2 & 7 ) *(1 or 2) !!! - [(set_attr_alternative "length" - [(const_int 14) - (const_int 28)])]) - -;;; asr -;(define_insn "" -; [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") -; (ashiftrt:HI (match_operand:HI 1 "general_operand" "0,0") -; (const_int 1)))] -; "" -; "asr %0" -; [(set_attr "length" "2,4")]) - -;; asrb -(define_insn "" - [(set (match_operand:QI 0 "nonimmediate_operand" "=r,o") - (ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0") - (match_operand:HI 2 "const_int_operand" "n,n")))] - "" - "* -{ /* allowing predec or post_inc is possible, but hairy! */ - int i, cnt; - - cnt = INTVAL(operands[2]) & 0x0007; - - for (i=0 ; i < cnt ; i++) - output_asm_insn(\"asrb %0\", operands); - - return \"\"; -}" - [(set_attr_alternative "length" - [(const_int 14) - (const_int 28)])]) - -;; the following is invalid - too complex!!! - just say 14 !!! -; [(set (attr "length") (plus (and (match_dup 2) -; (const_int 14)) -; (and (match_dup 2) -; (const_int 14))))]) - - - -;; can we get +-1 in the next pattern? should -;; have been caught by previous patterns! - -(define_insn "ashlhi3" - [(set (match_operand:HI 0 "register_operand" "=r,r") - (ashift:HI (match_operand:HI 1 "register_operand" "0,0") - (match_operand:HI 2 "general_operand" "rR,Qi")))] - "TARGET_40_PLUS" - "* -{ - if (GET_CODE(operands[2]) == CONST_INT) + if (!pdp11_expand_shift (operands, gen_ashiftrt<mode>_sc, gen_ashiftrt<mode>_base)) { - if (INTVAL(operands[2]) == 1) - return \"asl %0\"; - else if (INTVAL(operands[2]) == -1) - return \"asr %0\"; + operands[2] = negate_rtx (HImode, operands[2]); + if (<QHSint:e_mname> == E_QImode) + { + r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1])); + emit_insn (gen_aslhi_op (r, r, operands[2])); + emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0))); + } + else + { + emit_insn (gen_asl<QHSint:hmode>_op (operands[0], operands[1], operands[2])); + } } - - return \"ash %2,%0\"; -}" - [(set_attr "length" "2,4")]) - -;; Arithmetic right shift on the pdp works by negating the shift count. -(define_expand "ashrhi3" - [(set (match_operand:HI 0 "register_operand" "=r") - (ashift:HI (match_operand:HI 1 "register_operand" "0") - (match_operand:HI 2 "general_operand" "g")))] - "" - " -{ - operands[2] = negate_rtx (HImode, operands[2]); + DONE; }") -(define_expand "lshrhi3" - [(match_operand:HI 0 "register_operand" "") - (match_operand:HI 1 "register_operand" "") +(define_expand "lshr<mode>3" + [(match_operand:QHSint 0 "nonimmediate_operand" "") + (match_operand:QHSint 1 "general_operand" "") (match_operand:HI 2 "general_operand" "")] "" " { - rtx r; + rtx r, n; - if (!TARGET_40_PLUS && - (GET_CODE (operands[2]) != CONST_INT || - (unsigned) INTVAL (operands[2]) > 3)) - FAIL; - emit_insn (gen_lsrhi1 (operands[0], operands[1])); - if (GET_CODE (operands[2]) != CONST_INT) + if (!pdp11_expand_shift (operands, gen_lshiftrt<mode>_sc, gen_lshiftrt<mode>_base)) { - r = gen_reg_rtx (HImode); - emit_insn (gen_addhi3 (r, operands [2], GEN_INT (-1))); - emit_insn (gen_ashrhi3 (operands[0], operands[0], r)); + if (<QHSint:e_mname> == E_QImode) + { + r = copy_to_mode_reg (HImode, gen_rtx_ZERO_EXTEND (HImode, operands[1])); + emit_insn (gen_aslhi_op (r, r, operands[2])); + emit_insn (gen_movqi (operands[0], gen_rtx_SUBREG (QImode, r, 0))); + } + else + { + r = gen_reg_rtx (<QHSint:mname>); + emit_insn (gen_lshiftrt<mode>_sc (r, operands[1], const1_rtx)); + if (GET_CODE (operands[2]) != CONST_INT) + { + n = gen_reg_rtx (HImode); + emit_insn (gen_addhi3 (n, operands [2], GEN_INT (-1))); + emit_insn (gen_ashr<mode>3 (operands[0], r, n)); + } + else + emit_insn (gen_asl<QHSint:hmode>_op (operands[0], r, + GEN_INT (1 - INTVAL (operands[2])))); + } } - else if ((unsigned) INTVAL (operands[2]) != 1) - { - emit_insn (gen_ashlhi3 (operands[0], operands[0], - GEN_INT (1 - INTVAL (operands[2])))); - } DONE; -} -" -) +}") ;; absolute -(define_insn "absdf2" +(define_insn_and_split "absdf2" [(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q") (abs:DF (match_operand:DF 1 "general_operand" "0,0")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (abs:DF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "absdf2<fcc_cc>" + [(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q") + (abs:DF (match_operand:DF 1 "general_operand" "0,0"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{absd|absf} %0" [(set_attr "length" "2,4")]) - ;; negate insns -(define_insn "negdf2" - [(set (match_operand:DF 0 "float_nonimm_operand" "=fR,Q") - (neg:DF (match_operand:DF 1 "register_operand" "0,0")))] +(define_insn_and_split "negdf2" + [(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q") + (neg:DF (match_operand:DF 1 "general_operand" "0,0")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (neg:DF (match_dup 1))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "negdf2<fcc_cc>" + [(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q") + (neg:DF (match_operand:DF 1 "general_operand" "0,0"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{negd|negf} %0" [(set_attr "length" "2,4")]) -(define_insn "negdi2" +(define_insn_and_split "negdi2" [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o") (neg:DI (match_operand:DI 1 "general_operand" "0,0")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (neg:DI (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; TODO: this can be neg/adc/neg/adc... I believe. Check. Saves one word. +(define_insn "negdi2_nocc" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o") + (neg:DI (match_operand:DI 1 "general_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" { rtx exops[4][2]; @@ -1196,10 +1502,22 @@ } [(set_attr "length" "18,34")]) -(define_insn "negsi2" +(define_insn_and_split "negsi2" [(set (match_operand:SI 0 "nonimmediate_operand" "=r,o") (neg:SI (match_operand:SI 1 "general_operand" "0,0")))] "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (neg:SI (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +;; TODO: this can be neg/adc/neg/adc... I believe. Check. Saves one word. +(define_insn "negsi2_nocc" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,o") + (neg:SI (match_operand:SI 1 "general_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" { rtx exops[2][2]; @@ -1212,13 +1530,24 @@ return \"\"; } -[(set_attr "length" "12,20")]) +[(set_attr "length" "10,18")]) -(define_insn "neg<mode>2" +(define_insn_and_split "neg<mode>2" [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q") (neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))] "" - "neg<isfx> %0" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) (neg:PDPint (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "neg<mode>2<cc_ccnz>" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q") + (neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "" + "neg<PDPint:isfx> %0" [(set_attr "length" "2,4")]) @@ -1229,9 +1558,9 @@ "" "* { - if (get_attr_length (insn) == 2) + if (get_attr_length (insn) == 2) return \"br %l0\"; - return \"jmp %l0\"; + return \"jmp %l0\"; }" [(set (attr "length") (if_then_else (ior (lt (minus (match_dup 0) (pc)) @@ -1242,14 +1571,6 @@ (const_int 4) (const_int 2)))]) -(define_insn "" - [(set (pc) - (label_ref (match_operand 0 "" ""))) - (clobber (const_int 1))] - "" - "jmp %l0" - [(set_attr "length" "4")]) - (define_insn "tablejump" [(set (pc) (match_operand:HI 0 "general_operand" "r,R,Q")) (use (label_ref (match_operand 1 "" "")))] @@ -1260,22 +1581,20 @@ jmp %@%0" [(set_attr "length" "2,2,4")]) -;; indirect jump - let's be conservative! -;; allow only register_operand, even though we could also -;; allow labels etc. - +;; indirect jump. TODO: this needs a constraint that allows memory +;; references but not indirection, since we add a level of indirection +;; in the generated code. (define_insn "indirect_jump" - [(set (pc) (match_operand:HI 0 "register_operand" "r"))] + [(set (pc) (match_operand:HI 0 "general_operand" "r"))] "" - "jmp (%0)") + "jmp @%0" + [(set_attr "length" "2")]) ;;- jump to subroutine (define_insn "call" [(call (match_operand:HI 0 "general_operand" "rR,Q") - (match_operand:HI 1 "general_operand" "g,g")) -;; (use (reg:HI 0)) what was that ??? - ] + (match_operand:HI 1 "general_operand" "g,g"))] ;;- Don't use operand 1 for most machines. "" "jsr pc, %0" @@ -1285,14 +1604,38 @@ (define_insn "call_value" [(set (match_operand 0 "" "") (call (match_operand:HI 1 "general_operand" "rR,Q") - (match_operand:HI 2 "general_operand" "g,g"))) -;; (use (reg:HI 0)) - what was that ???? - ] + (match_operand:HI 2 "general_operand" "g,g")))] ;;- Don't use operand 2 for most machines. "" "jsr pc, %1" [(set_attr "length" "2,4")]) +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" +{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + /* The optimizer does not know that the call sets the function value + registers we stored in the result block. We avoid problems by + claiming that all hard registers are used and clobbered at this + point. */ + emit_insn (gen_blockage ()); + + DONE; +}) + ;;- nop instruction (define_insn "nop" [(const_int 0)] @@ -1302,124 +1645,134 @@ ;;- multiply -(define_insn "muldf3" +(define_insn_and_split "muldf3" [(set (match_operand:DF 0 "register_operand" "=a,a") (mult:DF (match_operand:DF 1 "register_operand" "%0,0") (match_operand:DF 2 "float_operand" "fR,QF")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (mult:DF (match_dup 1) (match_dup 2))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "muldf3<fcc_ccnz>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (mult:DF (match_operand:DF 1 "register_operand" "%0,0") + (match_operand:DF 2 "float_operand" "fR,QF"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{muld|mulf} %2, %0" [(set_attr "length" "2,4")]) -;; 16 bit result multiply: -;; currently we multiply only into odd registers, so we don't use two -;; registers - but this is a bit inefficient at times. If we define -;; a register class for each register, then we can specify properly -;; which register need which scratch register .... +;; 16 bit result multiply. This uses odd numbered registers. -(define_insn "mulhi3" +(define_insn_and_split "mulhi3" [(set (match_operand:HI 0 "register_operand" "=d,d") ; multiply regs (mult:HI (match_operand:HI 1 "register_operand" "%0,0") - (match_operand:HI 2 "float_operand" "rR,Qi")))] + (match_operand:HI 2 "general_operand" "rR,Qi")))] "TARGET_40_PLUS" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (mult:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn "mulhi3<cc_cc>" + [(set (match_operand:HI 0 "register_operand" "=d,d") + (mult:HI (match_operand:HI 1 "register_operand" "%0,0") + (match_operand:HI 2 "general_operand" "rR,Qi"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_40_PLUS && reload_completed" "mul %2, %0" [(set_attr "length" "2,4")]) -;; 32 bit result -(define_expand "mulhisi3" - [(set (match_dup 3) - (match_operand:HI 1 "nonimmediate_operand" "g,g")) - (set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered! - (mult:SI (truncate:HI - (match_dup 0)) - (match_operand:HI 2 "general_operand" "rR,Qi")))] +;; 32 bit result from 16 bit operands +(define_insn_and_split "mulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0,0")) + (sign_extend:SI (match_operand:HI 2 "general_operand" "rR,Qi"))))] "TARGET_40_PLUS" - "operands[3] = gen_lowpart(HImode, operands[1]);") + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) + (mult:SI (sign_extend:SI (match_dup 1)) + (sign_extend:SI (match_dup 2)))) + (clobber (reg:CC CC_REGNUM))])] + "") -(define_insn "" - [(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered! - (mult:SI (truncate:HI - (match_operand:SI 1 "register_operand" "%0,0")) - (match_operand:HI 2 "general_operand" "rR,Qi")))] - "TARGET_40_PLUS" +(define_insn "mulhisi3<cc_cc>" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0,0")) + (sign_extend:SI (match_operand:HI 2 "general_operand" "rR,Qi")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_40_PLUS && reload_completed" "mul %2, %0" [(set_attr "length" "2,4")]) -;(define_insn "mulhisi3" -; [(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered! -; (mult:SI (truncate:HI -; (match_operand:SI 1 "register_operand" "%0,0")) -; (match_operand:HI 2 "general_operand" "rR,Qi")))] -; "TARGET_40_PLUS" -; "mul %2, %0" -; [(set_attr "length" "2,4")]) - ;;- divide -(define_insn "divdf3" +(define_insn_and_split "divdf3" [(set (match_operand:DF 0 "register_operand" "=a,a") (div:DF (match_operand:DF 1 "register_operand" "0,0") (match_operand:DF 2 "general_operand" "fR,QF")))] "TARGET_FPU" + "#" + "&& reload_completed" + [(parallel [(set (match_dup 0) (div:DF (match_dup 1) (match_dup 2))) + (clobber (reg:CC FCC_REGNUM))])] + "") + +(define_insn "divdf3<fcc_ccnz>" + [(set (match_operand:DF 0 "register_operand" "=a,a") + (div:DF (match_operand:DF 1 "register_operand" "0,0") + (match_operand:DF 2 "general_operand" "fR,QF"))) + (clobber (reg:CC FCC_REGNUM))] + "TARGET_FPU && reload_completed" "{divd|divf} %2, %0" [(set_attr "length" "2,4")]) - -(define_expand "divhi3" - [(set (subreg:HI (match_dup 1) 0) +(define_expand "divmodhi4" + [(parallel + [(set (subreg:HI (match_dup 1) 0) (div:HI (match_operand:SI 1 "register_operand" "0") (match_operand:HI 2 "general_operand" "g"))) + (set (subreg:HI (match_dup 1) 2) + (mod:HI (match_dup 1) (match_dup 2)))]) (set (match_operand:HI 0 "register_operand" "=r") - (subreg:HI (match_dup 1) 0))] + (subreg:HI (match_dup 1) 0)) + (set (match_operand:HI 3 "register_operand" "=r") + (subreg:HI (match_dup 1) 2))] "TARGET_40_PLUS" "") -(define_insn "" - [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 0) - (div:HI (match_operand:SI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "g")))] +(define_insn_and_split "*divmodhi4" + [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r,r") 0) + (div:HI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:HI 2 "general_operand" "rR,Qi"))) + (set (subreg:HI (match_dup 1) 2) + (mod:HI (match_dup 1) (match_dup 2)))] "TARGET_40_PLUS" - "div %2,%0" - [(set_attr "length" "4")]) - -(define_expand "modhi3" - [(set (subreg:HI (match_dup 1) 2) - (mod:HI (match_operand:SI 1 "register_operand" "0") - (match_operand:HI 2 "general_operand" "g"))) - (set (match_operand:HI 0 "register_operand" "=r") - (subreg:HI (match_dup 1) 2))] - "TARGET_40_PLUS" + "#" + "&& reload_completed" + [(parallel [(set (subreg:HI (match_dup 0) 0) + (div:HI (match_dup 1) (match_dup 2))) + (set (subreg:HI (match_dup 1) 2) + (mod:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] "") -(define_insn "" - [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 2) - (mod:HI (match_operand:SI 1 "general_operand" "0") - (match_operand:HI 2 "general_operand" "g")))] +;; Note that there is no corresponding CC setter pattern. +;; The reason is that it won't be generated, because +;; compare-elim.c only does the transformation on input +;; insns that have a two-element PARALLEL, as opposed to +;; the three-element one we have here. +(define_insn "divmodhi4_nocc" + [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r,r") 0) + (div:HI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:HI 2 "general_operand" "rR,Qi"))) + (set (subreg:HI (match_dup 1) 2) + (mod:HI (match_dup 1) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))] "TARGET_40_PLUS" - "div %2,%0" - [(set_attr "length" "4")]) - -;(define_expand "divmodhi4" -; [(parallel [(set (subreg:HI (match_dup 1) 0) -; (div:HI (match_operand:SI 1 "register_operand" "0") -; (match_operand:HI 2 "general_operand" "g"))) -; (set (subreg:HI (match_dup 1) 2) -; (mod:HI (match_dup 1) -; (match_dup 2)))]) -; (set (match_operand:HI 3 "register_operand" "=r") -; (subreg:HI (match_dup 1) 2)) -; (set (match_operand:HI 0 "register_operand" "=r") -; (subreg:HI (match_dup 1) 0))] -; "TARGET_40_PLUS" -; "") -; -;(define_insn "" -; [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 0) -; (div:HI (match_operand:SI 1 "general_operand" "0") -; (match_operand:HI 2 "general_operand" "g"))) -; (set (subreg:HI (match_dup 0) 2) -; (mod:HI (match_dup 1) -; (match_dup 2)))] -; "TARGET_40_PLUS" -; "div %2, %0") -; - -;; is rotate doing the right thing to be included here ???? + "div %2,%0" + [(set_attr "length" "2,4")]) Index: config/pdp11/pdp11.opt =================================================================== --- config/pdp11/pdp11.opt (revision 262195) +++ config/pdp11/pdp11.opt (working copy) @@ -34,22 +34,6 @@ mac0 Target Report Mask(AC0) Return floating-point results in ac0 (fr0 in Unix assembler syntax). -mbcopy -Target RejectNegative Report Mask(BCOPY) -Do not use inline patterns for copying memory. - -mbcopy-builtin -Target RejectNegative Report InverseMask(BCOPY, BCOPY_BUILTIN) -Use inline patterns for copying memory. - -mbranch-cheap -Target RejectNegative Report InverseMask(BRANCH_EXPENSIVE, BRANCH_CHEAP) -Do not pretend that branches are expensive. - -mbranch-expensive -Target RejectNegative Report Mask(BRANCH_EXPENSIVE) -Pretend that branches are expensive. - mdec-asm Target RejectNegative Report InverseMask(UNIX_ASM) Use the DEC assembler syntax. Index: config/pdp11/predicates.md =================================================================== --- config/pdp11/predicates.md (revision 262195) +++ config/pdp11/predicates.md (working copy) @@ -23,18 +23,13 @@ (ior (match_operand 0 "register_operand") (match_test "op == CONST0_RTX (GET_MODE (op))"))) -;; Accept integer arguments in the range -4..-2 and 2..4, which are the +;; Accept integer arguments in the range 1..3, which are the ;; shift counts for which we unroll a shift. This matches the rule for ;; the "O" constraint. (define_predicate "expand_shift_operand" - (match_code "const_int") -{ - int sh; + (and (match_code "const_int") + (match_test "(unsigned) INTVAL (op) < 4"))) - sh = INTVAL (op); - return (abs (sh) > 1 && abs (sh) <= 4); -}) - ;; Accept anything general_operand accepts, except that registers must ;; be FPU registers. (define_predicate "float_operand" @@ -52,3 +47,7 @@ (match_test "REGNO_REG_CLASS (REGNO (op)) == LOAD_FPU_REGS") (match_test "REGNO_REG_CLASS (REGNO (op)) == NO_LOAD_FPU_REGS")) (match_operand 0 "nonimmediate_operand"))) + +;; Accept any comparison valid for CCNZmode +(define_predicate "ccnz_operator" + (match_code "eq,ne,ge,lt")) Index: doc/invoke.texi =================================================================== --- doc/invoke.texi (revision 262195) +++ doc/invoke.texi (working copy) @@ -969,11 +969,9 @@ Objective-C and Objective-C++ Dialects}. @emph{PDP-11 Options} @gccoptlist{-mfpu -msoft-float -mac0 -mno-ac0 -m40 -m45 -m10 @gol --mbcopy -mbcopy-builtin -mint32 -mno-int16 @gol --mint16 -mno-int32 -mfloat32 -mno-float64 @gol --mfloat64 -mno-float32 -mabshi -mno-abshi @gol --mbranch-expensive -mbranch-cheap @gol --munix-asm -mdec-asm} +-mint32 -mno-int16 -mint16 -mno-int32 @gol +-mfloat32 -mno-float64 -mfloat64 -mno-float32 @gol +-msplit -munix-asm -mdec-asm} @emph{picoChip Options} @gccoptlist{-mae=@var{ae_type} -mvliw-lookahead=@var{N} @gol @@ -22018,7 +22016,7 @@ These options are defined for the PDP-11: @item -mfpu @opindex mfpu Use hardware FPP floating point. This is the default. (FIS floating -point on the PDP-11/40 is not supported.) +point on the PDP-11/40 is not supported.) Implies -m45. @item -msoft-float @opindex msoft-float @@ -22034,7 +22032,7 @@ Return floating-point results in memory. This is @item -m40 @opindex m40 -Generate code for a PDP-11/40. +Generate code for a PDP-11/40. Implies -msoft-float -mno-split. @item -m45 @opindex m45 @@ -22042,17 +22040,8 @@ Generate code for a PDP-11/45. This is the defaul @item -m10 @opindex m10 -Generate code for a PDP-11/10. +Generate code for a PDP-11/10. Implies -msoft-float -mno-split. -@item -mbcopy-builtin -@opindex mbcopy-builtin -Use inline @code{movmemhi} patterns for copying memory. This is the -default. - -@item -mbcopy -@opindex mbcopy -Do not use inline @code{movmemhi} patterns for copying memory. - @item -mint16 @itemx -mno-int32 @opindex mint16 @@ -22077,32 +22066,17 @@ Use 64-bit @code{float}. This is the default. @opindex mno-float64 Use 32-bit @code{float}. -@item -mabshi -@opindex mabshi -Use @code{abshi2} pattern. This is the default. +@item -msplit +@opindex msplit +Target has split instruction and data space. Implies -m45. -@item -mno-abshi -@opindex mno-abshi -Do not use @code{abshi2} pattern. - -@item -mbranch-expensive -@opindex mbranch-expensive -Pretend that branches are expensive. This is for experimenting with -code generation only. - -@item -mbranch-cheap -@opindex mbranch-cheap -Do not pretend that branches are expensive. This is the default. - @item -munix-asm @opindex munix-asm -Use Unix assembler syntax. This is the default when configured for -@samp{pdp11-*-bsd}. +Use Unix assembler syntax. @item -mdec-asm @opindex mdec-asm -Use DEC assembler syntax. This is the default when configured for any -PDP-11 target other than @samp{pdp11-*-bsd}. +Use DEC assembler syntax. This is the default. @end table @node picoChip Options Index: doc/md.texi =================================================================== --- doc/md.texi (revision 262195) +++ doc/md.texi (working copy) @@ -2966,6 +2966,10 @@ memory with a single instruction. Odd numbered general registers (R1, R3, R5). These are used for 16-bit multiply operations. +@item D +A memory reference that is encoded within the opcode, but not +auto-increment or auto-decrement. + @item f Any of the floating point registers (AC0 through AC5). @@ -2972,6 +2976,10 @@ Any of the floating point registers (AC0 through A @item G Floating point constant 0. +@item h +Floating point registers AC4 and AC5. These cannot be loaded from/to +memory with a single instruction. + @item I An integer constant that fits in 16 bits. @@ -2992,7 +3000,7 @@ The integer constant @minus{}1. The integer constant 0. @item O -Integer constants @minus{}4 through @minus{}1 and 1 through 4; shifts by these +Integer constants 0 through 3; shifts by these amounts are handled as multiple single-bit shifts rather than a single variable-length shift.