From-SVN: r5041
122 KiB
;; GCC machine description for Intel 80386. ;; Copyright (C) 1988 Free Software Foundation, Inc. ;; Mostly by William Schelter.
;; 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, 675 Mass Ave, Cambridge, MA 02139, 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.
;; Macro #define NOTICE_UPDATE_CC in file i386.h handles condition code ;; updates for most instructions.
;; Macro REG_CLASS_FROM_LETTER in file i386.h defines the register ;; constraint letters.
;; the special asm out single letter directives following a '%' are: ;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of ;; operands[1]. ;; 'L' Print the opcode suffix for a 32-bit integer opcode. ;; 'W' Print the opcode suffix for a 16-bit integer opcode. ;; 'B' Print the opcode suffix for an 8-bit integer opcode. ;; 'S' Print the opcode suffix for a 32-bit float opcode. ;; 'Q' Print the opcode suffix for a 64-bit float opcode.
;; 'b' Print the QImode name of the register for the indicated operand. ;; %b0 would print %al if operands[0] is reg 0. ;; 'w' Likewise, print the HImode name of the register. ;; 'k' Likewise, print the SImode name of the register. ;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh. ;; 'y' Print "st(0)" instead of "st" as a register.
;; UNSPEC usage:
;; 0 This is a scas' operation. The mode of the UNSPEC is always SImode. ;; operand 0 is the memory address to scan. ;; operand 1 is a register containing the value to scan for. The mode ;; of the scas opcode will be the same as the mode of this operand. ;; operand 2 is the known alignment of operand 0. ;; 1 This is a
sin' operation. The mode of the UNSPEC is MODE_FLOAT.
;; operand 0 is the argument for sin'. ;; 2 This is a
cos' operation. The mode of the UNSPEC is MODE_FLOAT.
;; operand 0 is the argument for `cos'.
;; "movl MEM,REG / testl REG,REG" is faster on a 486 than "cmpl $0,MEM".
;; But restricting MEM here would mean that gcc could not remove a redundant
;; test in cases like "incl MEM / je TARGET".
;;
;; We don't want to allow a constant operand for test insns because
;; (set (cc0) (const_int foo)) has no mode information. Such insns will
;; be folded while optimizing anyway.
;; All test insns have expanders that save the operands away without ;; actually generating RTL. The bCOND or sCOND (emitted immediately ;; after the tstM or cmp) will actually emit the tstM or cmpM.
(define_insn "tstsi_1" [(set (cc0) (match_operand:SI 0 "nonimmediate_operand" "rm"))] "" "* { if (REG_P (operands[0])) return AS2 (test%L0,%0,%0);
operands[1] = const0_rtx; return AS2 (cmp%L0,%1,%0); }")
(define_expand "tstsi" [(set (cc0) (match_operand:SI 0 "nonimmediate_operand" ""))] "" " { i386_compare_gen = gen_tstsi_1; i386_compare_op0 = operands[0]; DONE; }")
(define_insn "tsthi_1" [(set (cc0) (match_operand:HI 0 "nonimmediate_operand" "rm"))] "" "* { if (REG_P (operands[0])) return AS2 (test%W0,%0,%0);
operands[1] = const0_rtx; return AS2 (cmp%W0,%1,%0); }")
(define_expand "tsthi" [(set (cc0) (match_operand:HI 0 "nonimmediate_operand" ""))] "" " { i386_compare_gen = gen_tsthi_1; i386_compare_op0 = operands[0]; DONE; }")
(define_insn "tstqi_1" [(set (cc0) (match_operand:QI 0 "nonimmediate_operand" "qm"))] "" "* { if (REG_P (operands[0])) return AS2 (test%B0,%0,%0);
operands[1] = const0_rtx; return AS2 (cmp%B0,%1,%0); }")
(define_expand "tstqi" [(set (cc0) (match_operand:QI 0 "nonimmediate_operand" ""))] "" " { i386_compare_gen = gen_tstqi_1; i386_compare_op0 = operands[0]; DONE; }")
(define_insn "tstsf_cc" [(set (cc0) (match_operand:SF 0 "register_operand" "f")) (clobber (match_scratch:HI 1 "=a"))] "TARGET_80387 && ! TARGET_IEEE_FP" "* { if (! STACK_TOP_P (operands[0])) abort ();
output_asm_insn ("ftst", operands);
if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) output_asm_insn (AS1 (fstp,%y0), operands);
return (char *) output_fp_cc0_set (insn); }")
;; Don't generate tstsf if generating IEEE code, since the `ftst' opcode ;; isn't IEEE compliant.
(define_expand "tstsf" [(parallel [(set (cc0) (match_operand:SF 0 "register_operand" "")) (clobber (match_scratch:HI 1 ""))])] "TARGET_80387 && ! TARGET_IEEE_FP" " { i386_compare_gen = gen_tstsf_cc; i386_compare_op0 = operands[0]; DONE; }")
(define_insn "tstdf_cc" [(set (cc0) (match_operand:DF 0 "register_operand" "f")) (clobber (match_scratch:HI 1 "=a"))] "TARGET_80387 && ! TARGET_IEEE_FP" "* { if (! STACK_TOP_P (operands[0])) abort ();
output_asm_insn ("ftst", operands);
if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG)) output_asm_insn (AS1 (fstp,%y0), operands);
return (char *) output_fp_cc0_set (insn); }")
;; Don't generate tstdf if generating IEEE code, since the `ftst' opcode ;; isn't IEEE compliant.
(define_expand "tstdf" [(parallel [(set (cc0) (match_operand:DF 0 "register_operand" "")) (clobber (match_scratch:HI 1 ""))])] "TARGET_80387 && ! TARGET_IEEE_FP" " { i386_compare_gen = gen_tstdf_cc; i386_compare_op0 = operands[0]; DONE; }") ;;- compare instructions. See comments above tstM patterns about ;; expansion of these insns.
(define_insn "cmpsi_1" [(set (cc0) (compare (match_operand:SI 0 "nonimmediate_operand" "mr,r") (match_operand:SI 1 "general_operand" "ri,mr")))] "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM" "* { if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) { cc_status.flags |= CC_REVERSED; return AS2 (cmp%L0,%0,%1); } return AS2 (cmp%L0,%1,%0); }")
(define_expand "cmpsi" [(set (cc0) (compare (match_operand:SI 0 "nonimmediate_operand" "") (match_operand:SI 1 "general_operand" "")))] "" " { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (SImode, operands[0]);
i386_compare_gen = gen_cmpsi_1; i386_compare_op0 = operands[0]; i386_compare_op1 = operands[1]; DONE; }")
(define_insn "cmphi_1" [(set (cc0) (compare (match_operand:HI 0 "nonimmediate_operand" "mr,r") (match_operand:HI 1 "general_operand" "ri,mr")))] "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM" "* { if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) { cc_status.flags |= CC_REVERSED; return AS2 (cmp%W0,%0,%1); } return AS2 (cmp%W0,%1,%0); }")
(define_expand "cmphi" [(set (cc0) (compare (match_operand:HI 0 "nonimmediate_operand" "") (match_operand:HI 1 "general_operand" "")))] "" " { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (HImode, operands[0]);
i386_compare_gen = gen_cmphi_1; i386_compare_op0 = operands[0]; i386_compare_op1 = operands[1]; DONE; }")
(define_insn "cmpqi_1" [(set (cc0) (compare (match_operand:QI 0 "nonimmediate_operand" "q,mq") (match_operand:QI 1 "general_operand" "qm,nq")))] "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM" "* { if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM) { cc_status.flags |= CC_REVERSED; return AS2 (cmp%B0,%0,%1); } return AS2 (cmp%B0,%1,%0); }")
(define_expand "cmpqi" [(set (cc0) (compare (match_operand:QI 0 "nonimmediate_operand" "") (match_operand:QI 1 "general_operand" "")))] "" " { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) operands[0] = force_reg (QImode, operands[0]);
i386_compare_gen = gen_cmpqi_1; i386_compare_op0 = operands[0]; i386_compare_op1 = operands[1]; DONE; }")
;; These implement float point compares. For each of DFmode and ;; SFmode, there is the normal insn, and an insn where the second operand ;; is converted to the desired mode.
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(match_operand:DF 0 "nonimmediate_operand" "f,fm") (match_operand:DF 1 "nonimmediate_operand" "fm,f")])) (clobber (match_scratch:HI 3 "=a,a"))] "TARGET_80387 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(match_operand:DF 0 "register_operand" "f") (float:DF (match_operand:SI 1 "nonimmediate_operand" "rm"))])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(float:DF (match_operand:SI 0 "nonimmediate_operand" "rm")) (match_operand:DF 1 "register_operand" "f")])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(match_operand:DF 0 "register_operand" "f") (float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm"))])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(float_extend:DF (match_operand:SF 0 "nonimmediate_operand" "fm")) (match_operand:DF 1 "register_operand" "f")])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f") (match_operand:DF 1 "register_operand" "f"))) (clobber (match_scratch:HI 2 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
;; These two insns will never be generated by combine due to the mode of ;; the COMPARE. ;(define_insn "" ; [(set (cc0) ; (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f") ; (float_extend:DF ; (match_operand:SF 1 "register_operand" "f")))) ; (clobber (match_scratch:HI 2 "=a"))] ; "TARGET_80387" ; "* return (char ) output_float_compare (insn, operands);") ; ;(define_insn "" ; [(set (cc0) ; (compare:CCFPEQ (float_extend:DF ; (match_operand:SF 0 "register_operand" "f")) ; (match_operand:DF 1 "register_operand" "f"))) ; (clobber (match_scratch:HI 2 "=a"))] ; "TARGET_80387" ; " return (char *) output_float_compare (insn, operands);")
(define_insn "cmpsf_cc_1" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(match_operand:SF 0 "nonimmediate_operand" "f,fm") (match_operand:SF 1 "nonimmediate_operand" "fm,f")])) (clobber (match_scratch:HI 3 "=a,a"))] "TARGET_80387 && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(match_operand:SF 0 "register_operand" "f") (float:SF (match_operand:SI 1 "nonimmediate_operand" "rm"))])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (match_operator 2 "VOIDmode_compare_op" [(float:SF (match_operand:SI 0 "nonimmediate_operand" "rm")) (match_operand:SF 1 "register_operand" "f")])) (clobber (match_scratch:HI 3 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_insn "" [(set (cc0) (compare:CCFPEQ (match_operand:SF 0 "register_operand" "f") (match_operand:SF 1 "register_operand" "f"))) (clobber (match_scratch:HI 2 "=a"))] "TARGET_80387" "* return (char *) output_float_compare (insn, operands);")
(define_expand "cmpdf" [(set (cc0) (compare (match_operand:DF 0 "register_operand" "") (match_operand:DF 1 "nonimmediate_operand" "")))] "TARGET_80387" " { i386_compare_gen = gen_cmpdf_cc; i386_compare_gen_eq = gen_cmpdf_ccfpeq; i386_compare_op0 = operands[0]; i386_compare_op1 = operands[1]; DONE; }")
(define_expand "cmpsf" [(set (cc0) (compare (match_operand:SF 0 "register_operand" "") (match_operand:SF 1 "nonimmediate_operand" "")))] "TARGET_80387" " { i386_compare_gen = gen_cmpsf_cc; i386_compare_gen_eq = gen_cmpsf_ccfpeq; i386_compare_op0 = operands[0]; i386_compare_op1 = operands[1]; DONE; }")
(define_expand "cmpdf_cc" [(parallel [(set (cc0) (compare (match_operand:DF 0 "register_operand" "") (match_operand:DF 1 "register_operand" ""))) (clobber (match_scratch:HI 2 ""))])] "TARGET_80387" "")
(define_expand "cmpdf_ccfpeq" [(parallel [(set (cc0) (compare:CCFPEQ (match_operand:DF 0 "register_operand" "") (match_operand:DF 1 "register_operand" ""))) (clobber (match_scratch:HI 2 ""))])] "TARGET_80387" " { if (! register_operand (operands[1], DFmode)) operands[1] = copy_to_mode_reg (DFmode, operands[1]); }")
(define_expand "cmpsf_cc" [(parallel [(set (cc0) (compare (match_operand:SF 0 "register_operand" "") (match_operand:SF 1 "register_operand" ""))) (clobber (match_scratch:HI 2 ""))])] "TARGET_80387" "")
(define_expand "cmpsf_ccfpeq" [(parallel [(set (cc0) (compare:CCFPEQ (match_operand:SF 0 "register_operand" "") (match_operand:SF 1 "register_operand" ""))) (clobber (match_scratch:HI 2 ""))])] "TARGET_80387" " { if (! register_operand (operands[1], SFmode)) operands[1] = copy_to_mode_reg (SFmode, operands[1]); }") ;; logical compare
(define_insn "" [(set (cc0) (and:SI (match_operand:SI 0 "general_operand" "%ro") (match_operand:SI 1 "general_operand" "ri")))] "" "* { /* For small integers, we may actually use testb. / if (GET_CODE (operands[1]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) { / We may set the sign bit spuriously. */
if ((INTVAL (operands[1]) & ~0xff) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
return AS2 (test%B0,%1,%b0);
}
if ((INTVAL (operands[1]) & ~0xff00) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (INTVAL (operands[1]) >> 8);
if (QI_REG_P (operands[0]))
return AS2 (test%B0,%1,%h0);
else
{
operands[0] = adj_offsettable_operand (operands[0], 1);
return AS2 (test%B0,%1,%b0);
}
}
if (GET_CODE (operands[0]) == MEM
&& (INTVAL (operands[1]) & ~0xff0000) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (INTVAL (operands[1]) >> 16);
operands[0] = adj_offsettable_operand (operands[0], 2);
return AS2 (test%B0,%1,%b0);
}
if (GET_CODE (operands[0]) == MEM
&& (INTVAL (operands[1]) & ~0xff000000) == 0)
{
operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff);
operands[0] = adj_offsettable_operand (operands[0], 3);
return AS2 (test%B0,%1,%b0);
}
}
if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) return AS2 (test%L0,%1,%0);
return AS2 (test%L1,%0,%1); }")
(define_insn "" [(set (cc0) (and:HI (match_operand:HI 0 "general_operand" "%ro") (match_operand:HI 1 "general_operand" "ri")))] "" "* { if (GET_CODE (operands[1]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])) && (! REG_P (operands[0]) || QI_REG_P (operands[0]))) { if ((INTVAL (operands[1]) & 0xff00) == 0) { /* ??? This might not be necessary. */ if (INTVAL (operands[1]) & 0xffff0000) operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
/* We may set the sign bit spuriously. */
cc_status.flags |= CC_NOT_NEGATIVE;
return AS2 (test%B0,%1,%b0);
}
if ((INTVAL (operands[1]) & 0xff) == 0)
{
operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff);
if (QI_REG_P (operands[0]))
return AS2 (test%B0,%1,%h0);
else
{
operands[0] = adj_offsettable_operand (operands[0], 1);
return AS2 (test%B0,%1,%b0);
}
}
}
if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) return AS2 (test%W0,%1,%0);
return AS2 (test%W1,%0,%1); }")
(define_insn "" [(set (cc0) (and:QI (match_operand:QI 0 "general_operand" "%qm") (match_operand:QI 1 "general_operand" "qi")))] "" "* { if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) return AS2 (test%B0,%1,%0);
return AS2 (test%B1,%0,%1); }") ;; move instructions. ;; There is one for each machine mode, ;; and each is preceded by a corresponding push-insn pattern ;; (since pushes are not general_operands on the 386).
(define_insn "" [(set (match_operand:SI 0 "push_operand" "=<") (match_operand:SI 1 "general_operand" "g"))] "! TARGET_486" "push%L0 %1")
;; On a 486, it is faster to move MEM to a REG and then push, rather than ;; push MEM directly.
(define_insn "" [(set (match_operand:SI 0 "push_operand" "=<") (match_operand:SI 1 "general_operand" "ri"))] "TARGET_486" "push%L0 %1")
;; General case of fullword move.
;; If generating PIC code and operands[1] is a symbolic CONST, emit a ;; move to get the address of the symbolic object from the GOT.
(define_expand "movsi" [(set (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" " { extern int flag_pic;
if (flag_pic && SYMBOLIC_CONST (operands[1])) emit_pic_move (operands, SImode); }")
;; On i486, incl reg is faster than movl $1,reg.
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=g,r") (match_operand:SI 1 "general_operand" "ri,m"))] "" "* { rtx link; if (operands[1] == const0_rtx && REG_P (operands[0])) return AS2 (xor%L0,%0,%0);
if (operands[1] == const1_rtx && (link = find_reg_note (insn, REG_WAS_0, 0)) /* Make sure the insn that stored the 0 is still present. / && ! INSN_DELETED_P (XEXP (link, 0)) && GET_CODE (XEXP (link, 0)) != NOTE / Make sure cross jumping didn't happen here. / && no_labels_between_p (XEXP (link, 0), insn) / Make sure the reg hasn't been clobbered. / && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) / Fastest way to change a 0 to a 1. */ return AS1 (inc%L0,%0);
return AS2 (mov%L0,%1,%0); }")
(define_insn "" [(set (match_operand:HI 0 "push_operand" "=<") (match_operand:HI 1 "general_operand" "g"))] "" "push%W0 %1")
;; On i486, an incl and movl are both faster than incw and movw.
(define_insn "movhi" [(set (match_operand:HI 0 "general_operand" "=g,r") (match_operand:HI 1 "general_operand" "ri,m"))] "" "* { rtx link; if (REG_P (operands[0]) && operands[1] == const0_rtx) return AS2 (xor%L0,%k0,%k0);
if (REG_P (operands[0]) && operands[1] == const1_rtx && (link = find_reg_note (insn, REG_WAS_0, 0)) /* Make sure the insn that stored the 0 is still present. / && ! INSN_DELETED_P (XEXP (link, 0)) && GET_CODE (XEXP (link, 0)) != NOTE / Make sure cross jumping didn't happen here. / && no_labels_between_p (XEXP (link, 0), insn) / Make sure the reg hasn't been clobbered. / && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) / Fastest way to change a 0 to a 1. */ return AS1 (inc%L0,%k0);
if (REG_P (operands[0])) { if (REG_P (operands[1])) return AS2 (mov%L0,%k1,%k0); else if (CONSTANT_P (operands[1])) return AS2 (mov%L0,%1,%k0); }
return AS2 (mov%W0,%1,%0); }")
(define_insn "movstricthi" [(set (strict_low_part (match_operand:HI 0 "general_operand" "+g,r")) (match_operand:HI 1 "general_operand" "ri,m"))] "" "* { rtx link; if (operands[1] == const0_rtx && REG_P (operands[0])) return AS2 (xor%W0,%0,%0);
if (operands[1] == const1_rtx && (link = find_reg_note (insn, REG_WAS_0, 0)) /* Make sure the insn that stored the 0 is still present. / && ! INSN_DELETED_P (XEXP (link, 0)) && GET_CODE (XEXP (link, 0)) != NOTE / Make sure cross jumping didn't happen here. / && no_labels_between_p (XEXP (link, 0), insn) / Make sure the reg hasn't been clobbered. / && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) / Fastest way to change a 0 to a 1. */ return AS1 (inc%W0,%0);
return AS2 (mov%W0,%1,%0); }")
;; emit_push_insn when it calls move_by_pieces ;; requires an insn to "push a byte". ;; But actually we use pushw, which has the effect of rounding ;; the amount pushed up to a halfword. (define_insn "" [(set (match_operand:QI 0 "push_operand" "=<") (match_operand:QI 1 "general_operand" "q"))] "" "* { operands[1] = gen_rtx (REG, HImode, REGNO (operands[1])); return AS1 (push%W0,%1); }")
;; On i486, incb reg is faster than movb $1,reg.
;; ??? Do a recognizer for zero_extract that looks just like this, but reads ;; or writes %ah, %bh, %ch, %dh.
(define_insn "movqi" [(set (match_operand:QI 0 "general_operand" "=q,*r,qm") (match_operand:QI 1 "general_operand" "g,q,qn"))] "" " { rtx link; if (operands[1] == const0_rtx && REG_P (operands[0])) return AS2 (xor%B0,%0,%0);
if (operands[1] == const1_rtx && (link = find_reg_note (insn, REG_WAS_0, 0)) /* Make sure the insn that stored the 0 is still present. / && ! INSN_DELETED_P (XEXP (link, 0)) && GET_CODE (XEXP (link, 0)) != NOTE / Make sure cross jumping didn't happen here. / && no_labels_between_p (XEXP (link, 0), insn) / Make sure the reg hasn't been clobbered. / && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) / Fastest way to change a 0 to a 1. */ return AS1 (inc%B0,%0);
/* If mov%B0 isn't allowed for one of these regs, use mov%L0. */ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) return (AS2 (mov%L0,%k1,%k0));
return (AS2 (mov%B0,%1,%0)); }")
;; If it becomes necessary to support movstrictqi into %esi or %edi, ;; use the insn sequence: ;; ;; shrdl $8,srcreg,dstreg ;; rorl $24,dstreg ;; ;; If operands[1] is a constant, then an andl/orl sequence would be ;; faster.
(define_insn "movstrictqi" [(set (strict_low_part (match_operand:QI 0 "general_operand" "+qm,q")) (match_operand:QI 1 "general_operand" "qn,m"))] "" " { rtx link; if (operands[1] == const0_rtx && REG_P (operands[0])) return AS2 (xor%B0,%0,%0);
if (operands[1] == const1_rtx && (link = find_reg_note (insn, REG_WAS_0, 0)) /* Make sure the insn that stored the 0 is still present. / && ! INSN_DELETED_P (XEXP (link, 0)) && GET_CODE (XEXP (link, 0)) != NOTE / Make sure cross jumping didn't happen here. / && no_labels_between_p (XEXP (link, 0), insn) / Make sure the reg hasn't been clobbered. / && ! reg_set_between_p (operands[0], XEXP (link, 0), insn)) / Fastest way to change a 0 to a 1. */ return AS1 (inc%B0,%0);
/* If mov%B0 isn't allowed for one of these regs, use mov%L0. */ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1])) { abort (); return (AS2 (mov%L0,%k1,%k0)); }
return AS2 (mov%B0,%1,%0); }")
(define_insn "" [(set (match_operand:SF 0 "push_operand" "=<,<") (match_operand:SF 1 "general_operand" "gF,f"))] "" "* { if (STACK_REG_P (operands[1])) { rtx xops[3];
if (! STACK_TOP_P (operands[1]))
abort ();
xops[0] = AT_SP (SFmode);
xops[1] = GEN_INT (4);
xops[2] = stack_pointer_rtx;
output_asm_insn (AS2 (sub%L2,%1,%2), xops);
if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
output_asm_insn (AS1 (fstp%S0,%0), xops);
else
output_asm_insn (AS1 (fst%S0,%0), xops);
RET;
}
return AS1 (push%L1,%1); }")
;; Allow MEM-MEM moves before reload. The reload class for such a ;; move will be ALL_REGS. PREFERRED_RELOAD_CLASS will narrow this to ;; GENERAL_REGS. For the purposes of regclass, prefer FLOAT_REGS.
(define_insn "movsf" [(set (match_operand:SF 0 "general_operand" "=*rfm,*rf,f,!*rm") (match_operand:SF 1 "general_operand" "*rf,rfm,fG,fF"))] "" " { int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
/* First handle a pop' insn or a
fld %st(0)' */
if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) { if (stack_top_dies) return AS1 (fstp,%y0); else return AS1 (fld,%y0); }
/* Handle a transfer between the 387 and a 386 register */
if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); RET; }
if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) { output_to_reg (operands[0], stack_top_dies); RET; }
/* Handle other kinds of writes from the 387 */
if (STACK_TOP_P (operands[1])) { if (stack_top_dies) return AS1 (fstp%z0,%y0); else return AS1 (fst%z0,%y0); }
/* Handle other kinds of reads to the 387 */
if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) return (char *) output_move_const_single (operands);
if (STACK_TOP_P (operands[0])) return AS1 (fld%z1,%y1);
/* Handle all SFmode moves not involving the 387 */
return (char *) singlemove_string (operands); }")
;;should change to handle the memory operands[1] without doing df push.. (define_insn "" [(set (match_operand:DF 0 "push_operand" "=<,<") (match_operand:DF 1 "general_operand" "gF,f"))] "" "* { if (STACK_REG_P (operands[1])) { rtx xops[3];
xops[0] = AT_SP (SFmode);
xops[1] = GEN_INT (8);
xops[2] = stack_pointer_rtx;
output_asm_insn (AS2 (sub%L2,%1,%2), xops);
if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
output_asm_insn (AS1 (fstp%Q0,%0), xops);
else
output_asm_insn (AS1 (fst%Q0,%0), xops);
RET;
}
else return (char *) output_move_double (operands); }")
(define_insn "swapdf" [(set (match_operand:DF 0 "register_operand" "f") (match_operand:DF 1 "register_operand" "f")) (set (match_dup 1) (match_dup 0))] "" "* { if (STACK_TOP_P (operands[0])) return AS1 (fxch,%1); else return AS1 (fxch,%0); }")
;; Allow MEM-MEM moves before reload. The reload class for such a ;; move will be ALL_REGS. PREFERRED_RELOAD_CLASS will narrow this to ;; GENERAL_REGS. For the purposes of regclass, prefer FLOAT_REGS.
(define_insn "movdf" [(set (match_operand:DF 0 "general_operand" "=*rfm,*rf,f,!*rm") (match_operand:DF 1 "general_operand" "*rf,rfm,fG,fF"))] "" " { int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
/* First handle a pop' insn or a
fld %st(0)' */
if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1])) { if (stack_top_dies) return AS1 (fstp,%y0); else return AS1 (fld,%y0); }
/* Handle a transfer between the 387 and a 386 register */
if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); RET; }
if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0])) { output_to_reg (operands[0], stack_top_dies); RET; }
/* Handle other kinds of writes from the 387 */
if (STACK_TOP_P (operands[1])) { if (stack_top_dies) return AS1 (fstp%z0,%y0); else return AS1 (fst%z0,%y0); }
/* Handle other kinds of reads to the 387 */
if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE) return (char *) output_move_const_single (operands);
if (STACK_TOP_P (operands[0])) return AS1 (fld%z1,%y1);
/* Handle all DFmode moves not involving the 387 */
return (char *) output_move_double (operands); }")
(define_insn "" [(set (match_operand:DI 0 "push_operand" "=<") (match_operand:DI 1 "general_operand" "roiF"))] "" "* { return (char *) output_move_double (operands); }")
(define_insn "movdi" [(set (match_operand:DI 0 "general_operand" "=r,rm") (match_operand:DI 1 "general_operand" "m,riF"))] "" "* { return (char *) output_move_double (operands); }") ;;- conversion instructions ;;- NONE
;;- zero extension instructions ;; See comments by `andsi' for when andl is faster than movzx.
(define_insn "zero_extendhisi2" [(set (match_operand:SI 0 "general_operand" "=r") (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))] "" "* { if ((TARGET_486 || REGNO (operands[0]) == 0) && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) { rtx xops[2]; xops[0] = operands[0]; xops[1] = GEN_INT (0xffff); output_asm_insn (AS2 (and%L0,%1,%k0), xops); RET; }
#ifdef INTEL_SYNTAX return AS2 (movzx,%1,%0); #else return AS2 (movz%W0%L0,%1,%0); #endif }")
(define_insn "zero_extendqihi2" [(set (match_operand:HI 0 "general_operand" "=r") (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "" "* { if ((TARGET_486 || REGNO (operands[0]) == 0) && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) { rtx xops[2]; xops[0] = operands[0]; xops[1] = GEN_INT (0xff); output_asm_insn (AS2 (and%L0,%1,%k0), xops); RET; }
#ifdef INTEL_SYNTAX return AS2 (movzx,%1,%0); #else return AS2 (movz%B0%W0,%1,%0); #endif }")
(define_insn "zero_extendqisi2" [(set (match_operand:SI 0 "general_operand" "=r") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "" "* { if ((TARGET_486 || REGNO (operands[0]) == 0) && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])) { rtx xops[2]; xops[0] = operands[0]; xops[1] = GEN_INT (0xff); output_asm_insn (AS2 (and%L0,%1,%k0), xops); RET; }
#ifdef INTEL_SYNTAX return AS2 (movzx,%1,%0); #else return AS2 (movz%B0%L0,%1,%0); #endif }")
(define_insn "zero_extendsidi2" [(set (match_operand:DI 0 "register_operand" "=r") (zero_extend:DI (match_operand:SI 1 "register_operand" "0")))] "" "* { operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); return AS2 (xor%L0,%0,%0); }") ;;- sign extension instructions
(define_insn "extendsidi2" [(set (match_operand:DI 0 "register_operand" "=r") (sign_extend:DI (match_operand:SI 1 "register_operand" "0")))] "" "* { if (REGNO (operands[0]) == 0) { /* This used to be cwtl, but that extends HI to SI somehow. */ #ifdef INTEL_SYNTAX return "cdq"; #else return "cltd"; #endif }
operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); output_asm_insn (AS2 (mov%L0,%0,%1), operands);
operands[0] = GEN_INT (31); return AS2 (sar%L1,%0,%1); }")
;; Note that the i386 programmers' manual says that the opcodes ;; are named movsx..., but the assembler on Unix does not accept that. ;; We use what the Unix assembler expects.
(define_insn "extendhisi2" [(set (match_operand:SI 0 "general_operand" "=r") (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))] "" "* { if (REGNO (operands[0]) == 0 && REG_P (operands[1]) && REGNO (operands[1]) == 0) #ifdef INTEL_SYNTAX return "cwde"; #else return "cwtl"; #endif
#ifdef INTEL_SYNTAX return AS2 (movsx,%1,%0); #else return AS2 (movs%W0%L0,%1,%0); #endif }")
(define_insn "extendqihi2" [(set (match_operand:HI 0 "general_operand" "=r") (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "" "* { if (REGNO (operands[0]) == 0 && REG_P (operands[1]) && REGNO (operands[1]) == 0) return "cbtw";
#ifdef INTEL_SYNTAX return AS2 (movsx,%1,%0); #else return AS2 (movs%B0%W0,%1,%0); #endif }")
(define_insn "extendqisi2" [(set (match_operand:SI 0 "general_operand" "=r") (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))] "" "* { #ifdef INTEL_SYNTAX return AS2 (movsx,%1,%0); #else return AS2 (movs%B0%L0,%1,%0); #endif }") ;; Conversions between float and double.
(define_insn "extendsfdf2" [(set (match_operand:DF 0 "general_operand" "=fm,f") (float_extend:DF (match_operand:SF 1 "general_operand" "f,fm")))] "TARGET_80387" "* { int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
if (NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fld%z0,%y1)); RET; }
if (NON_STACK_REG_P (operands[0])) { output_to_reg (operands[0], stack_top_dies); RET; }
if (STACK_TOP_P (operands[0])) return AS1 (fld%z1,%y1);
if (GET_CODE (operands[0]) == MEM) { if (stack_top_dies) return AS1 (fstp%z0,%y0); else return AS1 (fst%z0,%y0); }
abort (); }")
(define_expand "truncdfsf2" [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "") (float_truncate:SF (match_operand:DF 1 "register_operand" ""))) (clobber (match_dup 2))])] "TARGET_80387" " { operands[2] = (rtx) assign_386_stack_local (SFmode, 0); }")
;; This cannot output into an f-reg because there is no way to be sure ;; of truncating in that case. Otherwise this is just like a simple move ;; insn. So we pretend we can output to a reg in order to get better ;; register preferencing, but we really use a stack slot.
(define_insn "" [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m") (float_truncate:SF (match_operand:DF 1 "register_operand" "0,f"))) (clobber (match_operand:SF 2 "memory_operand" "m,m"))] "TARGET_80387" "* { int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
if (GET_CODE (operands[0]) == MEM) { if (stack_top_dies) return AS1 (fstp%z0,%0); else return AS1 (fst%z0,%0); } else if (STACK_TOP_P (operands[0])) { output_asm_insn (AS1 (fstp%z2,%y2), operands); return AS1 (fld%z2,%y2); } else abort (); }") ;; The 387 requires that the stack top dies after converting to DImode.
;; Represent an unsigned conversion from SImode to MODE_FLOAT by first ;; doing a signed conversion to DImode, and then taking just the low ;; part.
(define_expand "fixuns_truncdfsi2" [(set (match_dup 4) (match_operand:DF 1 "register_operand" "")) (parallel [(set (match_dup 2) (fix:DI (fix:DF (match_dup 4)))) (clobber (match_dup 4)) (clobber (match_dup 5)) (clobber (match_dup 6)) (clobber (match_scratch:SI 7 ""))]) (set (match_operand:SI 0 "general_operand" "") (match_dup 3))] "TARGET_80387" " { operands[2] = gen_reg_rtx (DImode); operands[3] = gen_lowpart (SImode, operands[2]); operands[4] = gen_reg_rtx (DFmode); operands[5] = (rtx) assign_386_stack_local (SImode, 0); operands[6] = (rtx) assign_386_stack_local (SImode, 1); }")
(define_expand "fixuns_truncsfsi2" [(set (match_dup 4) (match_operand:SF 1 "register_operand" "")) (parallel [(set (match_dup 2) (fix:DI (fix:SF (match_dup 4)))) (clobber (match_dup 4)) (clobber (match_dup 5)) (clobber (match_dup 6)) (clobber (match_scratch:SI 7 ""))]) (set (match_operand:SI 0 "general_operand" "") (match_dup 3))] "TARGET_80387" " { operands[2] = gen_reg_rtx (DImode); operands[3] = gen_lowpart (SImode, operands[2]); operands[4] = gen_reg_rtx (SFmode); operands[5] = (rtx) assign_386_stack_local (SImode, 0); operands[6] = (rtx) assign_386_stack_local (SImode, 1); }")
;; Signed conversion to DImode.
(define_expand "fix_truncdfdi2" [(set (match_dup 2) (match_operand:DF 1 "register_operand" "")) (parallel [(set (match_operand:DI 0 "general_operand" "") (fix:DI (fix:DF (match_dup 2)))) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_dup 4)) (clobber (match_scratch:SI 5 ""))])] "TARGET_80387" " { operands[1] = copy_to_mode_reg (DFmode, operands[1]); operands[2] = gen_reg_rtx (DFmode); operands[3] = (rtx) assign_386_stack_local (SImode, 0); operands[4] = (rtx) assign_386_stack_local (SImode, 1); }")
(define_expand "fix_truncsfdi2" [(set (match_dup 2) (match_operand:SF 1 "register_operand" "")) (parallel [(set (match_operand:DI 0 "general_operand" "") (fix:DI (fix:SF (match_dup 2)))) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_dup 4)) (clobber (match_scratch:SI 5 ""))])] "TARGET_80387" " { operands[1] = copy_to_mode_reg (SFmode, operands[1]); operands[2] = gen_reg_rtx (SFmode); operands[3] = (rtx) assign_386_stack_local (SImode, 0); operands[4] = (rtx) assign_386_stack_local (SImode, 1); }")
;; These match a signed conversion of either DFmode or SFmode to DImode.
(define_insn "" [(set (match_operand:DI 0 "general_operand" "=rm") (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f")))) (clobber (match_dup 1)) (clobber (match_operand:SI 2 "memory_operand" "m")) (clobber (match_operand:SI 3 "memory_operand" "m")) (clobber (match_scratch:SI 4 "=&q"))] "TARGET_80387" "* return (char *) output_fix_trunc (insn, operands);")
(define_insn "" [(set (match_operand:DI 0 "general_operand" "=rm") (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f")))) (clobber (match_dup 1)) (clobber (match_operand:SI 2 "memory_operand" "m")) (clobber (match_operand:SI 3 "memory_operand" "m")) (clobber (match_scratch:SI 4 "=&q"))] "TARGET_80387" "* return (char *) output_fix_trunc (insn, operands);")
;; Signed MODE_FLOAT conversion to SImode.
(define_expand "fix_truncdfsi2" [(parallel [(set (match_operand:SI 0 "general_operand" "") (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "")))) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_scratch:SI 4 ""))])] "TARGET_80387" " { operands[2] = (rtx) assign_386_stack_local (SImode, 0); operands[3] = (rtx) assign_386_stack_local (SImode, 1); }")
(define_expand "fix_truncsfsi2" [(parallel [(set (match_operand:SI 0 "general_operand" "") (fix:SI (fix:SF (match_operand:SF 1 "register_operand" "")))) (clobber (match_dup 2)) (clobber (match_dup 3)) (clobber (match_scratch:SI 4 ""))])] "TARGET_80387" " { operands[2] = (rtx) assign_386_stack_local (SImode, 0); operands[3] = (rtx) assign_386_stack_local (SImode, 1); }")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=rm") (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f")))) (clobber (match_operand:SI 2 "memory_operand" "m")) (clobber (match_operand:SI 3 "memory_operand" "m")) (clobber (match_scratch:SI 4 "=&q"))] "TARGET_80387" "* return (char *) output_fix_trunc (insn, operands);")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=rm") (fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f")))) (clobber (match_operand:SI 2 "memory_operand" "m")) (clobber (match_operand:SI 3 "memory_operand" "m")) (clobber (match_scratch:SI 4 "=&q"))] "TARGET_80387" "* return (char *) output_fix_trunc (insn, operands);") ;; Conversion between fixed point and floating point. ;; The actual pattern that matches these is at the end of this file.
;; ??? Possibly represent floatunssidf2 here in gcc2.
(define_expand "floatsisf2" [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "floatdisf2" [(set (match_operand:SF 0 "register_operand" "") (float:SF (match_operand:DI 1 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "floatsidf2" [(set (match_operand:DF 0 "register_operand" "") (float:DF (match_operand:SI 1 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "floatdidf2" [(set (match_operand:DF 0 "register_operand" "") (float:DF (match_operand:DI 1 "nonimmediate_operand" "")))] "TARGET_80387" "")
;; This will convert from SImode or DImode to MODE_FLOAT.
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (float:DF (match_operand:DI 1 "nonimmediate_operand" "rm")))] "TARGET_80387" "* { if (NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fild%z0,%1)); RET; } else if (GET_CODE (operands[1]) == MEM) return AS1 (fild%z1,%1); else abort (); }")
(define_insn "" [(set (match_operand:SF 0 "register_operand" "=f") (float:SF (match_operand:DI 1 "nonimmediate_operand" "rm")))] "TARGET_80387" "* { if (NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fild%z0,%1)); RET; } else if (GET_CODE (operands[1]) == MEM) return AS1 (fild%z1,%1); else abort (); }")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (float:DF (match_operand:SI 1 "nonimmediate_operand" "rm")))] "TARGET_80387" "* { if (NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fild%z0,%1)); RET; } else if (GET_CODE (operands[1]) == MEM) return AS1 (fild%z1,%1); else abort (); }")
(define_insn "" [(set (match_operand:SF 0 "register_operand" "=f") (float:SF (match_operand:SI 1 "nonimmediate_operand" "rm")))] "TARGET_80387" "* { if (NON_STACK_REG_P (operands[1])) { output_op_from_reg (operands[1], AS1 (fild%z0,%1)); RET; } else if (GET_CODE (operands[1]) == MEM) return AS1 (fild%z1,%1); else abort (); }") ;;- add instructions
(define_insn "adddi3" [(set (match_operand:DI 0 "general_operand" "=&r,ro") (plus:DI (match_operand:DI 1 "general_operand" "%0,0") (match_operand:DI 2 "general_operand" "o,riF")))] "" "* { rtx low[3], high[3];
CC_STATUS_INIT;
split_di (operands, 3, low, high);
if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) { output_asm_insn (AS2 (add%L0,%2,%0), low); output_asm_insn (AS2 (adc%L0,%2,%0), high); } else output_asm_insn (AS2 (add%L0,%2,%0), high); RET; }")
;; On a 486, it is faster to do movl/addl than to do a single leal if ;; operands[1] and operands[2] are both registers.
(define_insn "addsi3" [(set (match_operand:SI 0 "general_operand" "=?r,rm,r") (plus:SI (match_operand:SI 1 "general_operand" "%r,0,0") (match_operand:SI 2 "general_operand" "ri,ri,rm")))] "" "* { if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) { if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2])) return AS2 (add%L0,%1,%0);
if (! TARGET_486 || ! REG_P (operands[2]))
{
CC_STATUS_INIT;
if (operands[2] == stack_pointer_rtx)
{
rtx temp;
temp = operands[1];
operands[1] = operands[2];
operands[2] = temp;
}
if (operands[2] != stack_pointer_rtx)
{
operands[1] = SET_SRC (PATTERN (insn));
return AS2 (lea%L0,%a1,%0);
}
}
output_asm_insn (AS2 (mov%L0,%1,%0), operands);
}
if (operands[2] == const1_rtx) return AS1 (inc%L0,%0);
if (operands[2] == constm1_rtx) return AS1 (dec%L0,%0);
return AS2 (add%L0,%2,%0); }")
;; ??? `lea' here, for three operand add? If leaw is used, only %bx, ;; %si and %di can appear in SET_SRC, and output_asm_insn might not be ;; able to handle the operand. But leal always works?
(define_insn "addhi3" [(set (match_operand:HI 0 "general_operand" "=rm,r") (plus:HI (match_operand:HI 1 "general_operand" "%0,0") (match_operand:HI 2 "general_operand" "ri,rm")))] "" "* { if (operands[2] == const1_rtx) return AS1 (inc%W0,%0);
if (operands[2] == constm1_rtx) return AS1 (dec%W0,%0);
return AS2 (add%W0,%2,%0); }")
(define_insn "addqi3" [(set (match_operand:QI 0 "general_operand" "=qm,q") (plus:QI (match_operand:QI 1 "general_operand" "%0,0") (match_operand:QI 2 "general_operand" "qn,qmn")))] "" "* { if (operands[2] == const1_rtx) return AS1 (inc%B0,%0);
if (operands[2] == constm1_rtx) return AS1 (dec%B0,%0);
return AS2 (add%B0,%2,%0); }")
;Lennart Augustsson augustss@cs.chalmers.se ;says this pattern just makes slower code: ; pushl %ebp ; addl $-80,(%esp) ;instead of ; leal -80(%ebp),%eax ; pushl %eax ; ;(define_insn "" ; [(set (match_operand:SI 0 "push_operand" "=<") ; (plus:SI (match_operand:SI 1 "general_operand" "%r") ; (match_operand:SI 2 "general_operand" "ri")))] ; "" ; "* ;{ ; rtx xops[4]; ; xops[0] = operands[0]; ; xops[1] = operands[1]; ; xops[2] = operands[2]; ; xops[3] = gen_rtx (MEM, SImode, stack_pointer_rtx); ; output_asm_insn ("push%z1 %1", xops); ; output_asm_insn (AS2 (add%z3,%2,%3), xops); ; RET; ;}")
;; addsi3 is faster, so put this after.
(define_insn "" [(set (match_operand:SI 0 "register_operand" "=r") (match_operand:QI 1 "address_operand" "p"))] "" "* { CC_STATUS_INIT; /* Adding a constant to a register is faster with an add. / / ??? can this ever happen? */ if (GET_CODE (operands[1]) == PLUS && GET_CODE (XEXP (operands[1], 1)) == CONST_INT && rtx_equal_p (operands[0], XEXP (operands[1], 0))) { operands[1] = XEXP (operands[1], 1);
if (operands[1] == const1_rtx)
return AS1 (inc%L0,%0);
if (operands[1] == constm1_rtx)
return AS1 (dec%L0,%0);
return AS2 (add%L0,%1,%0);
}
return AS2 (lea%L0,%a1,%0); }")
;; The patterns that match these are at the end of this file.
(define_expand "adddf3" [(set (match_operand:DF 0 "register_operand" "") (plus:DF (match_operand:DF 1 "nonimmediate_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "addsf3" [(set (match_operand:SF 0 "register_operand" "") (plus:SF (match_operand:SF 1 "nonimmediate_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387" "") ;;- subtract instructions
(define_insn "subdi3" [(set (match_operand:DI 0 "general_operand" "=&r,ro") (minus:DI (match_operand:DI 1 "general_operand" "0,0") (match_operand:DI 2 "general_operand" "o,riF")))] "" "* { rtx low[3], high[3];
CC_STATUS_INIT;
split_di (operands, 3, low, high);
if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0) { output_asm_insn (AS2 (sub%L0,%2,%0), low); output_asm_insn (AS2 (sbb%L0,%2,%0), high); } else output_asm_insn (AS2 (sub%L0,%2,%0), high);
RET; }")
(define_insn "subsi3" [(set (match_operand:SI 0 "general_operand" "=rm,r") (minus:SI (match_operand:SI 1 "general_operand" "0,0") (match_operand:SI 2 "general_operand" "ri,rm")))] "" "* return AS2 (sub%L0,%2,%0);")
(define_insn "subhi3" [(set (match_operand:HI 0 "general_operand" "=rm,r") (minus:HI (match_operand:HI 1 "general_operand" "0,0") (match_operand:HI 2 "general_operand" "ri,rm")))] "" "* return AS2 (sub%W0,%2,%0);")
(define_insn "subqi3" [(set (match_operand:QI 0 "general_operand" "=qm,q") (minus:QI (match_operand:QI 1 "general_operand" "0,0") (match_operand:QI 2 "general_operand" "qn,qmn")))] "" "* return AS2 (sub%B0,%2,%0);")
;; The patterns that match these are at the end of this file.
(define_expand "subdf3" [(set (match_operand:DF 0 "register_operand" "") (minus:DF (match_operand:DF 1 "nonimmediate_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "subsf3" [(set (match_operand:SF 0 "register_operand" "") (minus:SF (match_operand:SF 1 "nonimmediate_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387" "") ;;- multiply instructions
;(define_insn "mulqi3" ; [(set (match_operand:QI 0 "general_operand" "=a") ; (mult:QI (match_operand:QI 1 "general_operand" "%0") ; (match_operand:QI 2 "general_operand" "qm")))] ; "" ; "imul%B0 %2,%0")
(define_insn "" [(set (match_operand:HI 0 "general_operand" "=r") (mult:SI (match_operand:HI 1 "general_operand" "%0") (match_operand:HI 2 "general_operand" "r")))] "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80" "* return AS2 (imul%W0,%2,%0);")
(define_insn "mulhi3" [(set (match_operand:HI 0 "general_operand" "=r,r") (mult:SI (match_operand:HI 1 "general_operand" "%0,rm") (match_operand:HI 2 "general_operand" "g,i")))] "" "* { if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == REGNO (operands[0]) && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) /* Assembler has weird restrictions. */ return AS2 (imul%W0,%2,%0); return AS3 (imul%W0,%2,%1,%0); }")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=r") (mult:SI (match_operand:SI 1 "general_operand" "%0") (match_operand:SI 2 "general_operand" "r")))] "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80" "* return AS2 (imul%L0,%2,%0);")
(define_insn "mulsi3" [(set (match_operand:SI 0 "general_operand" "=r,r") (mult:SI (match_operand:SI 1 "general_operand" "%0,rm") (match_operand:SI 2 "general_operand" "g,i")))] "" "* { if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == REGNO (operands[0]) && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG)) /* Assembler has weird restrictions. */ return AS2 (imul%L0,%2,%0); return AS3 (imul%L0,%2,%1,%0); }")
(define_insn "" [(set (match_operand:HI 0 "general_operand" "=a") (mult:SI (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "%0")) (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))] "" "mul%B0 %2")
;; The patterns that match these are at the end of this file.
(define_expand "muldf3" [(set (match_operand:DF 0 "register_operand" "") (mult:DF (match_operand:DF 1 "nonimmediate_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "mulsf3" [(set (match_operand:SF 0 "register_operand" "") (mult:SF (match_operand:SF 1 "nonimmediate_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387" "") ;;- divide instructions
(define_insn "divqi3" [(set (match_operand:QI 0 "general_operand" "=a") (div:QI (match_operand:HI 1 "general_operand" "0") (match_operand:QI 2 "general_operand" "qm")))] "" "idiv%B0 %2")
(define_insn "udivqi3" [(set (match_operand:QI 0 "general_operand" "=a") (udiv:QI (match_operand:HI 1 "general_operand" "0") (match_operand:QI 2 "general_operand" "qm")))] "" "div%B0 %2")
;; The patterns that match these are at the end of this file.
(define_expand "divdf3" [(set (match_operand:DF 0 "register_operand" "") (div:DF (match_operand:DF 1 "nonimmediate_operand" "") (match_operand:DF 2 "nonimmediate_operand" "")))] "TARGET_80387" "")
(define_expand "divsf3" [(set (match_operand:SF 0 "register_operand" "") (div:SF (match_operand:SF 1 "nonimmediate_operand" "") (match_operand:SF 2 "nonimmediate_operand" "")))] "TARGET_80387" "") ;; Remainder instructions.
(define_insn "divmodsi4" [(set (match_operand:SI 0 "register_operand" "=a") (div:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "general_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=&d") (mod:SI (match_dup 1) (match_dup 2)))] "" "* { #ifdef INTEL_SYNTAX output_asm_insn ("cdq", operands); #else output_asm_insn ("cltd", operands); #endif return AS1 (idiv%L0,%2); }")
(define_insn "divmodhi4" [(set (match_operand:HI 0 "register_operand" "=a") (div:HI (match_operand:HI 1 "register_operand" "0") (match_operand:HI 2 "general_operand" "rm"))) (set (match_operand:HI 3 "register_operand" "=&d") (mod:HI (match_dup 1) (match_dup 2)))] "" "cwtd;idiv%W0 %2")
;; ??? Can we make gcc zero extend operand[0]? (define_insn "udivmodsi4" [(set (match_operand:SI 0 "register_operand" "=a") (udiv:SI (match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "general_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=&d") (umod:SI (match_dup 1) (match_dup 2)))] "" "* { output_asm_insn (AS2 (xor%L3,%3,%3), operands); return AS1 (div%L0,%2); }")
;; ??? Can we make gcc zero extend operand[0]? (define_insn "udivmodhi4" [(set (match_operand:HI 0 "register_operand" "=a") (udiv:HI (match_operand:HI 1 "register_operand" "0") (match_operand:HI 2 "general_operand" "rm"))) (set (match_operand:HI 3 "register_operand" "=&d") (umod:HI (match_dup 1) (match_dup 2)))] "" "* { output_asm_insn (AS2 (xor%W0,%3,%3), operands); return AS1 (div%W0,%2); }")
/* ;;this should be a valid double division which we may want to add
(define_insn "" [(set (match_operand:SI 0 "register_operand" "=a") (udiv:DI (match_operand:DI 1 "register_operand" "a") (match_operand:SI 2 "general_operand" "rm"))) (set (match_operand:SI 3 "register_operand" "=d") (umod:SI (match_dup 1) (match_dup 2)))] "" "div%L0 %2,%0") */ ;;- and instructions
;; On i386, ;; movzbl %bl,%ebx ;; is faster than ;; andl $255,%ebx ;; ;; but if the reg is %eax, then the "andl" is faster. ;; ;; On i486, the "andl" is always faster than the "movzbl". ;; ;; On both i386 and i486, a three operand AND is as fast with movzbl or ;; movzwl as with andl, if operands[0] != operands[1].
;; The r' in
rm' for operand 3 looks redundant, but it causes
;; optional reloads to be generated if op 3 is a pseudo in a stack slot.
;; ??? What if we only change one byte of an offsettable memory reference? (define_insn "andsi3" [(set (match_operand:SI 0 "general_operand" "=r,r,rm,r") (and:SI (match_operand:SI 1 "general_operand" "%rm,qm,0,0") (match_operand:SI 2 "general_operand" "L,K,ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0]) && (! REG_P (operands[1]) || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0) && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1]))) { /* ??? tege: Should forget CC_STATUS only if we clobber a remembered operand. Fix that later. */ CC_STATUS_INIT; #ifdef INTEL_SYNTAX return AS2 (movzx,%w1,%0); #else return AS2 (movz%W0%L0,%w1,%0); #endif }
if (INTVAL (operands[2]) == 0xff && REG_P (operands[0])
&& !(REG_P (operands[1]) && NON_QI_REG_P (operands[1]))
&& (! REG_P (operands[1])
|| REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
&& (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1])))
{
/* ??? tege: Should forget CC_STATUS only if we clobber a
remembered operand. Fix that later. */
CC_STATUS_INIT;
#ifdef INTEL_SYNTAX return AS2 (movzx,%b1,%0); #else return AS2 (movz%B0%L0,%b1,%0); #endif }
if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0)
{
CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xffffff00)
{
operands[2] = const0_rtx;
return AS2 (mov%B0,%2,%b0);
}
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
return AS2 (and%B0,%2,%b0);
}
if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0)
{
CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xffff00ff)
{
operands[2] = const0_rtx;
return AS2 (mov%B0,%2,%h0);
}
operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
return AS2 (and%B0,%2,%h0);
}
if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000)
{
operands[2] = const0_rtx;
return AS2 (mov%W0,%2,%w0);
}
}
return AS2 (and%L0,%2,%0); }")
(define_insn "andhi3" [(set (match_operand:HI 0 "general_operand" "=rm,r") (and:HI (match_operand:HI 1 "general_operand" "%0,0") (match_operand:HI 2 "general_operand" "ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { /* Can we ignore the upper byte? */ if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) && (INTVAL (operands[2]) & 0xff00) == 0xff00) { CC_STATUS_INIT;
if ((INTVAL (operands[2]) & 0xff) == 0)
{
operands[2] = const0_rtx;
return AS2 (mov%B0,%2,%b0);
}
operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
return AS2 (and%B0,%2,%b0);
}
/* Can we ignore the lower byte? */
/* ??? what about offsettable memory references? */
if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff)
{
CC_STATUS_INIT;
if ((INTVAL (operands[2]) & 0xff00) == 0)
{
operands[2] = const0_rtx;
return AS2 (mov%B0,%2,%h0);
}
operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
return AS2 (and%B0,%2,%h0);
}
}
return AS2 (and%W0,%2,%0); }")
(define_insn "andqi3" [(set (match_operand:QI 0 "general_operand" "=qm,q") (and:QI (match_operand:QI 1 "general_operand" "%0,0") (match_operand:QI 2 "general_operand" "qn,qmn")))] "" "* return AS2 (and%B0,%2,%0);")
/* I am nervous about these two.. add them later.. ;I presume this means that we have something in say op0= eax which is small ;and we want to and it with memory so we can do this by just an ;andb m,%al and have success. (define_insn "" [(set (match_operand:SI 0 "general_operand" "=r") (and:SI (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")) (match_operand:SI 2 "general_operand" "0")))] "GET_CODE (operands[2]) == CONST_INT && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))" "and%W0 %1,%0")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=q") (and:SI (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")) (match_operand:SI 2 "general_operand" "0")))] "GET_CODE (operands[2]) == CONST_INT && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))" "and%L0 %1,%0")
*/ ;;- Bit set (inclusive or) instructions
;; ??? What if we only change one byte of an offsettable memory reference? (define_insn "iorsi3" [(set (match_operand:SI 0 "general_operand" "=rm,r") (ior:SI (match_operand:SI 1 "general_operand" "%0,0") (match_operand:SI 2 "general_operand" "ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) && (INTVAL (operands[2]) & ~0xff) == 0) { CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xff)
return AS2 (mov%B0,%2,%b0);
return AS2 (or%B0,%2,%b0);
}
if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
{
CC_STATUS_INIT;
operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
if (INTVAL (operands[2]) == 0xff)
return AS2 (mov%B0,%2,%h0);
return AS2 (or%B0,%2,%h0);
}
}
return AS2 (or%L0,%2,%0); }")
(define_insn "iorhi3" [(set (match_operand:HI 0 "general_operand" "=rm,r") (ior:HI (match_operand:HI 1 "general_operand" "%0,0") (match_operand:HI 2 "general_operand" "ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { /* Can we ignore the upper byte? */ if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) && (INTVAL (operands[2]) & 0xff00) == 0) { CC_STATUS_INIT; if (INTVAL (operands[2]) & 0xffff0000) operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
if (INTVAL (operands[2]) == 0xff)
return AS2 (mov%B0,%2,%b0);
return AS2 (or%B0,%2,%b0);
}
/* Can we ignore the lower byte? */
/* ??? what about offsettable memory references? */
if (QI_REG_P (operands[0])
&& (INTVAL (operands[2]) & 0xff) == 0)
{
CC_STATUS_INIT;
operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
if (INTVAL (operands[2]) == 0xff)
return AS2 (mov%B0,%2,%h0);
return AS2 (or%B0,%2,%h0);
}
}
return AS2 (or%W0,%2,%0); }")
(define_insn "iorqi3" [(set (match_operand:QI 0 "general_operand" "=qm,q") (ior:QI (match_operand:QI 1 "general_operand" "%0,0") (match_operand:QI 2 "general_operand" "qn,qmn")))] "" "* return AS2 (or%B0,%2,%0);") ;;- xor instructions
;; ??? What if we only change one byte of an offsettable memory reference? (define_insn "xorsi3" [(set (match_operand:SI 0 "general_operand" "=rm,r") (xor:SI (match_operand:SI 1 "general_operand" "%0,0") (match_operand:SI 2 "general_operand" "ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) && (INTVAL (operands[2]) & ~0xff) == 0) { CC_STATUS_INIT;
if (INTVAL (operands[2]) == 0xff)
return AS1 (not%B0,%b0);
return AS2 (xor%B0,%2,%b0);
}
if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
{
CC_STATUS_INIT;
operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
if (INTVAL (operands[2]) == 0xff)
return AS1 (not%B0,%h0);
return AS2 (xor%B0,%2,%h0);
}
}
return AS2 (xor%L0,%2,%0); }")
(define_insn "xorhi3" [(set (match_operand:HI 0 "general_operand" "=rm,r") (xor:HI (match_operand:HI 1 "general_operand" "%0,0") (match_operand:HI 2 "general_operand" "ri,rm")))] "" "* { if (GET_CODE (operands[2]) == CONST_INT && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))) { /* Can we ignore the upper byte? */ if ((! REG_P (operands[0]) || QI_REG_P (operands[0])) && (INTVAL (operands[2]) & 0xff00) == 0) { CC_STATUS_INIT; if (INTVAL (operands[2]) & 0xffff0000) operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
if (INTVAL (operands[2]) == 0xff)
return AS1 (not%B0,%b0);
return AS2 (xor%B0,%2,%b0);
}
/* Can we ignore the lower byte? */
/* ??? what about offsettable memory references? */
if (QI_REG_P (operands[0])
&& (INTVAL (operands[2]) & 0xff) == 0)
{
CC_STATUS_INIT;
operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
if (INTVAL (operands[2]) == 0xff)
return AS1 (not%B0,%h0);
return AS2 (xor%B0,%2,%h0);
}
}
return AS2 (xor%W0,%2,%0); }")
(define_insn "xorqi3" [(set (match_operand:QI 0 "general_operand" "=qm,q") (xor:QI (match_operand:QI 1 "general_operand" "%0,0") (match_operand:QI 2 "general_operand" "qn,qm")))] "" "* return AS2 (xor%B0,%2,%0);") ;;- negation instructions
(define_insn "negdi2" [(set (match_operand:DI 0 "general_operand" "=&ro") (neg:DI (match_operand:DI 1 "general_operand" "0")))] "" "* { rtx xops[2], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = const0_rtx; xops[1] = high[0];
output_asm_insn (AS1 (neg%L0,%0), low); output_asm_insn (AS2 (adc%L1,%0,%1), xops); output_asm_insn (AS1 (neg%L0,%0), high); RET; }")
(define_insn "negsi2" [(set (match_operand:SI 0 "general_operand" "=rm") (neg:SI (match_operand:SI 1 "general_operand" "0")))] "" "neg%L0 %0")
(define_insn "neghi2" [(set (match_operand:HI 0 "general_operand" "=rm") (neg:HI (match_operand:HI 1 "general_operand" "0")))] "" "neg%W0 %0")
(define_insn "negqi2" [(set (match_operand:QI 0 "general_operand" "=qm") (neg:QI (match_operand:QI 1 "general_operand" "0")))] "" "neg%B0 %0")
(define_insn "negsf2" [(set (match_operand:SF 0 "register_operand" "=f") (neg:SF (match_operand:SF 1 "general_operand" "0")))] "TARGET_80387" "fchs")
(define_insn "negdf2" [(set (match_operand:DF 0 "register_operand" "=f") (neg:DF (match_operand:DF 1 "general_operand" "0")))] "TARGET_80387" "fchs")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (neg:DF (float_extend:DF (match_operand:SF 1 "general_operand" "0"))))] "TARGET_80387" "fchs") ;; Absolute value instructions
(define_insn "abssf2" [(set (match_operand:SF 0 "register_operand" "=f") (abs:SF (match_operand:SF 1 "general_operand" "0")))] "TARGET_80387" "fabs")
(define_insn "absdf2" [(set (match_operand:DF 0 "register_operand" "=f") (abs:DF (match_operand:DF 1 "general_operand" "0")))] "TARGET_80387" "fabs")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (abs:DF (float_extend:DF (match_operand:SF 1 "general_operand" "0"))))] "TARGET_80387" "fabs")
(define_insn "sqrtsf2" [(set (match_operand:SF 0 "register_operand" "=f") (sqrt:SF (match_operand:SF 1 "general_operand" "0")))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsqrt")
(define_insn "sqrtdf2" [(set (match_operand:DF 0 "register_operand" "=f") (sqrt:DF (match_operand:DF 1 "general_operand" "0")))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsqrt")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (sqrt:DF (float_extend:DF (match_operand:SF 1 "general_operand" "0"))))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsqrt")
(define_insn "sindf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsin")
(define_insn "sinsf2" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 1))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsin")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(float_extend:DF (match_operand:SF 1 "register_operand" "0"))] 1))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fsin")
(define_insn "cosdf2" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 2))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fcos")
(define_insn "cossf2" [(set (match_operand:SF 0 "register_operand" "=f") (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 2))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fcos")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (unspec:DF [(float_extend:DF (match_operand:SF 1 "register_operand" "0"))] 2))] "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)" "fcos") ;;- one complement instructions
(define_insn "one_cmplsi2" [(set (match_operand:SI 0 "general_operand" "=rm") (not:SI (match_operand:SI 1 "general_operand" "0")))] "" "not%L0 %0")
(define_insn "one_cmplhi2" [(set (match_operand:HI 0 "general_operand" "=rm") (not:HI (match_operand:HI 1 "general_operand" "0")))] "" "not%W0 %0")
(define_insn "one_cmplqi2" [(set (match_operand:QI 0 "general_operand" "=qm") (not:QI (match_operand:QI 1 "general_operand" "0")))] "" "not%B0 %0") ;;- arithmetic shift instructions
;; DImode shifts are implemented using the i386 "shift double" opcode, ;; which is written as "sh[lr]d[lw] imm,reg,reg/mem". If the shift count ;; is variable, then the count is in %cl and the "imm" operand is dropped ;; from the assembler input.
;; This instruction shifts the target reg/mem as usual, but instead of ;; shifting in zeros, bits are shifted in from reg operand. If the insn ;; is a left shift double, bits are taken from the high order bits of ;; reg, else if the insn is a shift right double, bits are taken from the ;; low order bits of reg. So if %eax is "1234" and %edx is "5678", ;; "shldl $8,%edx,%eax" leaves %edx unchanged and sets %eax to "2345".
;; Since sh[lr]d does not change the `reg' operand, that is done ;; separately, making all shifts emit pairs of shift double and normal ;; shift. Since sh[lr]d does not shift more than 31 bits, and we wish to ;; support a 63 bit shift, each shift where the count is in a reg expands ;; to three pairs. If the overall shift is by N bits, then the first two ;; pairs shift by N / 2 and the last pair by N & 1.
;; If the shift count is a constant, we need never emit more than one ;; shift pair, instead using moves and sign extension for counts greater ;; than 31.
(define_expand "ashldi3" [(set (match_operand:DI 0 "register_operand" "") (ashift:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" "")))] "" " { if (GET_CODE (operands[2]) != CONST_INT || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) { operands[2] = copy_to_mode_reg (QImode, operands[2]); emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1], operands[2])); } else emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2]));
DONE; }")
(define_insn "ashldi3_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (ashift:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "const_int_operand" "J")))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
if (INTVAL (xops[0]) > 31) { output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */ output_asm_insn (AS2 (xor%L2,%2,%2), xops);
if (INTVAL (xops[0]) > 32)
{
xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */
}
}
else { output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops); output_asm_insn (AS2 (sal%L2,%0,%2), xops); } RET; }")
(define_insn "ashldi3_non_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (ashift:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "register_operand" "c"))) (clobber (match_dup 2))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); output_asm_insn (AS2 (sal%L2,%0,%2), xops); output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); output_asm_insn (AS2 (sal%L2,%0,%2), xops);
xops[1] = GEN_INT (7); /* shift count & 1 */
output_asm_insn (AS2 (shr%B0,%1,%0), xops);
output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops); output_asm_insn (AS2 (sal%L2,%0,%2), xops);
RET; }")
;; On i386 and i486, "addl reg,reg" is faster than "sall $1,reg" ;; On i486, movl/sall appears slightly faster than leal, but the leal ;; is smaller - use leal for now unless the shift count is 1.
(define_insn "ashlsi3" [(set (match_operand:SI 0 "general_operand" "=r,rm") (ashift:SI (match_operand:SI 1 "general_operand" "r,0") (match_operand:SI 2 "nonmemory_operand" "M,cI")))] "" "* { if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1])) { if (TARGET_486 && INTVAL (operands[2]) == 1) { output_asm_insn (AS2 (mov%L0,%1,%0), operands); return AS2 (add%L0,%1,%0); } else { CC_STATUS_INIT;
if (operands[1] == stack_pointer_rtx)
{
output_asm_insn (AS2 (mov%L0,%1,%0), operands);
operands[1] = operands[0];
}
operands[1] = gen_rtx (MULT, SImode, operands[1],
GEN_INT (1 << INTVAL (operands[2])));
return AS2 (lea%L0,%a1,%0);
}
}
if (REG_P (operands[2])) return AS2 (sal%L0,%b2,%0);
if (REG_P (operands[0]) && operands[2] == const1_rtx) return AS2 (add%L0,%0,%0);
return AS2 (sal%L0,%2,%0); }")
(define_insn "ashlhi3" [(set (match_operand:HI 0 "general_operand" "=rm") (ashift:HI (match_operand:HI 1 "general_operand" "0") (match_operand:HI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (sal%W0,%b2,%0);
if (REG_P (operands[0]) && operands[2] == const1_rtx) return AS2 (add%W0,%0,%0);
return AS2 (sal%W0,%2,%0); }")
(define_insn "ashlqi3" [(set (match_operand:QI 0 "general_operand" "=qm") (ashift:QI (match_operand:QI 1 "general_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (sal%B0,%b2,%0);
if (REG_P (operands[0]) && operands[2] == const1_rtx) return AS2 (add%B0,%0,%0);
return AS2 (sal%B0,%2,%0); }")
;; See comment above `ashldi3' about how this works.
(define_expand "ashrdi3" [(set (match_operand:DI 0 "register_operand" "") (ashiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" "")))] "" " { if (GET_CODE (operands[2]) != CONST_INT || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) { operands[2] = copy_to_mode_reg (QImode, operands[2]); emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1], operands[2])); } else emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2]));
DONE; }")
(define_insn "ashrdi3_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "const_int_operand" "J")))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
if (INTVAL (xops[0]) > 31) { xops[1] = GEN_INT (31); output_asm_insn (AS2 (mov%L2,%3,%2), xops); output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */
if (INTVAL (xops[0]) > 32)
{
xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */
}
}
else { output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (sar%L3,%0,%3), xops); }
RET; }")
(define_insn "ashrdi3_non_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "register_operand" "c"))) (clobber (match_dup 2))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (sar%L3,%0,%3), xops); output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (sar%L3,%0,%3), xops);
xops[1] = GEN_INT (7); /* shift count & 1 */
output_asm_insn (AS2 (shr%B0,%1,%0), xops);
output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (sar%L3,%0,%3), xops);
RET; }")
(define_insn "ashrsi3" [(set (match_operand:SI 0 "general_operand" "=rm") (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (sar%L0,%b2,%0); else return AS2 (sar%L0,%2,%0); }")
(define_insn "ashrhi3" [(set (match_operand:HI 0 "general_operand" "=rm") (ashiftrt:HI (match_operand:HI 1 "general_operand" "0") (match_operand:HI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (sar%W0,%b2,%0); else return AS2 (sar%W0,%2,%0); }")
(define_insn "ashrqi3" [(set (match_operand:QI 0 "general_operand" "=qm") (ashiftrt:QI (match_operand:QI 1 "general_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (sar%B0,%b2,%0); else return AS2 (sar%B0,%2,%0); }") ;;- logical shift instructions
;; See comment above `ashldi3' about how this works.
(define_expand "lshrdi3" [(set (match_operand:DI 0 "register_operand" "") (lshiftrt:DI (match_operand:DI 1 "register_operand" "") (match_operand:QI 2 "nonmemory_operand" "")))] "" " { if (GET_CODE (operands[2]) != CONST_INT || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')) { operands[2] = copy_to_mode_reg (QImode, operands[2]); emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1], operands[2])); } else emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2]));
DONE; }")
(define_insn "lshrdi3_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (lshiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "const_int_operand" "J")))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
if (INTVAL (xops[0]) > 31) { output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */ output_asm_insn (AS2 (xor%L3,%3,%3), xops);
if (INTVAL (xops[0]) > 32)
{
xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */
}
}
else { output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (shr%L3,%0,%3), xops); }
RET; }")
(define_insn "lshrdi3_non_const_int" [(set (match_operand:DI 0 "register_operand" "=&r") (lshiftrt:DI (match_operand:DI 1 "register_operand" "0") (match_operand:QI 2 "register_operand" "c"))) (clobber (match_dup 2))] "" "* { rtx xops[4], low[1], high[1];
CC_STATUS_INIT;
split_di (operands, 1, low, high); xops[0] = operands[2]; xops[1] = const1_rtx; xops[2] = low[0]; xops[3] = high[0];
output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (shr%L3,%0,%3), xops); output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (shr%L3,%0,%3), xops);
xops[1] = GEN_INT (7); /* shift count & 1 */
output_asm_insn (AS2 (shr%B0,%1,%0), xops);
output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops); output_asm_insn (AS2 (shr%L3,%0,%3), xops);
RET; }")
(define_insn "lshrsi3" [(set (match_operand:SI 0 "general_operand" "=rm") (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (shr%L0,%b2,%0); else return AS2 (shr%L0,%2,%1); }")
(define_insn "lshrhi3" [(set (match_operand:HI 0 "general_operand" "=rm") (lshiftrt:HI (match_operand:HI 1 "general_operand" "0") (match_operand:HI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (shr%W0,%b2,%0); else return AS2 (shr%W0,%2,%0); }")
(define_insn "lshrqi3" [(set (match_operand:QI 0 "general_operand" "=qm") (lshiftrt:QI (match_operand:QI 1 "general_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (shr%B0,%b2,%0); else return AS2 (shr%B0,%2,%0); }") ;;- rotate instructions
(define_insn "rotlsi3" [(set (match_operand:SI 0 "general_operand" "=rm") (rotate:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (rol%L0,%b2,%0); else return AS2 (rol%L0,%2,%0); }")
(define_insn "rotlhi3" [(set (match_operand:HI 0 "general_operand" "=rm") (rotate:HI (match_operand:HI 1 "general_operand" "0") (match_operand:HI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (rol%W0,%b2,%0); else return AS2 (rol%W0,%2,%0); }")
(define_insn "rotlqi3" [(set (match_operand:QI 0 "general_operand" "=qm") (rotate:QI (match_operand:QI 1 "general_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (rol%B0,%b2,%0); else return AS2 (rol%B0,%2,%0); }")
(define_insn "rotrsi3" [(set (match_operand:SI 0 "general_operand" "=rm") (rotatert:SI (match_operand:SI 1 "general_operand" "0") (match_operand:SI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (ror%L0,%b2,%0); else return AS2 (ror%L0,%2,%0); }")
(define_insn "rotrhi3" [(set (match_operand:HI 0 "general_operand" "=rm") (rotatert:HI (match_operand:HI 1 "general_operand" "0") (match_operand:HI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (ror%W0,%b2,%0); else return AS2 (ror%W0,%2,%0); }")
(define_insn "rotrqi3" [(set (match_operand:QI 0 "general_operand" "=qm") (rotatert:QI (match_operand:QI 1 "general_operand" "0") (match_operand:QI 2 "nonmemory_operand" "cI")))] "" "* { if (REG_P (operands[2])) return AS2 (ror%B0,%b2,%0); else return AS2 (ror%B0,%2,%0); }") /* ;; This usually looses. But try a define_expand to recognize a few case ;; we can do efficiently, such as accessing the "high" QImode registers, ;; %ah, %bh, %ch, %dh. (define_insn "insv" [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+&r") (match_operand:SI 1 "general_operand" "i") (match_operand:SI 2 "general_operand" "i")) (match_operand:SI 3 "general_operand" "ri"))] "" "* { if (INTVAL (operands[1]) + INTVAL (operands[2]) > GET_MODE_BITSIZE (SImode)) abort (); if (GET_CODE (operands[3]) == CONST_INT) { unsigned int mask = (1 << INTVAL (operands[1])) - 1; operands[1] = GEN_INT (~(mask << INTVAL (operands[2]))); output_asm_insn (AS2 (and%L0,%1,%0), operands); operands[3] = GEN_INT (INTVAL (operands[3]) << INTVAL (operands[2])); output_asm_insn (AS2 (or%L0,%3,%0), operands); } else { operands[0] = gen_rtx (REG, SImode, REGNO (operands[0])); if (INTVAL (operands[2])) output_asm_insn (AS2 (ror%L0,%2,%0), operands); output_asm_insn (AS3 (shrd%L0,%1,%3,%0), operands); operands[2] = GEN_INT (BITS_PER_WORD - INTVAL (operands[1]) - INTVAL (operands[2])); if (INTVAL (operands[2])) output_asm_insn (AS2 (ror%L0,%2,%0), operands); } RET; }") / / ;; ??? There are problems with the mode of operand[3]. The point of this ;; is to represent an HImode move to a "high byte" register.
(define_expand "insv" [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "immediate_operand" "") (match_operand:SI 2 "immediate_operand" "")) (match_operand:QI 3 "general_operand" "ri"))] "" " { if (GET_CODE (operands[1]) != CONST_INT || GET_CODE (operands[2]) != CONST_INT) FAIL;
if (! (INTVAL (operands[1]) == 8 && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 0)) && ! INTVAL (operands[1]) == 1) FAIL; }")
;; ??? Are these constraints right? (define_insn "" [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+&qo") (const_int 8) (const_int 8)) (match_operand:QI 1 "general_operand" "qn"))] "" "* { if (REG_P (operands[0])) return AS2 (mov%B0,%1,%h0);
operands[0] = adj_offsettable_operand (operands[0], 1); return AS2 (mov%B0,%1,%0); }") */
;; On i386, the register count for a bit operation is not truncated, ;; so SHIFT_COUNT_TRUNCATED must not be defined.
;; On i486, the shift & or/and code is faster than bts or btr. If ;; operands[0] is a MEM, the bt[sr] is half as fast as the normal code.
;; On i386, bts is a little faster if operands[0] is a reg, and a ;; little slower if operands[0] is a MEM, than the shift & or/and code. ;; Use bts & btr, since they reload better.
;; General bit set and clear. (define_insn "" [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+rm") (const_int 1) (match_operand:SI 2 "general_operand" "r")) (match_operand:SI 3 "const_int_operand" "n"))] "! TARGET_486 && GET_CODE (operands[2]) != CONST_INT" "* { CC_STATUS_INIT;
if (INTVAL (operands[3]) == 1) return AS2 (bts%L0,%2,%0); else return AS2 (btr%L0,%2,%0); }")
;; Bit complement. See comments on previous pattern. ;; ??? Is this really worthwhile? (define_insn "" [(set (match_operand:SI 0 "general_operand" "=rm") (xor:SI (ashift:SI (const_int 1) (match_operand:SI 1 "general_operand" "r")) (match_operand:SI 2 "general_operand" "0")))] "! TARGET_486 && GET_CODE (operands[1]) != CONST_INT" "* { CC_STATUS_INIT;
return AS2 (btc%L0,%1,%0); }")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=rm") (xor:SI (match_operand:SI 1 "general_operand" "0") (ashift:SI (const_int 1) (match_operand:SI 2 "general_operand" "r"))))] "! TARGET_486 && GET_CODE (operands[2]) != CONST_INT" "* { CC_STATUS_INIT;
return AS2 (btc%L0,%2,%0); }") ;; Recognizers for bit-test instructions.
;; The bt opcode allows a MEM in operands[0]. But on both i386 and ;; i486, it is faster to copy a MEM to REG and then use bt, than to use ;; bt on the MEM directly.
;; ??? The first argument of a zero_extract must not be reloaded, so ;; don't allow a MEM in the operand predicate without allowing it in the ;; constraint.
(define_insn "" [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r") (const_int 1) (match_operand:SI 1 "general_operand" "r")))] "GET_CODE (operands[1]) != CONST_INT" "* { cc_status.flags |= CC_Z_IN_NOT_C; return AS2 (bt%L0,%1,%0); }")
(define_insn "" [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r") (match_operand:SI 1 "const_int_operand" "n") (match_operand:SI 2 "const_int_operand" "n")))] "" "* { unsigned int mask;
mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); operands[1] = GEN_INT (mask);
if (QI_REG_P (operands[0])) { if ((mask & ~0xff) == 0) { cc_status.flags |= CC_NOT_NEGATIVE; return AS2 (test%B0,%1,%b0); }
if ((mask & ~0xff00) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (mask >> 8);
return AS2 (test%B0,%1,%h0);
}
}
return AS2 (test%L0,%1,%0); }")
;; ??? All bets are off if operand 0 is a volatile MEM reference. ;; The CPU may access unspecified bytes around the actual target byte.
(define_insn "" [(set (cc0) (zero_extract (match_operand:QI 0 "general_operand" "rm") (match_operand:SI 1 "const_int_operand" "n") (match_operand:SI 2 "const_int_operand" "n")))] "GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])" "* { unsigned int mask;
mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]); operands[1] = GEN_INT (mask);
if (! REG_P (operands[0]) || QI_REG_P (operands[0])) { if ((mask & ~0xff) == 0) { cc_status.flags |= CC_NOT_NEGATIVE; return AS2 (test%B0,%1,%b0); }
if ((mask & ~0xff00) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (mask >> 8);
if (QI_REG_P (operands[0]))
return AS2 (test%B0,%1,%h0);
else
{
operands[0] = adj_offsettable_operand (operands[0], 1);
return AS2 (test%B0,%1,%b0);
}
}
if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (mask >> 16);
operands[0] = adj_offsettable_operand (operands[0], 2);
return AS2 (test%B0,%1,%b0);
}
if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0)
{
cc_status.flags |= CC_NOT_NEGATIVE;
operands[1] = GEN_INT (mask >> 24);
operands[0] = adj_offsettable_operand (operands[0], 3);
return AS2 (test%B0,%1,%b0);
}
}
if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM) return AS2 (test%L0,%1,%0);
return AS2 (test%L1,%0,%1); }") ;; Store-flag instructions.
;; For all sCOND expanders, also expand the compare or test insn that
;; generates cc0. Generate an equality comparison if seq' or
sne'.
;; The 386 sCOND opcodes can write to memory. But a gcc sCOND insn may ;; not have any input reloads. A MEM write might need an input reload ;; for the address of the MEM. So don't allow MEM as the SET_DEST.
(define_expand "seq" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (eq:QI (cc0) (const_int 0)))] "" " { if (TARGET_IEEE_FP && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); else operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); }")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (eq:QI (cc0) (const_int 0)))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return AS1 (setnb,%0); else return AS1 (sete,%0); }")
(define_expand "sne" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (ne:QI (cc0) (const_int 0)))] "" " { if (TARGET_IEEE_FP && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); else operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); }")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (ne:QI (cc0) (const_int 0)))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return AS1 (setb,%0); else return AS1 (setne,%0); } ")
(define_expand "sgt" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (gt:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (gt:QI (cc0) (const_int 0)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (sete,%0);
OUTPUT_JUMP ("setg %0", "seta %0", NULL_PTR); }")
(define_expand "sgtu" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (gtu:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (gtu:QI (cc0) (const_int 0)))] "" "* return "seta %0"; ")
(define_expand "slt" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (lt:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (lt:QI (cc0) (const_int 0)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (sete,%0);
OUTPUT_JUMP ("setl %0", "setb %0", "sets %0"); }")
(define_expand "sltu" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (ltu:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (ltu:QI (cc0) (const_int 0)))] "" "* return "setb %0"; ")
(define_expand "sge" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (ge:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (ge:QI (cc0) (const_int 0)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (sete,%0);
OUTPUT_JUMP ("setge %0", "setae %0", "setns %0"); }")
(define_expand "sgeu" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (geu:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (geu:QI (cc0) (const_int 0)))] "" "* return "setae %0"; ")
(define_expand "sle" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (le:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (le:QI (cc0) (const_int 0)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (setb,%0);
OUTPUT_JUMP ("setle %0", "setbe %0", NULL_PTR); }")
(define_expand "sleu" [(match_dup 1) (set (match_operand:QI 0 "register_operand" "") (leu:QI (cc0) (const_int 0)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (match_operand:QI 0 "register_operand" "=q") (leu:QI (cc0) (const_int 0)))] "" "* return "setbe %0"; ") ;; Basic conditional jump instructions. ;; We ignore the overflow flag for signed branch instructions.
;; For all bCOND expanders, also expand the compare or test insn that
;; generates cc0. Generate an equality comparison if beq' or
bne'.
(define_expand "beq" [(match_dup 1) (set (pc) (if_then_else (eq (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" " { if (TARGET_IEEE_FP && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); else operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); }")
(define_insn "" [(set (pc) (if_then_else (eq (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return "jnc %l0"; else return "je %l0"; }")
(define_expand "bne" [(match_dup 1) (set (pc) (if_then_else (ne (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" " { if (TARGET_IEEE_FP && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT) operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1); else operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1); }")
(define_insn "" [(set (pc) (if_then_else (ne (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return "jc %l0"; else return "jne %l0"; }")
(define_expand "bgt" [(match_dup 1) (set (pc) (if_then_else (gt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (gt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (je,%l0);
OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); }")
(define_expand "bgtu" [(match_dup 1) (set (pc) (if_then_else (gtu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (gtu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "ja %l0")
(define_expand "blt" [(match_dup 1) (set (pc) (if_then_else (lt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (lt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (je,%l0);
OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); }")
(define_expand "bltu" [(match_dup 1) (set (pc) (if_then_else (ltu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (ltu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "jb %l0")
(define_expand "bge" [(match_dup 1) (set (pc) (if_then_else (ge (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (ge (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (je,%l0);
OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); }")
(define_expand "bgeu" [(match_dup 1) (set (pc) (if_then_else (geu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (geu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "jae %l0")
(define_expand "ble" [(match_dup 1) (set (pc) (if_then_else (le (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (le (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (jb,%l0);
OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); }")
(define_expand "bleu" [(match_dup 1) (set (pc) (if_then_else (leu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
(define_insn "" [(set (pc) (if_then_else (leu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "jbe %l0") ;; Negated conditional jump instructions.
(define_insn "" [(set (pc) (if_then_else (eq (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return "jc %l0"; else return "jne %l0"; }")
(define_insn "" [(set (pc) (if_then_else (ne (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (cc_prev_status.flags & CC_Z_IN_NOT_C) return "jnc %l0"; else return "je %l0"; }")
(define_insn "" [(set (pc) (if_then_else (gt (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (jne,%l0);
OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR); }")
(define_insn "" [(set (pc) (if_then_else (gtu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "jbe %l0")
(define_insn "" [(set (pc) (if_then_else (lt (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (jne,%l0);
OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0"); }")
(define_insn "" [(set (pc) (if_then_else (ltu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "jae %l0")
(define_insn "" [(set (pc) (if_then_else (ge (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (jne,%l0);
OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0"); }")
(define_insn "" [(set (pc) (if_then_else (geu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "jb %l0")
(define_insn "" [(set (pc) (if_then_else (le (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* { if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387)) return AS1 (jae,%l0);
OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR); }")
(define_insn "" [(set (pc) (if_then_else (leu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "ja %l0") ;; Unconditional and other jump instructions
(define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "jmp %l0")
(define_insn "indirect_jump" [(set (pc) (match_operand:SI 0 "general_operand" "rm"))] "" "* { CC_STATUS_INIT;
return AS1 (jmp,%*%0); }")
;; Implement switch statements when generating PIC code. Switches are ;; implemented by `tablejump' when not using -fpic.
;; Emit code here to do the range checking and make the index zero based.
(define_expand "casesi" [(set (match_dup 5) (minus:SI (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))) (set (cc0) (compare:CC (match_dup 5) (match_operand:SI 2 "general_operand" ""))) (set (pc) (if_then_else (gtu (cc0) (const_int 0)) (label_ref (match_operand 4 "" "")) (pc))) (parallel [(set (pc) (minus:SI (reg:SI 3) (mem:SI (plus:SI (mult:SI (match_dup 5) (const_int 4)) (label_ref (match_operand 3 "" "")))))) (clobber (match_scratch:SI 6 ""))])] "flag_pic" " { operands[5] = gen_reg_rtx (SImode); current_function_uses_pic_offset_table = 1; }")
;; Implement a casesi insn.
;; Each entry in the "addr_diff_vec" looks like this as the result of the ;; two rules below: ;; ;; .long GLOBAL_OFFSET_TABLE+[.-.L2] ;; ;; 1. An expression involving an external reference may only use the ;; addition operator, and only with an assembly-time constant. ;; The example above satisfies this because ".-.L2" is a constant. ;; ;; 2. The symbol GLOBAL_OFFSET_TABLE is magic, and at link time is ;; given the value of "GOT - .", where GOT is the actual address of ;; the Global Offset Table. Therefore, the .long above actually ;; stores the value "( GOT - . ) + [ . - .L2 ]", or "GOT - .L2". The ;; expression "GOT - .L2" by itself would generate an error from as(1). ;; ;; The pattern below emits code that looks like this: ;; ;; movl %ebx,reg ;; subl TABLE@GOTOFF(%ebx,index,4),reg ;; jmp reg ;; ;; The addr_diff_vec contents may be directly referenced with @GOTOFF, since ;; the addr_diff_vec is known to be part of this module. ;; ;; The subl above calculates "GOT - (( GOT - . ) + [ . - .L2 ])", which ;; evaluates to just ".L2".
(define_insn "" [(set (pc) (minus:SI (reg:SI 3) (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "r") (const_int 4)) (label_ref (match_operand 1 "" "")))))) (clobber (match_scratch:SI 2 "=&r"))] "" "* { rtx xops[4];
xops[0] = operands[0]; xops[1] = operands[1]; xops[2] = operands[2]; xops[3] = pic_offset_table_rtx;
output_asm_insn (AS2 (mov%L2,%3,%2), xops); output_asm_insn ("sub%L2 %l1@GOTOFF(%3,%0,4),%2", xops); output_asm_insn (AS1 (jmp,%*%2), xops); ASM_OUTPUT_ALIGN_CODE (asm_out_file); RET; }")
(define_insn "tablejump" [(set (pc) (match_operand:SI 0 "general_operand" "rm")) (use (label_ref (match_operand 1 "" "")))] "" "* { CC_STATUS_INIT;
return AS1 (jmp,%*%0); }")
;; Call insns.
;; If generating PIC code, the predicate indirect_operand will fail ;; for operands[0] containing symbolic references on all of the named ;; call* patterns. Each named pattern is followed by an unnamed pattern ;; that matches any call to a symbolic CONST (ie, a symbol_ref). The ;; unnamed patterns are only used while generating PIC code, because ;; otherwise the named patterns match.
;; Call subroutine returning no value.
(define_expand "call_pop" [(parallel [(call (match_operand:QI 0 "indirect_operand" "") (match_operand:SI 1 "general_operand" "")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "immediate_operand" "")))])] "" " { rtx addr;
if (flag_pic) current_function_uses_pic_offset_table = 1;
/* With half-pic, force the address into a register. */ addr = XEXP (operands[0], 0); if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) XEXP (operands[0], 0) = force_reg (Pmode, addr);
if (! expander_call_insn_operand (operands[0], QImode)) operands[0] = change_address (operands[0], VOIDmode, copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); }")
(define_insn "" [(call (match_operand:QI 0 "call_insn_operand" "m") (match_operand:SI 1 "general_operand" "g")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "immediate_operand" "i")))] "" "* { if (GET_CODE (operands[0]) == MEM && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) { operands[0] = XEXP (operands[0], 0); return AS1 (call,%*%0); } else return AS1 (call,%P0); }")
(define_insn "" [(call (mem:QI (match_operand:SI 0 "symbolic_operand" "")) (match_operand:SI 1 "general_operand" "g")) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 3 "immediate_operand" "i")))] "!HALF_PIC_P ()" "call %P0")
(define_expand "call" [(call (match_operand:QI 0 "indirect_operand" "") (match_operand:SI 1 "general_operand" ""))] ;; Operand 1 not used on the i386. "" " { rtx addr;
if (flag_pic) current_function_uses_pic_offset_table = 1;
/* With half-pic, force the address into a register. */ addr = XEXP (operands[0], 0); if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) XEXP (operands[0], 0) = force_reg (Pmode, addr);
if (! expander_call_insn_operand (operands[0], QImode)) operands[0] = change_address (operands[0], VOIDmode, copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); }")
(define_insn "" [(call (match_operand:QI 0 "call_insn_operand" "m") (match_operand:SI 1 "general_operand" "g"))] ;; Operand 1 not used on the i386. "" "* { if (GET_CODE (operands[0]) == MEM && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) { operands[0] = XEXP (operands[0], 0); return AS1 (call,%*%0); } else return AS1 (call,%P0); }")
(define_insn "" [(call (mem:QI (match_operand:SI 0 "symbolic_operand" "")) (match_operand:SI 1 "general_operand" "g"))] ;; Operand 1 not used on the i386. "!HALF_PIC_P ()" "call %P0")
;; Call subroutine, returning value in operand 0 ;; (which must be a hard register).
(define_expand "call_value_pop" [(parallel [(set (match_operand 0 "" "") (call (match_operand:QI 1 "indirect_operand" "") (match_operand:SI 2 "general_operand" ""))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 4 "immediate_operand" "")))])] "" " { rtx addr;
if (flag_pic) current_function_uses_pic_offset_table = 1;
/* With half-pic, force the address into a register. */ addr = XEXP (operands[1], 0); if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) XEXP (operands[1], 0) = force_reg (Pmode, addr);
if (! expander_call_insn_operand (operands[1], QImode)) operands[1] = change_address (operands[1], VOIDmode, copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); }")
(define_insn "" [(set (match_operand 0 "" "=rf") (call (match_operand:QI 1 "call_insn_operand" "m") (match_operand:SI 2 "general_operand" "g"))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 4 "immediate_operand" "i")))] "" "* { if (GET_CODE (operands[1]) == MEM && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) { operands[1] = XEXP (operands[1], 0); output_asm_insn (AS1 (call,%*%1), operands); } else output_asm_insn (AS1 (call,%P1), operands);
RET; }")
(define_insn "" [(set (match_operand 0 "" "=rf") (call (mem:QI (match_operand:SI 1 "symbolic_operand" "")) (match_operand:SI 2 "general_operand" "g"))) (set (reg:SI 7) (plus:SI (reg:SI 7) (match_operand:SI 4 "immediate_operand" "i")))] "!HALF_PIC_P ()" "call %P1")
(define_expand "call_value" [(set (match_operand 0 "" "") (call (match_operand:QI 1 "indirect_operand" "") (match_operand:SI 2 "general_operand" "")))] ;; Operand 2 not used on the i386. "" " { rtx addr;
if (flag_pic) current_function_uses_pic_offset_table = 1;
/* With half-pic, force the address into a register. */ addr = XEXP (operands[1], 0); if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) XEXP (operands[1], 0) = force_reg (Pmode, addr);
if (! expander_call_insn_operand (operands[1], QImode)) operands[1] = change_address (operands[1], VOIDmode, copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); }")
(define_insn "" [(set (match_operand 0 "" "=rf") (call (match_operand:QI 1 "call_insn_operand" "m") (match_operand:SI 2 "general_operand" "g")))] ;; Operand 2 not used on the i386. "" "* { if (GET_CODE (operands[1]) == MEM && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) { operands[1] = XEXP (operands[1], 0); output_asm_insn (AS1 (call,%*%1), operands); } else output_asm_insn (AS1 (call,%P1), operands);
RET; }")
(define_insn "" [(set (match_operand 0 "" "=rf") (call (mem:QI (match_operand:SI 1 "symbolic_operand" "")) (match_operand:SI 2 "general_operand" "g")))] ;; Operand 2 not used on the i386. "!HALF_PIC_P ()" "call %P1")
(define_expand "untyped_call" [(parallel [(call (match_operand:QI 0 "indirect_operand" "") (const_int 0)) (match_operand:BLK 1 "memory_operand" "") (match_operand 2 "" "")])] "" " { rtx addr;
if (flag_pic) current_function_uses_pic_offset_table = 1;
/* With half-pic, force the address into a register. */ addr = XEXP (operands[0], 0); if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr)) XEXP (operands[0], 0) = force_reg (Pmode, addr);
operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0)); if (! expander_call_insn_operand (operands[1], QImode)) operands[1] = change_address (operands[1], VOIDmode, copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); }")
(define_insn "" [(call (match_operand:QI 0 "call_insn_operand" "m") (const_int 0)) (match_operand:DI 1 "memory_operand" "o") (match_operand 2 "" "")] "" "* { rtx addr = operands[1];
if (GET_CODE (operands[0]) == MEM && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) { operands[0] = XEXP (operands[0], 0); output_asm_insn (AS1 (call,%*%0), operands); } else output_asm_insn (AS1 (call,%P0), operands);
operands[2] = gen_rtx (REG, SImode, 0); output_asm_insn (AS2 (mov%L2,%2,%1), operands);
operands[2] = gen_rtx (REG, SImode, 1); operands[1] = adj_offsettable_operand (addr, 4); output_asm_insn (AS2 (mov%L2,%2,%1), operands);
operands[1] = adj_offsettable_operand (addr, 8); return AS1 (fnsave,%1); }")
(define_insn "" [(call (mem:QI (match_operand:SI 0 "symbolic_operand" "")) (const_int 0)) (match_operand:DI 1 "memory_operand" "o") (match_operand 2 "" "")] "!HALF_PIC_P ()" "* { rtx addr = operands[1];
output_asm_insn (AS1 (call,%P0), operands);
operands[2] = gen_rtx (REG, SImode, 0); output_asm_insn (AS2 (mov%L2,%2,%1), operands);
operands[2] = gen_rtx (REG, SImode, 1); operands[1] = adj_offsettable_operand (addr, 4); output_asm_insn (AS2 (mov%L2,%2,%1), operands);
operands[1] = adj_offsettable_operand (addr, 8); return AS1 (fnsave,%1); }")
;; We use fnsave and frstor to save and restore the floating point result. ;; These are expensive instructions and require a large space to save the ;; FPU state. An more complicated alternative is to use fnstenv to store ;; the FPU environment and test whether the stack top is valid. Store the ;; result of the test, and if it is valid, pop and save the value. The ;; untyped_return would check the test and optionally push the saved value.
(define_expand "untyped_return" [(match_operand:BLK 0 "memory_operand" "") (match_operand 1 "" "")] "" " { rtx valreg1 = gen_rtx (REG, SImode, 0); rtx valreg2 = gen_rtx (REG, SImode, 1); rtx result = operands[0];
/* Restore the FPU state. */ emit_insn (gen_update_return (change_address (result, SImode, plus_constant (XEXP (result, 0), 8))));
/* Reload the function value registers. */ emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0))); emit_move_insn (valreg2, change_address (result, SImode, plus_constant (XEXP (result, 0), 4)));
/* Put USE insns before the return. */ emit_insn (gen_rtx (USE, VOIDmode, valreg1)); emit_insn (gen_rtx (USE, VOIDmode, valreg2));
/* Construct the return. */ expand_null_return ();
DONE; }")
(define_insn "update_return" [(unspec:SI [(match_operand:SI 0 "memory_operand" "m")] 0)] "" "frstor %0")
;; Insn emitted into the body of a function to return from a function. ;; This is only done if the function's epilogue is known to be simple. ;; See comments for simple_386_epilogue in i386.c.
(define_insn "return" [(return)] "simple_386_epilogue ()" "* { function_epilogue (asm_out_file, get_frame_size ()); RET; }")
(define_insn "nop" [(const_int 0)] "" "nop")
(define_expand "movstrsi" [(parallel [(set (match_operand:BLK 0 "memory_operand" "") (match_operand:BLK 1 "memory_operand" "")) (use (match_operand:SI 2 "const_int_operand" "")) (use (match_operand:SI 3 "const_int_operand" "")) (clobber (match_scratch:SI 4 "")) (clobber (match_dup 5)) (clobber (match_dup 6))])] "" " { rtx addr0, addr1;
if (GET_CODE (operands[2]) != CONST_INT) FAIL;
addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0)); addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
operands[5] = addr0; operands[6] = addr1;
operands[0] = gen_rtx (MEM, BLKmode, addr0); operands[1] = gen_rtx (MEM, BLKmode, addr1); }")
;; It might seem that operands 0 & 1 could use predicate register_operand. ;; But strength reduction might offset the MEM expression. So we let ;; reload put the address into %edi & %esi.
(define_insn "" [(set (mem:BLK (match_operand:SI 0 "address_operand" "D")) (mem:BLK (match_operand:SI 1 "address_operand" "S"))) (use (match_operand:SI 2 "const_int_operand" "n")) (use (match_operand:SI 3 "immediate_operand" "i")) (clobber (match_scratch:SI 4 "=&c")) (clobber (match_dup 0)) (clobber (match_dup 1))] "" "* { rtx xops[2];
output_asm_insn ("cld", operands); if (GET_CODE (operands[2]) == CONST_INT) { if (INTVAL (operands[2]) & ~0x03) { xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff); xops[1] = operands[4];
output_asm_insn (AS2 (mov%L1,%0,%1), xops);
#ifdef INTEL_SYNTAX output_asm_insn ("rep movsd", xops); #else output_asm_insn ("rep;movsl", xops); #endif } if (INTVAL (operands[2]) & 0x02) output_asm_insn ("movsw", operands); if (INTVAL (operands[2]) & 0x01) output_asm_insn ("movsb", operands); } else abort (); RET; }")
(define_expand "cmpstrsi" [(parallel [(set (match_operand:SI 0 "general_operand" "") (compare:SI (match_operand:BLK 1 "general_operand" "") (match_operand:BLK 2 "general_operand" ""))) (use (match_operand:SI 3 "general_operand" "")) (use (match_operand:SI 4 "immediate_operand" "")) (clobber (match_dup 5)) (clobber (match_dup 6)) (clobber (match_dup 3))])] "" " { rtx addr1, addr2;
addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0)); addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0)); operands[3] = copy_to_mode_reg (SImode, operands[3]);
operands[5] = addr1; operands[6] = addr2;
operands[1] = gen_rtx (MEM, BLKmode, addr1); operands[2] = gen_rtx (MEM, BLKmode, addr2);
}")
;; memcmp recognizers. The `cmpsb' opcode does nothing if the count is ;; zero. Emit extra code to make sure that a zero-length compare is EQ.
;; It might seem that operands 0 & 1 could use predicate register_operand. ;; But strength reduction might offset the MEM expression. So we let ;; reload put the address into %edi & %esi.
;; ??? Most comparisons have a constant length, and it's therefore ;; possible to know that the length is non-zero, and to avoid the extra ;; code to handle zero-length compares.
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=&r") (compare:SI (mem:BLK (match_operand:SI 1 "address_operand" "S")) (mem:BLK (match_operand:SI 2 "address_operand" "D")))) (use (match_operand:SI 3 "register_operand" "c")) (use (match_operand:SI 4 "immediate_operand" "i")) (clobber (match_dup 1)) (clobber (match_dup 2)) (clobber (match_dup 3))] "" "* { rtx xops[4], label;
label = gen_label_rtx ();
output_asm_insn ("cld", operands); output_asm_insn (AS2 (xor%L0,%0,%0), operands); output_asm_insn ("repz;cmps%B2", operands); output_asm_insn ("je %l0", &label);
xops[0] = operands[0]; xops[1] = gen_rtx (MEM, QImode, gen_rtx (PLUS, SImode, operands[1], constm1_rtx)); xops[2] = gen_rtx (MEM, QImode, gen_rtx (PLUS, SImode, operands[2], constm1_rtx)); xops[3] = operands[3];
output_asm_insn (AS2 (movz%B1%L0,%1,%0), xops); output_asm_insn (AS2 (movz%B2%L3,%2,%3), xops);
output_asm_insn (AS2 (sub%L0,%3,%0), xops); ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (label)); RET; }")
(define_insn "" [(set (cc0) (compare:SI (mem:BLK (match_operand:SI 0 "address_operand" "S")) (mem:BLK (match_operand:SI 1 "address_operand" "D")))) (use (match_operand:SI 2 "register_operand" "c")) (use (match_operand:SI 3 "immediate_operand" "i")) (clobber (match_dup 0)) (clobber (match_dup 1)) (clobber (match_dup 2))] "" "* { rtx xops[2];
cc_status.flags |= CC_NOT_SIGNED;
xops[0] = gen_rtx (REG, QImode, 0); xops[1] = CONST0_RTX (QImode);
output_asm_insn ("cld", operands); output_asm_insn (AS2 (test%B0,%1,%0), xops); return "repz;cmps%B2"; }")
(define_expand "ffssi2" [(set (match_dup 2) (plus:SI (ffs:SI (match_operand:SI 1 "general_operand" "")) (const_int -1))) (set (match_operand:SI 0 "general_operand" "") (plus:SI (match_dup 2) (const_int 1)))] "" "operands[2] = gen_reg_rtx (SImode);")
(define_insn "" [(set (match_operand:SI 0 "general_operand" "=&r") (plus:SI (ffs:SI (match_operand:SI 1 "general_operand" "rm")) (const_int -1)))] "" "* { rtx xops[3]; static int ffssi_label_number; char buffer[30];
xops[0] = operands[0]; xops[1] = operands[1]; xops[2] = constm1_rtx; /* Can there be a way to avoid the jump here? */ output_asm_insn (AS2 (bsf%L0,%1,%0), xops); #ifdef LOCAL_LABEL_PREFIX sprintf (buffer, "jnz %sLFFSSI%d", LOCAL_LABEL_PREFIX, ffssi_label_number); #else sprintf (buffer, "jnz %sLFFSSI%d", "", ffssi_label_number); #endif output_asm_insn (buffer, xops); output_asm_insn (AS2 (mov%L0,%2,%0), xops); #ifdef LOCAL_LABEL_PREFIX sprintf (buffer, "%sLFFSSI%d:", LOCAL_LABEL_PREFIX, ffssi_label_number); #else sprintf (buffer, "%sLFFSSI%d:", "", ffssi_label_number); #endif output_asm_insn (buffer, xops);
ffssi_label_number++; return ""; }")
(define_expand "ffshi2" [(set (match_dup 2) (plus:HI (ffs:HI (match_operand:HI 1 "general_operand" "")) (const_int -1))) (set (match_operand:HI 0 "general_operand" "") (plus:HI (match_dup 2) (const_int 1)))] "" "operands[2] = gen_reg_rtx (HImode);")
(define_insn "" [(set (match_operand:HI 0 "general_operand" "=&r") (plus:HI (ffs:HI (match_operand:SI 1 "general_operand" "rm")) (const_int -1)))] "" "* { rtx xops[3]; static int ffshi_label_number; char buffer[30];
xops[0] = operands[0]; xops[1] = operands[1]; xops[2] = constm1_rtx; output_asm_insn (AS2 (bsf%W0,%1,%0), xops); #ifdef LOCAL_LABEL_PREFIX sprintf (buffer, "jnz %sLFFSHI%d", LOCAL_LABEL_PREFIX, ffshi_label_number); #else sprintf (buffer, "jnz %sLFFSHI%d", "", ffshi_label_number); #endif output_asm_insn (buffer, xops); output_asm_insn (AS2 (mov%W0,%2,%0), xops); #ifdef LOCAL_LABEL_PREFIX sprintf (buffer, "%sLFFSHI%d:", LOCAL_LABEL_PREFIX, ffshi_label_number); #else sprintf (buffer, "%sLFFSHI%d:", "", ffshi_label_number); #endif output_asm_insn (buffer, xops);
ffshi_label_number++; return ""; }") ;; These patterns match the binary 387 instructions for addM3, subM3, ;; mulM3 and divM3. There are three patterns for each of DFmode and ;; SFmode. The first is the normal insn, the second the same insn but ;; with one operand a conversion, and the third the same insn but with ;; the other operand a conversion. The conversion may be SFmode or ;; SImode if the target mode DFmode, but only SImode if the target mode ;; is SFmode.
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_387_op" [(match_operand:DF 1 "nonimmediate_operand" "0,fm") (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (match_operator:DF 3 "binary_387_op" [(float:DF (match_operand:SI 1 "general_operand" "rm")) (match_operand:DF 2 "general_operand" "0")]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_387_op" [(float_extend:DF (match_operand:SF 1 "general_operand" "fm,0")) (match_operand:DF 2 "general_operand" "0,f")]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f") (match_operator:DF 3 "binary_387_op" [(match_operand:DF 1 "general_operand" "0") (float:DF (match_operand:SI 2 "general_operand" "rm"))]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:DF 0 "register_operand" "=f,f") (match_operator:DF 3 "binary_387_op" [(match_operand:DF 1 "general_operand" "0,f") (float_extend:DF (match_operand:SF 2 "general_operand" "fm,0"))]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:SF 0 "register_operand" "=f,f") (match_operator:SF 3 "binary_387_op" [(match_operand:SF 1 "nonimmediate_operand" "0,fm") (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:SF 0 "register_operand" "=f") (match_operator:SF 3 "binary_387_op" [(float:SF (match_operand:SI 1 "general_operand" "rm")) (match_operand:SF 2 "general_operand" "0")]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);")
(define_insn "" [(set (match_operand:SF 0 "register_operand" "=f") (match_operator:SF 3 "binary_387_op" [(match_operand:SF 1 "general_operand" "0") (float:SF (match_operand:SI 2 "general_operand" "rm"))]))] "TARGET_80387" "* return (char *) output_387_binary_op (insn, operands);") (define_expand "strlensi" [(parallel [(set (match_dup 4) (unspec:SI [(mem:BLK (match_operand:BLK 1 "general_operand" "")) (match_operand:QI 2 "register_operand" "") (match_operand:SI 3 "immediate_operand" "")] 0)) (clobber (match_dup 1))]) (set (match_dup 5) (not:SI (match_dup 4))) (set (match_operand:SI 0 "register_operand" "") (minus:SI (match_dup 5) (const_int 1)))] "" " { operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); operands[4] = gen_reg_rtx (SImode); operands[5] = gen_reg_rtx (SImode); }")
;; It might seem that operands 0 & 1 could use predicate register_operand. ;; But strength reduction might offset the MEM expression. So we let ;; reload put the address into %edi.
(define_insn "" [(set (match_operand:SI 0 "register_operand" "=&c") (unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "D")) (match_operand:QI 2 "register_operand" "a") (match_operand:SI 3 "immediate_operand" "i")] 0)) (clobber (match_dup 1))] "" "* { rtx xops[2];
xops[0] = operands[0]; xops[1] = constm1_rtx; output_asm_insn ("cld", operands); output_asm_insn (AS2 (mov%L0,%1,%0), xops); return "repnz;scas%B2"; }")