rt_gccstream/gcc/config/pdp11/pdp11.md

1475 lines
40 KiB
Markdown
Raw Permalink Normal View History

;;- Machine description for the pdp11 for GNU C compiler
;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2004, 2005
;; 2007, 2008 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
;; <http://www.gnu.org/licenses/>.
;; Match CONST_DOUBLE zero for tstd/tstf.
(define_predicate "register_or_const0_operand"
(ior (match_operand 0 "register_operand")
(match_test "op == CONST0_RTX (GET_MODE (op))")))
;; HI is 16 bit
;; QI is 8 bit
;;- 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 1 word each
(define_attr "length" "" (const_int 1))
;; a user's asm statement
(define_asm_attributes
[(set_attr "type" "unknown")
; all bets are off how long it is - make it 256, forces long jumps
; whenever jumping around it !!!
(set_attr "length" "256")])
;; 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,Q,F")
(match_operand:DF 1 "register_or_const0_operand" "G,a,G,a,a")))]
"TARGET_FPU"
"*
{
cc_status.flags = CC_IN_FPU;
if (which_alternative == 0 || which_alternative == 2)
return \"{tstd|tstf} %0, %1\;cfcc\";
else
return \"{cmpd|cmpf} %0, %1\;cfcc\";
}"
[(set_attr "length" "2,2,3,3,6")])
(define_insn "*cmphi"
[(set (cc0)
(compare (match_operand:HI 0 "general_operand" "rR,rR,rR,Q,Qi,Qi")
(match_operand:HI 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" "1,1,2,2,2,3")])
(define_insn "*cmpqi"
[(set (cc0)
(compare (match_operand:QI 0 "general_operand" "rR,rR,rR,Q,Qi,Qi")
(match_operand:QI 1 "general_operand" "N,rR,Qi,N,rR,Qi")))]
""
"@
tstb %0
cmpb %0,%1
cmpb %0,%1
tstb %0
cmpb %0,%1
cmpb %0,%1"
[(set_attr "length" "1,1,2,2,2,3")])
;; 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) == 1)
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 (le (minus (match_dup 0)
(pc))
(const_int -256))
(ge (minus (match_dup 0)
(pc))
(const_int 0)))
(const_int 4)
(const_int 1)))])
;; 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 "general_operand")))
(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
""
"")
(define_expand "cbranchhi4"
[(set (cc0)
(compare (match_operand:HI 1 "general_operand")
(match_operand:HI 2 "general_operand")))
(set (pc)
(if_then_else (match_operator 0 "ordered_comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
""
"")
(define_expand "cbranchqi4"
[(set (cc0)
(compare (match_operand:QI 1 "general_operand")
(match_operand:QI 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 (le (minus (match_dup 1)
(pc))
(const_int -128))
(ge (minus (match_dup 1)
(pc))
(const_int 128)))
(const_int 3)
(const_int 1)))])
;; 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 (le (minus (match_dup 1)
(pc))
(const_int -128))
(ge (minus (match_dup 1)
(pc))
(const_int 128)))
(const_int 3)
(const_int 1)))])
;; Move instructions
(define_insn "movdi"
[(set (match_operand:DI 0 "general_operand" "=g,rm,o")
(match_operand:DI 1 "general_operand" "m,r,a"))]
""
"* return output_move_quad (operands);"
;; what's the mose expensive code - say twice movsi = 16
[(set_attr "length" "16,16,16")])
(define_insn "movsi"
[(set (match_operand:SI 0 "general_operand" "=r,r,r,rm,m")
(match_operand:SI 1 "general_operand" "rN,IJ,K,m,r"))]
""
"* return output_move_double (operands);"
;; what's the most expensive code ? - I think 8!
;; we could split it up and make several sub-cases...
[(set_attr "length" "2,3,4,8,8")])
(define_insn "movhi"
[(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
(match_operand:HI 1 "general_operand" "rRN,Qi,rRN,Qi"))]
""
"*
{
if (operands[1] == const0_rtx)
return \"clr %0\";
return \"mov %1, %0\";
}"
[(set_attr "length" "1,2,2,3")])
(define_insn "movqi"
[(set (match_operand:QI 0 "nonimmediate_operand" "=g")
(match_operand:QI 1 "general_operand" "g"))]
""
"*
{
if (operands[1] == const0_rtx)
return \"clrb %0\";
return \"movb %1, %0\";
}"
[(set_attr "length" "1")])
;; do we have to supply all these moves? e.g. to
;; NO_LOAD_FPU_REGs ?
(define_insn "movdf"
[(set (match_operand:DF 0 "general_operand" "=a,fR,a,Q,m")
(match_operand:DF 1 "general_operand" "fFR,a,Q,a,m"))]
""
"* if (which_alternative ==0)
return \"ldd %1, %0\";
else if (which_alternative == 1)
return \"std %1, %0\";
else
return output_move_quad (operands); "
;; just a guess..
[(set_attr "length" "1,1,5,5,16")])
(define_insn "movsf"
[(set (match_operand:SF 0 "general_operand" "=g,r,g")
(match_operand:SF 1 "general_operand" "r,rmF,g"))]
"TARGET_FPU"
"* return output_move_double (operands);"
[(set_attr "length" "8,8,8")])
;; 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 "arith_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 "" ; "movmemhi"
[(set (mem:BLK (match_operand:HI 0 "general_operand" "=r,r"))
(mem:BLK (match_operand:HI 1 "general_operand" "r,r")))
(use (match_operand:HI 2 "arith_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" "40")])
;;- truncation instructions
(define_insn "truncdfsf2"
[(set (match_operand:SF 0 "general_operand" "=r,R,Q")
(float_truncate:SF (match_operand:DF 1 "register_operand" "a,a,a")))]
"TARGET_FPU"
"* if (which_alternative ==0)
{
output_asm_insn(\"{stcdf|movfo} %1, -(sp)\", 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 \"{stcdf|movfo} %1, %0\";
else
return \"{stcdf|movfo} %1, %0\";
"
[(set_attr "length" "3,1,2")])
(define_expand "truncsihi2"
[(set (match_operand:HI 0 "general_operand" "=g")
(subreg:HI
(match_operand:SI 1 "general_operand" "or")
0))]
""
"")
;;- zero extension instructions
(define_insn "zero_extendqihi2"
[(set (match_operand:HI 0 "general_operand" "=r")
(zero_extend:HI (match_operand:QI 1 "general_operand" "0")))]
""
"bic $0177400, %0"
[(set_attr "length" "2")])
(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" "=a,a,a")
(float_extend:DF (match_operand:SF 1 "general_operand" "r,R,Q")))]
"TARGET_FPU"
"@
mov %1, -(sp)\;{ldcfd|movof} (sp)+,%0
{ldcfd|movof} %1, %0
{ldcfd|movof} %1, %0"
[(set_attr "length" "2,1,2")])
;; 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" "1,2")])
(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" "2,3")])
;; 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 "general_operand" "=g")
(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))]
""
"")
(define_insn "" ; "extendhisi2"
[(set (match_operand:SI 0 "general_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" "5,3,3")])
(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" "6")])
;; 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" "5,3,4")])
(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" "1,2")])
;; cut float to int
(define_insn "fix_truncdfsi2"
[(set (match_operand:SI 0 "general_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" "5,3,4")])
(define_insn "fix_truncdfhi2"
[(set (match_operand:HI 0 "general_operand" "=rR,Q")
(fix:HI (fix:DF (match_operand:DF 1 "register_operand" "a,a"))))]
"TARGET_FPU"
"{stcdi|movfi} %1, %0"
[(set_attr "length" "1,2")])
;;- arithmetic instructions
;;- add instructions
(define_insn "adddf3"
[(set (match_operand:DF 0 "register_operand" "=a,a,a")
(plus:DF (match_operand:DF 1 "register_operand" "%0,0,0")
(match_operand:DF 2 "general_operand" "fR,Q,F")))]
"TARGET_FPU"
"{addd|addf} %2, %0"
[(set_attr "length" "1,2,5")])
(define_insn "addsi3"
[(set (match_operand:SI 0 "general_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")))]
""
"*
{ /* Here we trust that operands don't overlap
or is lateoperands the low word?? - looks like it! */
rtx lateoperands[3];
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);
if (! CONSTANT_P(operands[2]))
{
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 \"\";
}
lateoperands[2] = GEN_INT ((INTVAL (operands[2]) >> 16) & 0xffff);
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
if (INTVAL(operands[2]))
{
output_asm_insn (\"add %2, %0\", operands);
output_asm_insn (\"adc %0\", lateoperands);
}
if (INTVAL(lateoperands[2]))
output_asm_insn (\"add %2, %0\", lateoperands);
return \"\";
}"
[(set_attr "length" "3,5,6,8,3,1,5,5,3,8")])
(define_insn "addhi3"
[(set (match_operand:HI 0 "general_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" "1,2,2,3")])
(define_insn "addqi3"
[(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
(plus:QI (match_operand:QI 1 "general_operand" "%0,0,0,0")
(match_operand:QI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT)
{
if (INTVAL(operands[2]) == 1)
return \"incb %0\";
else if (INTVAL(operands[2]) == -1)
return \"decb %0\";
}
return \"add %2, %0\";
}"
[(set_attr "length" "1,2,2,3")])
;;- 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" "1,2")])
(define_insn "subsi3"
[(set (match_operand:SI 0 "general_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")))]
""
"*
{ /* Here we trust that operands don't overlap
or is lateoperands the low word?? - looks like it! */
rtx lateoperands[3];
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);
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 (\"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" "3,5,6,8")])
(define_insn "subhi3"
[(set (match_operand:HI 0 "general_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" "1,2,2,3")])
(define_insn "subqi3"
[(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
(minus:QI (match_operand:QI 1 "general_operand" "0,0,0,0")
(match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi")))]
""
"*
{
gcc_assert (GET_CODE (operands[2]) != CONST_INT);
return \"sub %2, %0\";
}"
[(set_attr "length" "1,2,2,3")])
;;;;- and instructions
;; Bit-and on the pdp (like on the VAX) is done with a clear-bits insn.
(define_insn "andsi3"
[(set (match_operand:SI 0 "general_operand" "=r,r,o,o,r,r,r,o,o,o")
(and:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0")
(not:SI (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K"))))]
""
"*
{ /* Here we trust that operands don't overlap
or is lateoperands the low word?? - looks like it! */
rtx lateoperands[3];
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);
if (! CONSTANT_P(operands[2]))
{
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 (\"bic %2, %0\", operands);
output_asm_insn (\"bic %2, %0\", lateoperands);
return \"\";
}
lateoperands[2] = GEN_INT ((INTVAL (operands[2]) >> 16) & 0xffff);
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
/* these have different lengths, so we should have
different constraints! */
if (INTVAL(operands[2]))
output_asm_insn (\"bic %2, %0\", operands);
if (INTVAL(lateoperands[2]))
output_asm_insn (\"bic %2, %0\", lateoperands);
return \"\";
}"
[(set_attr "length" "2,4,4,6,2,2,4,3,3,6")])
(define_insn "andhi3"
[(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
(and:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
(not:HI (match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi"))))]
""
"bic %2, %0"
[(set_attr "length" "1,2,2,3")])
(define_insn "andqi3"
[(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
(and:QI (match_operand:QI 1 "general_operand" "0,0,0,0")
(not:QI (match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi"))))]
""
"bicb %2, %0"
[(set_attr "length" "1,2,2,3")])
;;- Bit set (inclusive or) instructions
(define_insn "iorsi3"
[(set (match_operand:SI 0 "general_operand" "=r,r,o,o,r,r,r,o,o,o")
(ior: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")))]
""
"*
{ /* Here we trust that operands don't overlap
or is lateoperands the low word?? - looks like it! */
rtx lateoperands[3];
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);
if (! CONSTANT_P(operands[2]))
{
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 (\"bis %2, %0\", operands);
output_asm_insn (\"bis %2, %0\", lateoperands);
return \"\";
}
lateoperands[2] = GEN_INT ((INTVAL (operands[2]) >> 16) & 0xffff);
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
/* these have different lengths, so we should have
different constraints! */
if (INTVAL(operands[2]))
output_asm_insn (\"bis %2, %0\", operands);
if (INTVAL(lateoperands[2]))
output_asm_insn (\"bis %2, %0\", lateoperands);
return \"\";
}"
[(set_attr "length" "2,4,4,6,2,2,4,3,3,6")])
(define_insn "iorhi3"
[(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
(ior:HI (match_operand:HI 1 "general_operand" "%0,0,0,0")
(match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi")))]
""
"bis %2, %0"
[(set_attr "length" "1,2,2,3")])
(define_insn "iorqi3"
[(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
(ior:QI (match_operand:QI 1 "general_operand" "%0,0,0,0")
(match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi")))]
""
"bisb %2, %0")
;;- xor instructions
(define_insn "xorsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(xor:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "arith_operand" "r")))]
"TARGET_40_PLUS"
"*
{ /* Here we trust that operands don't overlap */
rtx lateoperands[3];
lateoperands[0] = operands[0];
operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
if (REG_P(operands[2]))
{
lateoperands[2] = operands[2];
operands[2] = gen_rtx_REG (HImode, REGNO (operands[2]) + 1);
output_asm_insn (\"xor %2, %0\", operands);
output_asm_insn (\"xor %2, %0\", lateoperands);
return \"\";
}
}"
[(set_attr "length" "2")])
(define_insn "xorhi3"
[(set (match_operand:HI 0 "general_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" "1,2")])
;;- one complement instructions
(define_insn "one_cmplhi2"
[(set (match_operand:HI 0 "general_operand" "=rR,Q")
(not:HI (match_operand:HI 1 "general_operand" "0,0")))]
""
"com %0"
[(set_attr "length" "1,2")])
(define_insn "one_cmplqi2"
[(set (match_operand:QI 0 "general_operand" "=rR,rR")
(not:QI (match_operand:QI 1 "general_operand" "0,g")))]
""
"@
comb %0
movb %1, %0\; comb %0"
[(set_attr "length" "1,2")])
;;- 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_45"
"ashc %2,%0"
[(set_attr "length" "1,2")])
;; 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 "general_operand" "=rR,Q")
(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int 1)))]
""
"asl %0"
[(set_attr "length" "1,2")])
;; 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 "general_operand" "=rR,Q")
(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int -1)))]
""
"asr %0"
[(set_attr "length" "1,2")])
;; lsr
(define_insn ""
[(set (match_operand:HI 0 "general_operand" "=rR,Q")
(lshiftrt:HI (match_operand:HI 1 "general_operand" "0,0")
(const_int 1)))]
""
"clc\;ror %0"
[(set_attr "length" "1,2")])
(define_insn "lshrsi3"
[(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" "5")])
;; 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 "general_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 4))])
;; aslb
(define_insn ""
[(set (match_operand:QI 0 "general_operand" "=r,o")
(ashift:QI (match_operand:QI 1 "general_operand" "0,0")
(match_operand:HI 2 "const_immediate_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 7)
(const_int 14)])])
;;; asr
;(define_insn ""
; [(set (match_operand:HI 0 "general_operand" "=rR,Q")
; (ashiftrt:HI (match_operand:HI 1 "general_operand" "0,0")
; (const_int 1)))]
; ""
; "asr %0"
; [(set_attr "length" "1,2")])
;; asrb
(define_insn ""
[(set (match_operand:QI 0 "general_operand" "=r,o")
(ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0")
(match_operand:HI 2 "const_immediate_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 7)
(const_int 14)])])
;; the following is invalid - too complex!!! - just say 14 !!!
; [(set (attr "length") (plus (and (match_dup 2)
; (const_int 7))
; (and (match_dup 2)
; (const_int 7))))])
;; 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")))]
""
"*
{
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" "1,2")])
;; 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]);
}")
;;;;- logical shift instructions
;;(define_insn "lshrsi3"
;; [(set (match_operand:HI 0 "register_operand" "=r")
;; (lshiftrt:HI (match_operand:HI 1 "register_operand" "0")
;; (match_operand:HI 2 "arith_operand" "rI")))]
;; ""
;; "srl %0,%2")
;; absolute
(define_insn "absdf2"
[(set (match_operand:DF 0 "general_operand" "=fR,Q")
(abs:DF (match_operand:DF 1 "general_operand" "0,0")))]
"TARGET_FPU"
"{absd|absf} %0"
[(set_attr "length" "1,2")])
(define_insn "abshi2"
[(set (match_operand:HI 0 "general_operand" "=r,o")
(abs:HI (match_operand:HI 1 "general_operand" "0,0")))]
"TARGET_ABSHI_BUILTIN"
"*
{
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" "3,5")])
;; 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 "general_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
(define_insn "negdf2"
[(set (match_operand:DF 0 "general_operand" "=fR,Q")
(neg:DF (match_operand:DF 1 "register_operand" "0,0")))]
"TARGET_FPU"
"{negd|negf} %0"
[(set_attr "length" "1,2")])
(define_insn "negsi2"
[(set (match_operand:SI 0 "register_operand" "=r")
(neg:SI (match_operand:SI 1 "general_operand" "0")))]
""
{
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 (\"com %0\", operands);
output_asm_insn (\"com %0\", lateoperands);
output_asm_insn (\"inc %0\", operands);
output_asm_insn (\"adc %0\", lateoperands);
return \"\";
}
[(set_attr "length" "5")])
(define_insn "neghi2"
[(set (match_operand:HI 0 "general_operand" "=rR,Q")
(neg:HI (match_operand:HI 1 "general_operand" "0,0")))]
""
"neg %0"
[(set_attr "length" "1,2")])
(define_insn "negqi2"
[(set (match_operand:QI 0 "general_operand" "=rR,Q")
(neg:QI (match_operand:QI 1 "general_operand" "0,0")))]
""
"negb %0"
[(set_attr "length" "1,2")])
;; Unconditional and other jump instructions
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"jmp %l0"
[(set_attr "length" "2")])
(define_insn ""
[(set (pc)
(label_ref (match_operand 0 "" "")))
(clobber (const_int 1))]
""
"jmp %l0"
[(set_attr "length" "2")])
(define_insn "tablejump"
[(set (pc) (match_operand:HI 0 "general_operand" "rR,Q"))
(use (label_ref (match_operand 1 "" "")))]
""
"jmp %0"
[(set_attr "length" "1,2")])
;; 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" "1,2")])
;;- 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" "1,2")])
;;- nop instruction
(define_insn "nop"
[(const_int 0)]
""
"nop")
;;- multiply
(define_insn "muldf3"
[(set (match_operand:DF 0 "register_operand" "=a,a,a")
(mult:DF (match_operand:DF 1 "register_operand" "%0,0,0")
(match_operand:DF 2 "general_operand" "fR,Q,F")))]
"TARGET_FPU"
"{muld|mulf} %2, %0"
[(set_attr "length" "1,2,5")])
;; 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 "general_operand" "rR,Qi")))]
"TARGET_45"
"mul %2, %0"
[(set_attr "length" "1,2")])
;; 32 bit result
(define_expand "mulhisi3"
[(set (match_dup 3)
(match_operand:HI 1 "general_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_45"
"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_45"
"mul %2, %0"
[(set_attr "length" "1,2")])
;(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_45"
; "mul %2, %0"
; [(set_attr "length" "1,2")])
;;- divide
(define_insn "divdf3"
[(set (match_operand:DF 0 "register_operand" "=a,a,a")
(div:DF (match_operand:DF 1 "register_operand" "0,0,0")
(match_operand:DF 2 "general_operand" "fR,Q,F")))]
"TARGET_FPU"
"{divd|divf} %2, %0"
[(set_attr "length" "1,2,5")])
(define_expand "divhi3"
[(set (subreg:HI (match_dup 1) 0)
(div:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))
(set (match_operand:HI 0 "general_operand" "=r")
(subreg:HI (match_dup 1) 0))]
"TARGET_45"
"")
(define_insn ""
[(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 0)
(div:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
"TARGET_45"
"div %2,%0"
[(set_attr "length" "2")])
(define_expand "modhi3"
[(set (subreg:HI (match_dup 1) 2)
(mod:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))
(set (match_operand:HI 0 "general_operand" "=r")
(subreg:HI (match_dup 1) 2))]
"TARGET_45"
"")
(define_insn ""
[(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 2)
(mod:HI (match_operand:SI 1 "general_operand" "0")
(match_operand:HI 2 "general_operand" "g")))]
"TARGET_45"
"div %2,%0"
[(set_attr "length" "2")])
;(define_expand "divmodhi4"
; [(parallel [(set (subreg:HI (match_dup 1) 0)
; (div:HI (match_operand:SI 1 "general_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 "general_operand" "=r")
; (subreg:HI (match_dup 1) 2))
; (set (match_operand:HI 0 "general_operand" "=r")
; (subreg:HI (match_dup 1) 0))]
; "TARGET_45"
; "")
;
;(define_insn ""
; [(set (subreg:HI (match_operand:SI 0 "general_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_45"
; "div %2, %0")
;
;; is rotate doing the right thing to be included here ????