; GCC machine description for Matsushita MN10300 ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. ;; Contributed by Jeff Law (law@cygnus.com). ;; This file is part of GNU CC. ;; GNU CC 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 2, or (at your option) ;; any later version. ;; GNU CC 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 GNU CC; see the file COPYING. If not, write to ;; the Free Software Foundation, 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;; The original PO technology requires these to be ordered by speed, ;; so that assigner will pick the fastest. ;; See file "rtl.def" for documentation on define_insn, match_*, et. al. ;; Condition code settings. ;; none - insn does not affect cc ;; none_0hit - insn does not affect cc but it does modify operand 0 ;; This attribute is used to keep track of when operand 0 changes. ;; See the description of NOTICE_UPDATE_CC for more info. ;; set_znv - insn sets z,n,v to useable values; c is unusable. ;; set_zn - insn sets z,n to useable values; v,c are unuseable. ;; compare - compare instruction ;; invert -- like compare, but flags are inverted. ;; clobber - value of cc is unknown (define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber,invert" (const_string "clobber")) ;; ---------------------------------------------------------------------- ;; MOVE INSTRUCTIONS ;; ---------------------------------------------------------------------- ;; movqi (define_expand "movqi" [(set (match_operand:QI 0 "general_operand" "") (match_operand:QI 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand0, QImode) && !register_operand (operand1, QImode)) operands[1] = copy_to_mode_reg (QImode, operand1); }") (define_insn "" [(set (match_operand:QI 0 "general_operand" "=d,*a,d,*a,d,*a,d,*a,d,m") (match_operand:QI 1 "general_operand" "0,0,I,I,a,d,di,ia,m,d"))] "register_operand (operands[0], QImode) || register_operand (operands[1], QImode)" "* { switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %0\"; case 3: if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %1,%0\", xoperands); else output_asm_insn (\"mov %1,%0\", xoperands); return \"\"; } /* FALLTHROUGH */ case 4: case 5: case 6: case 7: return \"mov %1,%0\"; case 8: case 9: return \"movbu %1,%0\"; } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")]) ;; movhi (define_expand "movhi" [(set (match_operand:HI 0 "general_operand" "") (match_operand:HI 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand1, HImode) && !register_operand (operand0, HImode)) operands[1] = copy_to_mode_reg (HImode, operand1); }") (define_insn "" [(set (match_operand:HI 0 "general_operand" "=d,*a,d,*a,d,*a,d,*a,d,m") (match_operand:HI 1 "general_operand" "0,0,I,I,a,d,di,ia,m,d"))] "register_operand (operands[0], HImode) || register_operand (operands[1], HImode)" "* { switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %0\"; case 3: if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %1,%0\", xoperands); else output_asm_insn (\"mov %1,%0\", xoperands); return \"\"; } /* FALLTHROUGH */ case 4: case 5: case 6: case 7: return \"mov %1,%0\"; case 8: case 9: return \"movhu %1,%0\"; } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")]) ;; movsi and helpers ;; We use this to handle addition of two values when one operand is the ;; stack pointer and the other is a memory reference of some kind. Reload ;; does not handle them correctly without this expander. (define_expand "reload_insi" [(set (match_operand:SI 0 "register_operand" "=a") (match_operand:SI 1 "impossible_plus_operand" "")) (clobber (match_operand:SI 2 "register_operand" "=&r"))] "" " { if (XEXP (operands[1], 0) == stack_pointer_rtx) { emit_move_insn (operands[0], XEXP (operands[1], 0)); if (GET_CODE (XEXP (operands[1], 1)) == SUBREG && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 1))) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 1)))))) emit_move_insn (operands[2], gen_rtx (ZERO_EXTEND, GET_MODE (XEXP (operands[1], 1)), SUBREG_REG (XEXP (operands[1], 1)))); else emit_move_insn (operands[2], XEXP (operands[1], 1)); } else { emit_move_insn (operands[0], XEXP (operands[1], 1)); if (GET_CODE (XEXP (operands[1], 0)) == SUBREG && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 0))) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 0)))))) emit_move_insn (operands[2], gen_rtx (ZERO_EXTEND, GET_MODE (XEXP (operands[1], 0)), SUBREG_REG (XEXP (operands[1], 0)))); else emit_move_insn (operands[2], XEXP (operands[1], 0)); } emit_insn (gen_addsi3 (operands[0], operands[0], operands[2])); DONE; }") (define_expand "movsi" [(set (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand1, SImode) && !register_operand (operand0, SImode)) operands[1] = copy_to_mode_reg (SImode, operand1); }") (define_insn "" [(set (match_operand:SI 0 "general_operand" "=d,a,d,a,dm,dm,am,am,d,d,a,a,aR,x") (match_operand:SI 1 "general_operand" "0,0,I,I,d,a,d,a,dim,aim,dim,aim,x,aR"))] "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)" "* { switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %0\"; case 3: if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %1,%0\", xoperands); else output_asm_insn (\"mov %1,%0\", xoperands); return \"\"; } /* FALLTHROUGH */ case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: return \"mov %1,%0\"; } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")]) (define_expand "movsf" [(set (match_operand:SF 0 "general_operand" "") (match_operand:SF 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand1, SFmode) && !register_operand (operand0, SFmode)) operands[1] = copy_to_mode_reg (SFmode, operand1); }") (define_insn "" [(set (match_operand:SF 0 "general_operand" "=d,a,d,a,dam,da") (match_operand:SF 1 "general_operand" "0,0,G,G,da,daim"))] "register_operand (operands[0], SFmode) || register_operand (operands[1], SFmode)" "* { switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %0\"; case 3: if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %1,%0\", xoperands); else output_asm_insn (\"mov %1,%0\", xoperands); return \"\"; } /* FALLTHROUGH */ case 4: case 5: return \"mov %1,%0\"; } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit")]) (define_expand "movdi" [(set (match_operand:DI 0 "general_operand" "") (match_operand:DI 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand1, DImode) && !register_operand (operand0, DImode)) operands[1] = copy_to_mode_reg (DImode, operand1); }") (define_insn "" [(set (match_operand:DI 0 "general_operand" "=d,a,d,a,dm,dm,am,am,d,d,a,a") (match_operand:DI 1 "general_operand" "0,0,I,I,d,a,d,a,dim,aim,dim,aim"))] "register_operand (operands[0], DImode) || register_operand (operands[1], DImode)" "* { long val[2]; REAL_VALUE_TYPE rv; switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %L0\;clr %H0\"; case 3: { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg ? zero_areg : operands[1]; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %L1,%L0\;mov %L0,%H0\", xoperands); else output_asm_insn (\"mov %1,%L0\;mov %L0,%H0\", xoperands); return \"\"; } case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (GET_CODE (operands[1]) == CONST_INT) { val[0] = INTVAL (operands[1]); val[1] = val[0] < 0 ? -1 : 0; } if (GET_CODE (operands[1]) == CONST_DOUBLE) { if (GET_MODE (operands[1]) == DFmode) { REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]); REAL_VALUE_TO_TARGET_DOUBLE (rv, val); } else if (GET_MODE (operands[1]) == VOIDmode || GET_MODE (operands[1]) == DImode) { val[0] = CONST_DOUBLE_LOW (operands[1]); val[1] = CONST_DOUBLE_HIGH (operands[1]); } } if (GET_CODE (operands[1]) == MEM && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0))) { rtx temp = operands[0]; while (GET_CODE (temp) == SUBREG) temp = SUBREG_REG (temp); if (GET_CODE (temp) != REG) abort (); if (reg_overlap_mentioned_p (gen_rtx (REG, SImode, REGNO (temp)), XEXP (operands[1], 0))) return \"mov %H1,%H0\;mov %L1,%L0\"; else return \"mov %L1,%L0\;mov %H1,%H0\"; } else if (GET_CODE (operands[1]) == MEM && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)) && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = XEXP (operands[1], 0); output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\", xoperands); return \"\"; } else { if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[0] == 0) { if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS) output_asm_insn (\"clr %L0\", operands); else if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %L0,%L0\", xoperands); else output_asm_insn (\"mov %1,%L0\", xoperands); } else output_asm_insn (\"mov %L1,%L0\", operands); } else output_asm_insn (\"mov %L1,%L0\", operands); if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[1] == 0) { if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS) output_asm_insn (\"clr %H0\", operands); else if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %H0,%H0\", xoperands); else output_asm_insn (\"mov %1,%H0\", xoperands); } else output_asm_insn (\"mov %H1,%H0\", operands); } else if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[0] == val[1]) output_asm_insn (\"mov %L0,%H0\", operands); else output_asm_insn (\"mov %H1,%H0\", operands); return \"\"; } } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")]) (define_expand "movdf" [(set (match_operand:DF 0 "general_operand" "") (match_operand:DF 1 "general_operand" ""))] "" " { /* One of the ops has to be in a register */ if (!register_operand (operand1, DFmode) && !register_operand (operand0, DFmode)) operands[1] = copy_to_mode_reg (DFmode, operand1); }") (define_insn "" [(set (match_operand:DF 0 "general_operand" "=d,a,d,a,dm,dm,am,am,d,d,a,a") (match_operand:DF 1 "general_operand" "0,0,G,G,d,a,d,a,dim,aim,dim,aim"))] "register_operand (operands[0], DFmode) || register_operand (operands[1], DFmode)" "* { long val[2]; REAL_VALUE_TYPE rv; switch (which_alternative) { case 0: case 1: return \"nop\"; case 2: return \"clr %L0\;clr %H0\"; case 3: { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg ? zero_areg : operands[1]; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %L1,%L0\;mov %L0,%H0\", xoperands); else output_asm_insn (\"mov %1,%L0\;mov %L0,%H0\", xoperands); return \"\"; } case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: if (GET_CODE (operands[1]) == CONST_INT) { val[0] = INTVAL (operands[1]); val[1] = val[0] < 0 ? -1 : 0; } if (GET_CODE (operands[1]) == CONST_DOUBLE) { if (GET_MODE (operands[1]) == DFmode) { REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]); REAL_VALUE_TO_TARGET_DOUBLE (rv, val); } else if (GET_MODE (operands[1]) == VOIDmode || GET_MODE (operands[1]) == DImode) { val[0] = CONST_DOUBLE_LOW (operands[1]); val[1] = CONST_DOUBLE_HIGH (operands[1]); } } if (GET_CODE (operands[1]) == MEM && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0))) { rtx temp = operands[0]; while (GET_CODE (temp) == SUBREG) temp = SUBREG_REG (temp); if (GET_CODE (temp) != REG) abort (); if (reg_overlap_mentioned_p (gen_rtx (REG, SImode, REGNO (temp)), XEXP (operands[1], 0))) return \"mov %H1,%H0\;mov %L1,%L0\"; else return \"mov %L1,%L0\;mov %H1,%H0\"; } else if (GET_CODE (operands[1]) == MEM && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)) && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = XEXP (operands[1], 0); output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\", xoperands); return \"\"; } else { if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[0] == 0) { if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS) output_asm_insn (\"clr %L0\", operands); else if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %L0,%L0\", xoperands); else output_asm_insn (\"mov %1,%L0\", xoperands); } else output_asm_insn (\"mov %L1,%L0\", operands); } else output_asm_insn (\"mov %L1,%L0\", operands); if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[1] == 0) { if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS) output_asm_insn (\"clr %H0\", operands); else if (zero_areg) { rtx xoperands[2]; xoperands[0] = operands[0]; xoperands[1] = zero_areg; if (rtx_equal_p (xoperands[0], xoperands[1])) output_asm_insn (\"sub %H0,%H0\", xoperands); else output_asm_insn (\"mov %1,%H0\", xoperands); } else output_asm_insn (\"mov %H1,%H0\", operands); } else if ((GET_CODE (operands[1]) == CONST_INT || GET_CODE (operands[1]) == CONST_DOUBLE) && val[0] == val[1]) output_asm_insn (\"mov %L0,%H0\", operands); else output_asm_insn (\"mov %H1,%H0\", operands); return \"\"; } } }" [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")]) ;; ---------------------------------------------------------------------- ;; TEST INSTRUCTIONS ;; ---------------------------------------------------------------------- ;; Go ahead and define tstsi so we can eliminate redundant tst insns ;; when we start trying to optimize this port. (define_insn "tstsi" [(set (cc0) (match_operand:SI 0 "register_operand" "da"))] "" "* return output_tst (operands[0], insn);" [(set_attr "cc" "set_znv")]) (define_insn "" [(set (cc0) (zero_extend:SI (match_operand:QI 0 "memory_operand" "d")))] "" "* return output_tst (operands[0], insn);" [(set_attr "cc" "set_znv")]) (define_insn "" [(set (cc0) (zero_extend:SI (match_operand:HI 0 "memory_operand" "d")))] "" "* return output_tst (operands[0], insn);" [(set_attr "cc" "set_znv")]) (define_insn "cmpsi" [(set (cc0) (compare (match_operand:SI 0 "register_operand" "!*d*a,da") (match_operand:SI 1 "nonmemory_operand" "!*0,dai")))] "" "@ add 0,%0 cmp %1,%0" [(set_attr "cc" "invert,compare")]) ;; ---------------------------------------------------------------------- ;; ADD INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_expand "addsi3" [(set (match_operand:SI 0 "register_operand" "") (plus:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))] "" " { /* We can't add a variable amount directly to the stack pointer; so do so via a temporary register. */ if (operands[0] == stack_pointer_rtx && GET_CODE (operands[1]) != CONST_INT && GET_CODE (operands[2]) != CONST_INT) { rtx temp = gen_reg_rtx (SImode); emit_move_insn (temp, gen_rtx (PLUS, SImode, operands[1], operands[2])); emit_move_insn (operands[0], temp); DONE; } }") (define_insn "" [(set (match_operand:SI 0 "register_operand" "=d,a,a,da,x,&!da") (plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,da") (match_operand:SI 2 "nonmemory_operand" "J,J,L,dai,i,da")))] "" "@ inc %0 inc %0 inc4 %0 add %2,%0 add %2,%0 mov %2,%0\;add %1,%0" [(set_attr "cc" "set_zn,none_0hit,none_0hit,set_zn,none_0hit,set_zn")]) ;; ---------------------------------------------------------------------- ;; SUBTRACT INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "subsi3" [(set (match_operand:SI 0 "register_operand" "=da") (minus:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "dai")))] "" "sub %2,%0" [(set_attr "cc" "set_zn")]) (define_expand "negsi2" [(set (match_operand:SI 0 "register_operand" "") (neg:SI (match_operand:SI 1 "register_operand" "")))] "" " { rtx target = gen_reg_rtx (SImode); emit_move_insn (target, GEN_INT (0)); emit_insn (gen_subsi3 (target, target, operands[1])); emit_move_insn (operands[0], target); DONE; }") ;; ---------------------------------------------------------------------- ;; MULTIPLY INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "mulsi3" [(set (match_operand:SI 0 "register_operand" "=d") (mult:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "register_operand" "d")))] "" "* { if (TARGET_MULT_BUG) return \"nop\;nop\;mul %2,%0\"; else return \"mul %2,%0\"; }" [(set_attr "cc" "set_zn")]) (define_insn "udivmodsi4" [(set (match_operand:SI 0 "general_operand" "=d") (udiv:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "general_operand" "d"))) (set (match_operand:SI 3 "general_operand" "=&d") (umod:SI (match_dup 1) (match_dup 2)))] "" "* { if (zero_dreg) output_asm_insn (\"mov %0,mdr\", &zero_dreg); else output_asm_insn (\"sub %3,%3\;mov %3,mdr\", operands); if (find_reg_note (insn, REG_UNUSED, operands[3])) return \"divu %2,%0\"; else return \"divu %2,%0\;mov mdr,%3\"; }" [(set_attr "cc" "set_zn")]) (define_insn "divmodsi4" [(set (match_operand:SI 0 "general_operand" "=d") (div:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "general_operand" "d"))) (set (match_operand:SI 3 "general_operand" "=d") (mod:SI (match_dup 1) (match_dup 2)))] "" "* { if (find_reg_note (insn, REG_UNUSED, operands[3])) return \"ext %0\;div %2,%0\"; else return \"ext %0\;div %2,%0\;mov mdr,%3\"; }" [(set_attr "cc" "set_zn")]) ;; ---------------------------------------------------------------------- ;; AND INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "andsi3" [(set (match_operand:SI 0 "register_operand" "=d,d") (and:SI (match_operand:SI 1 "register_operand" "%0,0") (match_operand:SI 2 "nonmemory_operand" "N,di")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff) return \"extbu %0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff) return \"exthu %0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff) return \"add %0,%0\;lsr 1,%0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff) return \"asl2 %0\;lsr 2,%0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff) return \"add %0,%0\;asl2 %0\;lsr 3,%0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff) return \"asl2 %0,%0\;asl2 %0\;lsr 4,%0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe) return \"lsr 1,%0\;add %0,%0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc) return \"lsr 2,%0\;asl2 %0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8) return \"lsr 3,%0\;add %0,%0\;asl2 %0\"; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0) return \"lsr 4,%0\;asl2 %0\;asl2 %0\"; return \"and %2,%0\"; }" [(set_attr "cc" "none_0hit,set_znv")]) ;; ---------------------------------------------------------------------- ;; OR INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=d") (ior:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "nonmemory_operand" "di")))] "" "or %2,%0" [(set_attr "cc" "set_znv")]) ;; ---------------------------------------------------------------------- ;; XOR INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=d") (xor:SI (match_operand:SI 1 "register_operand" "%0") (match_operand:SI 2 "nonmemory_operand" "di")))] "" "xor %2,%0" [(set_attr "cc" "set_znv")]) ;; ---------------------------------------------------------------------- ;; NOT INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "one_cmplsi2" [(set (match_operand:SI 0 "register_operand" "=d") (not:SI (match_operand:SI 1 "register_operand" "0")))] "" "not %0" [(set_attr "cc" "set_znv")]) ;; ----------------------------------------------------------------- ;; BIT FIELDS ;; ----------------------------------------------------------------- ;; These set/clear memory in byte sized chunks. ;; ;; They are no smaller/faster than loading the value into a register ;; and storing the register, but they don't need a scratch register ;; which may allow for better code generation. (define_insn "" [(set (match_operand:QI 0 "general_operand" "=R,d") (const_int 0))] "" "@ bclr 255,%A0 clr %0" [(set_attr "cc" "clobber")]) (define_insn "" [(set (match_operand:QI 0 "general_operand" "=R,d") (const_int -1))] "" "@ bset 255,%A0 mov -1,%0" [(set_attr "cc" "clobber,none_0hit")]) (define_insn "" [(set (match_operand:QI 0 "general_operand" "=R,d") (subreg:QI (and:SI (subreg:SI (match_dup 0) 0) (match_operand:SI 1 "const_int_operand" "i,i")) 0))] "" "@ bclr %N1,%A0 and %1,%0" [(set_attr "cc" "clobber,set_znv")]) (define_insn "" [(set (match_operand:QI 0 "general_operand" "=R,d") (subreg:QI (ior:SI (subreg:SI (match_dup 0) 0) (match_operand:SI 1 "const_int_operand" "i,i")) 0))] "" "@ bset %1,%A0 or %1,%0" [(set_attr "cc" "clobber,set_znv")]) (define_insn "" [(set (cc0) (zero_extract:SI (match_operand:SI 0 "register_operand" "d") (match_operand 1 "const_int_operand" "") (match_operand 2 "const_int_operand" "")))] "" "* { int len = INTVAL (operands[1]); int bit = INTVAL (operands[2]); int mask = 0; rtx xoperands[2]; while (len > 0) { mask |= (1 << bit); bit++; len--; } xoperands[0] = operands[0]; xoperands[1] = GEN_INT (mask); output_asm_insn (\"btst %1,%0\", xoperands); return \"\"; }" [(set_attr "cc" "set_znv")]) (define_insn "" [(set (cc0) (zero_extract:SI (match_operand:QI 0 "general_operand" "R,d") (match_operand 1 "const_int_operand" "") (match_operand 2 "const_int_operand" "")))] "mask_ok_for_mem_btst (INTVAL (operands[1]), INTVAL (operands[2]))" "* { int len = INTVAL (operands[1]); int bit = INTVAL (operands[2]); int mask = 0; rtx xoperands[2]; while (len > 0) { mask |= (1 << bit); bit++; len--; } /* If the source operand is not a reg (ie it is memory), then extract the bits from mask that we actually want to test. Note that the mask will never cross a byte boundary. */ if (!REG_P (operands[0])) { if (mask & 0xff) mask = mask & 0xff; else if (mask & 0xff00) mask = (mask >> 8) & 0xff; else if (mask & 0xff0000) mask = (mask >> 16) & 0xff; else if (mask & 0xff000000) mask = (mask >> 24) & 0xff; } xoperands[0] = operands[0]; xoperands[1] = GEN_INT (mask); if (GET_CODE (operands[0]) == REG) output_asm_insn (\"btst %1,%0\", xoperands); else output_asm_insn (\"btst %1,%A0\", xoperands); return \"\"; }" [(set_attr "cc" "set_znv")]) (define_insn "" [(set (cc0) (and:SI (match_operand:SI 0 "register_operand" "d") (match_operand:SI 1 "const_int_operand" "")))] "" "btst %1,%0" [(set_attr "cc" "set_znv")]) (define_insn "" [(set (cc0) (and:SI (subreg:SI (match_operand:QI 0 "general_operand" "R,d") 0) (match_operand:SI 1 "const_8bit_operand" "")))] "" "@ btst %1,%A0 btst %1,%0" [(set_attr "cc" "set_znv")]) ;; ---------------------------------------------------------------------- ;; JUMP INSTRUCTIONS ;; ---------------------------------------------------------------------- ;; Conditional jump instructions (define_expand "ble" [(set (pc) (if_then_else (le (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bleu" [(set (pc) (if_then_else (leu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bge" [(set (pc) (if_then_else (ge (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bgeu" [(set (pc) (if_then_else (geu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "blt" [(set (pc) (if_then_else (lt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bltu" [(set (pc) (if_then_else (ltu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bgt" [(set (pc) (if_then_else (gt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bgtu" [(set (pc) (if_then_else (gtu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "beq" [(set (pc) (if_then_else (eq (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_expand "bne" [(set (pc) (if_then_else (ne (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "") (define_insn "" [(set (pc) (if_then_else (match_operator 1 "comparison_operator" [(cc0) (const_int 0)]) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0 && (GET_CODE (operands[1]) == GT || GET_CODE (operands[1]) == GE || GET_CODE (operands[1]) == LE || GET_CODE (operands[1]) == LT)) return 0; return \"b%b1 %0\"; }" [(set_attr "cc" "none")]) (define_insn "" [(set (pc) (if_then_else (match_operator 1 "comparison_operator" [(cc0) (const_int 0)]) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0 && (GET_CODE (operands[1]) == GT || GET_CODE (operands[1]) == GE || GET_CODE (operands[1]) == LE || GET_CODE (operands[1]) == LT)) return 0; return \"b%B1 %0\"; }" [(set_attr "cc" "none")]) ;; Unconditional and other jump instructions. (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "jmp %l0" [(set_attr "cc" "none")]) (define_insn "indirect_jump" [(set (pc) (match_operand:SI 0 "register_operand" "a"))] "" "jmp (%0)" [(set_attr "cc" "none")]) (define_insn "tablejump" [(set (pc) (match_operand:SI 0 "register_operand" "a")) (use (label_ref (match_operand 1 "" "")))] "" "jmp (%0)" [(set_attr "cc" "none")]) ;; Call subroutine with no return value. (define_expand "call" [(call (match_operand:QI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" " { if (! call_address_operand (XEXP (operands[0], 0))) XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0)); emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1])); DONE; }") (define_insn "call_internal" [(call (mem:QI (match_operand:SI 0 "call_address_operand" "aS")) (match_operand:SI 1 "general_operand" "g"))] "" "* { if (REG_P (operands[0])) return \"calls %C0\"; else return \"call %C0,[],0\"; }" [(set_attr "cc" "clobber")]) ;; Call subroutine, returning value in operand 0 ;; (which must be a hard register). (define_expand "call_value" [(set (match_operand 0 "" "") (call (match_operand:QI 1 "general_operand" "") (match_operand:SI 2 "general_operand" "")))] "" " { if (! call_address_operand (XEXP (operands[1], 0))) XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0)); emit_call_insn (gen_call_value_internal (operands[0], XEXP (operands[1], 0), operands[2])); DONE; }") (define_insn "call_value_internal" [(set (match_operand 0 "" "=da") (call (mem:QI (match_operand:SI 1 "call_address_operand" "aS")) (match_operand:SI 2 "general_operand" "g")))] "" "* { if (REG_P (operands[1])) return \"calls %C1\"; else return \"call %C1,[],0\"; }" [(set_attr "cc" "clobber")]) (define_expand "untyped_call" [(parallel [(call (match_operand 0 "" "") (const_int 0)) (match_operand 1 "" "") (match_operand 2 "" "")])] "" " { int i; emit_call_insn (gen_call (operands[0], const0_rtx)); for (i = 0; i < XVECLEN (operands[2], 0); i++) { rtx set = XVECEXP (operands[2], 0, i); emit_move_insn (SET_DEST (set), SET_SRC (set)); } DONE; }") (define_insn "nop" [(const_int 0)] "" "nop" [(set_attr "cc" "none")]) ;; ---------------------------------------------------------------------- ;; EXTEND INSTRUCTIONS ;; ---------------------------------------------------------------------- (define_insn "zero_extendqisi2" [(set (match_operand:SI 0 "general_operand" "=d,d,d") (zero_extend:SI (match_operand:QI 1 "general_operand" "0,d,m")))] "" "@ extbu %0 mov %1,%0\;extbu %0 movbu %1,%0" [(set_attr "cc" "none_0hit")]) (define_insn "zero_extendhisi2" [(set (match_operand:SI 0 "general_operand" "=d,d,d") (zero_extend:SI (match_operand:HI 1 "general_operand" "0,d,m")))] "" "@ exthu %0 mov %1,%0\;exthu %0 movhu %1,%0" [(set_attr "cc" "none_0hit")]) ;;- sign extension instructions (define_insn "extendqisi2" [(set (match_operand:SI 0 "general_operand" "=d,d") (sign_extend:SI (match_operand:QI 1 "general_operand" "0,d")))] "" "@ extb %0 mov %1,%0\;extb %0" [(set_attr "cc" "none_0hit")]) (define_insn "extendhisi2" [(set (match_operand:SI 0 "general_operand" "=d,d") (sign_extend:SI (match_operand:HI 1 "general_operand" "0,d")))] "" "@ exth %0 mov %1,%0\;exth %0" [(set_attr "cc" "none_0hit")]) ;; ---------------------------------------------------------------------- ;; SHIFTS ;; ---------------------------------------------------------------------- (define_insn "ashlsi3" [(set (match_operand:SI 0 "register_operand" "=da,d,d,d,d") (ashift:SI (match_operand:SI 1 "register_operand" "0,0,0,0,0") (match_operand:QI 2 "nonmemory_operand" "J,K,M,L,di")))] "" "@ add %0,%0 asl2 %0 asl2 %0\;add %0,%0 asl2 %0\;asl2 %0 asl %S2,%0" [(set_attr "cc" "set_zn")]) (define_insn "lshrsi3" [(set (match_operand:SI 0 "register_operand" "=d") (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "di")))] "" "lsr %S2,%0" [(set_attr "cc" "set_zn")]) (define_insn "ashrsi3" [(set (match_operand:SI 0 "register_operand" "=d") (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") (match_operand:QI 2 "nonmemory_operand" "di")))] "" "asr %S2,%0" [(set_attr "cc" "set_zn")]) ;; ---------------------------------------------------------------------- ;; PROLOGUE/EPILOGUE ;; ---------------------------------------------------------------------- (define_expand "prologue" [(const_int 0)] "" "expand_prologue (); DONE;") (define_expand "epilogue" [(return)] "" " { expand_epilogue (); DONE; }") (define_insn "return_internal" [(const_int 2)] "" "rets" [(set_attr "cc" "clobber")]) ;; This insn restores the callee saved registers and does a return, it ;; can also deallocate stack space. (define_insn "return_internal_regs" [(const_int 0) (match_operand:SI 0 "const_int_operand" "i") (return)] "" "ret [d2,d3,a2,a3],%0" [(set_attr "cc" "clobber")]) (define_insn "store_movm" [(const_int 1)] "" "movm [d2,d3,a2,a3],(sp)" [(set_attr "cc" "clobber")]) (define_insn "return" [(return)] "can_use_return_insn ()" "* { rtx next = next_active_insn (insn); if (next && GET_CODE (next) == JUMP_INSN && GET_CODE (PATTERN (next)) == RETURN) return \"\"; else return \"rets\"; }" [(set_attr "cc" "clobber")]) ;; Try to combine consecutive updates of the stack pointer (or any ;; other register for that matter). (define_peephole [(set (match_operand:SI 0 "register_operand" "=dax") (plus:SI (match_dup 0) (match_operand 1 "const_int_operand" ""))) (set (match_dup 0) (plus:SI (match_dup 0) (match_operand 2 "const_int_operand" "")))] "" "* { operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1])); return \"add %1,%0\"; }" [(set_attr "cc" "clobber")]) ;; ;; We had patterns to check eq/ne, but the they don't work because ;; 0x80000000 + 0x80000000 = 0x0 with a carry out. ;; ;; The Z flag and C flag would be set, and we have no way to ;; check for the Z flag set and C flag clear. ;; ;; This will work on the mn10200 because we can check the ZX flag ;; if the comparison is in HImode. (define_peephole [(set (cc0) (match_operand:SI 0 "register_operand" "d")) (set (pc) (if_then_else (ge (cc0) (const_int 0)) (match_operand 1 "" "") (pc)))] "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])" "add %0,%0\;bcc %1" [(set_attr "cc" "clobber")]) (define_peephole [(set (cc0) (match_operand:SI 0 "register_operand" "d")) (set (pc) (if_then_else (lt (cc0) (const_int 0)) (match_operand 1 "" "") (pc)))] "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])" "add %0,%0\;bcs %1" [(set_attr "cc" "clobber")]) (define_peephole [(set (cc0) (match_operand:SI 0 "register_operand" "d")) (set (pc) (if_then_else (ge (cc0) (const_int 0)) (pc) (match_operand 1 "" "")))] "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])" "add %0,%0\;bcs %1" [(set_attr "cc" "clobber")]) (define_peephole [(set (cc0) (match_operand:SI 0 "register_operand" "d")) (set (pc) (if_then_else (lt (cc0) (const_int 0)) (pc) (match_operand 1 "" "")))] "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])" "add %0,%0\;bcc %1" [(set_attr "cc" "clobber")])