;; Machine description for OpenRISC
;; Copyright (C) 2018-2022 Free Software Foundation, Inc.
;; Contributed by Stafford Horne
;; 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
;; .
;; -------------------------------------------------------------------------
;; OpenRISC specific constraints, predicates and attributes
;; -------------------------------------------------------------------------
(include "constraints.md")
(include "predicates.md")
;; Register numbers
(define_constants
[(SP_REGNUM 1)
(HFP_REGNUM 2)
(LR_REGNUM 9)
(TLS_REGNUM 10)
(RV_REGNUM 11)
(PE_TMP_REGNUM 13)
(AP_REGNUM 32)
(SFP_REGNUM 33)
(SR_F_REGNUM 34)]
)
(define_c_enum "unspec" [
UNSPEC_SET_GOT
UNSPEC_GOT
UNSPEC_GOTOFF
UNSPEC_TPOFF
UNSPEC_GOTTPOFF
UNSPEC_TLSGD
UNSPEC_MSYNC
])
(define_c_enum "unspecv" [
UNSPECV_SET_GOT
UNSPECV_LL
UNSPECV_SC
])
;; Instruction scheduler
; Most instructions are 4 bytes long.
(define_attr "length" "" (const_int 4))
(define_attr "type"
"alu,st,ld,control,multi,fpu"
(const_string "alu"))
(define_attr "insn_support" "class1,sext,sfimm,shftimm,ror,rori" (const_string "class1"))
(define_attr "enabled" ""
(cond [(eq_attr "insn_support" "class1") (const_int 1)
(and (eq_attr "insn_support" "sext")
(ne (symbol_ref "TARGET_SEXT") (const_int 0))) (const_int 1)
(and (eq_attr "insn_support" "sfimm")
(ne (symbol_ref "TARGET_SFIMM") (const_int 0))) (const_int 1)
(and (eq_attr "insn_support" "shftimm")
(ne (symbol_ref "TARGET_SHFTIMM") (const_int 0))) (const_int 1)
(and (eq_attr "insn_support" "ror")
(ne (symbol_ref "TARGET_ROR") (const_int 0))) (const_int 1)
(and (eq_attr "insn_support" "rori")
(ne (symbol_ref "TARGET_RORI") (const_int 0))) (const_int 1)]
(const_int 0)))
;; Describe a user's asm statement.
(define_asm_attributes
[(set_attr "type" "multi")])
(define_automaton "or1k")
(define_cpu_unit "cpu" "or1k")
(define_insn_reservation "alu" 1
(eq_attr "type" "alu")
"cpu")
(define_insn_reservation "st" 1
(eq_attr "type" "st")
"cpu")
(define_insn_reservation "ld" 3
(eq_attr "type" "st")
"cpu")
(define_insn_reservation "control" 1
(eq_attr "type" "control")
"cpu")
(define_insn_reservation "fpu" 2
(eq_attr "type" "fpu")
"cpu")
; Define delay slots for any branch
(define_delay (eq_attr "type" "control")
[(eq_attr "type" "alu,st,ld") (nil) (nil)])
;; -------------------------------------------------------------------------
;; nop instruction
;; -------------------------------------------------------------------------
(define_insn "nop"
[(const_int 0)]
""
"l.nop")
;; -------------------------------------------------------------------------
;; Arithmetic instructions
;; -------------------------------------------------------------------------
(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 "reg_or_s16_operand" " r,I")))]
""
"@
l.add\t%0, %1, %2
l.addi\t%0, %1, %2")
(define_insn "mulsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI
(match_operand:SI 1 "register_operand" "%r,r")
(match_operand:SI 2 "reg_or_s16_operand" " r,I")))]
"!TARGET_SOFT_MUL"
"@
l.mul\t%0, %1, %2
l.muli\t%0, %1, %2")
(define_insn "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")))]
"!TARGET_SOFT_DIV"
"l.div\t%0, %1, %2")
(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_SOFT_DIV"
"l.divu\t%0, %1, %2")
(define_insn "subsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI
(match_operand:SI 1 "reg_or_0_operand" "rO")
(match_operand:SI 2 "register_operand" "r")))]
""
"l.sub\t%0, %r1, %2")
;; -------------------------------------------------------------------------
;; Floating Point Arithmetic instructions
;; -------------------------------------------------------------------------
;; Mode iterator for single/double float
(define_mode_iterator F [(SF "TARGET_HARD_FLOAT")
(DF "TARGET_DOUBLE_FLOAT")])
(define_mode_attr f [(SF "s") (DF "d")])
(define_mode_attr fr [(SF "r") (DF "d")])
(define_mode_attr fi [(SF "si") (DF "di")])
(define_mode_attr FI [(SF "SI") (DF "DI")])
;; Basic arithmetic instructions
(define_code_iterator FOP [plus minus mult div])
(define_code_attr fop [(plus "add") (minus "sub") (mult "mul") (div "div")])
(define_insn "3"
[(set (match_operand:F 0 "register_operand" "=")
(FOP:F (match_operand:F 1 "register_operand" "")
(match_operand:F 2 "register_operand" "")))]
"TARGET_HARD_FLOAT"
"lf..\t%d0, %d1, %d2"
[(set_attr "type" "fpu")])
;; Basic float<->int conversion
(define_insn "float2"
[(set (match_operand:F 0 "register_operand" "=")
(float:F
(match_operand: 1 "register_operand" "")))]
"TARGET_HARD_FLOAT"
"lf.itof.\t%d0, %d1"
[(set_attr "type" "fpu")])
(define_insn "fix_trunc2"
[(set (match_operand: 0 "register_operand" "=")
(fix:
(match_operand:F 1 "register_operand" "")))]
"TARGET_HARD_FLOAT"
"lf.ftoi.\t%d0, %d1"
[(set_attr "type" "fpu")])
;; -------------------------------------------------------------------------
;; Logical operators
;; -------------------------------------------------------------------------
(define_code_iterator SHIFT [ashift ashiftrt lshiftrt])
(define_code_attr shift_op [(ashift "ashl") (ashiftrt "ashr")
(lshiftrt "lshr")])
(define_code_attr shift_asm [(ashift "sll") (ashiftrt "sra")
(lshiftrt "srl")])
(define_insn "si3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(SHIFT:SI (match_operand:SI 1 "register_operand" "r,r")
(match_operand:SI 2 "reg_or_u6_operand" "r,n")))]
""
"@
l.\t%0, %1, %2
l.i\t%0, %1, %2"
[(set_attr "insn_support" "*,shftimm")])
(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 "ror_reg_or_u6_operand" "r,n")))]
"TARGET_ROR || TARGET_RORI"
"@
l.ror\t%0, %1, %2
l.rori\t%0, %1, %2"
[(set_attr "insn_support" "ror,rori")])
(define_insn "andsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(and:SI
(match_operand:SI 1 "register_operand" "%r,r")
(match_operand:SI 2 "reg_or_u16_operand" " r,K")))]
""
"@
l.and\t%0, %1, %2
l.andi\t%0, %1, %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 "reg_or_s16_operand" " r,I")))]
""
"@
l.xor\t%0, %1, %2
l.xori\t%0, %1, %2")
(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 "reg_or_u16_operand" " r,K")))]
""
"@
l.or\t%0, %1, %2
l.ori\t%0, %1, %2")
(define_expand "one_cmplsi2"
[(set (match_operand:SI 0 "register_operand" "")
(xor:SI (match_operand:SI 1 "register_operand" "") (const_int -1)))]
""
"")
;; -------------------------------------------------------------------------
;; Move instructions
;; -------------------------------------------------------------------------
(define_mode_iterator I [QI HI SI])
(define_mode_iterator I12 [QI HI])
(define_mode_attr ldst [(QI "b") (HI "h") (SI "w")])
(define_mode_attr zext_andi [(QI "0xff") (HI "0xffff")])
(define_expand "mov"
[(set (match_operand:I 0 "nonimmediate_operand" "")
(match_operand:I 1 "general_operand" ""))]
""
{
or1k_expand_move (mode, operands[0], operands[1]);
DONE;
})
;; 8-bit, 16-bit and 32-bit moves
(define_insn "*mov_internal"
[(set (match_operand:I 0 "nonimmediate_operand" "=r,r,r,r, m,r")
(match_operand:I 1 "input_operand" " r,M,K,I,rO,m"))]
"register_operand (operands[0], mode)
|| reg_or_0_operand (operands[1], mode)"
"@
l.or\t%0, %1, %1
l.movhi\t%0, hi(%1)
l.ori\t%0, r0, %1
l.xori\t%0, r0, %1
l.s\t%0, %r1
l.lz\t%0, %1"
[(set_attr "type" "alu,alu,alu,alu,st,ld")])
;; Hi/Low moves for constant and symbol loading
(define_insn "movsi_high"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (match_operand:SI 1 "high_operand" "")))]
""
"l.movhi\t%0, %h1"
[(set_attr "type" "alu")])
(define_insn "*movsi_lo_sum_iori"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "losum_ior_operand" "")))]
""
"l.ori\t%0, %1, %L2"
[(set_attr "type" "alu")])
(define_insn "*movsi_lo_sum_addi"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "losum_add_operand" "")))]
""
"l.addi\t%0, %1, %L2"
[(set_attr "type" "alu")])
;; 64-bit moves
;; ??? The clobber that emit_move_multi_word emits is arguably incorrect.
;; Consider gcc.c-torture/execute/20030222-1.c, where a reg-reg DImode
;; move gets register allocated to a no-op move. At which point the
;; we actively clobber the input.
(define_expand "movdi"
[(set (match_operand:DI 0 "nonimmediate_operand" "")
(match_operand:DI 1 "general_operand" ""))]
""
{
if (MEM_P (operands[0]) && !const0_operand(operands[1], DImode))
operands[1] = force_reg (DImode, operands[1]);
})
(define_insn_and_split "*movdi"
[(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,o,r")
(match_operand:DI 1 "general_operand" " r,o,rO,n"))]
"register_operand (operands[0], DImode)
|| reg_or_0_operand (operands[1], DImode)"
"#"
"&& 1"
[(const_int 0)]
{
rtx l0 = operand_subword (operands[0], 0, 0, DImode);
rtx l1 = operand_subword (operands[1], 0, 0, DImode);
rtx h0 = operand_subword (operands[0], 1, 0, DImode);
rtx h1 = operand_subword (operands[1], 1, 0, DImode);
if (reload_completed && reg_overlap_mentioned_p (l0, h1))
{
gcc_assert (!reg_overlap_mentioned_p (h0, l1));
emit_move_insn (h0, h1);
emit_move_insn (l0, l1);
}
else
{
emit_move_insn (l0, l1);
emit_move_insn (h0, h1);
}
DONE;
})
;; -------------------------------------------------------------------------
;; Sign Extending
;; -------------------------------------------------------------------------
;; Zero extension can always be done with AND or an extending load.
(define_insn "zero_extendsi2"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extend:SI (match_operand:I12 1 "reg_or_mem_operand" "r,m")))]
""
"@
l.andi\t%0, %1,
l.lz\t%0, %1")
;; Sign extension in registers is an optional extension, but the
;; extending load is always available. If SEXT is not available,
;; force the middle-end to do the expansion to shifts.
(define_insn "extendsi2"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(sign_extend:SI (match_operand:I12 1 "reg_or_mem_operand" "r,m")))]
"TARGET_SEXT"
"@
l.exts\t%0, %1
l.ls\t%0, %1")
(define_insn "*extendsi2_mem"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:I12 1 "memory_operand" "m")))]
""
"l.ls\t%0, %1")
;; -------------------------------------------------------------------------
;; Compare instructions
;; -------------------------------------------------------------------------
;; OpenRISC supports these integer comparisons:
;;
;; l.sfeq[i] - equality, r r or r i
;; l.sfne[i] - not equal, r r or r i
;; l.sflt{s,u}[i] - less than, signed or unsigned, r r or r i
;; l.sfle{s,u}[i] - less than or equal, signed or unsigned, r r or r i
;; l.sfgt{s,u}[i] - greater than, signed or unsigned, r r or r i
;; l.sfge{s,u}[i] - greater than or equal, signed or unsigned, r r or r i
;;
;; EQ,NE,LT,LTU,LE,LEU,GT,GTU,GE,GEU
;; We iterate through all of these
;;
(define_code_iterator intcmpcc [ne eq lt ltu gt gtu ge le geu leu])
(define_code_attr insn [(ne "ne") (eq "eq") (lt "lts") (ltu "ltu")
(gt "gts") (gtu "gtu") (ge "ges") (le "les")
(geu "geu") (leu "leu")])
(define_insn "*sf_insn"
[(set (reg:BI SR_F_REGNUM)
(intcmpcc:BI (match_operand:SI 0 "reg_or_0_operand" "rO,rO")
(match_operand:SI 1 "reg_or_s16_operand" "r,I")))]
""
"@
l.sf\t%r0, %1
l.sfi\t%r0, %1"
[(set_attr "insn_support" "*,sfimm")])
;; Support FP comparisons too
;; The OpenRISC FPU supports these comparisons:
;;
;; lf.sfeq.{d,s} - equality, r r, double or single precision
;; lf.sfge.{d,s} - greater than or equal, r r, double or single precision
;; lf.sfgt.{d,s} - greater than, r r, double or single precision
;; lf.sfle.{d,s} - less than or equal, r r, double or single precision
;; lf.sflt.{d,s} - less than, r r, double or single precision
;; lf.sfne.{d,s} - not equal, r r, double or single precision
;;
;; Double precision is only supported on some hardware. Only register/register
;; comparisons are supported. All comparisons are signed.
(define_code_iterator fpcmpcc [ne eq lt gt ge le uneq unle unlt ungt unge
unordered])
(define_code_attr fpcmpinsn [(ne "ne") (eq "eq") (lt "lt") (gt "gt") (ge "ge")
(le "le") (uneq "ueq") (unle "ule") (unlt "ult")
(ungt "ugt") (unge "uge") (unordered "un")])
(define_insn "*sf_fp_insn"
[(set (reg:BI SR_F_REGNUM)
(fpcmpcc:BI (match_operand:F 0 "register_operand" "")
(match_operand:F 1 "register_operand" "")))]
"TARGET_HARD_FLOAT"
"lf.sf.\t%d0, %d1"
[(set_attr "type" "fpu")])
;; -------------------------------------------------------------------------
;; Conditional Store instructions
;; -------------------------------------------------------------------------
(define_expand "cstoresi4"
[(set (match_operand:SI 0 "register_operand" "")
(if_then_else:SI
(match_operator 1 "comparison_operator"
[(match_operand:SI 2 "reg_or_0_operand" "")
(match_operand:SI 3 "reg_or_s16_operand" "")])
(match_dup 0)
(const_int 0)))]
""
{
or1k_expand_compare (operands + 1);
PUT_MODE (operands[1], SImode);
emit_insn (gen_rtx_SET (operands[0], operands[1]));
DONE;
})
;; Support FP cstores too
(define_expand "cstore4"
[(set (match_operand:SI 0 "register_operand" "")
(if_then_else:F
(match_operator 1 "fp_comparison_operator"
[(match_operand:F 2 "register_operand" "")
(match_operand:F 3 "register_operand" "")])
(match_dup 0)
(const_int 0)))]
"TARGET_HARD_FLOAT"
{
or1k_expand_compare (operands + 1);
PUT_MODE (operands[1], SImode);
emit_insn (gen_rtx_SET (operands[0], operands[1]));
DONE;
})
;; Being able to "copy" SR_F to a general register is helpful for
;; the atomic insns, wherein the usual usage is to test the success
;; of the compare-and-swap. Representing the operation in this way,
;; rather than exposing the cmov immediately, allows the optimizers
;; to propagate the use of SR_F directly into a branch.
(define_expand "sne_sr_f"
[(set (match_operand:SI 0 "register_operand" "=r")
(ne:SI (reg:BI SR_F_REGNUM) (const_int 0)))]
"")
(define_insn_and_split "*scc"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 1 "equality_comparison_operator"
[(reg:BI SR_F_REGNUM) (const_int 0)]))]
""
"#"
"reload_completed"
[(set (match_dup 0) (const_int 1))
(set (match_dup 0)
(if_then_else:SI (match_dup 1)
(match_dup 0)
(const_int 0)))]
"")
(define_expand "movcc"
[(set (match_operand:I 0 "register_operand" "")
(if_then_else:I (match_operand 1 "comparison_operator" "")
(match_operand:I 2 "reg_or_0_operand" "")
(match_operand:I 3 "reg_or_0_operand" "")))]
""
{
rtx xops[3] = { operands[1], XEXP (operands[1], 0), XEXP (operands[1], 1) };
or1k_expand_compare (xops);
operands[1] = xops[0];
})
(define_insn_and_split "*cmov"
[(set (match_operand:I 0 "register_operand" "=r")
(if_then_else:I
(match_operator 3 "equality_comparison_operator"
[(reg:BI SR_F_REGNUM) (const_int 0)])
(match_operand:I 1 "reg_or_0_operand" "rO")
(match_operand:I 2 "reg_or_0_operand" "rO")))]
""
{
return (GET_CODE (operands[3]) == NE
? "l.cmov\t%0, %r1, %r2"
: "l.cmov\t%0, %r2, %r1");
}
"!TARGET_CMOV"
[(const_int 0)]
{
rtx x;
rtx label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
/* Generated a *cbranch pattern. */
if (rtx_equal_p (operands[0], operands[2]))
{
PUT_CODE (operands[3], (GET_CODE (operands[3]) == NE) ? EQ : NE);
x = gen_rtx_IF_THEN_ELSE (VOIDmode, operands[3], label, pc_rtx);
emit_jump_insn (gen_rtx_SET (pc_rtx, x));
emit_move_insn (operands[0], operands[1]);
}
else
{
x = gen_rtx_IF_THEN_ELSE (VOIDmode, operands[3], label, pc_rtx);
emit_move_insn (operands[0], operands[1]);
emit_jump_insn (gen_rtx_SET (pc_rtx, x));
emit_move_insn (operands[0], operands[2]);
}
emit_label (XEXP (label, 0));
DONE;
})
;; -------------------------------------------------------------------------
;; Branch instructions
;; -------------------------------------------------------------------------
(define_expand "cbranchsi4"
[(set (pc)
(if_then_else
(match_operator 0 "comparison_operator"
[(match_operand:SI 1 "reg_or_0_operand" "")
(match_operand:SI 2 "reg_or_s16_operand" "")])
(label_ref (match_operand 3 "" ""))
(pc)))]
""
{
or1k_expand_compare (operands);
})
;; Support FP branching
(define_expand "cbranch4"
[(set (pc)
(if_then_else
(match_operator 0 "fp_comparison_operator"
[(match_operand:F 1 "register_operand" "")
(match_operand:F 2 "register_operand" "")])
(label_ref (match_operand 3 "" ""))
(pc)))]
"TARGET_HARD_FLOAT"
{
or1k_expand_compare (operands);
})
(define_insn "*cbranch"
[(set (pc)
(if_then_else
(match_operator 1 "equality_comparison_operator"
[(reg:BI SR_F_REGNUM) (const_int 0)])
(label_ref (match_operand 0 "" ""))
(pc)))]
""
{
return (GET_CODE (operands[1]) == NE
? "l.bf\t%0%#"
: "l.bnf\t%0%#");
}
[(set_attr "type" "control")])
;; -------------------------------------------------------------------------
;; Jump instructions
;; -------------------------------------------------------------------------
(define_insn "jump"
[(set (pc) (label_ref (match_operand 0 "" "")))]
""
"l.j\t%0%#"
[(set_attr "type" "control")])
(define_insn "indirect_jump"
[(set (pc) (match_operand:SI 0 "register_operand" "r"))]
""
"l.jr\t%0%#"
[(set_attr "type" "control")])
;; -------------------------------------------------------------------------
;; Prologue & Epilogue
;; -------------------------------------------------------------------------
(define_expand "prologue"
[(const_int 1)]
""
{
or1k_expand_prologue ();
DONE;
})
;; Expand epilogue as RTL
(define_expand "epilogue"
[(return)]
""
{
or1k_expand_epilogue ();
emit_jump_insn (gen_simple_return ());
DONE;
})
(define_expand "sibcall_epilogue"
[(return)]
""
{
or1k_expand_epilogue ();
/* Placing a USE of LR here, rather than as a REG_USE on the
sibcall itself, means that LR is not unnecessarily live
within the function itself, which would force creation of
a stack frame. */
emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, LR_REGNUM)));
DONE;
})
(define_expand "simple_return"
[(parallel [(simple_return) (use (match_dup 0))])]
""
{
operands[0] = gen_rtx_REG (Pmode, LR_REGNUM);
})
(define_insn "*simple_return"
[(simple_return)
(use (match_operand:SI 0 "register_operand" "r"))]
""
"l.jr\t%0%#"
[(set_attr "type" "control")])
(define_expand "eh_return"
[(use (match_operand 0 "general_operand"))]
""
{
or1k_expand_eh_return (operands[0]);
DONE;
})
;; This is a placeholder, during RA, in order to create the PIC regiter.
;; We do this so that we don't unconditionally mark the LR register as
;; clobbered. It is replaced during prologue generation with the proper
;; set_got pattern below. This works because the set_got_tmp insn is the
;; first insn in the stream and that it isn't moved during RA.
(define_insn "set_got_tmp"
[(set (match_operand:SI 0 "register_operand" "=t")
(unspec_volatile:SI [(const_int 0)] UNSPECV_SET_GOT))]
""
{
gcc_unreachable ();
})
;; The insn to initialize the GOT.
(define_insn "set_got"
[(set (match_operand:SI 0 "register_operand" "=t")
(unspec:SI [(const_int 0)] UNSPEC_SET_GOT))
(clobber (reg:SI LR_REGNUM))]
""
{
return ("l.jal\t8\;"
" l.movhi\t%0, gotpchi(_GLOBAL_OFFSET_TABLE_-4)\;"
"l.ori\t%0, %0, gotpclo(_GLOBAL_OFFSET_TABLE_+0)\;"
"l.add\t%0, %0, r9");
}
[(set_attr "length" "16")
(set_attr "type" "multi")])
;; Block memory operations from being scheduled across frame (de)allocation.
(define_insn "frame_addsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI
(match_operand:SI 1 "register_operand" "%r,r")
(match_operand:SI 2 "reg_or_s16_operand" " r,I")))
(clobber (mem:BLK (scratch)))]
"reload_completed"
"@
l.add\t%0, %1, %2
l.addi\t%0, %1, %2")
;; -------------------------------------------------------------------------
;; Atomic Operations
;; -------------------------------------------------------------------------
;; Note that MULT stands in for the non-existant NAND rtx_code.
(define_code_iterator FETCHOP [plus minus ior xor and mult])
(define_code_attr fetchop_name
[(plus "add")
(minus "sub")
(ior "or")
(xor "xor")
(and "and")
(mult "nand")])
(define_code_attr fetchop_pred
[(plus "reg_or_s16_operand")
(minus "register_operand")
(ior "reg_or_u16_operand")
(xor "reg_or_s16_operand")
(and "reg_or_u16_operand")
(mult "reg_or_u16_operand")])
(define_expand "mem_thread_fence"
[(match_operand:SI 0 "const_int_operand" "")] ;; model
""
{
memmodel model = memmodel_base (INTVAL (operands[0]));
if (model != MEMMODEL_RELAXED)
emit_insn (gen_msync ());
DONE;
})
(define_expand "msync"
[(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_MSYNC))]
""
{
operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
MEM_VOLATILE_P (operands[0]) = 1;
})
(define_insn "*msync"
[(set (match_operand:BLK 0 "" "")
(unspec:BLK [(match_dup 0)] UNSPEC_MSYNC))]
""
"l.msync")
(define_insn "load_locked_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(unspec_volatile:SI
[(match_operand:SI 1 "memory_operand" "m")] UNSPECV_LL))]
""
"l.lwa\t%0,%1"
[(set_attr "type" "ld")])
(define_insn "store_conditional_si"
[(set (reg:BI SR_F_REGNUM)
(unspec_volatile:BI [(const_int 0)] UNSPECV_SC))
(set (match_operand:SI 0 "memory_operand" "=m")
(match_operand:SI 1 "reg_or_0_operand" "rO"))]
""
"l.swa\t%0,%r1"
[(set_attr "type" "st")])
(define_expand "atomic_compare_and_swapsi"
[(match_operand:SI 0 "register_operand") ;; bool output
(match_operand:SI 1 "register_operand") ;; val output
(match_operand:SI 2 "memory_operand") ;; memory
(match_operand:SI 3 "reg_or_s16_operand") ;; expected
(match_operand:SI 4 "reg_or_0_operand") ;; desired
(match_operand:SI 5 "const_int_operand") ;; is_weak
(match_operand:SI 6 "const_int_operand") ;; mod_s
(match_operand:SI 7 "const_int_operand")] ;; mod_f
""
{
or1k_expand_atomic_compare_and_swap (operands);
DONE;
})
(define_expand "atomic_compare_and_swap"
[(match_operand:SI 0 "register_operand") ;; bool output
(match_operand:I12 1 "register_operand") ;; val output
(match_operand:I12 2 "memory_operand") ;; memory
(match_operand:I12 3 "register_operand") ;; expected
(match_operand:I12 4 "reg_or_0_operand") ;; desired
(match_operand:SI 5 "const_int_operand") ;; is_weak
(match_operand:SI 6 "const_int_operand") ;; mod_s
(match_operand:SI 7 "const_int_operand")] ;; mod_f
""
{
or1k_expand_atomic_compare_and_swap_qihi (operands);
DONE;
})
(define_expand "atomic_exchangesi"
[(match_operand:SI 0 "register_operand") ;; output
(match_operand:SI 1 "memory_operand") ;; memory
(match_operand:SI 2 "reg_or_0_operand") ;; input
(match_operand:SI 3 "const_int_operand")] ;; model
""
{
or1k_expand_atomic_exchange (operands);
DONE;
})
(define_expand "atomic_exchange"
[(match_operand:I12 0 "register_operand") ;; output
(match_operand:I12 1 "memory_operand") ;; memory
(match_operand:I12 2 "reg_or_0_operand") ;; input
(match_operand:SI 3 "const_int_operand")] ;; model
""
{
or1k_expand_atomic_exchange_qihi (operands);
DONE;
})
(define_expand "atomic_si"
[(match_operand:SI 0 "memory_operand") ;; memory
(FETCHOP:SI (match_dup 0)
(match_operand:SI 1 "")) ;; operand
(match_operand:SI 2 "const_int_operand")] ;; model
""
{
or1k_expand_atomic_op (, operands[0], operands[1], NULL, NULL);
DONE;
})
(define_expand "atomic_"
[(match_operand:I12 0 "memory_operand") ;; memory
(FETCHOP:I12 (match_dup 0)
(match_operand:I12 1 "register_operand")) ;; operand
(match_operand:SI 2 "const_int_operand")] ;; model
""
{
or1k_expand_atomic_op_qihi (, operands[0], operands[1], NULL, NULL);
DONE;
})
(define_expand "atomic_fetch_si"
[(match_operand:SI 0 "register_operand" "") ;; output
(match_operand:SI 1 "memory_operand" "") ;; memory
(FETCHOP:SI (match_dup 1)
(match_operand:SI 2 "" "")) ;; operand
(match_operand:SI 3 "const_int_operand" "")] ;; model
""
{
or1k_expand_atomic_op (, operands[1], operands[2], operands[0], NULL);
DONE;
})
(define_expand "atomic_fetch_"
[(match_operand:I12 0 "register_operand" "") ;; output
(match_operand:I12 1 "memory_operand" "") ;; memory
(FETCHOP:I12 (match_dup 1)
(match_operand:I12 2 "" "")) ;; operand
(match_operand:SI 3 "const_int_operand" "")] ;; model
""
{
or1k_expand_atomic_op_qihi (, operands[1], operands[2],
operands[0], NULL);
DONE;
})
(define_expand "atomic__fetchsi"
[(match_operand:SI 0 "register_operand" "") ;; output
(match_operand:SI 1 "memory_operand" "") ;; memory
(FETCHOP:SI (match_dup 1)
(match_operand:SI 2 "" "")) ;; operand
(match_operand:SI 3 "const_int_operand" "")] ;; model
""
{
or1k_expand_atomic_op (, operands[1], operands[2], NULL, operands[0]);
DONE;
})
(define_expand "atomic__fetch"
[(match_operand:I12 0 "register_operand" "") ;; output
(match_operand:I12 1 "memory_operand" "") ;; memory
(FETCHOP:I12 (match_dup 1)
(match_operand:I12 2 "" "")) ;; operand
(match_operand:SI 3 "const_int_operand" "")] ;; model
""
{
or1k_expand_atomic_op_qihi (, operands[1], operands[2],
NULL, operands[0]);
DONE;
})
;; -------------------------------------------------------------------------
;; Call Instructions
;; -------------------------------------------------------------------------
;; Leave these to last, as the modeless operand for call_value
;; interferes with normal patterns.
(define_expand "call"
[(call (match_operand 0) (match_operand 1))]
""
{
or1k_expand_call (NULL, operands[0], operands[1], false);
DONE;
})
(define_expand "sibcall"
[(call (match_operand 0) (match_operand 1))]
""
{
or1k_expand_call (NULL, operands[0], operands[1], true);
DONE;
})
(define_expand "call_value"
[(set (match_operand 0) (call (match_operand 1) (match_operand 2)))]
""
{
or1k_expand_call (operands[0], operands[1], operands[2], false);
DONE;
})
(define_expand "sibcall_value"
[(set (match_operand 0) (call (match_operand 1) (match_operand 2)))]
""
{
or1k_expand_call (operands[0], operands[1], operands[2], true);
DONE;
})
(define_insn "*call"
[(call (mem:SI (match_operand:SI 0 "call_insn_operand" "r,s"))
(match_operand 1))
(clobber (reg:SI LR_REGNUM))]
"!SIBLING_CALL_P (insn)"
"@
l.jalr\t%0%#
l.jal\t%P0%#"
[(set_attr "type" "control")])
(define_insn "*sibcall"
[(call (mem:SI (match_operand:SI 0 "call_insn_operand" "c,s"))
(match_operand 1))]
"SIBLING_CALL_P (insn)"
"@
l.jr\t%0%#
l.j\t%P0%#"
[(set_attr "type" "control")])
(define_insn "*call_value"
[(set (match_operand 0)
(call (mem:SI (match_operand:SI 1 "call_insn_operand" "r,s"))
(match_operand 2)))
(clobber (reg:SI LR_REGNUM))]
"!SIBLING_CALL_P (insn)"
"@
l.jalr\t%1%#
l.jal\t%P1%#"
[(set_attr "type" "control")])
(define_insn "*sibcall_value"
[(set (match_operand 0)
(call (mem:SI (match_operand:SI 1 "call_insn_operand" "c,s"))
(match_operand 2)))]
"SIBLING_CALL_P (insn)"
"@
l.jr\t%1%#
l.j\t%P1%#"
[(set_attr "type" "control")])