;;- Machine description Acorn RISC Machine for GNU compiler ;; Copyright (C) 1991 Free Software Foundation, Inc. ;; Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl) ;; and Martin Simmons (@harleqn.co.uk). ;; 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. ;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. ;; Every template must be output by arm_output_asm_insn, since this keeps ;; track of the offset of labels within the text segment. This is needed to ;; to be able to (correctly) output instructions for loading a value from a ;; function's constant pool, since different instructions are needed when the ;; constant pool is more than 4095 bytes away from the PC. ;; Addition insns. (define_insn "adddi3" [(set (match_operand:DI 0 "di_operand" "=r") (plus:DI (match_operand:DI 1 "di_operand" "%r") (match_operand:DI 2 "di_operand" "r")))] "" "* arm_output_asm_insn (\"adds\\t%0, %1, %2\", operands); return (arm_output_asm_insn (\"adc\\t%R0, %R1, %R2\", operands)); ") (define_insn "addsi3" [(set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_operand:SI 1 "register_operand" "r,r") (match_operand:SI 2 "general_operand" "r,n")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"add\\t%0, %1, %2\", operands)); case 1: return (output_add_immediate (operands)); } ") (define_insn "addsf3" [(set (match_operand:SF 0 "register_operand" "=f") (plus:SF (match_operand:SF 1 "register_operand" "f") (match_operand:SF 2 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"adfs\\t%0, %1, %2\", operands)); ") (define_insn "adddf3" [(set (match_operand:DF 0 "register_operand" "=f") (plus:DF (match_operand:DF 1 "register_operand" "f") (match_operand:DF 2 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"adfd\\t%0, %1, %2\", operands)); ") (define_insn "subdi3" [(set (match_operand:DI 0 "di_operand" "=r") (minus:DI (match_operand:DI 1 "di_operand" "%r") (match_operand:DI 2 "di_operand" "r")))] "" "* arm_output_asm_insn (\"subs\\t%0, %1, %2\", operands); return (arm_output_asm_insn (\"sbc\\t%R0, %R1, %R2\", operands)); ") (define_insn "subsi3" [(set (match_operand:SI 0 "register_operand" "=r,r,r") (minus:SI (match_operand:SI 1 "arm_rhs_operand" "r,r,I") (match_operand:SI 2 "general_operand" "r,n,r")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"sub\\t%0, %1, %2\", operands)); case 1: operands[2] = gen_rtx (CONST_INT, VOIDmode, -INTVAL (operands[2])); return (output_add_immediate (operands)); case 2: return (arm_output_asm_insn (\"rsb\\t%0, %2, %1\", operands)); } ") (define_insn "subsf3" [(set (match_operand:SF 0 "register_operand" "=f,f") (minus:SF (match_operand:SF 1 "fpu_rhs_operand" "f,G") (match_operand:SF 2 "fpu_rhs_operand" "fG,f")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"sufs\\t%0, %1, %2\", operands)); case 1: return (arm_output_asm_insn (\"rsfs\\t%0, %2, %1\", operands)); } ") (define_insn "subdf3" [(set (match_operand:DF 0 "register_operand" "=f,f") (minus:DF (match_operand:DF 1 "fpu_rhs_operand" "f,G") (match_operand:DF 2 "fpu_rhs_operand" "fG,f")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"sufd\\t%0, %1, %2\", operands)); case 2: return (arm_output_asm_insn (\"rsfd\\t%0, %2, %1\", operands)); } ") ;; Multiplication insns ;; The `&' is too strict, but at least generates correct code. (define_insn "mulsi3" [(set (match_operand:SI 0 "register_operand" "=&r") (mult:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "register_operand" "r")))] "" "* if (REGNO (operands[0]) == REGNO (operands[1])) return (arm_output_asm_insn (\"mul\\t%0, %2, %1\", operands)); else return (arm_output_asm_insn (\"mul\\t%0, %1, %2\", operands)); ") ;; Unnamed templates to match MLA instruction. (define_insn "" [(set (match_operand:SI 0 "register_operand" "=&r") (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "register_operand" "r")) (match_operand:SI 3 "register_operand" "r")))] "" "* if (REGNO (operands[0]) == REGNO (operands[1])) return (arm_output_asm_insn (\"mla\\t%0, %2, %1, %3\", operands)); else return (arm_output_asm_insn (\"mla\\t%0, %1, %2, %3\", operands)); ") (define_insn "" [(set (match_operand:SI 0 "register_operand" "=&r") (plus:SI (match_operand:SI 3 "register_operand" "r") (mult:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "register_operand" "r"))))] "" "* if (REGNO (operands[0]) == REGNO (operands[1])) return (arm_output_asm_insn (\"mla\\t%0, %2, %1, %3\", operands)); else return (arm_output_asm_insn (\"mla\\t%0, %1, %2, %3\", operands)); ") (define_insn "mulsf3" [(set (match_operand:SF 0 "register_operand" "=f") (mult:SF (match_operand:SF 1 "register_operand" "f") (match_operand:SF 2 "fpu_rhs_operand" "fG")))] "" "*return (arm_output_asm_insn (\"mufs\\t%0, %1, %2\", operands));") (define_insn "muldf3" [(set (match_operand:DF 0 "register_operand" "=f") (mult:DF (match_operand:DF 1 "register_operand" "f") (match_operand:DF 2 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"mufd\\t%0, %1, %2\", operands)); ") ;; Division insns (define_insn "divsf3" [(set (match_operand:SF 0 "register_operand" "=f,f") (div:SF (match_operand:SF 1 "fpu_rhs_operand" "f,G") (match_operand:SF 2 "fpu_rhs_operand" "fG,f")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"dvfs\\t%0, %1, %2\", operands)); case 1: return (arm_output_asm_insn (\"rdfs\\t%0, %2, %1\", operands)); } ") (define_insn "divdf3" [(set (match_operand:DF 0 "register_operand" "=f,f") (div:DF (match_operand:DF 1 "fpu_rhs_operand" "f,G") (match_operand:DF 2 "fpu_rhs_operand" "fG,f")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"dvfd\\t%0, %1, %2\", operands)); case 1: return (arm_output_asm_insn (\"rdfd\\t%0, %2, %1\", operands)); } ") ;; Modulo insns (define_insn "modsf3" [(set (match_operand:SF 0 "register_operand" "=f") (mod:SF (match_operand:SF 1 "register_operand" "f") (match_operand:SF 2 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"rmfs\\t%0, %1, %2\", operands)); ") (define_insn "moddf3" [(set (match_operand:DF 0 "register_operand" "=f") (mod:DF (match_operand:DF 1 "register_operand" "f") (match_operand:DF 2 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"rmfd\\t%0, %1, %2\", operands)); ") ;; Boolean and,ior,xor insns (define_insn "anddi3" [(set (match_operand:DI 0 "di_operand" "=r") (and:DI (match_operand:DI 1 "di_operand" "%r") (match_operand:DI 2 "di_operand" "r")))] "" "* arm_output_asm_insn (\"and\\t%0, %1, %2\", operands); return (arm_output_asm_insn (\"and\\t%R0, %R1, %R2\", operands)); ") (define_insn "andsi3" [(set (match_operand:SI 0 "register_operand" "=r") (and:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "arm_rhs_operand" "rI")))] "" "* return (arm_output_asm_insn (\"and\\t%0, %1, %2\", operands)); ") (define_insn "andcbsi3" [(set (match_operand:SI 0 "register_operand" "=r") (and:SI (match_operand:SI 1 "register_operand" "r") (not:SI (match_operand:SI 2 "arm_rhs_operand" "rI"))))] "" "* return (arm_output_asm_insn (\"bic\\t%0, %1, %2\", operands)); ") (define_insn "iordi3" [(set (match_operand:DI 0 "di_operand" "=r") (ior:DI (match_operand:DI 1 "di_operand" "%r") (match_operand:DI 2 "di_operand" "r")))] "" "* arm_output_asm_insn (\"orr\\t%0, %1, %2\", operands); return (arm_output_asm_insn (\"orr\\t%R0, %R1, %R2\", operands)); ") (define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=r,r") (ior:SI (match_operand:SI 1 "register_operand" "r,r") (match_operand:SI 2 "nonmemory_operand" "r,n")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"orr\\t%0, %1, %2\", operands)); case 1: return (output_multi_immediate (operands, \"orr\\t%0, %1, %2\", \"orr\\t%0, %0, %2\", 2, INTVAL (operands[2]))); } ") (define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=r,r") (xor:SI (match_operand:SI 1 "register_operand" "r,r") (match_operand:SI 2 "nonmemory_operand" "r,n")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"eor\\t%0, %1, %2\", operands)); case 1: return (output_multi_immediate (operands, \"eor\\t%0, %1, %2\", \"eor\\t%0, %0, %2\", 2, INTVAL (operands[2]))); } ") ;; Shift and rotation insns (define_insn "ashlsi3" [(set (match_operand:SI 0 "register_operand" "=r") (ashift:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "general_operand" "rn")))] "" "* return (output_shifted_move (ASHIFT, operands)); ") (define_insn "ashrsi3" [(set (match_operand:SI 0 "register_operand" "=r") (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "general_operand" "rn")))] "" "* return (output_shifted_move (ASHIFTRT, operands)); ") ;; lshlsi3 is not defined because shift counts cannot be negative ;; An unnamed pattern is needed for expansion of zero_extend. (define_insn "" [(set (match_operand:SI 0 "register_operand" "=r") (lshift:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "general_operand" "rn")))] "" "* return (output_shifted_move (LSHIFT, operands)); ") (define_insn "lshrsi3" [(set (match_operand:SI 0 "register_operand" "=r") (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "general_operand" "rn")))] "" "* return (output_shifted_move (LSHIFTRT, operands)); ") ;; rotlsi3 is not defined yet to see what happens (define_insn "rotrsi3" [(set (match_operand:SI 0 "register_operand" "=r,r") (rotatert:SI (match_operand:SI 1 "register_operand" "r,r") (match_operand:SI 2 "general_operand" "r,n")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mov\\t%0, %1,ror %2\", operands)); case 1: if (INTVAL(operands[2]) > 31) operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) % 32); return (arm_output_asm_insn (\"mov\\t%0, %1,ror%2\", operands)); } ") ;; Unary arithmetic insns (define_insn "negdi2" [(set (match_operand:DI 0 "di_operand" "=r") (neg:DI (match_operand:DI 1 "di_operand" "r")))] "" "* arm_output_asm_insn (\"rsb\\t%0, %1, #0\", operands); return (arm_output_asm_insn (\"rsc\\t%R0, %R1, #0\", operands)); ") (define_insn "negsi2" [(set (match_operand:SI 0 "register_operand" "=r") (neg:SI (match_operand:SI 1 "register_operand" "r")))] "" "* return (arm_output_asm_insn (\"rsb\\t%0, %1, #0\", operands)); ") (define_insn "negsf2" [(set (match_operand:SF 0 "register_operand" "=f") (neg:SF (match_operand:SF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"mnfs\\t%0, %1\", operands)); ") (define_insn "negdf2" [(set (match_operand:DF 0 "register_operand" "=f") (neg:DF (match_operand:DF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"mnfd\\t%0, %1\", operands)); ") (define_insn "abssf2" [(set (match_operand:SF 0 "register_operand" "=f") (abs:SF (match_operand:SF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"abss\\t%0, %1\", operands)); ") (define_insn "absdf2" [(set (match_operand:DF 0 "register_operand" "=f") (abs:DF (match_operand:DF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"absd\\t%0, %1\", operands)); ") (define_insn "sqrtsf2" [(set (match_operand:SF 0 "register_operand" "=f") (sqrt:SF (match_operand:SF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"sqts\\t%0, %1\", operands)); ") (define_insn "sqrtdf2" [(set (match_operand:DF 0 "register_operand" "=f") (sqrt:DF (match_operand:DF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"sqtd\\t%0, %1\", operands)); ") (define_insn "one_cmplsi2" [(set (match_operand:SI 0 "register_operand" "=r") (not:SI (match_operand:SI 1 "register_operand" "r")))] "" "* return (arm_output_asm_insn (\"mvn\\t%0, %1\", operands)); ") ;; Fixed <--> Floating conversion insns (define_insn "floatsisf2" [(set (match_operand:SF 0 "register_operand" "=f") (float:SF (match_operand:SI 1 "register_operand" "r")))] "" "* return (arm_output_asm_insn (\"flts\\t%0, %1\", operands)); ") (define_insn "floatsidf2" [(set (match_operand:DF 0 "register_operand" "=f") (float:DF (match_operand:SI 1 "register_operand" "r")))] "" "* return (arm_output_asm_insn (\"fltd\\t%0, %1\", operands)); ") ;; Truncation insns (define_insn "truncsiqi2" [(set (match_operand:QI 0 "general_operand" "=mr") (truncate:QI (match_operand:SI 1 "register_operand" "r")))] "" "* if (GET_CODE (operands[0]) == MEM) return (arm_output_asm_insn (\"strb\\t%1, %0\\t@ truncsiqi2\", operands)); else return (arm_output_asm_insn (\"and\\t%0, %1, #255\\t@ truncsiqi2\", operands)); ") (define_insn "trunchiqi2" [(set (match_operand:QI 0 "general_operand" "=mr") (truncate:QI (match_operand:HI 1 "register_operand" "r")))] "" "* if (GET_CODE(operands[0]) == MEM) return (arm_output_asm_insn (\"strb\\t%1, %0\\t@ trunchiqi2\", operands)); else return (arm_output_asm_insn (\"and\\t%0, %1, #255\\t@ trunchiqi2\", operands)); ") ;; Mode is changed to SI below (define_expand "truncsihi2" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:HI 1 "register_operand" "") (const_int 16))) (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 16)))] "" " if (GET_CODE (operands[1]) == SUBREG) operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]), SUBREG_WORD (operands[1])); else operands[1] = gen_rtx(SUBREG, SImode, operands[1], 0); ") (define_insn "truncdfsf2" [(set (match_operand:SF 0 "register_operand" "=f") (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"mvfs\\t%0, %1\", operands)); ") ;; Zero extension instructions. (define_expand "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:HI 1 "register_operand" "") (const_int 16))) (set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 16)))] "" " if (GET_CODE (operands[1]) == SUBREG) operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]), SUBREG_WORD (operands[1])); else operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0); ") (define_insn "zero_extendqihi2" [(set (match_operand:HI 0 "register_operand" "=r") (zero_extend:HI (match_operand:QI 1 "register_operand" "r")))] "" "* return (arm_output_asm_insn (\"and\\t%0, %1, #255\\t@ zero_extendqihi2\", operands)); ") (define_insn "zero_extendqisi2" [(set (match_operand:SI 0 "register_operand" "=r,r") (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"and\\t%0, %1, #255\\t@ zero_extendqisi2\", operands)); case 1: return (arm_output_asm_insn (\"ldrb\\t%0, %1\\t@ zero_extendqisi2\", operands)); } ") ;; Note that the ones starting from HImode come before those for QImode so ;; that a constant operand will match HImode, not QImode. (define_expand "extendhisi2" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:HI 1 "register_operand" "") (const_int 16))) (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 16)))] "" " if (GET_CODE (operands[1]) == SUBREG) operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]), SUBREG_WORD (operands[1])); else operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0); ") ;; XXX Is this ever used? (define_insn "extendqihi2" [(set (match_operand:HI 0 "register_operand" "=r") (sign_extend:SI (match_operand:QI 1 "register_operand" "r")))] "" "* arm_output_asm_insn (\"mov\\t%0, %1, lsl#24\\t@ extendqihi\", operands); return (arm_output_asm_insn (\"mov\\t%0, %0, asr#24\", operands)); ") (define_expand "extendqisi2" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:QI 1 "register_operand" "") (const_int 24))) (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 24)))] "" " if (GET_CODE (operands[1]) == SUBREG) operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]), SUBREG_WORD(operands[1])); else operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0); ") (define_insn "extendsfdf2" [(set (match_operand:DF 0 "register_operand" "=f") (float_extend:DF (match_operand:SF 1 "register_operand" "f")))] "" "* return (arm_output_asm_insn (\"mvfd\\t%0, %1\", operands)); ") ;; Move insns (including loads and stores) ;; XXX Just some ideas about movti. ;;(define_expand "loadti" ;; [(set (match_operand:TI 0 "register_operand" "") ;; (mem:TI (match_operand:SI 1 "address_operand" "")))] ;; "" "") ;;(define_expand "storeti" ;; [(set (mem:TI (match_operand:TI 0 "address_operand" "")) ;; (match_operand:TI 1 "register_operand" ""))] ;; "" "") ;;(define_expand "movti" ;; [(set (match_operand:TI 0 "general_operand" "") ;; (match_operand:TI 1 "general_operand" ""))] ;; "" ;; " ;;{ ;; rtx insn; ;; ;; if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM) ;; operands[1] = copy_to_reg (operands[1]); ;; if (GET_CODE (operands[0]) == MEM) ;; insn = gen_storeti (XEXP (operands[0], 0), operands[1]); ;; else if (GET_CODE (operands[1]) == MEM) ;; insn = gen_loadti (operands[0], XEXP (operands[1], 0)); ;; else ;; FAIL; ;; ;; emit_insn (insn); ;; DONE; ;;}") ;; Recognise garbage generated above. ;;(define_insn "" ;; [(set (match_operand:TI 0 "general_operand" "=r,r,r,<,>,m") ;; (match_operand:TI 1 "general_operand" "<,>,m,r,r,r"))] ;; "" ;; "* ;; { ;; register mem = (which_alternative < 3); ;; register char *template; ;; ;; operands[mem] = XEXP (operands[mem], 0); ;; switch (which_alternative) ;; { ;; case 0: template = \"ldmdb\\t%1!, %M0\"; break; ;; case 1: template = \"ldmia\\t%1!, %M0\"; break; ;; case 2: template = \"ldmia\\t%1, %M0\"; break; ;; case 3: template = \"stmdb\\t%0!, %M1\"; break; ;; case 4: template = \"stmia\\t%0!, %M1\"; break; ;; case 5: template = \"stmia\\t%0, %M1\"; break; ;; } ;; return (arm_output_asm_insn (template, operands)); ;; }") (define_insn "movdi" [(set (match_operand:DI 0 "di_operand" "=r,r,r,o,r") (match_operand:DI 1 "di_operand" "r,n,o,r,F"))] "" "* return (output_move_double (operands)); ") (define_insn "movsi" [(set (match_operand:SI 0 "general_operand" "=r,r,r,m") (match_operand:SI 1 "general_operand" "r,n,m,r"))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mov\\t%0, %1\", operands)); case 1: return (output_mov_immediate (operands)); case 2: if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (XEXP (operands[1], 0))) return (arm_output_llc (operands)); else return (arm_output_asm_insn (\"ldr\\t%0, %1\", operands)); case 3: return (arm_output_asm_insn (\"str\\t%1, %0\", operands)); } ") ;; XXX The movhi stuff isn't as correct or as nice as it could be... ;; Subroutine to load a half word into a register from memory. ;; Operand 0 is the destination register (HImode). ;; Operand 1 is the source address (SImode). ;; Operand 2 is a temporary (SImode). ;;(define_expand "loadhi" ;; [;; load the whole word (ARM realigns it if not on word boundary) ;; (set (match_operand:SI 2 "register_operand" "") ;; (mem:SI (match_operand:SI 1 "address_operand" ""))) ;; ;; quietly forget the upper 16 bits ;; (set (match_operand:HI 0 "register_operand" "") ;; (subreg:HI (match_dup 2) 0))] ;; "" ;; "" ;;) ;; Load op0 from mem:op1. Subroutine in case we're reloading and the normal ;; loadhi is not allowed. ;;(define_expand "reloadhi" ;; [(set (reg:SI 10) ;; (mem:SI (match_operand:SI 1 "address_operand" ""))) ;; (set (match_operand:HI 0 "register_operand" "") ;; (subreg:HI (reg:SI 10) 0))] ;; "" "") ;; Store op0 into mem:op1. Subroutine in case we're reloading and the normal ;; storehi is not allowed. (define_expand "restorehi" [(set (mem:QI (match_operand:SI 1 "address_operand" "")) (truncate:QI (match_operand:HI 0 "register_operand" ""))) (set (reg:HI 10) (ashiftrt:HI (match_dup 0) (const_int 8))) (set (mem:QI (plus:SI (match_dup 1) (const_int 1))) (truncate:QI (reg:HI 10)))] "" "") ;; Subroutine to store a half word from a register into memory. ;; Operand 0 is the source register (HImode) ;; Operand 1 is the destination address (SImode) ;; Operand 2 is a temporary (SImode). ;; Operand 3 is a temporary (SImode). ;; Operand 4 is a temporary (SImode). ;; Operand 5 is a local temporary (SImode). (define_expand "storehi" [;; compute the address into a register (set (match_operand:SI 2 "register_operand" "") (match_operand:SI 1 "address_operand" "")) ;; get the half word into a full word register (set (match_operand:SI 3 "register_operand" "") (match_dup 5)) ;; store the low byte (set (mem:QI (match_dup 2)) (truncate:QI (match_dup 3))) ;; extract the high byte (set (match_operand:SI 4 "register_operand" "") (ashiftrt:SI (match_dup 3) (const_int 8))) ;; store the high byte (set (mem:QI (plus (match_dup 2) (const_int 1))) (truncate:QI (match_dup 4)))] "" " if (GET_CODE(operands[0]) == SUBREG) operands[5] = gen_rtx(SUBREG, SImode, SUBREG_REG(operands[0]), SUBREG_WORD(operands[0])); else operands[5] = gen_rtx(SUBREG, SImode, operands[0], 0); ") ;; Subroutine to store a half word integer constant into memory. ;; Operand 0 is the constant ;; Operand 1 is the destination address (SImode) ;; Operand 2 is a temporary (SImode). ;; Operand 3 is a temporary (QImode). ;; Operand 4 is a temporary (QImode). ;; Operand 5 is a local CONST_INT. ;; Operand 6 is a local CONST_INT. (define_expand "storeinthi" [;; compute the address into a register (set (match_operand:SI 2 "register_operand" "") (match_operand:SI 1 "address_operand" "")) ;; load the low byte (set (match_operand:QI 3 "register_operand" "") (match_dup 5)) ;; store the low byte (set (mem:QI (match_dup 2)) (match_dup 3)) ;; load the high byte (set (match_operand:QI 4 "register_operand" "") (match_dup 6)) ;; store the high byte (set (mem:QI (plus (match_dup 2) (const_int 1))) (match_dup 4))] "" " { int value = INTVAL(operands[0]); operands[5] = gen_rtx(CONST_INT, VOIDmode, value & 255); operands[6] = gen_rtx(CONST_INT, VOIDmode,(value>>8) & 255); } ") (define_expand "movhi" [(set (match_operand:HI 0 "general_operand" "") (match_operand:HI 1 "general_operand" ""))] "" " { rtx insn; if (reload_in_progress || reload_completed) { if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == REG) insn = gen_restorehi (operands[1], XEXP (operands[0], 0)); else insn = gen_rtx (SET, VOIDmode, operands[0], operands[1]); } else { if (GET_CODE (operands[0]) == MEM) { if (GET_CODE (operands[1]) == MEM) operands[1] = copy_to_reg (operands[1]); if (GET_CODE (operands[1]) == CONST_INT) { insn = gen_storeinthi (operands[1], XEXP (operands[0], 0), gen_reg_rtx (SImode), gen_reg_rtx (QImode), gen_reg_rtx (QImode)); } else { insn = gen_storehi (operands[1], XEXP (operands[0], 0), gen_reg_rtx (SImode), gen_reg_rtx (SImode), gen_reg_rtx (SImode)); } } #if 0 else if (GET_CODE (operands[1]) == MEM) { insn = gen_loadhi (operands[0], XEXP (operands[1], 0), gen_reg_rtx (SImode)); } #endif else insn = gen_rtx (SET, VOIDmode, operands[0], operands[1]); } emit_insn (insn); DONE; }") ;; Pattern to recognise insn generated default case above (define_insn "" [(set (match_operand:HI 0 "general_operand" "=r,r,r,m") (match_operand:HI 1 "general_operand" "r,n,m,r"))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mov\\t%0, %1\\t@movhi\", operands)); case 1: return (output_mov_immediate (operands)); case 2: return (arm_output_asm_insn (\"ldr\\t%0, %1\\t@movhi\", operands)); case 3: return (arm_output_asm_insn (\"str\\t%1, %0\\t@movhi\", operands)); } ") (define_insn "movqi" [(set (match_operand:QI 0 "general_operand" "=r,r,r,m") (match_operand:QI 1 "general_operand" "r,n,m,r"))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mov\\t%0, %1\", operands)); case 1: return (output_mov_immediate (operands)); case 2: return (arm_output_asm_insn (\"ldrb\\t%0, %1\", operands)); case 3: return (arm_output_asm_insn (\"strb\\t%1, %0\", operands)); } ") (define_insn "movsf" [(set (match_operand:SF 0 "general_operand" "=f,f,m,f,r,r") (match_operand:SF 1 "general_operand" "fG,m,f,r,f,r"))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mvfs\\t%0, %1\", operands)); case 1: return (arm_output_asm_insn (\"ldfs\\t%0, %1\", operands)); case 2: return (arm_output_asm_insn (\"stfs\\t%1, %0\", operands)); case 3: arm_output_asm_insn(\"stmfd\\tsp!, {%1}\", operands); return (arm_output_asm_insn (\"ldfs\\t%0, [sp],#4\", operands)); case 4: arm_output_asm_insn(\"stfs\\t%1, [sp,#-4]!\", operands); return (arm_output_asm_insn (\"ldmfd\\tsp!, {%0}\", operands)); case 5: return (arm_output_asm_insn (\"mov\\t%0, %1\", operands)); } ") (define_insn "movdf" [(set (match_operand:DF 0 "general_operand" "=f,f,m,f,r,r") (match_operand:DF 1 "general_operand" "fG,m,f,r,f,r"))] "" "* switch (which_alternative) { case 0: return (arm_output_asm_insn (\"mvfd\\t%0, %1\", operands)); case 1: return (arm_output_asm_insn (\"ldfd\\t%0, %1\", operands)); case 2: return (arm_output_asm_insn (\"stfd\\t%1, %0\", operands)); case 3: return (output_mov_double_fpu_from_arm (operands)); case 4: return (output_mov_double_arm_from_fpu (operands)); case 5: return (output_move_double (operands)); } ") ;; Comparison and test insns (define_insn "cmpsi" [(set (cc0) (compare (match_operand:SI 0 "register_operand" "r") (match_operand:SI 1 "arm_rhs_operand" "rI")))] "" "* return (arm_output_asm_insn (\"cmp\\t%0, %1\", operands)); ") (define_insn "tstsi" [(set (cc0) (match_operand:SI 0 "register_operand" "r"))] "" "* return (arm_output_asm_insn (\"cmp\\t%0, #0\", operands)); ") (define_insn "" [(set (cc0) (compare (match_operand:SI 0 "register_operand" "r") (neg:SI (match_operand:SI 1 "arm_rhs_operand" "rI"))))] "" "* return (arm_output_asm_insn (\"cmn\\t%0, %1\", operands)); ") (define_insn "cmpsf" [(set (cc0) (compare (match_operand:SF 0 "register_operand" "f") (match_operand:SF 1 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"cmf\\t%0, %1\", operands)); ") (define_insn "cmpdf" [(set (cc0) (compare (match_operand:DF 0 "register_operand" "f") (match_operand:DF 1 "fpu_rhs_operand" "fG")))] "" "* return (arm_output_asm_insn (\"cmf\\t%0, %1\", operands)); ") ;; Conditional branch insns (define_insn "beq" [(set (pc) (if_then_else (eq (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"beq\\t%l0\", operands)); ") (define_insn "bne" [(set (pc) (if_then_else (ne (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bne\\t%l0\", operands)); ") (define_insn "bgt" [(set (pc) (if_then_else (gt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bgt\\t%l0\", operands)); ") (define_insn "ble" [(set (pc) (if_then_else (le (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"ble\\t%l0\", operands)); ") (define_insn "bge" [(set (pc) (if_then_else (ge (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bge\\t%l0\", operands)); ") (define_insn "blt" [(set (pc) (if_then_else (lt (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"blt\\t%l0\", operands)); ") (define_insn "bgtu" [(set (pc) (if_then_else (gtu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bhi\\t%l0\", operands)); ") (define_insn "bleu" [(set (pc) (if_then_else (leu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bls\\t%l0\", operands)); ") (define_insn "bgeu" [(set (pc) (if_then_else (geu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"bhs\\t%l0\", operands)); ") (define_insn "bltu" [(set (pc) (if_then_else (ltu (cc0) (const_int 0)) (label_ref (match_operand 0 "" "")) (pc)))] "" "* return (arm_output_asm_insn (\"blo\\t%l0\", operands)); ") ;; Inverted conditional branch insns (define_insn "" [(set (pc) (if_then_else (eq (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bne\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (ne (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"beq\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (gt (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"ble\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (le (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bgt\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (ge (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"blt\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (lt (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bge\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (gtu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bls\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (leu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bhi\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (geu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"blo\\t%l0\", operands)); ") (define_insn "" [(set (pc) (if_then_else (ltu (cc0) (const_int 0)) (pc) (label_ref (match_operand 0 "" ""))))] "" "* return (arm_output_asm_insn (\"bhs\\t%l0\", operands)); ") ;; Jump and linkage insns ;; `return' is still a jump-to-epilogue... (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "* return (arm_output_asm_insn (\"b\\t%l0\", operands)); ") (define_insn "call" [(call (match_operand 0 "memory_operand" "m") (match_operand 1 "general_operand" "g")) (clobber (reg:SI 14))] "" "* return (output_call (operands)); ") (define_insn "call_value" [(set (match_operand 0 "" "rf") (call (match_operand 1 "memory_operand" "m") (match_operand 2 "general_operand" "g"))) (clobber (reg:SI 14))] "" "* return (output_call (&operands[1])); ") ;; Allow calls to SYMBOL_REFs specially as they are not valid general addresses ;; The 'a' causes the operand to be treated as an address, i.e. no '#' output. (define_insn "" [(call (mem:SI (match_operand:SI 0 "" "i")) (match_operand:SI 1 "general_operand" "g")) (clobber (reg:SI 14))] "GET_CODE (operands[0]) == SYMBOL_REF" "* return (arm_output_asm_insn (\"bl\\t%a0\", operands)); ") (define_insn "" [(set (match_operand 0 "register_operand" "=rf") (call (mem:SI (match_operand:SI 1 "" "i")) (match_operand:SI 2 "general_operand" "g"))) (clobber (reg:SI 14))] "GET_CODE(operands[1]) == SYMBOL_REF" "* return (arm_output_asm_insn (\"bl\\t%a1\", operands)); ") (define_insn "tablejump" [(set (pc) (match_operand:SI 0 "register_operand" "r")) (use (label_ref (match_operand 1 "" "")))] "" "* return (arm_output_asm_insn (\"mov\\tpc, %0\\t@ table jump, label %l1\", operands)); ") (define_insn "indirect_jump" [(set (pc) (match_operand:SI 0 "register_operand" "r"))] "" "* return (arm_output_asm_insn (\"mov\\tpc, %0\\t@ indirect jump\", operands)); ") ;; Misc insns (define_insn "nop" [(const_int 0)] "" "* return (arm_output_asm_insn (\"mov\\tr0, r0\\t@ nop\", operands)); ") ;; Patterns to allow combination of arithmetic, cond code and shifts ;(define_insn "" ; [(set (match_operand:SI 0 "register_operand" "=r") ; (match_operator:SI 1 "shiftable_operator" ; [(match_operand:SI 2 "register_operand" "r") ; (match_operator:SI 3 "shift_operator" ; [(match_operand:SI 4 "register_operand" "r") ; (match_operand:SI 5 "nonmemory_operand" "rn")])]))] ; "" ; "* ; return (output_arithmetic_with_shift (operands, FALSE, FALSE)); ; " ;) ;(define_insn "" ; [(set (match_operand:SI 0 "register_operand" "=r") ; (match_operator:SI 1 "shiftable_operator" ; [(match_operator:SI 3 "shift_operator" ; [(match_operand:SI 4 "register_operand" "r") ; (match_operand:SI 5 "nonmemory_operand" "rI")]) ; (match_operand:SI 2 "register_operand" "r")]))] ; "" ; "* ; return (output_arithmetic_with_shift (operands, TRUE, FALSE)); ;") ;; Patterns to allow combination of arithmetic and multiplication ;(define_insn "" ; [(set (match_operand:SI 0 "register_operand" "=r") ; (match_operator:SI 1 "shiftable_operator" ; [(match_operand:SI 2 "register_operand" "r") ; (mult:SI ; (match_operand:SI 3 "register_operand" "r") ; (match_operand:SI 4 "power_of_two_operand" "n"))]))] ; "" ; "* ; return (output_arithmetic_with_immediate_multiply (operands, FALSE)); ;") ; Uncomment this to show combiner problem (see ../COMBINER-PROBLEM). ;(define_insn "" ; [(set (match_operand:SI 0 "register_operand" "=r") ; (match_operator:SI 1 "shiftable_operator" ; [(mult:SI ; (match_operand:SI 3 "register_operand" "r") ; (match_operand:SI 4 "power_of_two_operand" "n")) ; (match_operand:SI 2 "register_operand" "r")]))] ; "" ; "* ; return (output_arithmetic_with_immediate_multiply (operands, TRUE)); ;") ;; Peephole optimizations. ;; When testing a bitset smaller than 9 bits for (un)equality, a ;; shift/and/cmp/b{eq,ne} sequence can be replaced by one tst and the same ;; branch sequence. ;;(define_peephole ;; [(set (match_operand:SI 0 "register_operand" "=r") ;; (lshiftrt:SI (match_dup 0) ;; (match_operand 1 "immediate_operand" ""))) ;; (set (match_dup 0) ;; (and:SI (match_dup 0) ;; (match_operand 2 "immediate_operand" ""))) ;; (set (cc0) (match_dup 0)) ;; (set (pc) ;; (if_then_else (ne (cc0) (const_int 0)) ;; (label_ref (match_operand 3 "" "")) ;; (pc)))] ;; "dead_or_set_p (PREV_INSN (insn), operands[0]) ;; && GET_CODE (operands[2]) == CONST_INT && GET_CODE (operands[1]) == CONST_INT ;; && const_ok_for_arm (INTVAL (operands[2]) << INTVAL (operands[1]))" ;; "* ;; operands[2] = gen_rtx (CONST_INT, VOIDmode, ;; INTVAL (operands[2]) << INTVAL (operands[1])); ;; arm_output_asm_insn (\"tst\\t%0, %2\\t\\t@ ph test bitfield\", operands); ;; return (arm_output_asm_insn (\"bne\\t%l3\", operands)); ;;") ;;(define_peephole ;; [(set (match_operand:SI 0 "register_operand" "=r") ;; (lshiftrt:SI (match_dup 0) ;; (match_operand 1 "immediate_operand" ""))) ;; (set (match_dup 0) ;; (and:SI (match_dup 0) ;; (match_operand 2 "immediate_operand" ""))) ;; (set (cc0) (match_dup 0)) ;; (set (pc) ;; (if_then_else (ne (cc0) (const_int 0)) ;; (pc) ;; (label_ref (match_operand 3 "" ""))))] ;; "dead_or_set_p (prev_real_insn (insn), operands[0]) ;; && GET_CODE (operands[2]) == CONST_INT && GET_CODE (operands[1]) == CONST_INT ;; && const_ok_for_arm (INTVAL (operands[2]) << INTVAL (operands[1]))" ;; "* ;; operands[2] = gen_rtx (CONST_INT, VOIDmode, ;; INTVAL (operands[2]) << INTVAL (operands[1])); ;; arm_output_asm_insn (\"tst\\t%0, %2\\t\\t@ ph test bitfield\", operands); ;; return (arm_output_asm_insn (\"beq\\t%l3\", operands)); ;;") ;; This allows negative constants to be compared since GCC appears not to try ;; converting them with a NEG. ;;(define_peephole ;; [(set (match_operand:SI 2 "register_operand" "=r") ;; (match_operand:SI 1 "immediate_operand" "n")) ;; (set (cc0) ;; (compare (match_operand:SI 0 "register_operand" "r") ;; (match_dup 1)))] ;; "const_ok_for_arm (-INTVAL (operands[1])) ;; && dead_or_set_p (prev_real_insn (insn), operands[0])" ;; "* ;; operands[1] = gen_rtx (CONST_INT, VOIDmode, -INTVAL (operands[1])); ;; return (arm_output_asm_insn (\"cmn\\t%0, %1\\t\\t@ ph negate comparison\", operands)); ;;") ;; Local variables: ;; mode:emacs-lisp ;; eval: (setq comment-start ";; ") ;; eval: (setq comment-end "") ;; eval: (setq comment-start-skip ";;+ *") ;; eval: (set-syntax-table (copy-sequence (syntax-table))) ;; eval: (modify-syntax-entry ?[ "(]") ;; eval: (modify-syntax-entry ?] ")[") ;; eval: (modify-syntax-entry ?{ "(}") ;; eval: (modify-syntax-entry ?} "){") ;; End: