#include "memmodel.h"
#include "tm_p.h"
#include "insn-config.h"
+#include "insn-attr.h"
#include "regs.h"
#include "emit-rtl.h"
#include "recog.h"
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);
+static int pdp11_addr_cost (rtx, machine_mode, addr_space_t, bool);
+static int pdp11_insn_cost (rtx_insn *insn, bool speed);
+static rtx_insn *pdp11_md_asm_adjust (vec<rtx> &, vec<rtx> &,
+ vec<const char *> &,
+ vec<rtx> &, HARD_REG_SET &);
static bool pdp11_return_in_memory (const_tree, const_tree);
static rtx pdp11_function_value (const_tree, const_tree, bool);
static rtx pdp11_libcall_value (machine_mode, const_rtx);
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER pdp11_assemble_integer
+/* These two apply to Unix and GNU assembler; for DEC, they are
+ overridden during option processing. */
#undef TARGET_ASM_OPEN_PAREN
#define TARGET_ASM_OPEN_PAREN "["
#undef TARGET_ASM_CLOSE_PAREN
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS pdp11_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST pdp11_addr_cost
+
+#undef TARGET_INSN_COST
+#define TARGET_INSN_COST pdp11_insn_cost
+
+#undef TARGET_MD_ASM_ADJUST
+#define TARGET_MD_ASM_ADJUST pdp11_md_asm_adjust
+
#undef TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG pdp11_function_arg
#undef TARGET_FUNCTION_ARG_ADVANCE
#undef TARGET_CAN_CHANGE_MODE_CLASS
#define TARGET_CAN_CHANGE_MODE_CLASS pdp11_can_change_mode_class
+
+#undef TARGET_INVALID_WITHIN_DOLOOP
+#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_insn_null
\f
/* A helper function to determine if REGNO should be saved in the
current function's stack frame. */
}
-/* 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. */
+/* Register to register moves are cheap if both are general
+ registers. */
static int
pdp11_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
reg_class_t c1, reg_class_t c2)
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;
+ return 2;
else
return 22;
}
-
+/* This tries to approximate what pdp11_insn_cost would do, but
+ without visibility into the actual instruction being generated it's
+ inevitably a rough approximation. */
static bool
-pdp11_rtx_costs (rtx x, machine_mode mode, int outer_code ATTRIBUTE_UNUSED,
- int opno ATTRIBUTE_UNUSED, int *total,
- bool speed ATTRIBUTE_UNUSED)
+pdp11_rtx_costs (rtx x, machine_mode mode, int outer_code,
+ int opno ATTRIBUTE_UNUSED, int *total, bool speed)
{
- int code = GET_CODE (x);
-
+ const int code = GET_CODE (x);
+ const int asize = (mode == QImode) ? 2 : GET_MODE_SIZE (mode);
+ rtx src, dest;
+
switch (code)
{
case CONST_INT:
- if (INTVAL (x) == 0 || INTVAL (x) == -1 || INTVAL (x) == 1)
+ /* Treat -1, 0, 1 as things that are optimized as clr or dec
+ etc. though that doesn't apply to every case. */
+ if (INTVAL (x) >= -1 && INTVAL (x) <= 1)
{
*total = 0;
return true;
}
- /* FALLTHRU */
-
+ /* FALL THROUGH. */
+ case REG:
+ case MEM:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
- /* Twice as expensive as REG. */
- *total = 2;
- return true;
-
case CONST_DOUBLE:
- /* Twice (or 4 times) as expensive as 16 bit. */
- *total = 4;
+ *total = pdp11_addr_cost (x, mode, ADDR_SPACE_GENERIC, speed);
return true;
+ }
- case MULT:
- /* ??? There is something wrong in MULT because MULT is not
- as cheap as total = 2 even if we can shift! */
- /* If optimizing for size make mult etc cheap, but not 1, so when
- in doubt the faster insn is chosen. */
- if (optimize_size)
- *total = COSTS_N_INSNS (2);
- else
- *total = COSTS_N_INSNS (11);
- return false;
+ /* Pick up source and dest. We don't necessarily use the standard
+ recursion in rtx_costs to figure the cost, because that would
+ count the destination operand twice for three-operand insns.
+ Also, this way we can catch special cases like move of zero, or
+ add one. */
+ if (GET_RTX_LENGTH (code) > 1)
+ src = XEXP (x, 1);
+ dest = XEXP (x, 0);
+
+ /* If optimizing for size, claim everything costs 2 per word, plus
+ whatever the operands require. */
+ if (!speed)
+ *total = asize;
+ else
+ {
+ if (FLOAT_MODE_P (mode))
+ {
+ switch (code)
+ {
+ case MULT:
+ case DIV:
+ case MOD:
+ *total = 20;
+ break;
- case DIV:
- if (optimize_size)
- *total = COSTS_N_INSNS (2);
- else
- *total = COSTS_N_INSNS (25);
- return false;
+ case COMPARE:
+ *total = 4;
+ break;
- case MOD:
- if (optimize_size)
- *total = COSTS_N_INSNS (2);
+ case PLUS:
+ case MINUS:
+ *total = 6;
+ break;
+
+ default:
+ *total = 2;
+ break;
+ }
+ }
else
- *total = COSTS_N_INSNS (26);
- return false;
+ {
+ /* Integer operations are scaled for SI and DI modes, though the
+ scaling is not exactly accurate. */
+ switch (code)
+ {
+ case MULT:
+ *total = 5 * asize * asize;
+ break;
- case ABS:
- /* Equivalent to length, so same for optimize_size. */
- *total = COSTS_N_INSNS (3);
- return false;
+ case DIV:
+ *total = 10 * asize * asize;
+ break;
+
+ case MOD:
+ /* Fake value because it's accounted for under DIV, since we
+ use a divmod pattern. */
+ total = 0;
+ break;
- case ZERO_EXTEND:
- /* Only used for qi->hi. */
- *total = COSTS_N_INSNS (1);
- return false;
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* This is a bit problematic because the cost depends on the
+ shift amount. Make it <asize> for now, which is for the
+ case of a one bit shift. */
+ *total = asize;
+ break;
+
+ default:
+ *total = asize;
+ break;
+ }
+ }
+ }
+
+ /* Now see if we're looking at a SET. If yes, then look at the
+ source to see if this is a move or an arithmetic operation, and
+ continue accordingly to handle the operands. */
+ if (code == SET)
+ {
+ switch (GET_CODE (src))
+ {
+ case REG:
+ case MEM:
+ case CONST_INT:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ /* It's a move. */
+ *total += pdp11_addr_cost (dest, mode, ADDR_SPACE_GENERIC, speed);
+ if (src != const0_rtx)
+ *total += pdp11_addr_cost (src, mode, ADDR_SPACE_GENERIC, speed);
+ return true;
+ default:
+ /* Not a move. Get the cost of the source operand and add
+ that in, but not the destination operand since we're
+ dealing with read/modify/write operands. */
+ *total += rtx_cost (src, mode, (enum rtx_code) outer_code, 1, speed);
+ return true;
+ }
+ }
+ else if (code == PLUS || code == MINUS)
+ {
+ if (GET_CODE (src) == CONST_INT &&
+ (INTVAL (src) == 1 || INTVAL (src) == -1))
+ {
+ *total += rtx_cost (dest, mode, (enum rtx_code) outer_code, 0, speed);
+ return true;
+ }
+ }
+ return false;
+}
- case SIGN_EXTEND:
- if (mode == HImode)
- *total = COSTS_N_INSNS (1);
- else if (mode == SImode)
- *total = COSTS_N_INSNS (6);
- else
- *total = COSTS_N_INSNS (2);
- return false;
-
- case ASHIFT:
- case ASHIFTRT:
- if (optimize_size)
- *total = COSTS_N_INSNS (1);
- else if (mode == QImode)
- {
- if (GET_CODE (XEXP (x, 1)) != CONST_INT)
- *total = COSTS_N_INSNS (8); /* worst case */
- else
- *total = COSTS_N_INSNS (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 (1);
- else
- *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1)));
- }
- else
- *total = COSTS_N_INSNS (10); /* worst case */
- }
- else if (mode == SImode)
- {
- if (GET_CODE (XEXP (x, 1)) == CONST_INT)
- *total = COSTS_N_INSNS (2.5 + 0.5 * INTVAL (XEXP (x, 1)));
- else /* worst case */
- *total = COSTS_N_INSNS (18);
- }
- 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;
+/* Return cost of accessing the supplied operand. Registers are free.
+ Anything else starts with a cost of two. Add to that for memory
+ references the memory accesses of the addressing mode (if any) plus
+ the data reference; for other operands just the memory access (if
+ any) for the mode. */
+static int
+pdp11_addr_cost (rtx addr, machine_mode mode, addr_space_t as ATTRIBUTE_UNUSED,
+ bool speed)
+{
+ int cost = 0;
+
+ if (GET_CODE (addr) != REG)
+ {
+ if (!simple_memory_operand (addr, mode))
+ cost = 2;
+
+ /* If optimizing for speed, account for the memory reference if
+ any. */
+ if (speed && !CONSTANT_P (addr))
+ cost += (mode == QImode) ? 2 : GET_MODE_SIZE (mode);
+ }
+ return cost;
+}
+
+static int
+pdp11_insn_cost (rtx_insn *insn, bool speed)
+{
+ int base_cost, i;
+ rtx pat, set, dest, src, src2;
+ machine_mode mode;
+ const char *fmt;
+ enum rtx_code op;
+
+ if (recog_memoized (insn) < 0)
+ return 0;
+
+ /* If optimizing for size, we want the insn size. */
+ if (!speed)
+ return get_attr_length (insn);
+ else
+ {
+ /* Optimizing for speed. Get the base cost of the insn, then
+ adjust for the cost of accessing operands. Zero means use
+ the length as the cost even when optimizing for speed. */
+ base_cost = get_attr_base_cost (insn);
+ if (base_cost <= 0)
+ base_cost = get_attr_length (insn);
+ }
+ /* Look for the operands. Often we have a PARALLEL that's either
+ the actual operation plus a clobber, or the implicit compare plus
+ the actual operation. Find the actual operation. */
+ pat = PATTERN (insn);
+
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ set = XVECEXP (pat, 0, 0);
+ if (GET_CODE (set) != SET || GET_CODE (XEXP (set, 1)) == COMPARE)
+ set = XVECEXP (pat, 0, 1);
+ if (GET_CODE (set) != SET || GET_CODE (XEXP (set, 1)) == COMPARE)
+ return 0;
+ }
+ else
+ {
+ set = pat;
+ if (GET_CODE (set) != SET)
+ return 0;
+ }
+
+ /* Pick up the SET source and destination RTL. */
+ dest = XEXP (set, 0);
+ src = XEXP (set, 1);
+ mode = GET_MODE (dest);
+
+ /* See if we have a move, or some arithmetic operation. If a move,
+ account for source and destination operand costs. Otherwise,
+ account for the destination and for the second operand of the
+ operation -- the first is also destination and we don't want to
+ double-count it. */
+ base_cost += pdp11_addr_cost (dest, mode, ADDR_SPACE_GENERIC, speed);
+ op = GET_CODE (src);
+ switch (op)
+ {
+ case REG:
+ case MEM:
+ case CONST_INT:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ /* It's a move. */
+ if (src != const0_rtx)
+ base_cost += pdp11_addr_cost (src, mode, ADDR_SPACE_GENERIC, speed);
+ return base_cost;
default:
- return false;
+ break;
}
+ /* There are some other cases where souce and dest are distinct. */
+ if (FLOAT_MODE_P (mode) &&
+ (op == FLOAT_TRUNCATE || op == FLOAT_EXTEND || op == FIX || op == FLOAT))
+ {
+ src2 = XEXP (src, 0);
+ base_cost += pdp11_addr_cost (src2, mode, ADDR_SPACE_GENERIC, speed);
+ }
+ /* Otherwise, pick up the second operand of the arithmetic
+ operation, if it has two operands. */
+ else if (op != SUBREG && op != UNSPEC && GET_RTX_LENGTH (op) > 1)
+ {
+ src2 = XEXP (src, 1);
+ base_cost += pdp11_addr_cost (src2, mode, ADDR_SPACE_GENERIC, speed);
+ }
+
+ return base_cost;
}
const char *
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)
+pdp11_cc_mode (enum rtx_code op ATTRIBUTE_UNUSED, rtx x, rtx y ATTRIBUTE_UNUSED)
{
if (FLOAT_MODE_P (GET_MODE (x)))
{
if (GET_MODE (x) == VOIDmode)
{
/* We can use %o if the number is one word and positive. */
- gcc_assert (!CONST_DOUBLE_HIGH (x));
if (TARGET_DEC_ASM)
- fprintf (file, "%ho", CONST_DOUBLE_LOW (x) & 0xffff);
+ fprintf (file, "%o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
else
- fprintf (file, "%#ho", CONST_DOUBLE_LOW (x) & 0xffff);
+ fprintf (file, "%#o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
}
else
/* We can't handle floating point constants;
return shift_size;
}
+/* Prepend to CLOBBERS hard registers that are automatically clobbered
+ for an asm We do this for CC_REGNUM and FCC_REGNUM (on FPU target)
+ to maintain source compatibility with the original cc0-based
+ compiler. */
+
+static rtx_insn *
+pdp11_md_asm_adjust (vec<rtx> &/*outputs*/, vec<rtx> &/*inputs*/,
+ vec<const char *> &/*constraints*/,
+ vec<rtx> &clobbers, HARD_REG_SET &clobbered_regs)
+{
+ clobbers.safe_push (gen_rtx_REG (CCmode, CC_REGNUM));
+ SET_HARD_REG_BIT (clobbered_regs, CC_REGNUM);
+ if (TARGET_FPU)
+ {
+ clobbers.safe_push (gen_rtx_REG (CCmode, FCC_REGNUM));
+ SET_HARD_REG_BIT (clobbered_regs, FCC_REGNUM);
+ }
+ return NULL;
+}
+
/* Worker function for TARGET_TRAMPOLINE_INIT.
trampoline - how should i do it in separate i+d ?
;; End of hard registers
(FIRST_PSEUDO_REGISTER 18)
- ;; Branch offset limits, as byte offsets from instruction address
+ ;; Branch offset limits, as byte offsets from (pc). That is NOT
+ ;; the same thing as "instruction address" -- it is for backward
+ ;; branches, but for forward branches it refers to the address
+ ;; following the instruction. So the max forward distance
+ ;; matches what the processor handbook says, while the max
+ ;; backward branch is 2 less than the book.
(MIN_BRANCH -254)
- (MAX_BRANCH 256)
- (MIN_SOB -126)
+ (MAX_BRANCH 254)
+ (MIN_SOB -124)
(MAX_SOB 0)])
;; DF is 64 bit
;; length default is 2 bytes each
(define_attr "length" "" (const_int 2))
+;; instruction base cost (not counting operands)
+(define_attr "base_cost" "" (const_int 2))
+
;; a user's asm statement
(define_asm_attributes
[(set_attr "type" "unknown")
(define_insn "*rts"
[(return)]
""
- "rts pc")
+ "rts\tpc")
(define_insn "blockage"
[(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)]
return \"{cmpd|cmpf}\t%0,%1\";
}"
[(set_attr "length" "2,2,4,4")
+ (set_attr "base_cost" "4")
(set_attr "type" "fp")])
;; Copy floating point processor condition code register to main CPU
cmp<PDPint:isfx>\t%0,%1"
[(set_attr "length" "2,2,4,4,4,6")])
-;; sob instruction - FIXME: this doesn't do anything, need to use doloop_end.
-
-(define_insn ""
+;; sob instruction
+;;
+;; Do a define_expand because some alternatives clobber CC.
+;; Some don't, but it isn't all that interesting to cover that case.
+(define_insn_and_split "doloop_end"
[(set (pc)
(if_then_else
- (ne (plus:HI (match_operand:HI 0 "register_operand" "+r")
- (const_int -1))
- (const_int 0))
+ (ne (match_operand:HI 0 "nonimmediate_operand" "+r,!m")
+ (const_int 1))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0)
(const_int -1)))]
"TARGET_40_PLUS"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (pc)
+ (if_then_else
+ (ne (match_dup 0) (const_int 1))
+ (label_ref (match_dup 1))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC CC_REGNUM))])]
+ "")
+
+;; Note that there is a memory alternative here. This is as documented
+;; in gccint, which says that doloop_end, since it has both a jump and
+;; an output interrupt "must handle its own reloads". That translates
+;; to: must accept memory operands as valid though they may be deprecated.
+(define_insn "doloop_end_nocc"
+ [(set (pc)
+ (if_then_else
+ (ne (match_operand:HI 0 "nonimmediate_operand" "+r,!m")
+ (const_int 1))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:HI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC CC_REGNUM))]
+ "TARGET_40_PLUS && reload_completed"
"*
{
+ rtx lb[1];
+
if (get_attr_length (insn) == 2)
return \"sob\t%0,%l1\";
/* emulate sob */
- operands[2] = gen_label_rtx ();
+ lb[0] = gen_label_rtx ();
output_asm_insn (\"dec\t%0\", operands);
- output_asm_insn (\"beq\t%l2\", operands);
+ output_asm_insn (\"beq\t%l0\", lb);
output_asm_insn (\"jmp\t%l1\", operands);
- output_asm_label (operands[2]);
+ output_asm_label (lb[0]);
fputs (\":\\n\", asm_out_file);
return \"\";
}"
- [(set (attr "length") (if_then_else (ior (lt (minus (match_dup 0)
- (pc))
- (const_int MIN_SOB))
- (gt (minus (match_dup 0)
- (pc))
- (const_int MAX_SOB)))
- (const_int 8)
- (const_int 2)))])
+ [(set (attr "length")
+ (if_then_else (eq (symbol_ref ("which_alternative")) (const_int 1))
+ (const_int 10)
+ (if_then_else (ior (lt (minus (match_dup 1) (pc))
+ (const_int MIN_SOB))
+ (gt (minus (match_dup 1) (pc))
+ (const_int MAX_SOB)))
+ (const_int 8)
+ (const_int 2))))])
;; These control RTL generation for conditional jump insns
;; and match them for register allocation.
\f
;; Move instructions
+;; "length" is defined even though this pattern won't appear at
+;; assembly language output time. But the length is used by
+;; pdp11_insn_cost, before the post-reload splitter adds the
+;; CC clobber to the insn.
(define_insn "movdi"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g")
(match_operand:DI 1 "general_operand" "rN,g"))]
""
- "")
+ ""
+ [(set_attr "length" "16,32")])
(define_insn "*movdi_nocc"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g")
(match_operand:SI 1 "general_operand" "rN,IJ,IJ,g"))]
""
- "")
+ ""
+ [(set_attr "length" "4,6,8,16")])
(define_insn "*movsi_nocc"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g")
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))]
""
- "")
+ ""
+ [(set_attr "length" "2,4,4,6")])
;; This splits all the integer moves: DI and SI modes as well as
;; the simple machine operations.
;; 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"))]
+ [(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q,a")
+ (match_operand:SF 1 "float_operand" "fRG,a,FQ,a,G"))]
"TARGET_FPU"
"#"
"&& reload_completed"
[(parallel [(set (match_dup 0)
(match_dup 1))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,2,4,4,2")])
(define_insn "*movsf<fcc_ccnz>"
[(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q,a")
[(set_attr "length" "2,4")])
\f
-;;- zero extension instructions
+;;- zero extension instruction
(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")))]
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rD,Q,&r,&r")
+ (zero_extend:HI (match_operand:QI 1 "general_operand" "0,0,rR,Q")))]
""
"#"
"reload_completed"
[(parallel [(set (match_dup 0) (zero_extend:HI (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ "{
+ rtx r;
+
+ if (!REG_P (operands[0]))
+ {
+ r = gen_rtx_MEM (QImode, operands[0]);
+ adjust_address (r, QImode, 1);
+ emit_move_insn (r, const0_rtx);
+ DONE;
+ }
+ else if (!rtx_equal_p (operands[0], operands[1]))
+ {
+ /* Alternatives 2 and 3 */
+ emit_move_insn (operands[0], const0_rtx);
+ r = gen_rtx_REG (QImode, REGNO (operands[0]));
+ emit_insn (gen_iorqi3_nocc (r, r, operands[1]));
+ DONE;
+ }
+ }"
+ [(set_attr "length" "4,4,4,6")])
(define_insn "*zero_extendqihi2<cc_cc>"
[(parallel [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
"bic\t%#0177400,%0"
[(set_attr "length" "4,6")])
-(define_expand "zero_extendhisi2"
- [(set (subreg:HI
- (match_dup 0)
- 2)
- (match_operand:HI 1 "register_operand" "r"))
- (set (subreg:HI
- (match_operand:SI 0 "register_operand" "=r")
- 0)
- (const_int 0))]
- ""
- "/* operands[1] = make_safe_from (operands[1], operands[0]); */")
-
-\f
;;- sign extension instructions
;; We sometimes end up doing a register to register extend,
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{ldcfd|movof}\t%1,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "6")])
;; movb sign extends if destination is a register
(define_insn_and_split "extendqihi2"
"reload_completed"
[(parallel [(set (match_dup 0) (sign_extend:HI (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
;; MOVB clears V
(define_insn "*extendqihi2<cc_cc>"
"&& reload_completed"
[(parallel [(set (match_dup 0) (sign_extend:SI (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "10,6,6")])
(define_insn "*extendhisi2_nocc"
[(set (match_operand:SI 0 "nonimmediate_operand" "=o,<,r")
"&& reload_completed"
[(parallel [(set (match_dup 0) (float:DF (match_dup 1)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "10,6,8")])
(define_insn "*floatsidf2<fcc_cc>"
[(set (match_operand:DF 0 "register_operand" "=a,a,a")
else
return \"setl\;{ldcld|movif}\t%1,%0\;seti\";
"
- [(set_attr "length" "10,6,8")])
+ [(set_attr "length" "10,6,8")
+ (set_attr "base_cost" "12")])
(define_insn_and_split "floathidf2"
[(set (match_operand:DF 0 "register_operand" "=a,a")
"&& reload_completed"
[(parallel [(set (match_dup 0) (float:DF (match_dup 1)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "*floathidf2<fcc_cc>"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{ldcid|movif}\t%1,%0"
- [(set_attr "length" "2,4")])
-
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "12")])
+
;; cut float to int
;; Note: the hardware treats register destination as
[(parallel [(set (match_dup 0) (fix:SI (fix:DF (match_dup 1))))
(clobber (reg:CC CC_REGNUM))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "10,6,8")])
;; Note: this clobbers both sets of condition codes!
(define_insn "*fix_truncdfsi2_nocc"
else
return \"setl\;{stcdl|movfi}\t%1,%0\;seti\";
"
- [(set_attr "length" "10,6,8")])
+ [(set_attr "length" "10,6,8")
+ (set_attr "base_cost" "12")])
(define_insn_and_split "fix_truncdfhi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
[(parallel [(set (match_dup 0) (fix:HI (fix:DF (match_dup 1))))
(clobber (reg:CC CC_REGNUM))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
;; Note: this clobbers both sets of condition codes!
(define_insn "*fix_truncdfhi2_nocc"
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{stcdi|movfi}\t%1,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "12")])
\f
;;- arithmetic instructions
[(parallel [(set (match_dup 0)
(plus:DF (match_dup 1) (match_dup 2)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
;; Float add sets V if overflow from add
(define_insn "*adddf3<fcc_ccnz>"
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{addd|addf}\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "6")])
(define_insn_and_split "adddi3"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
"reload_completed"
[(parallel [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "20,28,40,48")])
(define_insn "*adddi3_nocc"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
return \"\";
}"
- [(set_attr "length" "20,28,40,48")])
+ [(set_attr "length" "20,28,40,48")
+ (set_attr "base_cost" "0")])
;; Note that the register operand is not marked earlyclobber.
;; The reason is that SI values go in register pairs, so they
"reload_completed"
[(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "6,10,12,16")])
(define_insn "*addsi3_nocc"
[(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o")
return \"\";
}"
- [(set_attr "length" "6,10,12,16")])
+ [(set_attr "length" "6,10,12,16")
+ (set_attr "base_cost" "0")])
(define_insn_and_split "addhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q")
[(parallel [(set (match_dup 0)
(plus:HI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4,4,6")])
;; Add sets V if overflow from the add
(define_insn "*addhi3<cc_ccnz>"
[(parallel [(set (match_dup 0)
(minus:DF (match_dup 1) (match_dup 2)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "*subdf3<fcc_ccnz>"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{subd|subf}\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "6")])
(define_insn_and_split "subdi3"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
"reload_completed"
[(parallel [(set (match_dup 0) (minus:DI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "20,28,40,48")])
(define_insn "*subdi3_nocc"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
return \"\";
}"
- [(set_attr "length" "20,28,40,48")])
+ [(set_attr "length" "20,28,40,48")
+ (set_attr "base_cost" "0")])
(define_insn_and_split "subsi3"
[(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o")
"reload_completed"
[(parallel [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "6,10,12,16")])
(define_insn "*subsi3_nocc"
[(set (match_operand:SI 0 "nonimmediate_operand" "=&r,r,o,o")
return \"\";
}"
- [(set_attr "length" "6,10,12,16")])
+ [(set_attr "length" "6,10,12,16")
+ (set_attr "base_cost" "0")])
(define_insn_and_split "subhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q")
[(parallel [(set (match_dup 0)
(minus:HI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4,4,6")])
;; 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
operands[1] = GEN_INT (~INTVAL (op1));
else
operands[1] = expand_unop (<MODE>mode, one_cmpl_optab, op1, 0, 1);
-}")
+}"
+ [(set_attr "length" "2,4,4,6")])
(define_insn_and_split "*bic<mode>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
[(parallel [(set (match_dup 0)
(ior:PDPint (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4,4,6")])
(define_insn "ior<mode>3<cc_cc>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
[(parallel [(set (match_dup 0)
(xor:HI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "*xorhi3<cc_cc>"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
[(parallel [(set (match_dup 0)
(not:PDPint (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "*one_cmpl<mode>2<cc_cc>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
;; used to reduce the amount of very similar code.
;;
;; First the insns used for small constant shifts.
-(define_insn "<code><mode>_sc"
+(define_insn_and_split "<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>);"
+ "#"
+ "reload_completed"
+ [(parallel [(set (match_dup 0) (SHF:QHSint (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
[(set (attr "length")
(symbol_ref "pdp11_shift_length (operands, <QHSint:mname>,
- <CODE>, which_alternative == 0)"))])
+ <CODE>, which_alternative == 0)"))
+ (set_attr "base_cost" "0")])
+
+(define_insn "<code><mode>_sc<cc_ccnz>"
+ [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rD,Q")
+ (SHF:PDPint (match_operand:PDPint 1 "general_operand" "0,0")
+ (match_operand:HI 2 "expand_shift_operand" "O,O")))
+ (clobber (reg:CC CC_REGNUM))]
+ "reload_completed"
+ "* return pdp11_assemble_shift (operands, <PDPint:mname>, <CODE>);"
+ [(set (attr "length")
+ (symbol_ref "pdp11_shift_length (operands, <PDPint:mname>,
+ <CODE>, which_alternative == 0)"))
+ (set_attr "base_cost" "0")])
+
+;; This one comes only in clobber flavor.
+(define_insn "<code>si_sc_nocc"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=rD,Q")
+ (SHF:SI (match_operand:SI 1 "general_operand" "0,0")
+ (match_operand:HI 2 "expand_shift_operand" "O,O")))
+ (clobber (reg:CC CC_REGNUM))]
+ "reload_completed"
+ "* return pdp11_assemble_shift (operands, SImode, <CODE>);"
+ [(set (attr "length")
+ (symbol_ref "pdp11_shift_length (operands, SImode,
+ <CODE>, which_alternative == 0)"))
+ (set_attr "base_cost" "0")])
;; 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"
+(define_insn_and_split "<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))]
""
+ "#"
+ "reload_completed"
+ [(parallel [(set (match_dup 0) (SHF:QHSint (match_dup 1) (match_dup 2)))
+ (clobber (match_dup 2))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
+ [(set (attr "length")
+ (symbol_ref "pdp11_shift_length (operands, <QHSint:mname>,
+ <CODE>, which_alternative == 0)"))
+ (set_attr "base_cost" "0")])
+
+(define_insn "<code><mode>_base_nocc"
+ [(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))
+ (clobber (reg:CC CC_REGNUM))]
+ "reload_completed"
"* return pdp11_assemble_shift (operands, <QHSint:mname>, <CODE>);"
[(set (attr "length")
(symbol_ref "pdp11_shift_length (operands, <QHSint:mname>,
- <CODE>, which_alternative == 0)"))])
+ <CODE>, which_alternative == 0)"))
+ (set_attr "base_cost" "0")])
;; 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"
+(define_insn_and_split "aslhi_op"
[(set (match_operand:HI 0 "nonimmediate_operand" "=r,r")
(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
- (match_operand:HI 2 "general_operand" "rR,Q")))]
+ (match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (match_dup 0)
+ (ashift:HI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
+
+(define_insn "aslhi_op<cc_ccnz>"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r")
+ (ashift:HI (match_operand:HI 1 "general_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "rR,Qi")))
+ (clobber (reg:CC CC_REGNUM))]
+ "TARGET_40_PLUS && reload_completed"
"ash\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
-(define_insn "aslsi_op"
+(define_insn_and_split "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")))]
+ (match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (match_dup 0)
+ (ashift:SI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
+
+(define_insn "aslsi_op_<cc_ccnz>"
+ [(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,Qi")))
+ (clobber (reg:CC CC_REGNUM))]
+ "TARGET_40_PLUS && reload_completed"
"ashc\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
;; Now the expanders that produce the insns defined above.
(define_expand "ashl<mode>3"
"&& reload_completed"
[(parallel [(set (match_dup 0) (abs:DF (match_dup 1)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "absdf2<fcc_cc>"
[(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q")
"&& reload_completed"
[(parallel [(set (match_dup 0) (neg:DF (match_dup 1)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "negdf2<fcc_cc>"
[(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q")
"reload_completed"
[(parallel [(set (match_dup 0) (neg:DI (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "18,34")])
;; TODO: this can be neg/adc/neg/adc... I believe. Check. Saves one word.
(define_insn "negdi2_nocc"
(neg:DI (match_operand:DI 1 "general_operand" "0,0")))
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
-{
- rtx exops[4][2];
+ {
+ rtx exops[4][2];
+
+ pdp11_expand_operands (operands, exops, 1, NULL, either);
- pdp11_expand_operands (operands, exops, 1, NULL, either);
-
- output_asm_insn (\"com\t%0\", exops[3]);
- output_asm_insn (\"com\t%0\", exops[2]);
- output_asm_insn (\"com\t%0\", exops[1]);
- output_asm_insn (\"com\t%0\", exops[0]);
- output_asm_insn (\"add\t%#1,%0\", exops[3]);
- output_asm_insn (\"adc\t%0\", exops[2]);
- output_asm_insn (\"adc\t%0\", exops[1]);
- output_asm_insn (\"adc\t%0\", exops[0]);
-
- return \"\";
-}
-[(set_attr "length" "18,34")])
+ output_asm_insn (\"com\t%0\", exops[3]);
+ output_asm_insn (\"com\t%0\", exops[2]);
+ output_asm_insn (\"com\t%0\", exops[1]);
+ output_asm_insn (\"com\t%0\", exops[0]);
+ output_asm_insn (\"add\t%#1,%0\", exops[3]);
+ output_asm_insn (\"adc\t%0\", exops[2]);
+ output_asm_insn (\"adc\t%0\", exops[1]);
+ output_asm_insn (\"adc\t%0\", exops[0]);
+
+ return \"\";
+ }
+ [(set_attr "length" "18,34")
+ (set_attr "base_cost" "0")])
(define_insn_and_split "negsi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,o")
"reload_completed"
[(parallel [(set (match_dup 0) (neg:SI (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "10,18")])
;; TODO: this can be neg/adc/neg/adc... I believe. Check. Saves one word.
(define_insn "negsi2_nocc"
(neg:SI (match_operand:SI 1 "general_operand" "0,0")))
(clobber (reg:CC CC_REGNUM))]
"reload_completed"
-{
- rtx exops[2][2];
+ {
+ rtx exops[2][2];
+
+ pdp11_expand_operands (operands, exops, 1, NULL, either);
- pdp11_expand_operands (operands, exops, 1, NULL, either);
-
- output_asm_insn (\"com\t%0\", exops[1]);
- output_asm_insn (\"com\t%0\", exops[0]);
- output_asm_insn (\"add\t%#1,%0\", exops[1]);
- output_asm_insn (\"adc\t%0\", exops[0]);
-
- return \"\";
-}
-[(set_attr "length" "10,18")])
+ output_asm_insn (\"com\t%0\", exops[1]);
+ output_asm_insn (\"com\t%0\", exops[0]);
+ output_asm_insn (\"add\t%#1,%0\", exops[1]);
+ output_asm_insn (\"adc\t%0\", exops[0]);
+
+ return \"\";
+ }
+ [(set_attr "length" "10,18")
+ (set_attr "base_cost" "0")])
(define_insn_and_split "neg<mode>2"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
"reload_completed"
[(parallel [(set (match_dup 0) (neg:PDPint (match_dup 1)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "neg<mode>2<cc_ccnz>"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
(define_insn "indirect_jump"
[(set (pc) (match_operand:HI 0 "general_operand" "r"))]
""
- "jmp @%0"
+ "jmp\t@%0"
[(set_attr "length" "2")])
;;- jump to subroutine
(match_operand:HI 1 "general_operand" "g,g"))]
;;- Don't use operand 1 for most machines.
""
- "jsr pc,%0"
+ "jsr\tpc,%0"
[(set_attr "length" "2,4")])
;;- jump to subroutine
(match_operand:HI 2 "general_operand" "g,g")))]
;;- Don't use operand 2 for most machines.
""
- "jsr pc,%1"
+ "jsr\tpc,%1"
[(set_attr "length" "2,4")])
(define_expand "untyped_call"
"&& reload_completed"
[(parallel [(set (match_dup 0) (mult:DF (match_dup 1) (match_dup 2)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "muldf3<fcc_ccnz>"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{muld|mulf}\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "20")])
;; 16 bit result multiply. This uses odd numbered registers.
"&& reload_completed"
[(parallel [(set (match_dup 0) (mult:HI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "mulhi3<cc_cc>"
[(set (match_operand:HI 0 "register_operand" "=d,d")
(clobber (reg:CC CC_REGNUM))]
"TARGET_40_PLUS && reload_completed"
"mul\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "20")])
;; 32 bit result from 16 bit operands
(define_insn_and_split "mulhisi3"
(mult:SI (sign_extend:SI (match_dup 1))
(sign_extend:SI (match_dup 2))))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "mulhisi3<cc_cc>"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(clobber (reg:CC CC_REGNUM))]
"TARGET_40_PLUS && reload_completed"
"mul\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "20")])
;;- divide
(define_insn_and_split "divdf3"
"&& reload_completed"
[(parallel [(set (match_dup 0) (div:DF (match_dup 1) (match_dup 2)))
(clobber (reg:CC FCC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
(define_insn "divdf3<fcc_ccnz>"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(clobber (reg:CC FCC_REGNUM))]
"TARGET_FPU && reload_completed"
"{divd|divf}\t%2,%0"
- [(set_attr "length" "2,4")])
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "20")])
(define_expand "divmodhi4"
[(parallel
(set (subreg:HI (match_dup 1) 2)
(mod:HI (match_dup 1) (match_dup 2)))
(clobber (reg:CC CC_REGNUM))])]
- "")
+ ""
+ [(set_attr "length" "2,4")])
;; Note that there is no corresponding CC setter pattern.
;; The reason is that it won't be generated, because
(clobber (reg:CC CC_REGNUM))]
"TARGET_40_PLUS"
"div\t%2,%0"
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "40")])
+
+;; Byte swap
+(define_insn_and_split "bswaphi2"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
+ (bswap:HI (match_operand:HI 1 "general_operand" "0,0")))]
+ ""
+ "#"
+ "reload_completed"
+ [(parallel [(set (match_dup 0) (bswap:HI (match_dup 1)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
[(set_attr "length" "2,4")])
+
+(define_insn "bswaphi2<cc_ccnz>"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
+ (bswap:HI (match_operand:HI 1 "general_operand" "0,0")))
+ (clobber (reg:CC CC_REGNUM))]
+ ""
+ "swab\t%0"
+ [(set_attr "length" "2,4")])
+
+(define_insn_and_split "bswapsi2"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (bswap:SI (match_operand:SI 1 "general_operand" "g")))]
+ ""
+ "#"
+ "reload_completed"
+ [(parallel [(set (match_dup 0)
+ (bswap:SI (match_dup 1)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
+ [(set_attr "length" "10")])
+
+(define_insn "bswapsi2_nocc"
+ [(set (match_operand:SI 0 "register_operand" "=&r,&r,&r")
+ (bswap:SI (match_operand:SI 1 "general_operand" "r,D,Q")))
+ (clobber (reg:CC CC_REGNUM))]
+ ""
+ {
+ rtx exops[2][2];
+ rtx t;
+
+ pdp11_expand_operands (operands, exops, 2, NULL, either);
+
+ t = exops[0][0];
+ exops[0][0] = exops[1][0];
+ exops[1][0] = t;
+
+ output_asm_insn ("mov\t%0,%1", exops[0]);
+ output_asm_insn ("mov\t%0,%1", exops[1]);
+ output_asm_insn ("swab\t%0", exops[0]);
+ output_asm_insn ("swab\t%0", exops[1]);
+ return "";
+ }
+ [(set_attr "length" "8,10,12")])
+
+(define_expand "rotrhi3"
+ [(match_operand:HI 0 "register_operand" "")
+ (match_operand:HI 1 "register_operand" "")
+ (match_operand:HI 2 "general_operand" "")]
+ "TARGET_40_PLUS"
+ "
+{
+ operands[2] = negate_rtx (HImode, operands[2]);
+ emit_insn (gen_rotlhi3 (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn_and_split "rotlhi3"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (rotate:HI (match_operand:HI 1 "register_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "rR,Qi")))]
+ "TARGET_40_PLUS"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (match_dup 0)
+ (rotate:HI (match_dup 1) (match_dup 2)))
+ (clobber (reg:CC CC_REGNUM))])]
+ ""
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
+
+(define_insn "rotlhi3<cc_ccnz>"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (rotate: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"
+ "ashc\t%2,%0"
+ [(set_attr "length" "2,4")
+ (set_attr "base_cost" "8")])
+
+
+
+;; Some peephole optimizations
+
+;; Move then conditional branch on the result of the move is handled
+;; by compare elimination, but an earlier pass sometimes changes the
+;; compare operand to the move input, and then the compare is not
+;; eliminated. Do so here.
+(define_peephole2
+ [(parallel [(set (match_operand:PDPint 0 "nonimmediate_operand" "")
+ (match_operand:PDPint 1 "general_operand" ""))
+ (clobber (reg:CC CC_REGNUM))])
+ (set (reg:CC CC_REGNUM) (compare:CC (match_dup 1) (const_int 0)))]
+ ""
+ [(parallel [(set (reg:CC CC_REGNUM) (compare:CC (match_dup 1) (const_int 0)))
+ (set (match_dup 0) (match_dup 1))])]
+ "")