;; Machine Description for Altera Nios II.
;; Copyright (C) 2012-2020 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<mode>"
[(set (match_operand:M 0 "nonimmediate_operand" "")
(match_operand:M 1 "general_operand" ""))]
""
{
if (nios2_emit_move_sequence (operands, <MODE>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 "ld<bh>io"
[(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_ld<bh>io_signed (tmp, operands[1]));
emit_insn (gen_mov<mode> (operands[0], gen_lowpart (<MODE>mode, tmp)));
DONE;
})
(define_insn_and_split "ld<bh>io_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)))]
""
"ld<bh>io\\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 "st<bhw>io"
[(set (match_operand:BHW 0 "ldstio_memory_operand" "=w")
(unspec_volatile:BHW
[(match_operand:BHW 1 "reg_or_0_operand" "rM")] UNSPECV_STXIO))]
""
"st<bhw>io\\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_extendqi<mode>2"
[(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 "extendqi<mode>2"
[(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 "<us>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<us><us>\\t%0, %1, %2"
[(set_attr "type" "mul")])
(define_expand "<mul>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_<us>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 "<code>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 "<fop3><mode>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<fop3><f>)"
{ return nios2_fpu_insn_asm (n2fpu_f<fop3><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 "<code><mode>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<minmax><f>)"
{ return nios2_fpu_insn_asm (n2fpu_f<minmax><f>); }
[(set_attr "type" "custom")])
;; These 2-operand FP operations can be collected together
(define_code_iterator FOP2 [abs neg sqrt])
(define_insn "<code><mode>2"
[(set (match_operand:F 0 "register_operand" "=r")
(FOP2:F (match_operand:F 1 "register_operand" "r")))]
"nios2_fpu_insn_enabled (n2fpu_f<code><f>)"
{ return nios2_fpu_insn_asm (n2fpu_f<code><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 "<mathfunc><mode>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<mathfunc><f>)"
{ return nios2_fpu_insn_asm (n2fpu_f<mathfunc><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>si<mode>2"
[(set (match_operand:F 0 "register_operand" "=r")
(FLOAT:F (match_operand:SI 1 "register_operand" "r")))]
"nios2_fpu_insn_enabled (n2fpu_float<i><f>)"
{ return nios2_fpu_insn_asm (n2fpu_float<i><f>); }
[(set_attr "type" "custom")])
;; Float to integer conversions
(define_insn "<conv_op>_trunc<mode>si2"
[(set (match_operand:SI 0 "register_operand" "=r")
(FIX:SI (match_operand:F 1 "general_operand" "r")))]
"nios2_fpu_insn_enabled (n2fpu_fix<f><i>)"
{ return nios2_fpu_insn_asm (n2fpu_fix<f><i>); }
[(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 "cstore<mode>4"
[(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>mode, &operands[1], &operands[2],
&operands[3]))
FAIL;
})
(define_expand "cbranch<mode>4"
[(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>mode, &operands[0], &operands[1],
&operands[2]))
FAIL;
if (GET_MODE_CLASS (<MODE>mode) == MODE_FLOAT
|| !reg_or_0_operand (operands[2], <MODE>mode))
{
rtx condreg = gen_reg_rtx (SImode);
emit_insn (gen_cstore<mode>4
(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<code><mode>"
[(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<code><f>)"
{ return nios2_fpu_insn_asm (n2fpu_fcmp<code><f>); }
[(set_attr "type" "custom")])
;; Integer comparisons
(define_code_iterator EQNE [eq ne])
(define_insn "nios2_cmp<code>"
[(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<code>%i2\\t%0, %1, %z2"
[(set_attr "type" "alu")])
(define_code_iterator SCMP [ge lt])
(define_insn "nios2_cmp<code>"
[(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<code>%i2\\t%0, %z1, %z2"
[(set_attr "type" "alu")])
(define_code_iterator UCMP [geu ltu])
(define_insn "nios2_cmp<code>"
[(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<code>%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")