;; Machine Description for Altera Nios II. ;; Copyright (C) 2012-2021 Free Software Foundation, Inc. ;; Contributed by Jonah Graham (jgraham@altera.com) and ;; Will Reece (wreece@altera.com). ;; Contributed by Mentor Graphics, Inc. ;; ;; This file is part of GCC. ;; ;; GCC is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; ;; GCC is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GCC; see the file COPYING3. If not see ;; http://www.gnu.org/licenses/.

;; Register numbers (define_constants [ (FIRST_RETVAL_REGNO 2) ; Return value registers (LAST_RETVAL_REGNO 3) ; (FIRST_ARG_REGNO 4) ; Argument registers (LAST_ARG_REGNO 7) ;

(TP_REGNO 23) ; Thread pointer register (GP_REGNO 26) ; Global pointer register (SP_REGNO 27) ; Stack pointer register (FP_REGNO 28) ; Frame pointer register (EA_REGNO 29) ; Exception return address register (RA_REGNO 31) ; Return address register (LAST_GP_REG 31) ; Last general purpose register

;; Target register definitions (STATIC_CHAIN_REGNUM 12) (STACK_POINTER_REGNUM 27) (HARD_FRAME_POINTER_REGNUM 28) (PC_REGNUM 37) (FRAME_POINTER_REGNUM 38) (ARG_POINTER_REGNUM 39) (FIRST_PSEUDO_REGISTER 40) ] )

;; Enumeration of UNSPECs

(define_c_enum “unspecv” [ UNSPECV_BLOCKAGE UNSPECV_WRCTL UNSPECV_RDCTL UNSPECV_FWRX UNSPECV_FWRY UNSPECV_FRDXLO UNSPECV_FRDXHI UNSPECV_FRDY UNSPECV_CUSTOM_NXX UNSPECV_CUSTOM_XNXX UNSPECV_LDXIO UNSPECV_STXIO UNSPECV_RDPRS UNSPECV_FLUSHD UNSPECV_FLUSHDA UNSPECV_WRPIE UNSPECV_ENI UNSPECV_LDEX UNSPECV_LDSEX UNSPECV_STEX UNSPECV_STSEX ])

(define_c_enum “unspec” [ UNSPEC_FCOS UNSPEC_FSIN UNSPEC_FTAN UNSPEC_FATAN UNSPEC_FEXP UNSPEC_FLOG UNSPEC_ROUND UNSPEC_LOAD_GOT_REGISTER UNSPEC_PIC_SYM UNSPEC_PIC_CALL_SYM UNSPEC_PIC_GOTOFF_SYM UNSPEC_LOAD_TLS_IE UNSPEC_ADD_TLS_LE UNSPEC_ADD_TLS_GD UNSPEC_ADD_TLS_LDM UNSPEC_ADD_TLS_LDO UNSPEC_EH_RETURN UNSPEC_SYNC ])

;; Instruction scheduler

; No schedule info is currently available, using an assumption that no ; instruction can use the results of the previous instruction without ; incuring a stall.

; length of an instruction (in bytes) (define_attr “length” "" (if_then_else (match_test “nios2_cdx_narrow_form_p (insn)”) (const_int 2) (const_int 4)))

(define_attr “type” “unknown,complex,control,alu,cond_alu,st,ld,stwm,ldwm,push,pop,mul,div,
custom,add,sub,mov,and,or,xor,neg,not,sll,srl,sra,rol,ror,nop” (const_string “complex”))

(define_asm_attributes [(set_attr “length” “4”) (set_attr “type” “complex”)])

(define_automaton “nios2”) (automata_option “v”) ;(automata_option “no-minimization”) (automata_option “ndfa”)

; The nios2 pipeline is fairly straightforward for the fast model. ; Every alu operation is pipelined so that an instruction can ; be issued every cycle. However, there are still potential ; stalls which this description tries to deal with.

(define_cpu_unit “cpu” “nios2”)

(define_insn_reservation “complex” 1 (eq_attr “type” “complex”) “cpu”)

(define_insn_reservation “control” 1 (eq_attr “type” “control,pop”) “cpu”)

(define_insn_reservation “alu” 1 (eq_attr “type” “alu,add,sub,mov,and,or,xor,neg,not”) “cpu”)

(define_insn_reservation “cond_alu” 1 (eq_attr “type” “cond_alu”) “cpu”)

(define_insn_reservation “st” 1 (eq_attr “type” “st,stwm,push”) “cpu”)

(define_insn_reservation “custom” 1 (eq_attr “type” “custom”) “cpu”)

; shifts, muls and lds have three cycle latency (define_insn_reservation “ld” 3 (eq_attr “type” “ld,ldwm”) “cpu”)

(define_insn_reservation “shift” 3 (eq_attr “type” “sll,srl,sra,rol,ror”) “cpu”)

(define_insn_reservation “mul” 3 (eq_attr “type” “mul”) “cpu”)

(define_insn_reservation “div” 1 (eq_attr “type” “div”) “cpu”)

(include “predicates.md”) (include “constraints.md”)

;; Move instructions

(define_mode_iterator M [QI HI SI])

(define_expand “mov” [(set (match_operand:M 0 “nonimmediate_operand” "") (match_operand:M 1 “general_operand” ""))] "" { if (nios2_emit_move_sequence (operands, mode)) DONE; })

(define_insn “*high” [(set (match_operand:SI 0 “register_operand” “=r”) (high:SI (match_operand:SI 1 “immediate_operand” “i”)))] "" “movhi\t%0, %H1” [(set_attr “type” “alu”)])

(define_insn “*lo_sum” [(set (match_operand:SI 0 “register_operand” “=r”) (lo_sum:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “immediate_operand” “i”)))] "" “addi\t%0, %1, %L2” [(set_attr “type” “alu”)])

(define_insn_and_split “movqi_internal” [(set (match_operand:QI 0 “nonimmediate_operand” “=m, r,r”) (match_operand:QI 1 “general_operand” “rM,m,rI”))] “(register_operand (operands[0], QImode) || reg_or_0_operand (operands[1], QImode))” { switch (which_alternative) { case 0: if (get_attr_length (insn) != 2) return “stb%o0\t%z1, %0”; else if (const_0_operand (operands[1], QImode)) return “stbz.n\t%z1, %0”; else return “stb.n\t%z1, %0”; case 1: return “ldbu%o1%.\t%0, %1”; case 2: return “mov%i1%.\t%0, %z1”; default: gcc_unreachable (); } } “(nios2_large_constant_memory_operand_p (operands[0]) || nios2_large_constant_memory_operand_p (operands[1]))” [(set (match_dup 0) (match_dup 1))] { if (nios2_large_constant_memory_operand_p (operands[0])) operands[0] = nios2_split_large_constant_memory_operand (operands[0]); else operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “st,ld,mov”)])

(define_insn_and_split “movhi_internal” [(set (match_operand:HI 0 “nonimmediate_operand” “=m, r,r”) (match_operand:HI 1 “general_operand” “rM,m,rI”))] “(register_operand (operands[0], HImode) || reg_or_0_operand (operands[1], HImode))” { switch (which_alternative) { case 0: return “sth%o0%.\t%z1, %0”; case 1: return “ldhu%o1%.\t%0, %1”; case 2: return “mov%i1%.\t%0, %z1”; default: gcc_unreachable (); } } “(nios2_large_constant_memory_operand_p (operands[0]) || nios2_large_constant_memory_operand_p (operands[1]))” [(set (match_dup 0) (match_dup 1))] { if (nios2_large_constant_memory_operand_p (operands[0])) operands[0] = nios2_split_large_constant_memory_operand (operands[0]); else operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “st,ld,mov”)])

(define_insn_and_split “movsi_internal” [(set (match_operand:SI 0 “nonimmediate_operand” “=m, r,r, r”) (match_operand:SI 1 “general_operand” “rM,m,rIJK,S”))] “(register_operand (operands[0], SImode) || reg_or_0_operand (operands[1], SImode))” { switch (which_alternative) { case 0: if (get_attr_length (insn) != 2) return “stw%o0\t%z1, %0”; else if (stack_memory_operand (operands[0], SImode)) return “stwsp.n\t%z1, %0”; else if (const_0_operand (operands[1], SImode)) return “stwz.n\t%z1, %0”; else return “stw.n\t%z1, %0”; case 1: if (get_attr_length (insn) != 2) return “ldw%o1\t%0, %1”; else if (stack_memory_operand (operands[1], SImode)) return “ldwsp.n\t%0, %1”; else return “ldw.n\t%0, %1”; case 2: return “mov%i1%.\t%0, %z1”; case 3: return “addi\t%0, gp, %%gprel(%1)”; default: gcc_unreachable (); } } “(nios2_large_constant_memory_operand_p (operands[0]) || nios2_large_constant_memory_operand_p (operands[1]) || (nios2_large_constant_p (operands[1]) && !(CONST_INT_P (operands[1]) && (SMALL_INT_UNSIGNED (INTVAL (operands[1])) || UPPER16_INT (INTVAL (operands[1]))))))” [(set (match_dup 0) (match_dup 1))] { if (nios2_large_constant_memory_operand_p (operands[0])) operands[0] = nios2_split_large_constant_memory_operand (operands[0]); else if (nios2_large_constant_memory_operand_p (operands[1])) operands[1] = nios2_split_large_constant_memory_operand (operands[1]); else operands[1] = nios2_split_large_constant (operands[1], operands[0]); } [(set_attr “type” “st,ld,mov,alu”)])

(define_mode_iterator BH [QI HI]) (define_mode_iterator BHW [QI HI SI]) (define_mode_attr bh [(QI “b”) (HI “h”)]) (define_mode_attr bhw [(QI “b”) (HI “h”) (SI “w”)]) (define_mode_attr bhw_uns [(QI “bu”) (HI “hu”) (SI “w”)])

(define_insn_and_split “ld<bhw_uns>io” [(set (match_operand:BHW 0 “register_operand” “=r”) (unspec_volatile:BHW [(match_operand:BHW 1 “ldstio_memory_operand” “w”)] UNSPECV_LDXIO))] "" “ld<bhw_uns>io\t%0, %1” “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (unspec_volatile:BHW [(match_dup 1)] UNSPECV_LDXIO))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “ld”)])

(define_expand “ldio” [(set (match_operand:BH 0 “register_operand” “=r”) (match_operand:BH 1 “ldstio_memory_operand” “w”))] "" { rtx tmp = gen_reg_rtx (SImode); emit_insn (gen_ldio_signed (tmp, operands[1])); emit_insn (gen_mov (operands[0], gen_lowpart (mode, tmp))); DONE; })

(define_insn_and_split “ldio_signed” [(set (match_operand:SI 0 “register_operand” “=r”) (sign_extend:SI (unspec_volatile:BH [(match_operand:BH 1 “ldstio_memory_operand” “w”)] UNSPECV_LDXIO)))] "" “ldio\t%0, %1” “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (sign_extend:SI (unspec_volatile:BH [(match_dup 1)] UNSPECV_LDXIO)))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “ld”)])

(define_insn_and_split “stio” [(set (match_operand:BHW 0 “ldstio_memory_operand” “=w”) (unspec_volatile:BHW [(match_operand:BHW 1 “reg_or_0_operand” “rM”)] UNSPECV_STXIO))] "" “stio\t%z1, %0” “nios2_large_constant_memory_operand_p (operands[0])” [(set (match_dup 0) (unspec_volatile:BHW [(match_dup 1)] UNSPECV_STXIO))] { operands[0] = nios2_split_large_constant_memory_operand (operands[0]); } [(set_attr “type” “st”)])

;; QI to [HI, SI] extension patterns are collected together (define_mode_iterator QX [HI SI])

;; Zero extension patterns (define_insn_and_split “zero_extendhisi2” [(set (match_operand:SI 0 “register_operand” “=r,r”) (zero_extend:SI (match_operand:HI 1 “nonimmediate_operand” “r,m”)))] "" “@ andi%.\t%0, %1, 0xffff ldhu%o1%.\t%0, %1” “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (zero_extend:SI (match_dup 1)))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “and,ld”)])

(define_insn_and_split “zero_extendqi2” [(set (match_operand:QX 0 “register_operand” “=r,r”) (zero_extend:QX (match_operand:QI 1 “nonimmediate_operand” “r,m”)))] "" “@ andi%.\t%0, %1, 0xff ldbu%o1%.\t%0, %1” “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (zero_extend:QX (match_dup 1)))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “and,ld”)])

;; Sign extension patterns

(define_insn_and_split “extendhisi2” [(set (match_operand:SI 0 “register_operand” “=r,r”) (sign_extend:SI (match_operand:HI 1 “nonimmediate_operand” “r,m”)))] "" "@

ldh%o1%.\t%0, %1" “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (sign_extend:SI (match_dup 1)))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “alu,ld”)])

(define_insn_and_split “extendqi2” [(set (match_operand:QX 0 “register_operand” “=r,r”) (sign_extend:QX (match_operand:QI 1 “nonimmediate_operand” “r,m”)))] "" "@

ldb%o1%.\t%0, %1" “nios2_large_constant_memory_operand_p (operands[1])” [(set (match_dup 0) (sign_extend:QX (match_dup 1)))] { operands[1] = nios2_split_large_constant_memory_operand (operands[1]); } [(set_attr “type” “alu,ld”)])

;; Split patterns for register alternative cases. (define_split [(set (match_operand:SI 0 “register_operand” "") (sign_extend:SI (match_operand:HI 1 “register_operand” "")))] “reload_completed” [(set (match_dup 0) (and:SI (match_dup 1) (const_int 65535))) (set (match_dup 0) (xor:SI (match_dup 0) (const_int 32768))) (set (match_dup 0) (plus:SI (match_dup 0) (const_int -32768)))] “operands[1] = gen_lowpart (SImode, operands[1]);”)

(define_split [(set (match_operand:QX 0 “register_operand” "") (sign_extend:QX (match_operand:QI 1 “register_operand” "")))] “reload_completed” [(set (match_dup 0) (and:SI (match_dup 1) (const_int 255))) (set (match_dup 0) (xor:SI (match_dup 0) (const_int 128))) (set (match_dup 0) (plus:SI (match_dup 0) (const_int -128)))] “operands[0] = gen_lowpart (SImode, operands[0]); operands[1] = gen_lowpart (SImode, operands[1]);”)

;; Arithmetic Operations

(define_insn “addsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (plus:SI (match_operand:SI 1 “register_operand” “%r”) (match_operand:SI 2 “add_regimm_operand” “rIT”)))] "" { return nios2_add_insn_asm (insn, operands); } [(set_attr “type” “add”)])

(define_insn “subsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (minus:SI (match_operand:SI 1 “reg_or_0_operand” “rM”) (match_operand:SI 2 “register_operand” “r”)))] "" “sub%.\t%0, %z1, %2” [(set_attr “type” “sub”)])

(define_insn “mulsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (mult:SI (match_operand:SI 1 “register_operand” “%r”) (match_operand:SI 2 “arith_operand” “rI”)))] “TARGET_HAS_MUL” “mul%i2\t%0, %1, %z2” [(set_attr “type” “mul”)])

(define_expand “divsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (div:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “register_operand” “r”)))] "" { if (!TARGET_HAS_DIV) { if (TARGET_FAST_SW_DIV) { nios2_emit_expensive_div (operands, SImode); DONE; } else FAIL; } })

(define_insn “divsi3_insn” [(set (match_operand:SI 0 “register_operand” “=r”) (div:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “register_operand” “r”)))] “TARGET_HAS_DIV” “div\t%0, %1, %2” [(set_attr “type” “div”)])

(define_insn “udivsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (udiv:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “register_operand” “r”)))] “TARGET_HAS_DIV” “divu\t%0, %1, %2” [(set_attr “type” “div”)])

(define_code_iterator EXTEND [sign_extend zero_extend]) (define_code_attr us [(sign_extend “s”) (zero_extend “u”)]) (define_code_attr mul [(sign_extend “mul”) (zero_extend “umul”)])

(define_insn “mulsi3_highpart” [(set (match_operand:SI 0 “register_operand” “=r”) (truncate:SI (lshiftrt:DI (mult:DI (EXTEND:DI (match_operand:SI 1 “register_operand” “r”)) (EXTEND:DI (match_operand:SI 2 “register_operand” “r”))) (const_int 32))))] “TARGET_HAS_MULX” “mulx\t%0, %1, %2” [(set_attr “type” “mul”)])

(define_expand “sidi3” [(set (match_operand:DI 0 “register_operand” "") (mult:DI (EXTEND:DI (match_operand:SI 1 “register_operand” "")) (EXTEND:DI (match_operand:SI 2 “register_operand” ""))))] “TARGET_HAS_MULX” { rtx hi = gen_reg_rtx (SImode); rtx lo = gen_reg_rtx (SImode);

emit_insn (gen_mulsi3_highpart (hi, operands[1], operands[2])); emit_insn (gen_mulsi3 (lo, operands[1], operands[2])); emit_move_insn (gen_lowpart (SImode, operands[0]), lo); emit_move_insn (gen_highpart (SImode, operands[0]), hi); DONE; })

;; Negate and ones complement

(define_insn “negsi2” [(set (match_operand:SI 0 “register_operand” “=r”) (neg:SI (match_operand:SI 1 “register_operand” “r”)))] "" { if (get_attr_length (insn) == 2) return “neg.n\t%0, %1”; else return “sub\t%0, zero, %1”; } [(set_attr “type” “neg”)])

(define_insn “one_cmplsi2” [(set (match_operand:SI 0 “register_operand” “=r”) (not:SI (match_operand:SI 1 “register_operand” “r”)))] "" { if (get_attr_length (insn) == 2) return “not.n\t%0, %1”; else return “nor\t%0, zero, %1”; } [(set_attr “type” “not”)])

;; Integer logical Operations

(define_insn “andsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (and:SI (match_operand:SI 1 “register_operand” “%r”) (match_operand:SI 2 “and_operand” “rJKP”)))] "" “and%x2%.\t%0, %1, %y2” [(set_attr “type” “and”)])

(define_code_iterator LOGICAL [ior xor]) (define_code_attr logical_asm [(ior “or”) (xor “xor”)])

(define_insn “si3” [(set (match_operand:SI 0 “register_operand” “=r”) (LOGICAL:SI (match_operand:SI 1 “register_operand” “%r”) (match_operand:SI 2 “logical_operand” “rJK”)))] "" “<logical_asm>%x2%.\t%0, %1, %y2” [(set_attr “type” “<logical_asm>”)])

(define_insn “*norsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (and:SI (not:SI (match_operand:SI 1 “register_operand” “%r”)) (not:SI (match_operand:SI 2 “register_operand” “r”))))] "" “nor\t%0, %1, %2” [(set_attr “type” “alu”)])

;; Shift instructions

(define_code_iterator SHIFT [ashift ashiftrt lshiftrt rotate]) (define_code_attr shift_op [(ashift “ashl”) (ashiftrt “ashr”) (lshiftrt “lshr”) (rotate “rotl”)]) (define_code_attr shift_asm [(ashift “sll”) (ashiftrt “sra”) (lshiftrt “srl”) (rotate “rol”)])

(define_insn “<shift_op>si3” [(set (match_operand:SI 0 “register_operand” “=r”) (SHIFT:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “shift_operand” “rL”)))] "" “<shift_asm>%i2%.\t%0, %1, %z2” [(set_attr “type” “<shift_asm>”)])

(define_insn “rotrsi3” [(set (match_operand:SI 0 “register_operand” “=r”) (rotatert:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “register_operand” “r”)))] "" “ror\t%0, %1, %2” [(set_attr “type” “ror”)])

;; Nios II R2 Bit Manipulation Extension (BMX), provides ;; bit merge/insertion/extraction instructions.

(define_insn “*merge” [(set (zero_extract:SI (match_operand:SI 0 “register_operand” “+r”) (match_operand:SI 1 “const_shift_operand” “L”) (match_operand:SI 2 “const_shift_operand” “L”)) (zero_extract:SI (match_operand:SI 3 “register_operand” “r”) (match_dup 1) (match_dup 2)))] “TARGET_HAS_BMX” { operands[4] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1); return “merge\t%0, %3, %4, %2”; } [(set_attr “type” “alu”)])

(define_insn “extzv” [(set (match_operand:SI 0 “register_operand” “=r”) (zero_extract:SI (match_operand:SI 1 “register_operand” “r”) (match_operand:SI 2 “const_shift_operand” “L”) (match_operand:SI 3 “const_shift_operand” “L”)))] “TARGET_HAS_BMX” { operands[4] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1); return “extract\t%0, %1, %4, %3”; } [(set_attr “type” “alu”)])

(define_insn “insv” [(set (zero_extract:SI (match_operand:SI 0 “register_operand” “+r”) (match_operand:SI 1 “const_shift_operand” “L”) (match_operand:SI 2 “const_shift_operand” “L”)) (match_operand:SI 3 “reg_or_0_operand” “rM”))] “TARGET_HAS_BMX” { operands[4] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1); return “insert\t%0, %z3, %4, %2”; } [(set_attr “type” “alu”)])

;; Floating point instructions

;; Mode iterator for single/double float (define_mode_iterator F [SF DF]) (define_mode_attr f [(SF “s”) (DF “d”)])

;; Basic arithmetic instructions (define_code_iterator FOP3 [plus minus mult div]) (define_code_attr fop3 [(plus “add”) (minus “sub”) (mult “mul”) (div “div”)])

(define_insn “3” [(set (match_operand:F 0 “register_operand” “=r”) (FOP3:F (match_operand:F 1 “register_operand” “r”) (match_operand:F 2 “register_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_f)” { return nios2_fpu_insn_asm (n2fpu_f); } [(set_attr “type” “custom”)])

;; Floating point min/max operations (define_code_iterator SMINMAX [smin smax]) (define_code_attr minmax [(smin “min”) (smax “max”)]) (define_insn “3” [(set (match_operand:F 0 “register_operand” “=r”) (SMINMAX:F (match_operand:F 1 “register_operand” “r”) (match_operand:F 2 “register_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_f)” { return nios2_fpu_insn_asm (n2fpu_f); } [(set_attr “type” “custom”)])

;; These 2-operand FP operations can be collected together (define_code_iterator FOP2 [abs neg sqrt]) (define_insn “2” [(set (match_operand:F 0 “register_operand” “=r”) (FOP2:F (match_operand:F 1 “register_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_f)” { return nios2_fpu_insn_asm (n2fpu_f); } [(set_attr “type” “custom”)])

;; X, Y register access instructions (define_insn “nios2_fwrx” [(unspec_volatile [(match_operand:DF 0 “register_operand” “r”)] UNSPECV_FWRX)] “nios2_fpu_insn_enabled (n2fpu_fwrx)” { return nios2_fpu_insn_asm (n2fpu_fwrx); } [(set_attr “type” “custom”)])

(define_insn “nios2_fwry” [(unspec_volatile [(match_operand:SF 0 “register_operand” “r”)] UNSPECV_FWRY)] “nios2_fpu_insn_enabled (n2fpu_fwry)” { return nios2_fpu_insn_asm (n2fpu_fwry); } [(set_attr “type” “custom”)])

;; The X, Y read insns uses an int iterator (define_int_iterator UNSPEC_READ_XY [UNSPECV_FRDXLO UNSPECV_FRDXHI UNSPECV_FRDY]) (define_int_attr read_xy [(UNSPECV_FRDXLO “frdxlo”) (UNSPECV_FRDXHI “frdxhi”) (UNSPECV_FRDY “frdy”)]) (define_insn “nios2_<read_xy>” [(set (match_operand:SF 0 “register_operand” “=r”) (unspec_volatile:SF [(const_int 0)] UNSPEC_READ_XY))] “nios2_fpu_insn_enabled (n2fpu_<read_xy>)” { return nios2_fpu_insn_asm (n2fpu_<read_xy>); } [(set_attr “type” “custom”)])

;; Various math functions (define_int_iterator MATHFUNC [UNSPEC_FCOS UNSPEC_FSIN UNSPEC_FTAN UNSPEC_FATAN UNSPEC_FEXP UNSPEC_FLOG]) (define_int_attr mathfunc [(UNSPEC_FCOS “cos”) (UNSPEC_FSIN “sin”) (UNSPEC_FTAN “tan”) (UNSPEC_FATAN “atan”) (UNSPEC_FEXP “exp”) (UNSPEC_FLOG “log”)])

(define_insn “2” [(set (match_operand:F 0 “register_operand” “=r”) (unspec:F [(match_operand:F 1 “register_operand” “r”)] MATHFUNC))] “nios2_fpu_insn_enabled (n2fpu_f)” { return nios2_fpu_insn_asm (n2fpu_f); } [(set_attr “type” “custom”)])

;; Converting between floating point and fixed point

(define_code_iterator FLOAT [float unsigned_float]) (define_code_iterator FIX [fix unsigned_fix])

(define_code_attr conv_op [(float “float”) (unsigned_float “floatuns”) (fix “fix”) (unsigned_fix “fixuns”)]) (define_code_attr i [(float “i”) (unsigned_float “u”) (fix “i”) (unsigned_fix “u”)])

;; Integer to float conversions (define_insn “<conv_op>si2” [(set (match_operand:F 0 “register_operand” “=r”) (FLOAT:F (match_operand:SI 1 “register_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_float)” { return nios2_fpu_insn_asm (n2fpu_float); } [(set_attr “type” “custom”)])

;; Float to integer conversions (define_insn “<conv_op>_truncsi2” [(set (match_operand:SI 0 “register_operand” “=r”) (FIX:SI (match_operand:F 1 “general_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_fix)” { return nios2_fpu_insn_asm (n2fpu_fix); } [(set_attr “type” “custom”)])

(define_insn “lroundsfsi2” [(set (match_operand:SI 0 “register_operand” “=r”) (unspec:SI [(match_operand:SF 1 “general_operand” “r”)] UNSPEC_ROUND))] “nios2_fpu_insn_enabled (n2fpu_round)” { return nios2_fpu_insn_asm (n2fpu_round); } [(set_attr “type” “custom”)])

(define_insn “extendsfdf2” [(set (match_operand:DF 0 “register_operand” “=r”) (float_extend:DF (match_operand:SF 1 “general_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_fextsd)” { return nios2_fpu_insn_asm (n2fpu_fextsd); } [(set_attr “type” “custom”)])

(define_insn “truncdfsf2” [(set (match_operand:SF 0 “register_operand” “=r”) (float_truncate:SF (match_operand:DF 1 “general_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_ftruncds)” { return nios2_fpu_insn_asm (n2fpu_ftruncds); } [(set_attr “type” “custom”)])

;; Prologue, Epilogue and Return

(define_expand “prologue” [(const_int 1)] "" { nios2_expand_prologue (); DONE; })

(define_expand “epilogue” [(return)] "" { nios2_expand_epilogue (false); DONE; })

(define_expand “sibcall_epilogue” [(return)] "" { nios2_expand_epilogue (true); DONE; })

(define_expand “return” [(simple_return)] “nios2_can_use_return_insn ()” { if (nios2_expand_return ()) DONE; })

(define_insn “simple_return” [(simple_return)] "" “ret%.” [(set_attr “type” “control”)])

;; Block any insns from being moved before this point, since the ;; profiling call to mcount can use various registers that aren't ;; saved or used to pass arguments.

(define_insn “blockage” [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] "" "" [(set_attr “type” “unknown”) (set_attr “length” “0”)])

;; This is used in compiling the unwind routines. (define_expand “eh_return” [(use (match_operand 0 “general_operand”))] "" { if (GET_MODE (operands[0]) != Pmode) operands[0] = convert_to_mode (Pmode, operands[0], 0); emit_insn (gen_eh_set_ra (operands[0])); DONE; })

;; Modify the return address for EH return. We can't expand this ;; until we know where it will be put in the stack frame.

(define_insn_and_split “eh_set_ra” [(unspec [(match_operand:SI 0 “register_operand” “r”)] UNSPEC_EH_RETURN) (clobber (match_scratch:SI 1 “=&r”))] "" “#” “reload_completed” [(const_int 0)] { nios2_set_return_address (operands[0], operands[1]); DONE; })

;; Jumps and calls

; Note that the assembler fixes up any out-of-range branch instructions not ; caught by the compiler branch shortening code. The sequence emitted by ; the assembler can be very inefficient, but it is correct for PIC code. ; For non-PIC we are better off converting to an absolute JMPI. ; ; Direct calls and sibcalls use the CALL and JMPI instructions, respectively. ; These instructions have an immediate operand that specifies the low 28 bits ; of the PC, effectively allowing direct calls within a 256MB memory segment. ; Per the Nios II Processor Reference Handbook, the linker is not required to ; check or adjust for overflow.

(define_insn “indirect_jump” [(set (pc) (match_operand:SI 0 “register_operand” “c”))] "" “jmp%!\t%0” [(set_attr “type” “control”)])

(define_insn “jump” [(set (pc) (label_ref (match_operand 0 "" "")))] "" { if (get_attr_length (insn) == 2) return “br.n\t%0”; else if (get_attr_length (insn) == 4) return “br\t%0”; else return “jmpi\t%0”; } [(set_attr “type” “control”) (set (attr “length”) (if_then_else (and (match_test “TARGET_HAS_CDX”) (and (ge (minus (match_dup 0) (pc)) (const_int -1022)) (le (minus (match_dup 0) (pc)) (const_int 1022)))) (const_int 2) (if_then_else (ior (match_test “flag_pic”) (and (ge (minus (match_dup 0) (pc)) (const_int -32764)) (le (minus (match_dup 0) (pc)) (const_int 32764)))) (const_int 4) (const_int 8))))])

(define_expand “call” [(parallel [(call (match_operand 0 "" "") (match_operand 1 "" "")) (clobber (reg:SI RA_REGNO))])] "" “nios2_adjust_call_address (&operands[0], NULL_RTX);”)

(define_expand “call_value” [(parallel [(set (match_operand 0 "" "") (call (match_operand 1 "" "") (match_operand 2 "" ""))) (clobber (reg:SI RA_REGNO))])] "" “nios2_adjust_call_address (&operands[1], NULL_RTX);”)

(define_insn “*call” [(call (mem:QI (match_operand:SI 0 “call_operand” “i,r”)) (match_operand 1 "" "")) (clobber (reg:SI RA_REGNO))] "" “@ call\t%0 callr%.\t%0” [(set_attr “type” “control”)])

(define_insn “*call_value” [(set (match_operand 0 "" "") (call (mem:QI (match_operand:SI 1 “call_operand” “i,r”)) (match_operand 2 "" ""))) (clobber (reg:SI RA_REGNO))] "" “@ call\t%1 callr%.\t%1” [(set_attr “type” “control”)])

(define_expand “sibcall” [(parallel [(call (match_operand 0 "" "") (match_operand 1 "" "")) (return)])] "" “nios2_adjust_call_address (&operands[0], NULL_RTX);”)

(define_expand “sibcall_value” [(parallel [(set (match_operand 0 "" "") (call (match_operand 1 "" "") (match_operand 2 "" ""))) (return)])] "" “nios2_adjust_call_address (&operands[1], NULL_RTX);”)

(define_insn “sibcall_internal” [(call (mem:QI (match_operand:SI 0 “call_operand” “i,j”)) (match_operand 1 "" "")) (return)] "" “@ jmpi\t%0 jmp%!\t%0” [(set_attr “type” “control”)])

(define_insn “sibcall_value_internal” [(set (match_operand 0 “register_operand” "") (call (mem:QI (match_operand:SI 1 “call_operand” “i,j”)) (match_operand 2 "" ""))) (return)] "" “@ jmpi\t%1 jmp%!\t%1” [(set_attr “type” “control”)])

(define_expand “tablejump” [(parallel [(set (pc) (match_operand 0 “register_operand” “r”)) (use (label_ref (match_operand 1 "" "")))])] "" { if (flag_pic) { /* Hopefully, CSE will eliminate this copy. */ rtx reg1 = copy_addr_to_reg (gen_rtx_LABEL_REF (Pmode, operands[1])); rtx reg2 = gen_reg_rtx (SImode);

  emit_insn (gen_addsi3 (reg2, operands[0], reg1));
  operands[0] = reg2;
}

})

(define_insn “*tablejump” [(set (pc) (match_operand:SI 0 “register_operand” “c”)) (use (label_ref (match_operand 1 "" "")))] "" “jmp%!\t%0” [(set_attr “type” “control”)])

;; cstore, cbranch patterns

(define_mode_iterator CM [SI SF DF])

(define_expand “cstore4” [(set (match_operand:SI 0 “register_operand” “=r”) (match_operator:SI 1 “expandable_comparison_operator” [(match_operand:CM 2 “register_operand”) (match_operand:CM 3 “nonmemory_operand”)]))] "" { if (!nios2_validate_compare (mode, &operands[1], &operands[2], &operands[3])) FAIL; })

(define_expand “cbranch4” [(set (pc) (if_then_else (match_operator 0 “expandable_comparison_operator” [(match_operand:CM 1 “register_operand”) (match_operand:CM 2 “nonmemory_operand”)]) (label_ref (match_operand 3 "")) (pc)))] "" { if (!nios2_validate_compare (mode, &operands[0], &operands[1], &operands[2])) FAIL; if (GET_MODE_CLASS (mode) == MODE_FLOAT || !reg_or_0_operand (operands[2], mode)) { rtx condreg = gen_reg_rtx (SImode); emit_insn (gen_cstore4 (condreg, operands[0], operands[1], operands[2])); operands[1] = condreg; operands[2] = const0_rtx; operands[0] = gen_rtx_fmt_ee (NE, VOIDmode, condreg, const0_rtx); } })

(define_insn “nios2_cbranch” [(set (pc) (if_then_else (match_operator 0 “ordered_comparison_operator” [(match_operand:SI 1 “reg_or_0_operand” “rM”) (match_operand:SI 2 “reg_or_0_operand” “rM”)]) (label_ref (match_operand 3 "" "")) (pc)))] "" { if (get_attr_length (insn) == 2) return “b%0z.n\t%z1, %l3”; else if (get_attr_length (insn) == 4) return “b%0\t%z1, %z2, %l3”; else if (get_attr_length (insn) == 6) return “b%R0z.n\t%z1, .+6;jmpi\t%l3”; else return “b%R0\t%z1, %z2, .+8;jmpi\t%l3”; } [(set_attr “type” “control”) (set (attr “length”) (cond [(and (match_test “nios2_cdx_narrow_form_p (insn)”) (ge (minus (match_dup 3) (pc)) (const_int -126)) (le (minus (match_dup 3) (pc)) (const_int 126))) (const_int 2) (ior (match_test “flag_pic”) (and (ge (minus (match_dup 3) (pc)) (const_int -32764)) (le (minus (match_dup 3) (pc)) (const_int 32764)))) (const_int 4) (match_test “nios2_cdx_narrow_form_p (insn)”) (const_int 6)] (const_int 8)))])

;; Floating point comparisons (define_code_iterator FCMP [eq ne gt ge le lt]) (define_insn “nios2_s” [(set (match_operand:SI 0 “register_operand” “=r”) (FCMP:SI (match_operand:F 1 “register_operand” “r”) (match_operand:F 2 “register_operand” “r”)))] “nios2_fpu_insn_enabled (n2fpu_fcmp)” { return nios2_fpu_insn_asm (n2fpu_fcmp); } [(set_attr “type” “custom”)])

;; Integer comparisons

(define_code_iterator EQNE [eq ne]) (define_insn “nios2_cmp” [(set (match_operand:SI 0 “register_operand” “=r”) (EQNE:SI (match_operand:SI 1 “register_operand” “%r”) (match_operand:SI 2 “arith_operand” “rI”)))] "" “cmp%i2\t%0, %1, %z2” [(set_attr “type” “alu”)])

(define_code_iterator SCMP [ge lt]) (define_insn “nios2_cmp” [(set (match_operand:SI 0 “register_operand” “=r”) (SCMP:SI (match_operand:SI 1 “reg_or_0_operand” “rM”) (match_operand:SI 2 “arith_operand” “rI”)))] "" “cmp%i2\t%0, %z1, %z2” [(set_attr “type” “alu”)])

(define_code_iterator UCMP [geu ltu]) (define_insn “nios2_cmp” [(set (match_operand:SI 0 “register_operand” “=r”) (UCMP:SI (match_operand:SI 1 “reg_or_0_operand” “rM”) (match_operand:SI 2 “uns_arith_operand” “rJ”)))] "" “cmp%u2\t%0, %z1, %z2” [(set_attr “type” “alu”)])

;; Custom instruction patterns. The operands are intentionally ;; mode-less, to serve as generic carriers of all Altera defined ;; built-in instruction/function types.

(define_insn “custom_nxx” [(unspec_volatile [(match_operand 0 “custom_insn_opcode” “N”) (match_operand 1 “reg_or_0_operand” “rM”) (match_operand 2 “reg_or_0_operand” “rM”)] UNSPECV_CUSTOM_NXX)] "" “custom\t%0, zero, %z1, %z2” [(set_attr “type” “custom”)])

(define_insn “custom_xnxx” [(set (match_operand 0 “register_operand” “=r”) (unspec_volatile [(match_operand 1 “custom_insn_opcode” “N”) (match_operand 2 “reg_or_0_operand” “rM”) (match_operand 3 “reg_or_0_operand” “rM”)] UNSPECV_CUSTOM_XNXX))] "" “custom\t%1, %0, %z2, %z3” [(set_attr “type” “custom”)])

;; Misc. patterns

(define_insn “nop” [(const_int 0)] "" “nop%.” [(set_attr “type” “nop”)])

;; Connect ‘sync’ to ‘memory_barrier’ standard expand name (define_expand “memory_barrier” [(const_int 0)] "" { emit_insn (gen_sync ()); DONE; })

;; For the nios2 __builtin_sync built-in function (define_expand “sync” [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_SYNC))] "" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; })

(define_insn “*sync_insn” [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_SYNC))] "" “sync” [(set_attr “type” “control”)])

(define_insn “rdctl” [(set (match_operand:SI 0 “register_operand” “=r”) (unspec_volatile:SI [(match_operand:SI 1 “rdwrctl_operand” “O”)] UNSPECV_RDCTL))] "" “rdctl\t%0, ctl%1” [(set_attr “type” “control”)])

(define_insn “wrctl” [(unspec_volatile:SI [(match_operand:SI 0 “rdwrctl_operand” “O”) (match_operand:SI 1 “reg_or_0_operand” “rM”)] UNSPECV_WRCTL)] "" “wrctl\tctl%0, %z1” [(set_attr “type” “control”)])

(define_insn “rdprs” [(set (match_operand:SI 0 “register_operand” “=r”) (unspec_volatile:SI [(match_operand:SI 1 “rdwrctl_operand” “O”) (match_operand:SI 2 “arith_operand” “U”)] UNSPECV_RDPRS))] "" “rdprs\t%0, %1, %2” [(set_attr “type” “control”)])

;; Cache Instructions

(define_insn “flushd” [(unspec_volatile:SI [(match_operand:SI 0 “ldstio_memory_operand” “w”)] UNSPECV_FLUSHD)] "" “flushd\t%0” [(set_attr “type” “control”)])

(define_insn “flushda” [(unspec_volatile:SI [(match_operand:SI 0 “ldstio_memory_operand” “w”)] UNSPECV_FLUSHDA)] "" “flushda\t%0” [(set_attr “type” “control”)])

;; R2 Instructions

(define_insn “wrpie” [(set (match_operand:SI 0 “register_operand” “=r”) (unspec_volatile:SI [(match_operand:SI 1 “register_operand” “r”)] UNSPECV_WRPIE))] “TARGET_ARCH_R2” “wrpie\t%0, %1” [(set_attr “type” “control”)])

(define_insn “eni” [(unspec:VOID [(match_operand 0 “const_int_operand” “i”)] UNSPECV_ENI)] “TARGET_ARCH_R2” “eni\t%0” [(set_attr “type” “control”)])

;; Trap patterns (define_insn “trap” [(trap_if (const_int 1) (const_int 3))] "" “trap%.\t3” [(set_attr “type” “control”)])

(define_insn “ctrapsi4” [(trap_if (match_operator 0 “ordered_comparison_operator” [(match_operand:SI 1 “reg_or_0_operand” “rM”) (match_operand:SI 2 “reg_or_0_operand” “rM”)]) (match_operand 3 “const_int_operand” “i”))] "" { if (get_attr_length (insn) == 6) return “b%R0\t%z1, %z2, 1f;trap.n\t%3;1:”; else return “b%R0\t%z1, %z2, 1f;trap\t%3;1:”; } [(set_attr “type” “control”) (set (attr “length”) (if_then_else (match_test “nios2_cdx_narrow_form_p (insn)”) (const_int 6) (const_int 8)))])

;; Load the GOT register. (define_insn “load_got_register” [(set (match_operand:SI 0 “register_operand” “=&r”) (unspec:SI [(const_int 0)] UNSPEC_LOAD_GOT_REGISTER)) (set (match_operand:SI 1 “register_operand” “=r”) (unspec:SI [(const_int 0)] UNSPEC_LOAD_GOT_REGISTER))] "" “nextpc\t%0 \t1: \tmovhi\t%1, %%hiadj(_gp_got - 1b) \taddi\t%1, %1, %%lo(_gp_got - 1b)” [(set_attr “length” “12”)])

;; Read thread pointer register (define_expand “get_thread_pointersi” [(match_operand:SI 0 “register_operand” “=r”)] “TARGET_LINUX_ABI” { emit_move_insn (operands[0], gen_rtx_REG (Pmode, TP_REGNO)); DONE; })

;; Synchronization Primitives (include “sync.md”)

;; Include the ldwm/stwm/push.n/pop.n patterns and peepholes. (include “ldstwm.md”)