;;- Machine description for the pdp11 for GNU C compiler
;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2004, 2005
;; 2007, 2008, 2010 Free Software Foundation, Inc.
;; Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at).
;; This file is part of GCC.
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; .
(include "predicates.md")
(include "constraints.md")
(define_constants
[
;; Register numbers
(R0_REGNUM 0)
(RETVAL_REGNUM 0)
(HARD_FRAME_POINTER_REGNUM 5)
(STACK_POINTER_REGNUM 6)
(PC_REGNUM 7)
(AC0_REGNUM 8)
(AC3_REGNUM 11)
(AC4_REGNUM 12)
(AC5_REGNUM 13)
;; The next two are not physical registers but are used for addressing
;; arguments.
(FRAME_POINTER_REGNUM 14)
(ARG_POINTER_REGNUM 15)
(FIRST_PSEUDO_REGISTER 16)
;; Branch offset limits, as byte offsets from instruction address
(MIN_BRANCH -254)
(MAX_BRANCH 256)
(MIN_SOB -126)
(MAX_SOB 0)])
;; HI is 16 bit
;; QI is 8 bit
;; Integer modes supported on the PDP11, with a mapping from machine mode
;; to mnemonic suffix. SImode and DImode always are special cases.
(define_mode_iterator PDPint [QI HI])
(define_mode_attr isfx [(QI "b") (HI "")])
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
;;- updates for most instructions.
;;- Operand classes for the register allocator:
;; Compare instructions.
;; currently we only support df floats, which saves us quite some
;; hassle switching the FP mode!
;; we assume that CPU is always in long float mode, and
;; 16 bit integer mode - currently, the prologue for main does this,
;; but maybe we should just set up a NEW crt0 properly,
;; -- and what about signal handling code?
;; (we don't even let sf floats in the register file, so
;; we only should have to worry about truncating and widening
;; when going to memory)
;; abort() call by g++ - must define libfunc for cmp_optab
;; and ucmp_optab for mode SImode, because we don't have that!!!
;; - yet since no libfunc is there, we abort ()
;; The only thing that remains to be done then is output
;; the floats in a way the assembler can handle it (and
;; if you're really into it, use a PDP11 float emulation
;; library to do floating point constant folding - but
;; I guess you'll get reasonable results even when not
;; doing this)
;; the last thing to do is fix the UPDATE_CC macro to check
;; for floating point condition codes, and set cc_status
;; properly, also setting the CC_IN_FCCR flag.
;; define attributes
;; currently type is only fpu or arith or unknown, maybe branch later ?
;; default is arith
(define_attr "type" "unknown,arith,fp" (const_string "arith"))
;; length default is 2 bytes each
(define_attr "length" "" (const_int 2))
;; a user's asm statement
(define_asm_attributes
[(set_attr "type" "unknown")
; length for asm is the max length per statement. That would be
; 3 words, for a two-operand instruction with extra word addressing
; modes for both operands.
(set_attr "length" "6")])
;; define function units
;; arithmetic - values here immediately when next insn issued
;; or does it mean the number of cycles after this insn was issued?
;; how do I say that fpu insns use cpu also? (pre-interaction phase)
;(define_function_unit "cpu" 1 1 (eq_attr "type" "arith") 0 0)
;(define_function_unit "fpu" 1 1 (eq_attr "type" "fp") 0 0)
;; compare
(define_insn "*cmpdf"
[(set (cc0)
(compare (match_operand:DF 0 "general_operand" "fR,fR,Q,QF")
(match_operand:DF 1 "register_or_const0_operand" "G,a,G,a")))]
"TARGET_FPU"
"*
{
cc_status.flags = CC_IN_FPU;
if (which_alternative == 0 || which_alternative == 2)
return \"{tstd|tstf} %0\;cfcc\";
else
return \"{cmpd|cmpf} %0, %1\;cfcc\";
}"
[(set_attr "length" "4,4,6,6")])
(define_insn "*cmp"
[(set (cc0)
(compare (match_operand:PDPint 0 "general_operand" "rR,rR,rR,Q,Qi,Qi")
(match_operand:PDPint 1 "general_operand" "N,rR,Qi,N,rR,Qi")))]
""
"@
tst %0
cmp %0,%1
cmp %0,%1
tst %0
cmp %0,%1
cmp %0,%1"
[(set_attr "length" "2,2,4,4,4,6")])
;; sob instruction - we need an assembler which can make this instruction
;; valid under _all_ circumstances!
(define_insn ""
[(set (pc)
(if_then_else
(ne (plus:HI (match_operand:HI 0 "register_operand" "+r")
(const_int -1))
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:HI (match_dup 0)
(const_int -1)))]
"TARGET_40_PLUS"
"*
{
static int labelcount = 0;
static char buf[1000];
if (get_attr_length (insn) == 2)
return \"sob %0, %l1\";
/* emulate sob */
output_asm_insn (\"dec %0\", operands);
sprintf (buf, \"bge LONG_SOB%d\", labelcount);
output_asm_insn (buf, NULL);
output_asm_insn (\"jmp %l1\", operands);
sprintf (buf, \"LONG_SOB%d:\", labelcount++);
output_asm_insn (buf, NULL);
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)))])
;; These control RTL generation for conditional jump insns
;; and match them for register allocation.
(define_expand "cbranchdf4"
[(set (cc0)
(compare (match_operand:DF 1 "general_operand")
(match_operand:DF 2 "register_or_const0_operand")))
(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"TARGET_FPU"
"")
(define_expand "cbranch4"
[(set (cc0)
(compare (match_operand:PDPint 1 "general_operand")
(match_operand:PDPint 2 "general_operand")))
(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
""
"")
;; problem with too short jump distance! we need an assembler which can
;; make this valid for all jump distances!
;; e.g. gas!
;; these must be changed to check for CC_IN_FCCR if float is to be
;; enabled
(define_insn "*branch"
[(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 1 "" ""))
(pc)))]
""
"* return output_jump(GET_CODE (operands[0]), 0, get_attr_length(insn));"
[(set (attr "length") (if_then_else (ior (lt (minus (match_dup 1)
(pc))
(const_int MIN_BRANCH))
(gt (minus (match_dup 1)
(pc))
(const_int MAX_BRANCH)))
(const_int 6)
(const_int 2)))])
;; These match inverted jump insns for register allocation.
(define_insn "*branch_inverted"
[(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(pc)
(label_ref (match_operand 1 "" ""))))]
""
"* return output_jump(GET_CODE (operands[0]), 1, get_attr_length(insn));"
[(set (attr "length") (if_then_else (ior (lt (minus (match_dup 1)
(pc))
(const_int MIN_BRANCH))
(gt (minus (match_dup 1)
(pc))
(const_int MAX_BRANCH)))
(const_int 6)
(const_int 2)))])
;; Move instructions
(define_insn "movdi"
[(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g")
(match_operand:DI 1 "general_operand" "rN,g"))]
""
"* return output_move_multiple (operands);"
;; what's the mose expensive code - say twice movsi = 16
[(set_attr "length" "16,32")])
(define_insn "movsi"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g")
(match_operand:SI 1 "general_operand" "rN,IJ,IJ,g"))]
""
"* 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")])
(define_insn "mov"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))]
""
"*
{
if (operands[1] == const0_rtx)
return \"clr %0\";
return \"mov %1, %0\";
}"
[(set_attr "length" "2,4,4,6")])
(define_insn "movdf"
[(set (match_operand:DF 0 "float_nonimm_operand" "=a,fR,a,Q,g")
(match_operand:DF 1 "float_operand" "fR,a,FQ,a,g"))]
"TARGET_FPU"
"* if (which_alternative ==0 || which_alternative == 2)
return \"ldd %1, %0\";
else if (which_alternative == 1 || which_alternative == 3)
return \"std %1, %0\";
else
return output_move_multiple (operands); "
;; last one is worst-case
[(set_attr "length" "2,2,4,4,24")])
(define_insn "movsf"
[(set (match_operand:SF 0 "float_nonimm_operand" "=a,fR,a,Q,g")
(match_operand:SF 1 "float_operand" "fR,a,FQ,a,g"))]
"TARGET_FPU"
"* if (which_alternative ==0 || which_alternative == 2)
return \"{ldcfd|movof} %1, %0\";
else if (which_alternative == 1 || which_alternative == 3)
return \"{stcdf|movfo} %1, %0\";
else
return output_move_multiple (operands); "
;; last one is worst-case
[(set_attr "length" "2,2,4,4,12")])
;; maybe fiddle a bit with move_ratio, then
;; let constraints only accept a register ...
(define_expand "movmemhi"
[(parallel [(set (match_operand:BLK 0 "general_operand" "=g,g")
(match_operand:BLK 1 "general_operand" "g,g"))
(use (match_operand:HI 2 "general_operand" "n,mr"))
(use (match_operand:HI 3 "immediate_operand" "i,i"))
(clobber (match_scratch:HI 4 "=&r,X"))
(clobber (match_dup 5))
(clobber (match_dup 6))
(clobber (match_dup 2))])]
"(TARGET_BCOPY_BUILTIN)"
"
{
operands[0]
= replace_equiv_address (operands[0],
copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
operands[1]
= replace_equiv_address (operands[1],
copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
operands[5] = XEXP (operands[0], 0);
operands[6] = XEXP (operands[1], 0);
}")
(define_insn "movmemhi1"
[(set (mem:BLK (match_operand:HI 0 "register_operand" "r,r"))
(mem:BLK (match_operand:HI 1 "register_operand" "r,r")))
(use (match_operand:HI 2 "general_operand" "n,r"))
(use (match_operand:HI 3 "immediate_operand" "i,i"))
(clobber (match_scratch:HI 4 "=&r,X"))
(clobber (match_dup 0))
(clobber (match_dup 1))
(clobber (match_dup 2))]
"(TARGET_BCOPY_BUILTIN)"
"* return output_block_move (operands);"
;;; just a guess
[(set_attr "length" "80")])
;;- truncation instructions
(define_insn "truncdfsf2"
[(set (match_operand:SF 0 "float_nonimm_operand" "=f,R,Q")
(float_truncate:SF (match_operand:DF 1 "register_operand" "f,a,a")))]
"TARGET_FPU"
"* if (which_alternative ==0)
{
return \"\";
}
else if (which_alternative == 1)
return \"{stcdf|movfo} %1, %0\";
else
return \"{stcdf|movfo} %1, %0\";
"
[(set_attr "length" "0,2,4")])
(define_expand "truncsihi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=g")
(subreg:HI
(match_operand:SI 1 "general_operand" "or")
0))]
""
"")
;;- zero extension instructions
(define_insn "zero_extendqihi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(zero_extend:HI (match_operand:QI 1 "general_operand" "0,0")))]
""
"bic $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]); */")
;;- sign extension instructions
(define_insn "extendsfdf2"
[(set (match_operand:DF 0 "register_operand" "=f,a,a")
(float_extend:DF (match_operand:SF 1 "float_operand" "f,R,Q")))]
"TARGET_FPU"
"@
/* nothing */
{ldcfd|movof} %1, %0
{ldcfd|movof} %1, %0"
[(set_attr "length" "0,2,4")])
;; does movb sign extend in register-to-register move?
(define_insn "extendqihi2"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(sign_extend:HI (match_operand:QI 1 "general_operand" "rR,Q")))]
""
"movb %1, %0"
[(set_attr "length" "2,4")])
(define_insn "extendqisi2"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(sign_extend:SI (match_operand:QI 1 "general_operand" "rR,Q")))]
"TARGET_40_PLUS"
"*
{
rtx latehalf[2];
/* make register pair available */
latehalf[0] = operands[0];
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0])+ 1);
output_asm_insn(\"movb %1, %0\", operands);
output_asm_insn(\"sxt %0\", latehalf);
return \"\";
}"
[(set_attr "length" "4,6")])
;; maybe we have to use define_expand to say that we have the instruction,
;; unconditionally, and then match dependent on CPU type:
(define_expand "extendhisi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=g")
(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))]
""
"")
(define_insn "" ; "extendhisi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=o,<,r")
(sign_extend:SI (match_operand:HI 1 "general_operand" "g,g,g")))]
"TARGET_40_PLUS"
"*
{
rtx latehalf[2];
/* we don't want to mess with auto increment */
switch (which_alternative)
{
case 0:
latehalf[0] = operands[0];
operands[0] = adjust_address(operands[0], HImode, 2);
output_asm_insn(\"mov %1, %0\", operands);
output_asm_insn(\"sxt %0\", latehalf);
return \"\";
case 1:
/* - auto-decrement - right direction ;-) */
output_asm_insn(\"mov %1, %0\", operands);
output_asm_insn(\"sxt %0\", operands);
return \"\";
case 2:
/* make register pair available */
latehalf[0] = operands[0];
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
output_asm_insn(\"mov %1, %0\", operands);
output_asm_insn(\"sxt %0\", latehalf);
return \"\";
default:
gcc_unreachable ();
}
}"
[(set_attr "length" "10,6,6")])
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:HI 1 "general_operand" "0")))]
"(! TARGET_40_PLUS)"
"*
{
static int count = 0;
char buf[100];
rtx lateoperands[2];
lateoperands[0] = operands[0];
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
output_asm_insn(\"tst %0\", operands);
sprintf(buf, \"bge extendhisi%d\", count);
output_asm_insn(buf, NULL);
output_asm_insn(\"mov -1, %0\", lateoperands);
sprintf(buf, \"bne extendhisi%d\", count+1);
output_asm_insn(buf, NULL);
sprintf(buf, \"\\nextendhisi%d:\", count);
output_asm_insn(buf, NULL);
output_asm_insn(\"clr %0\", lateoperands);
sprintf(buf, \"\\nextendhisi%d:\", count+1);
output_asm_insn(buf, NULL);
count += 2;
return \"\";
}"
[(set_attr "length" "12")])
;; make float to int and vice versa
;; using the cc_status.flag field we could probably cut down
;; on seti and setl
;; assume that we are normally in double and integer mode -
;; what do pdp library routines do to fpu mode ?
(define_insn "floatsidf2"
[(set (match_operand:DF 0 "register_operand" "=a,a,a")
(float:DF (match_operand:SI 1 "general_operand" "r,R,Q")))]
"TARGET_FPU"
"* if (which_alternative ==0)
{
rtx latehalf[2];
latehalf[0] = NULL;
latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
output_asm_insn(\"mov %1, -(sp)\", latehalf);
output_asm_insn(\"mov %1, -(sp)\", operands);
output_asm_insn(\"setl\", operands);
output_asm_insn(\"{ldcld|movif} (sp)+, %0\", operands);
output_asm_insn(\"seti\", operands);
return \"\";
}
else if (which_alternative == 1)
return \"setl\;{ldcld|movif} %1, %0\;seti\";
else
return \"setl\;{ldcld|movif} %1, %0\;seti\";
"
[(set_attr "length" "10,6,8")])
(define_insn "floathidf2"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(float:DF (match_operand:HI 1 "general_operand" "rR,Qi")))]
"TARGET_FPU"
"{ldcid|movif} %1, %0"
[(set_attr "length" "2,4")])
;; cut float to int
(define_insn "fix_truncdfsi2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=r,R,Q")
(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "a,a,a"))))]
"TARGET_FPU"
"* if (which_alternative ==0)
{
output_asm_insn(\"setl\", operands);
output_asm_insn(\"{stcdl|movfi} %1, -(sp)\", operands);
output_asm_insn(\"seti\", operands);
output_asm_insn(\"mov (sp)+, %0\", operands);
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
output_asm_insn(\"mov (sp)+, %0\", operands);
return \"\";
}
else if (which_alternative == 1)
return \"setl\;{stcdl|movfi} %1, %0\;seti\";
else
return \"setl\;{stcdl|movfi} %1, %0\;seti\";
"
[(set_attr "length" "10,6,8")])
(define_insn "fix_truncdfhi2"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(fix:HI (fix:DF (match_operand:DF 1 "register_operand" "a,a"))))]
"TARGET_FPU"
"{stcdi|movfi} %1, %0"
[(set_attr "length" "2,4")])
;;- arithmetic instructions
;;- add instructions
(define_insn "adddf3"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(plus:DF (match_operand:DF 1 "register_operand" "%0,0")
(match_operand:DF 2 "general_operand" "fR,QF")))]
"TARGET_FPU"
"{addd|addf} %2, %0"
[(set_attr "length" "2,4")])
(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")))]
""
"*
{
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 (\"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 (!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]);
}
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];
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]);
}
return \"\";
}"
[(set_attr "length" "6,10,12,16")])
(define_insn "addhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0")
(match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT)
{
if (INTVAL(operands[2]) == 1)
return \"inc %0\";
else if (INTVAL(operands[2]) == -1)
return \"dec %0\";
}
return \"add %2, %0\";
}"
[(set_attr "length" "2,4,4,6")])
;;- subtract instructions
;; we don't have to care for constant second
;; args, since they are canonical plus:xx now!
;; also for minus:DF ??
(define_insn "subdf3"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(minus:DF (match_operand:DF 1 "register_operand" "0,0")
(match_operand:DF 2 "general_operand" "fR,Q")))]
"TARGET_FPU"
"{subd|subf} %2, %0"
[(set_attr "length" "2,4")])
(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")))]
""
"*
{
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]);
}
return \"\";
}"
[(set_attr "length" "20,28,40,48")])
(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];
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]);
}
return \"\";
}"
[(set_attr "length" "6,10,12,16")])
(define_insn "subhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
(match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi")))]
""
"*
{
gcc_assert (GET_CODE (operands[2]) != CONST_INT);
return \"sub %2, %0\";
}"
[(set_attr "length" "2,4,4,6")])
;;;;- and instructions
;; Bit-and on the pdp (like on the VAX) is done with a clear-bits insn.
(define_expand "and3"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "")
(and:PDPint (not:PDPint (match_operand:PDPint 1 "general_operand" ""))
(match_operand:PDPint 2 "general_operand" "")))]
""
"
{
rtx op1 = operands[1];
/* If there is a constant argument, complement that one.
Similarly, if one of the inputs is the same as the output,
complement the other input. */
if ((CONST_INT_P (operands[2]) && ! CONST_INT_P (op1)) ||
rtx_equal_p (operands[0], operands[1]))
{
operands[1] = operands[2];
operands[2] = op1;
op1 = operands[1];
}
if (CONST_INT_P (op1))
operands[1] = GEN_INT (~INTVAL (op1));
else
operands[1] = expand_unop (mode, one_cmpl_optab, op1, 0, 1);
}")
(define_insn "*bic"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(and:PDPint
(not: PDPint (match_operand:PDPint 1 "general_operand" "rR,Qi,rR,Qi"))
(match_operand:PDPint 2 "general_operand" "0,0,0,0")))]
""
"bic %1, %0"
[(set_attr "length" "2,4,4,6")])
;;- Bit set (inclusive or) instructions
(define_insn "ior3"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
(ior:PDPint (match_operand:PDPint 1 "general_operand" "%0,0,0,0")
(match_operand:PDPint 2 "general_operand" "rR,Qi,rR,Qi")))]
""
"bis %2, %0"
[(set_attr "length" "2,4,4,6")])
;;- xor instructions
(define_insn "xorhi3"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(xor:HI (match_operand:HI 1 "general_operand" "%0,0")
(match_operand:HI 2 "register_operand" "r,r")))]
"TARGET_40_PLUS"
"xor %2, %0"
[(set_attr "length" "2,4")])
;;- one complement instructions
(define_insn "one_cmpl2"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
(not:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))]
""
"com %0"
[(set_attr "length" "2,4")])
;;- arithmetic shift instructions
(define_insn "ashlsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(ashift:SI (match_operand:SI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
"ashc %2,%0"
[(set_attr "length" "2,4")])
;; Arithmetic right shift on the pdp works by negating the shift count.
(define_expand "ashrsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
""
"
{
operands[2] = negate_rtx (HImode, operands[2]);
}")
;; define asl aslb asr asrb - ashc missing!
;; asl
(define_insn ""
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int 1)))]
""
"asl %0"
[(set_attr "length" "2,4")])
;; and another possibility for asr is << -1
;; might cause problems since -1 can also be encoded as 65535!
;; not in gcc2 ???
;; asr
(define_insn ""
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int -1)))]
""
"asr %0"
[(set_attr "length" "2,4")])
;; lsr
(define_insn "lsrhi1"
[(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
(lshiftrt:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int 1)))]
""
"clc\;ror %0"
[(set_attr "length" "2,4")])
(define_insn "lsrsi1"
[(set (match_operand:SI 0 "register_operand" "=r")
(lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
(const_int 1)))]
""
{
rtx lateoperands[2];
lateoperands[0] = operands[0];
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
lateoperands[1] = operands[1];
operands[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
output_asm_insn (\"clc\", operands);
output_asm_insn (\"ror %0\", lateoperands);
output_asm_insn (\"ror %0\", operands);
return \"\";
}
[(set_attr "length" "10")])
(define_expand "lshrsi3"
[(match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "")]
""
"
{
rtx r;
if (!TARGET_40_PLUS &&
(GET_CODE (operands[2]) != CONST_INT ||
(unsigned) INTVAL (operands[2]) > 3))
FAIL;
emit_insn (gen_lsrsi1 (operands[0], operands[1]));
if (GET_CODE (operands[2]) != CONST_INT)
{
r = gen_reg_rtx (HImode);
emit_insn (gen_addhi3 (r, operands [2], GEN_INT (-1)));
emit_insn (gen_ashrsi3 (operands[0], operands[0], r));
}
else if ((unsigned) INTVAL (operands[2]) != 1)
{
emit_insn (gen_ashlsi3 (operands[0], operands[0],
GEN_INT (1 - INTVAL (operands[2]))));
}
DONE;
}
"
)
;; shift is by arbitrary count is expensive,
;; shift by one cheap - so let's do that, if
;; space doesn't matter
(define_insn ""
[(set (match_operand:HI 0 "nonimmediate_operand" "=r")
(ashift:HI (match_operand:HI 1 "general_operand" "0")
(match_operand:HI 2 "expand_shift_operand" "O")))]
"! optimize_size"
"*
{
register int i;
for (i = 1; i <= abs(INTVAL(operands[2])); i++)
if (INTVAL(operands[2]) < 0)
output_asm_insn(\"asr %0\", operands);
else
output_asm_insn(\"asl %0\", operands);
return \"\";
}"
;; longest is 4
[(set (attr "length") (const_int 8))])
;; aslb
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,o")
(ashift:QI (match_operand:QI 1 "general_operand" "0,0")
(match_operand:HI 2 "const_int_operand" "n,n")))]
""
"*
{ /* allowing predec or post_inc is possible, but hairy! */
int i, cnt;
cnt = INTVAL(operands[2]) & 0x0007;
for (i=0 ; i < cnt ; i++)
output_asm_insn(\"aslb %0\", operands);
return \"\";
}"
;; set attribute length ( match_dup 2 & 7 ) *(1 or 2) !!!
[(set_attr_alternative "length"
[(const_int 14)
(const_int 28)])])
;;; asr
;(define_insn ""
; [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
; (ashiftrt:HI (match_operand:HI 1 "general_operand" "0,0")
; (const_int 1)))]
; ""
; "asr %0"
; [(set_attr "length" "2,4")])
;; asrb
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=r,o")
(ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0")
(match_operand:HI 2 "const_int_operand" "n,n")))]
""
"*
{ /* allowing predec or post_inc is possible, but hairy! */
int i, cnt;
cnt = INTVAL(operands[2]) & 0x0007;
for (i=0 ; i < cnt ; i++)
output_asm_insn(\"asrb %0\", operands);
return \"\";
}"
[(set_attr_alternative "length"
[(const_int 14)
(const_int 28)])])
;; the following is invalid - too complex!!! - just say 14 !!!
; [(set (attr "length") (plus (and (match_dup 2)
; (const_int 14))
; (and (match_dup 2)
; (const_int 14))))])
;; can we get +-1 in the next pattern? should
;; have been caught by previous patterns!
(define_insn "ashlhi3"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
"*
{
if (GET_CODE(operands[2]) == CONST_INT)
{
if (INTVAL(operands[2]) == 1)
return \"asl %0\";
else if (INTVAL(operands[2]) == -1)
return \"asr %0\";
}
return \"ash %2,%0\";
}"
[(set_attr "length" "2,4")])
;; Arithmetic right shift on the pdp works by negating the shift count.
(define_expand "ashrhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(ashift:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
""
"
{
operands[2] = negate_rtx (HImode, operands[2]);
}")
(define_expand "lshrhi3"
[(match_operand:HI 0 "register_operand" "")
(match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "general_operand" "")]
""
"
{
rtx r;
if (!TARGET_40_PLUS &&
(GET_CODE (operands[2]) != CONST_INT ||
(unsigned) INTVAL (operands[2]) > 3))
FAIL;
emit_insn (gen_lsrhi1 (operands[0], operands[1]));
if (GET_CODE (operands[2]) != CONST_INT)
{
r = gen_reg_rtx (HImode);
emit_insn (gen_addhi3 (r, operands [2], GEN_INT (-1)));
emit_insn (gen_ashrhi3 (operands[0], operands[0], r));
}
else if ((unsigned) INTVAL (operands[2]) != 1)
{
emit_insn (gen_ashlhi3 (operands[0], operands[0],
GEN_INT (1 - INTVAL (operands[2]))));
}
DONE;
}
"
)
;; absolute
(define_insn "absdf2"
[(set (match_operand:DF 0 "nonimmediate_operand" "=fR,Q")
(abs:DF (match_operand:DF 1 "general_operand" "0,0")))]
"TARGET_FPU"
"{absd|absf} %0"
[(set_attr "length" "2,4")])
;; negate insns
(define_insn "negdf2"
[(set (match_operand:DF 0 "float_nonimm_operand" "=fR,Q")
(neg:DF (match_operand:DF 1 "register_operand" "0,0")))]
"TARGET_FPU"
"{negd|negf} %0"
[(set_attr "length" "2,4")])
(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);
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]);
return \"\";
}
[(set_attr "length" "18,34")])
(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\", 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" "12,20")])
(define_insn "neg2"
[(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
(neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))]
""
"neg %0"
[(set_attr "length" "2,4")])
;; Unconditional and other jump instructions
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"*
{
if (get_attr_length (insn) == 2)
return \"br %l0\";
return \"jmp %l0\";
}"
[(set (attr "length") (if_then_else (ior (lt (minus (match_dup 0)
(pc))
(const_int MIN_BRANCH))
(gt (minus (match_dup 0)
(pc))
(const_int MAX_BRANCH)))
(const_int 4)
(const_int 2)))])
(define_insn ""
[(set (pc)
(label_ref (match_operand 0 "" "")))
(clobber (const_int 1))]
""
"jmp %l0"
[(set_attr "length" "4")])
(define_insn "tablejump"
[(set (pc) (match_operand:HI 0 "general_operand" "r,R,Q"))
(use (label_ref (match_operand 1 "" "")))]
""
"@
jmp (%0)
jmp %@%0
jmp %@%0"
[(set_attr "length" "2,2,4")])
;; indirect jump - let's be conservative!
;; allow only register_operand, even though we could also
;; allow labels etc.
(define_insn "indirect_jump"
[(set (pc) (match_operand:HI 0 "register_operand" "r"))]
""
"jmp (%0)")
;;- jump to subroutine
(define_insn "call"
[(call (match_operand:HI 0 "general_operand" "rR,Q")
(match_operand:HI 1 "general_operand" "g,g"))
;; (use (reg:HI 0)) what was that ???
]
;;- Don't use operand 1 for most machines.
""
"jsr pc, %0"
[(set_attr "length" "2,4")])
;;- jump to subroutine
(define_insn "call_value"
[(set (match_operand 0 "" "")
(call (match_operand:HI 1 "general_operand" "rR,Q")
(match_operand:HI 2 "general_operand" "g,g")))
;; (use (reg:HI 0)) - what was that ????
]
;;- Don't use operand 2 for most machines.
""
"jsr pc, %1"
[(set_attr "length" "2,4")])
;;- nop instruction
(define_insn "nop"
[(const_int 0)]
""
"nop")
;;- multiply
(define_insn "muldf3"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(mult:DF (match_operand:DF 1 "register_operand" "%0,0")
(match_operand:DF 2 "float_operand" "fR,QF")))]
"TARGET_FPU"
"{muld|mulf} %2, %0"
[(set_attr "length" "2,4")])
;; 16 bit result multiply:
;; currently we multiply only into odd registers, so we don't use two
;; registers - but this is a bit inefficient at times. If we define
;; a register class for each register, then we can specify properly
;; which register need which scratch register ....
(define_insn "mulhi3"
[(set (match_operand:HI 0 "register_operand" "=d,d") ; multiply regs
(mult:HI (match_operand:HI 1 "register_operand" "%0,0")
(match_operand:HI 2 "float_operand" "rR,Qi")))]
"TARGET_40_PLUS"
"mul %2, %0"
[(set_attr "length" "2,4")])
;; 32 bit result
(define_expand "mulhisi3"
[(set (match_dup 3)
(match_operand:HI 1 "nonimmediate_operand" "g,g"))
(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
(mult:SI (truncate:HI
(match_dup 0))
(match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
"operands[3] = gen_lowpart(HImode, operands[1]);")
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
(mult:SI (truncate:HI
(match_operand:SI 1 "register_operand" "%0,0"))
(match_operand:HI 2 "general_operand" "rR,Qi")))]
"TARGET_40_PLUS"
"mul %2, %0"
[(set_attr "length" "2,4")])
;(define_insn "mulhisi3"
; [(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
; (mult:SI (truncate:HI
; (match_operand:SI 1 "register_operand" "%0,0"))
; (match_operand:HI 2 "general_operand" "rR,Qi")))]
; "TARGET_40_PLUS"
; "mul %2, %0"
; [(set_attr "length" "2,4")])
;;- divide
(define_insn "divdf3"
[(set (match_operand:DF 0 "register_operand" "=a,a")
(div:DF (match_operand:DF 1 "register_operand" "0,0")
(match_operand:DF 2 "general_operand" "fR,QF")))]
"TARGET_FPU"
"{divd|divf} %2, %0"
[(set_attr "length" "2,4")])
(define_expand "divhi3"
[(set (subreg:HI (match_dup 1) 0)
(div:HI (match_operand:SI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "g")))
(set (match_operand:HI 0 "register_operand" "=r")
(subreg:HI (match_dup 1) 0))]
"TARGET_40_PLUS"
"")
(define_insn ""
[(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 0)
(div:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
"TARGET_40_PLUS"
"div %2,%0"
[(set_attr "length" "4")])
(define_expand "modhi3"
[(set (subreg:HI (match_dup 1) 2)
(mod:HI (match_operand:SI 1 "register_operand" "0")
(match_operand:HI 2 "general_operand" "g")))
(set (match_operand:HI 0 "register_operand" "=r")
(subreg:HI (match_dup 1) 2))]
"TARGET_40_PLUS"
"")
(define_insn ""
[(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 2)
(mod:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
"TARGET_40_PLUS"
"div %2,%0"
[(set_attr "length" "4")])
;(define_expand "divmodhi4"
; [(parallel [(set (subreg:HI (match_dup 1) 0)
; (div:HI (match_operand:SI 1 "register_operand" "0")
; (match_operand:HI 2 "general_operand" "g")))
; (set (subreg:HI (match_dup 1) 2)
; (mod:HI (match_dup 1)
; (match_dup 2)))])
; (set (match_operand:HI 3 "register_operand" "=r")
; (subreg:HI (match_dup 1) 2))
; (set (match_operand:HI 0 "register_operand" "=r")
; (subreg:HI (match_dup 1) 0))]
; "TARGET_40_PLUS"
; "")
;
;(define_insn ""
; [(set (subreg:HI (match_operand:SI 0 "register_operand" "=r") 0)
; (div:HI (match_operand:SI 1 "general_operand" "0")
; (match_operand:HI 2 "general_operand" "g")))
; (set (subreg:HI (match_dup 0) 2)
; (mod:HI (match_dup 1)
; (match_dup 2)))]
; "TARGET_40_PLUS"
; "div %2, %0")
;
;; is rotate doing the right thing to be included here ????