From: pkoning Date: Fri, 10 Dec 2010 01:31:08 +0000 (+0000) Subject: * config/pdp11/pdp11-protos.h (output_move_double, X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=commitdiff_plain;h=868076919e618b118385e5bfa85c56ebaa5c0362 * config/pdp11/pdp11-protos.h (output_move_double, output_move_quad): Delete. (output_move_multiple, pdp11_expand_operands): New functions. (pdp11_action, pdp11_partorder): New enums. * config/pdp11/pdp11.md (movdi, movsi, movdf, movsf): Use output_move_multiple. (adddi3, subdi3, negdi2): New patterns. (addsi3, subsi3, negsi2): Use pdp11_expand_operands. (abshi2): Delete. (neghi2, negqi2): Use PDPint iterator. * config/pdp11/pdp11.c (find_addr_reg, output_move_double, output_move_quad): Delete. (pdp11_expand_operands, output_move_multiple): New functions. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@167676 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 196fcbd9513..988dd2e7660 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2010-12-09 Paul Koning + + * config/pdp11/pdp11-protos.h (output_move_double, + output_move_quad): Delete. + (output_move_multiple, pdp11_expand_operands): New functions. + (pdp11_action, pdp11_partorder): New enums. + * config/pdp11/pdp11.md (movdi, movsi, movdf, movsf): Use + output_move_multiple. + (adddi3, subdi3, negdi2): New patterns. + (addsi3, subsi3, negsi2): Use pdp11_expand_operands. + (abshi2): Delete. + (neghi2, negqi2): Use PDPint iterator. + * config/pdp11/pdp11.c (find_addr_reg, output_move_double, + output_move_quad): Delete. + (pdp11_expand_operands, output_move_multiple): New functions. + 2010-12-09 Joseph Myers * config/vax/linux.h (WCHAR_TYPE, WCHAR_TYPE_SIZE): Define. diff --git a/gcc/config/pdp11/pdp11-protos.h b/gcc/config/pdp11/pdp11-protos.h index 97f0589db03..09ba7b21e66 100644 --- a/gcc/config/pdp11/pdp11-protos.h +++ b/gcc/config/pdp11/pdp11-protos.h @@ -26,8 +26,7 @@ extern int simple_memory_operand (rtx, enum 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_double (rtx *); -extern const char *output_move_quad (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 void print_operand_address (FILE *, rtx); @@ -35,6 +34,10 @@ extern bool pdp11_cannot_change_mode_class (enum machine_mode, enum machine_mode, enum reg_class); extern bool pdp11_secondary_memory_needed (reg_class_t, reg_class_t, enum machine_mode); +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, + pdp11_action *, pdp11_partorder); extern int pdp11_initial_elimination_offset (int, int); extern enum reg_class pdp11_regno_reg_class (int); diff --git a/gcc/config/pdp11/pdp11.c b/gcc/config/pdp11/pdp11.c index bb220f0ac2f..fedb22a12d4 100644 --- a/gcc/config/pdp11/pdp11.c +++ b/gcc/config/pdp11/pdp11.c @@ -140,7 +140,6 @@ decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED, static bool pdp11_handle_option (size_t, const char *, int); static void pdp11_option_init_struct (struct gcc_options *); -static rtx find_addr_reg (rtx); static const char *singlemove_string (rtx *); static bool pdp11_assemble_integer (rtx, unsigned int, int); static void pdp11_output_function_prologue (FILE *, HOST_WIDE_INT); @@ -236,7 +235,6 @@ static const struct default_options pdp11_option_optimization_table[] = #undef TARGET_ASM_FUNCTION_SECTION #define TARGET_ASM_FUNCTION_SECTION pdp11_function_section - /* Implement TARGET_HANDLE_OPTION. */ @@ -483,408 +481,216 @@ singlemove_string (rtx *operands) } -/* Output assembler code to perform a doubleword move insn - with operands OPERANDS. */ - -const char * -output_move_double (rtx *operands) +/* Expand multi-word operands (SImode or DImode) into the 2 or 4 + corresponding HImode operands. The number of operands is given + as the third argument, and the required order of the parts as + the fourth argument. */ +bool +pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount, + pdp11_action *action, pdp11_partorder order) { - enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; - rtx latehalf[2]; - rtx addreg0 = 0, addreg1 = 0; - - /* First classify both operands. */ - - if (REG_P (operands[0])) - optype0 = REGOP; - else if (offsettable_memref_p (operands[0])) - optype0 = OFFSOP; - else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) - optype0 = POPOP; - else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) - optype0 = PUSHOP; - else if (GET_CODE (operands[0]) == MEM) - optype0 = MEMOP; - else - optype0 = RNDOP; - - if (REG_P (operands[1])) - optype1 = REGOP; - else if (CONSTANT_P (operands[1]) -#if 0 - || GET_CODE (operands[1]) == CONST_DOUBLE -#endif - ) - optype1 = CNSTOP; - else if (offsettable_memref_p (operands[1])) - optype1 = OFFSOP; - else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) - optype1 = POPOP; - else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) - optype1 = PUSHOP; - else if (GET_CODE (operands[1]) == MEM) - optype1 = MEMOP; - else - optype1 = RNDOP; - - /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ - - gcc_assert (optype0 != RNDOP && optype1 != RNDOP); - - /* If one operand is decrementing and one is incrementing - decrement the former register explicitly - and change that operand into ordinary indexing. */ - - if (optype0 == PUSHOP && optype1 == POPOP) - { - operands[0] = XEXP (XEXP (operands[0], 0), 0); - output_asm_insn ("sub $4,%0", operands); - operands[0] = gen_rtx_MEM (SImode, operands[0]); - optype0 = OFFSOP; - } - if (optype0 == POPOP && optype1 == PUSHOP) + int words, op, w, i, sh; + pdp11_partorder useorder; + bool sameoff = false; + enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype; + REAL_VALUE_TYPE r; + long sval[2]; + + words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16; + + /* If either piece order is accepted and one is pre-decrement + while the other is post-increment, set order to be high order + word first. That will force the pre-decrement to be turned + into a pointer adjust, then offset addressing. + Otherwise, if either operand uses pre-decrement, that means + the order is low order first. + Otherwise, if both operands are registers and destination is + higher than source and they overlap, do low order word (highest + register number) first. */ + useorder = either; + if (opcount == 2) { - operands[1] = XEXP (XEXP (operands[1], 0), 0); - output_asm_insn ("sub $4,%1", operands); - operands[1] = gen_rtx_MEM (SImode, operands[1]); - optype1 = OFFSOP; + if (!REG_P (operands[0]) && !REG_P (operands[1]) && + !(CONSTANT_P (operands[1]) || + GET_CODE (operands[1]) == CONST_DOUBLE) && + ((GET_CODE (XEXP (operands[0], 0)) == POST_INC && + GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) || + (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC && + GET_CODE (XEXP (operands[1], 0)) == POST_INC))) + useorder = big; + else if ((!REG_P (operands[0]) && + GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) || + (!REG_P (operands[1]) && + !(CONSTANT_P (operands[1]) || + GET_CODE (operands[1]) == CONST_DOUBLE) && + GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)) + useorder = little; + else if (REG_P (operands[0]) && REG_P (operands[1]) && + REGNO (operands[0]) > REGNO (operands[1]) && + REGNO (operands[0]) < REGNO (operands[1]) + words) + useorder = little; + + /* Check for source == offset from register and dest == push of + the same register. In that case, we have to use the same + offset (the one for the low order word) for all words, because + the push increases the offset to each source word. + In theory there are other cases like this, for example dest == pop, + but those don't occur in real life so ignore those. */ + if (GET_CODE (operands[0]) == MEM + && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC + && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM + && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) + sameoff = true; } - /* If an operand is an unoffsettable memory ref, find a register - we can increment temporarily to make it refer to the second word. */ - - if (optype0 == MEMOP) - addreg0 = find_addr_reg (XEXP (operands[0], 0)); - - if (optype1 == MEMOP) - addreg1 = find_addr_reg (XEXP (operands[1], 0)); - - /* Ok, we can do one word at a time. - Normally we do the low-numbered word first, - but if either operand is autodecrementing then we - do the high-numbered word first. - - In either case, set up in LATEHALF the operands to use - for the high-numbered word and in some cases alter the - operands in OPERANDS to be suitable for the low-numbered word. */ - - if (optype0 == REGOP) - latehalf[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); - else if (optype0 == OFFSOP) - latehalf[0] = adjust_address (operands[0], HImode, 2); + /* If the caller didn't specify order, use the one we computed, + or high word first if we don't care either. If the caller did + specify, verify we don't have a problem with that order. + (If it matters to the caller, constraints need to be used to + ensure this case doesn't occur). */ + if (order == either) + order = (useorder == either) ? big : useorder; else - latehalf[0] = operands[0]; + gcc_assert (useorder == either || useorder == order); - if (optype1 == REGOP) - latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1); - else if (optype1 == OFFSOP) - latehalf[1] = adjust_address (operands[1], HImode, 2); - else if (optype1 == CNSTOP) + + for (op = 0; op < opcount; op++) { - if (CONSTANT_P (operands[1])) + /* First classify the operand. */ + if (REG_P (operands[op])) + optype = REGOP; + else if (CONSTANT_P (operands[op]) + || GET_CODE (operands[op]) == CONST_DOUBLE) + optype = CNSTOP; + else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC) + optype = POPOP; + else if (GET_CODE (XEXP (operands[op], 0)) == PRE_DEC) + optype = PUSHOP; + else if (!reload_in_progress || offsettable_memref_p (operands[op])) + optype = OFFSOP; + else if (GET_CODE (operands[op]) == MEM) + optype = MEMOP; + else + optype = RNDOP; + + /* Check for the cases that the operand constraints are not + supposed to allow to happen. Return failure for such cases. */ + if (optype == RNDOP) + return false; + + if (action != NULL) + action[op] = no_action; + + /* If the operand uses pre-decrement addressing but we + want to get the parts high order first, + decrement the former register explicitly + and change the operand into ordinary indexing. */ + if (optype == PUSHOP && order == big) + { + gcc_assert (action != NULL); + action[op] = dec_before; + operands[op] = gen_rtx_MEM (GET_MODE (operands[op]), + XEXP (XEXP (operands[op], 0), 0)); + optype = OFFSOP; + } + /* If the operand uses post-increment mode but we want + to get the parts low order first, change the operand + into ordinary indexing and remember to increment + the register explicitly when we're done. */ + else if (optype == POPOP && order == little) { - /* now the mess begins, high word is in lower word??? + gcc_assert (action != NULL); + action[op] = inc_after; + operands[op] = gen_rtx_MEM (GET_MODE (operands[op]), + XEXP (XEXP (operands[op], 0), 0)); + optype = OFFSOP; + } - that's what ashc makes me think, but I don't remember :-( */ - latehalf[1] = GEN_INT (INTVAL(operands[1]) >> 16); - operands[1] = GEN_INT (INTVAL(operands[1]) & 0xff); + if (GET_CODE (operands[op]) == CONST_DOUBLE) + { + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[op]); + REAL_VALUE_TO_TARGET_DOUBLE (r, sval); + } + + for (i = 0; i < words; i++) + { + if (order == big) + w = i; + else if (sameoff) + w = words - 1; + else + w = words - 1 - i; + + /* Set the output operand to be word "w" of the input. */ + if (optype == REGOP) + exops[i][op] = gen_rtx_REG (HImode, REGNO (operands[op]) + w); + else if (optype == OFFSOP) + exops[i][op] = adjust_address (operands[op], HImode, w * 2); + else if (optype == CNSTOP) + { + if (GET_CODE (operands[op]) == CONST_DOUBLE) + { + sh = 16 - (w & 1) * 16; + exops[i][op] = gen_rtx_CONST_INT (HImode, (sval[w / 2] >> sh) & 0xffff); + } + else + { + sh = ((words - 1 - w) * 16); + exops[i][op] = gen_rtx_CONST_INT (HImode, trunc_int_for_mode (INTVAL(operands[op]) >> sh, HImode)); + } + } + else + exops[i][op] = operands[op]; } - else - /* immediate 32-bit values not allowed */ - gcc_assert (GET_CODE (operands[1]) != CONST_DOUBLE); - } - else - latehalf[1] = operands[1]; - - /* If insn is effectively movd N(sp),-(sp) then we will do the - high word first. We should use the adjusted operand 1 (which is N+4(sp)) - for the low word as well, to compensate for the first decrement of sp. */ - if (optype0 == PUSHOP - && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM - && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) - operands[1] = latehalf[1]; - - /* If one or both operands autodecrementing, - do the two words, high-numbered first. */ - - /* Likewise, the first move would clobber the source of the second one, - do them in the other order. This happens only for registers; - such overlap can't happen in memory unless the user explicitly - sets it up, and that is an undefined circumstance. */ - - if (optype0 == PUSHOP || optype1 == PUSHOP - || (optype0 == REGOP && optype1 == REGOP - && REGNO (operands[0]) == REGNO (latehalf[1]))) - { - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - output_asm_insn ("add $2,%0", &addreg0); - if (addreg1) - output_asm_insn ("add $2,%0", &addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - output_asm_insn ("sub $2,%0", &addreg0); - if (addreg1) - output_asm_insn ("sub $2,%0", &addreg1); - - /* Do low-numbered word. */ - return singlemove_string (operands); } - - /* Normal case: do the two words, low-numbered first. */ - - output_asm_insn (singlemove_string (operands), operands); - - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - output_asm_insn ("add $2,%0", &addreg0); - if (addreg1) - output_asm_insn ("add $2,%0", &addreg1); - - /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - output_asm_insn ("sub $2,%0", &addreg0); - if (addreg1) - output_asm_insn ("sub $2,%0", &addreg1); - - return ""; + return true; } -/* Output assembler code to perform a quadword move insn - with operands OPERANDS. */ + +/* Output assembler code to perform a multiple-word move insn + with operands OPERANDS. This moves 2 or 4 words depending + on the machine mode of the operands. */ const char * -output_move_quad (rtx *operands) +output_move_multiple (rtx *operands) { - enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; - rtx latehalf[2]; - rtx addreg0 = 0, addreg1 = 0; - - output_asm_insn(";/* movdi/df: %1 -> %0 */", operands); + rtx exops[4][2]; + pdp11_action action[2]; + int i, words; - if (REG_P (operands[0])) - optype0 = REGOP; - else if (offsettable_memref_p (operands[0])) - optype0 = OFFSOP; - else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) - optype0 = POPOP; - else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) - optype0 = PUSHOP; - else if (GET_CODE (operands[0]) == MEM) - optype0 = MEMOP; - else - optype0 = RNDOP; - - if (REG_P (operands[1])) - optype1 = REGOP; - else if (CONSTANT_P (operands[1]) - || GET_CODE (operands[1]) == CONST_DOUBLE) - optype1 = CNSTOP; - else if (offsettable_memref_p (operands[1])) - optype1 = OFFSOP; - else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) - optype1 = POPOP; - else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) - optype1 = PUSHOP; - else if (GET_CODE (operands[1]) == MEM) - optype1 = MEMOP; - else - optype1 = RNDOP; - - /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ + words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16; - gcc_assert (optype0 != RNDOP && optype1 != RNDOP); + pdp11_expand_operands (operands, exops, 2, action, either); - if (optype0 == REGOP || optype1 == REGOP) - { - /* check for use of clrd???? - if you ever allow ac4 and ac5 (now we require secondary load) - you must check whether - you want to load into them or store from them - - then dump ac0 into $help$ movce ac4/5 to ac0, do the - store from ac0, and restore ac0 - if you can find - an unused ac[0-3], use that and you save a store and a load!*/ - - if (FPU_REG_P(REGNO(operands[0]))) - { - if (GET_CODE(operands[1]) == CONST_DOUBLE) - { - REAL_VALUE_TYPE r; - REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); - - if (REAL_VALUES_EQUAL (r, dconst0)) - return "{clrd|clrf} %0"; - } - - return "{ldd|movf} %1, %0"; - } - - if (FPU_REG_P(REGNO(operands[1]))) - return "{std|movf} %1, %0"; - } - - /* If one operand is decrementing and one is incrementing - decrement the former register explicitly - and change that operand into ordinary indexing. */ - - if (optype0 == PUSHOP && optype1 == POPOP) + /* Check for explicit decrement before. */ + if (action[0] == dec_before) { - operands[0] = XEXP (XEXP (operands[0], 0), 0); - output_asm_insn ("sub $8,%0", operands); - operands[0] = gen_rtx_MEM (DImode, operands[0]); - optype0 = OFFSOP; + operands[0] = XEXP (operands[0], 0); + output_asm_insn ("sub $4,%0", operands); } - if (optype0 == POPOP && optype1 == PUSHOP) + if (action[1] == dec_before) { - operands[1] = XEXP (XEXP (operands[1], 0), 0); - output_asm_insn ("sub $8,%1", operands); - operands[1] = gen_rtx_MEM (SImode, operands[1]); - optype1 = OFFSOP; + operands[1] = XEXP (operands[1], 0); + output_asm_insn ("sub $4,%1", operands); } - /* If an operand is an unoffsettable memory ref, find a register - we can increment temporarily to make it refer to the second word. */ - - if (optype0 == MEMOP) - addreg0 = find_addr_reg (XEXP (operands[0], 0)); - - if (optype1 == MEMOP) - addreg1 = find_addr_reg (XEXP (operands[1], 0)); - - /* Ok, we can do one word at a time. - Normally we do the low-numbered word first, - but if either operand is autodecrementing then we - do the high-numbered word first. - - In either case, set up in LATEHALF the operands to use - for the high-numbered word and in some cases alter the - operands in OPERANDS to be suitable for the low-numbered word. */ + /* Do the words. */ + for (i = 0; i < words; i++) + output_asm_insn (singlemove_string (exops[i]), exops[i]); - if (optype0 == REGOP) - latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2); - else if (optype0 == OFFSOP) - latehalf[0] = adjust_address (operands[0], SImode, 4); - else - latehalf[0] = operands[0]; - - if (optype1 == REGOP) - latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2); - else if (optype1 == OFFSOP) - latehalf[1] = adjust_address (operands[1], SImode, 4); - else if (optype1 == CNSTOP) + /* Check for increment after. */ + if (action[0] == inc_after) { - if (GET_CODE (operands[1]) == CONST_DOUBLE) - { - REAL_VALUE_TYPE r; - long dval[2]; - REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); - REAL_VALUE_TO_TARGET_DOUBLE (r, dval); - latehalf[1] = GEN_INT (dval[1]); - operands[1] = GEN_INT (dval[0]); - } - else if (GET_CODE(operands[1]) == CONST_INT) - { - latehalf[1] = const0_rtx; - } - else - gcc_unreachable (); + operands[0] = XEXP (operands[0], 0); + output_asm_insn ("add $4,%0", operands); } - else - latehalf[1] = operands[1]; - - /* If insn is effectively movd N(sp),-(sp) then we will do the - high word first. We should use the adjusted operand 1 (which is N+4(sp)) - for the low word as well, to compensate for the first decrement of sp. */ - if (optype0 == PUSHOP - && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM - && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) - operands[1] = latehalf[1]; - - /* If one or both operands autodecrementing, - do the two words, high-numbered first. */ - - /* Likewise, the first move would clobber the source of the second one, - do them in the other order. This happens only for registers; - such overlap can't happen in memory unless the user explicitly - sets it up, and that is an undefined circumstance. */ - - if (optype0 == PUSHOP || optype1 == PUSHOP - || (optype0 == REGOP && optype1 == REGOP - && REGNO (operands[0]) == REGNO (latehalf[1]))) + if (action[1] == inc_after) { - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - output_asm_insn ("add $4,%0", &addreg0); - if (addreg1) - output_asm_insn ("add $4,%0", &addreg1); - - /* Do that word. */ - output_asm_insn(output_move_double(latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - output_asm_insn ("sub $4,%0", &addreg0); - if (addreg1) - output_asm_insn ("sub $4,%0", &addreg1); - - /* Do low-numbered word. */ - return output_move_double (operands); + operands[1] = XEXP (operands[1], 0); + output_asm_insn ("add $4,%1", operands); } - /* Normal case: do the two words, low-numbered first. */ - - output_asm_insn (output_move_double (operands), operands); - - /* Make any unoffsettable addresses point at high-numbered word. */ - if (addreg0) - output_asm_insn ("add $4,%0", &addreg0); - if (addreg1) - output_asm_insn ("add $4,%0", &addreg1); - - /* Do that word. */ - output_asm_insn (output_move_double (latehalf), latehalf); - - /* Undo the adds we just did. */ - if (addreg0) - output_asm_insn ("sub $4,%0", &addreg0); - if (addreg1) - output_asm_insn ("sub $4,%0", &addreg1); - return ""; } - - -/* Return a REG that occurs in ADDR with coefficient 1. - ADDR can be effectively incremented by incrementing REG. */ - -static rtx -find_addr_reg (rtx addr) -{ - while (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 0)) == REG) - addr = XEXP (addr, 0); - if (GET_CODE (XEXP (addr, 1)) == REG) - addr = XEXP (addr, 1); - if (CONSTANT_P (XEXP (addr, 0))) - addr = XEXP (addr, 1); - if (CONSTANT_P (XEXP (addr, 1))) - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) == REG) - return addr; - return 0; -} /* Output an ascii string. */ void @@ -1806,6 +1612,7 @@ pdp11_legitimate_address_p (enum machine_mode mode, return false; } } + /* Return the class number of the smallest class containing reg number REGNO. */ enum reg_class diff --git a/gcc/config/pdp11/pdp11.md b/gcc/config/pdp11/pdp11.md index 2d4eb8a829e..a070c3726cc 100644 --- a/gcc/config/pdp11/pdp11.md +++ b/gcc/config/pdp11/pdp11.md @@ -260,21 +260,21 @@ ;; Move instructions (define_insn "movdi" - [(set (match_operand:DI 0 "nonimmediate_operand" "=g,rm,o") - (match_operand:DI 1 "general_operand" "m,r,a"))] + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g") + (match_operand:DI 1 "general_operand" "rN,g"))] "" - "* return output_move_quad (operands);" + "* return output_move_multiple (operands);" ;; what's the mose expensive code - say twice movsi = 16 - [(set_attr "length" "32,32,32")]) + [(set_attr "length" "16,32")]) (define_insn "movsi" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,rm,m") - (match_operand:SI 1 "general_operand" "rN,IJ,K,m,r"))] + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g") + (match_operand:SI 1 "general_operand" "rN,IJ,IJ,g"))] "" - "* return output_move_double (operands);" + "* return output_move_multiple (operands);" ;; what's the most expensive code ? - I think 8! ;; we could split it up and make several sub-cases... - [(set_attr "length" "4,6,8,16,16")]) + [(set_attr "length" "4,6,8,16")]) (define_insn "mov" [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q") @@ -298,7 +298,7 @@ else if (which_alternative == 1 || which_alternative == 3) return \"std %1, %0\"; else - return output_move_quad (operands); " + return output_move_multiple (operands); " ;; just a guess.. [(set_attr "length" "2,2,10,10,32")]) @@ -311,7 +311,7 @@ else if (which_alternative == 1 || which_alternative == 3) return \"{stcdf|movfo} %1, %0\"; else - return output_move_double (operands); " + return output_move_multiple (operands); " ;; just a guess.. [(set_attr "length" "2,2,10,10,16")]) @@ -614,55 +614,79 @@ "{addd|addf} %2, %0" [(set_attr "length" "2,4,10")]) -(define_insn "addsi3" - [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o,r,r,r,o,o,o") - (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0") - (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K")))] +(define_insn "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")))] "" "* -{ /* Here we trust that operands don't overlap - - or is lateoperands the low word?? - looks like it! */ - - rtx lateoperands[3]; +{ + rtx inops[2]; + rtx exops[4][2]; - lateoperands[0] = operands[0]; - - if (REG_P (operands[0])) - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); - else - operands[0] = adjust_address (operands[0], HImode, 2); + inops[0] = operands[0]; + inops[1] = operands[2]; + pdp11_expand_operands (inops, exops, 2, NULL, either); - if (! CONSTANT_P(operands[2])) + if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0) + output_asm_insn (\"add %1, %0\", exops[0]); + if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0) { - lateoperands[2] = operands[2]; - - if (REG_P (operands[2])) - operands[2] = gen_rtx_REG (HImode, REGNO (operands[2]) + 1); - else - operands[2] = adjust_address (operands[2], HImode, 2); - - output_asm_insn (\"add %2, %0\", operands); - output_asm_insn (\"adc %0\", lateoperands); - output_asm_insn (\"add %2, %0\", lateoperands); - return \"\"; + output_asm_insn (\"add %1, %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); + } + if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0) + { + output_asm_insn (\"add %1, %0\", exops[2]); + output_asm_insn (\"adc %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); + } + if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0) + { + output_asm_insn (\"add %1, %0\", exops[3]); + output_asm_insn (\"adc %0\", exops[2]); + output_asm_insn (\"adc %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); } - lateoperands[2] = GEN_INT ((INTVAL (operands[2]) >> 16) & 0xffff); - operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); + return \"\"; +}" + [(set_attr "length" "20,28,40,48")]) + +;; Note that the register operand is not marked earlyclobber. +;; The reason is that SI values go in register pairs, so they +;; can't partially overlap. They can be either disjoint, or +;; source and destination can be equal. The latter case is +;; handled properly because of the ordering of the individual +;; instructions used. Specifically, carry from the low to the +;; 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") + (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0") + (match_operand:SI 2 "general_operand" "r,on,r,on")))] + "" + "* +{ + rtx inops[2]; + rtx exops[2][2]; - if (INTVAL(operands[2])) - { - output_asm_insn (\"add %2, %0\", operands); - output_asm_insn (\"adc %0\", lateoperands); + inops[0] = operands[0]; + inops[1] = operands[2]; + pdp11_expand_operands (inops, exops, 2, NULL, either); + + if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0) + output_asm_insn (\"add %1, %0\", exops[0]); + if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0) + { + output_asm_insn (\"add %1, %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); } - if (INTVAL(lateoperands[2])) - output_asm_insn (\"add %2, %0\", lateoperands); - return \"\"; }" - [(set_attr "length" "6,10,12,16,6,2,10,10,6,16")]) + [(set_attr "length" "6,10,12,16")]) (define_insn "addhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q") @@ -697,38 +721,69 @@ "{subd|subf} %2, %0" [(set_attr "length" "2,4")]) -(define_insn "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,o,r,o")))] +(define_insn "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")))] "" "* -{ /* Here we trust that operands don't overlap +{ + rtx inops[2]; + rtx exops[4][2]; + + inops[0] = operands[0]; + inops[1] = operands[2]; + pdp11_expand_operands (inops, exops, 2, NULL, either); + + if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0) + output_asm_insn (\"sub %1, %0\", exops[0]); + if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0) + { + output_asm_insn (\"sub %1, %0\", exops[1]); + output_asm_insn (\"sbc %0\", exops[0]); + } + if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0) + { + output_asm_insn (\"sub %1, %0\", exops[2]); + output_asm_insn (\"sbc %0\", exops[1]); + output_asm_insn (\"sbc %0\", exops[0]); + } + if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0) + { + output_asm_insn (\"sub %1, %0\", exops[3]); + output_asm_insn (\"sbc %0\", exops[2]); + output_asm_insn (\"sbc %0\", exops[1]); + output_asm_insn (\"sbc %0\", exops[0]); + } - or is lateoperands the low word?? - looks like it! */ + return \"\"; +}" + [(set_attr "length" "20,28,40,48")]) - rtx lateoperands[3]; +(define_insn "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")))] + "" + "* +{ + rtx inops[2]; + rtx exops[2][2]; - lateoperands[0] = operands[0]; - - if (REG_P (operands[0])) - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); - else - operands[0] = adjust_address (operands[0], HImode, 2); + inops[0] = operands[0]; + inops[1] = operands[2]; + pdp11_expand_operands (inops, exops, 2, NULL, either); - lateoperands[2] = operands[2]; - - if (REG_P (operands[2])) - operands[2] = gen_rtx_REG (HImode, REGNO (operands[2]) + 1); - else - operands[2] = adjust_address (operands[2], HImode, 2); + if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0) + output_asm_insn (\"sub %1, %0\", exops[0]); + if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0) + { + output_asm_insn (\"sub %1, %0\", exops[1]); + output_asm_insn (\"sbc %0\", exops[0]); + } - output_asm_insn (\"sub %2, %0\", operands); - output_asm_insn (\"sbc %0\", lateoperands); - output_asm_insn (\"sub %2, %0\", lateoperands); return \"\"; }" -;; offsettable memory addresses always are expensive!!! [(set_attr "length" "6,10,12,16")]) (define_insn "subhi3" @@ -1070,60 +1125,6 @@ "{absd|absf} %0" [(set_attr "length" "2,4")]) -(define_insn "abshi2" - [(set (match_operand:HI 0 "nonimmediate_operand" "=r,o") - (abs:HI (match_operand:HI 1 "general_operand" "0,0")))] - "" - "* -{ - static int count = 0; - char buf[200]; - - output_asm_insn(\"tst %0\", operands); - sprintf(buf, \"bge abshi%d\", count); - output_asm_insn(buf, NULL); - output_asm_insn(\"neg %0\", operands); - sprintf(buf, \"\\nabshi%d:\", count++); - output_asm_insn(buf, NULL); - - return \"\"; -}" - [(set_attr "length" "6,10")]) - - -;; define expand abshi - is much better !!! - but -;; will it be optimized into an abshi2 ? -;; it will leave better code, because the tsthi might be -;; optimized away!! -; -- just a thought - don't have time to check -; -;(define_expand "abshi2" -; [(match_operand:HI 0 "nonimmediate_operand" "") -; (match_operand:HI 1 "general_operand" "")] -; "" -; " -;{ -; rtx label = gen_label_rtx (); -; -; /* do I need this? */ -; do_pending_stack_adjust (); -; -; emit_move_insn (operands[0], operands[1]); -; -; emit_insn (gen_tsthi (operands[0])); -; emit_insn (gen_bge (label1)); -; -; emit_insn (gen_neghi(operands[0], operands[0]) -; -; emit_barrier (); -; -; emit_label (label); -; -; /* 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); -; DONE; -;}") ;; negate insns @@ -1134,41 +1135,51 @@ "{negd|negf} %0" [(set_attr "length" "2,4")]) -(define_insn "negsi2" - [(set (match_operand:SI 0 "register_operand" "=r") - (neg:SI (match_operand:SI 1 "general_operand" "0")))] +(define_insn "negdi2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o") + (neg:DI (match_operand:DI 1 "general_operand" "0,0")))] "" { + rtx exops[4][2]; + + pdp11_expand_operands (operands, exops, 1, NULL, either); - rtx lateoperands[2]; + output_asm_insn (\"com %0\", exops[3]); + output_asm_insn (\"com %0\", exops[2]); + output_asm_insn (\"com %0\", exops[1]); + output_asm_insn (\"com %0\", exops[0]); + output_asm_insn (\"add $1, %0\", exops[3]); + output_asm_insn (\"adc %0\", exops[2]); + output_asm_insn (\"adc %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); - lateoperands[0] = operands[0]; - operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1); + return \"\"; +} +[(set_attr "length" "18,34")]) - lateoperands[1] = operands[1]; - operands[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1); +(define_insn "negsi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,o") + (neg:SI (match_operand:SI 1 "general_operand" "0,0")))] + "" +{ + rtx exops[2][2]; + + pdp11_expand_operands (operands, exops, 1, NULL, either); - output_asm_insn (\"com %0\", lateoperands); - output_asm_insn (\"com %0\", operands); - output_asm_insn (\"add $1, %0\", operands); - output_asm_insn (\"adc %0\", lateoperands); + output_asm_insn (\"com %0\", exops[1]); + output_asm_insn (\"com %0\", exops[0]); + output_asm_insn (\"add $1, %0\", exops[1]); + output_asm_insn (\"adc %0\", exops[0]); return \"\"; } - [(set_attr "length" "14")]) +[(set_attr "length" "12,20")]) -(define_insn "neghi2" - [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q") - (neg:HI (match_operand:HI 1 "general_operand" "0,0")))] - "" - "neg %0" - [(set_attr "length" "2,4")]) - -(define_insn "negqi2" - [(set (match_operand:QI 0 "nonimmediate_operand" "=rR,Q") - (neg:QI (match_operand:QI 1 "general_operand" "0,0")))] +(define_insn "neg2" + [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q") + (neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))] "" - "negb %0" + "neg %0" [(set_attr "length" "2,4")])