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);
#undef TARGET_ASM_FUNCTION_SECTION
#define TARGET_ASM_FUNCTION_SECTION pdp11_function_section
-
\f
/* Implement TARGET_HANDLE_OPTION. */
}
\f
-/* 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 "";
}
-
-\f
-/* 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;
-}
\f
/* Output an ascii string. */
void
return false;
}
}
+
/* Return the class number of the smallest class containing
reg number REGNO. */
enum reg_class
;; 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<mode>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
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")])
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")])
"{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")
"{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"
"{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
"{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 "neg<mode>2"
+ [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
+ (neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))]
""
- "negb %0"
+ "neg<isfx> %0"
[(set_attr "length" "2,4")])