;; Machine description for OpenRISC ;; Copyright (C) 2018-2021 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 ;; http://www.gnu.org/licenses/.

;; ------------------------------------------------------------------------- ;; 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 “<F:mode>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 “float<F:mode>2” [(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_trunc<F:mode>2” [(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 “<shift_op>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.<shift_asm>\t%0, %1, %2 l.<shift_asm>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<I:mode>” [(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<I:mode>_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], <I:MODE>mode) || reg_or_0_operand (operands[1], <I:MODE>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<I:ldst>\t%0, %r1 l.l<I:ldst>z\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, <zext_andi> 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 “cstore<F:mode>4” [(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 “mov<I:mode>cc” [(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<I:mode>” [(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 “cbranch<F:mode>4” [(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_<fetchop_name>si” [(match_operand:SI 0 “memory_operand”) ;; memory (FETCHOP:SI (match_dup 0) (match_operand:SI 1 “<fetchop_pred>”)) ;; operand (match_operand:SI 2 “const_int_operand”)] ;; model "" { or1k_expand_atomic_op (, operands[0], operands[1], NULL, NULL); DONE; })

(define_expand “atomic_<fetchop_name>” [(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_<fetchop_name>si” [(match_operand:SI 0 “register_operand” "") ;; output (match_operand:SI 1 “memory_operand” "") ;; memory (FETCHOP:SI (match_dup 1) (match_operand:SI 2 “<fetchop_pred>” "")) ;; 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_<fetchop_name>” [(match_operand:I12 0 “register_operand” "") ;; output (match_operand:I12 1 “memory_operand” "") ;; memory (FETCHOP:I12 (match_dup 1) (match_operand:I12 2 “<fetchop_pred>” "")) ;; 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_<fetchop_name>_fetchsi” [(match_operand:SI 0 “register_operand” "") ;; output (match_operand:SI 1 “memory_operand” "") ;; memory (FETCHOP:SI (match_dup 1) (match_operand:SI 2 “<fetchop_pred>” "")) ;; operand (match_operand:SI 3 “const_int_operand” "")] ;; model "" { or1k_expand_atomic_op (, operands[1], operands[2], NULL, operands[0]); DONE; })

(define_expand “atomic_<fetchop_name>_fetch” [(match_operand:I12 0 “register_operand” "") ;; output (match_operand:I12 1 “memory_operand” "") ;; memory (FETCHOP:I12 (match_dup 1) (match_operand:I12 2 “<fetchop_pred>” "")) ;; 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”)])