📄 c4x.md
字号:
(define_attr "setlda_ir1" "" (cond [(eq_attr "type" "lda") (if_then_else (match_operand 0 "ir1_reg_operand" "") (const_int 1) (const_int 0))] (const_int 0)))(define_attr "useir1" "" (cond [(eq_attr "type" "compare,store") (if_then_else (match_operand 0 "ir1_mem_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc") (if_then_else (match_operand 1 "ir1_mem_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "binary,binarycc") (if_then_else (match_operand 2 "ir1_mem_operand" "") (const_int 1) (const_int 0))] (const_int 0))); With the C3x, things are simpler, but slower, i.e. more pipeline conflicts :(; There are three functional groups:; (1) AR0-AR7, IR0-IR1, BK; (2) DP; (3) SP;; When a register in one of these functional groups is loaded,; the contents of that or any other register in its group; will not be available to the next instruction for 2 machine cycles.; Similarly, when a register in one of the functional groups is read; excepting (IR0-IR1, BK, DP) the contents of that or any other register; in its group will not be available to the next instruction for; 1 machine cycle.;; Let's ignore functional groups 2 and 3 for now, since they are not; so important.;(define_function_unit "group1" 1 0; (and (eq_attr "cpu" "c3x"); (and (eq_attr "setgroup1" "1"); (eq_attr "usegroup1" "1"))); 3 1);(define_function_unit "group1" 1 0; (and (eq_attr "cpu" "c3x"); (and (eq_attr "usegroup1" "1"); (eq_attr "readarx" "1"))); 2 1)(define_attr "setgroup1" "" (cond [(eq_attr "type" "lda,unary,binary") (if_then_else (match_operand 0 "group1_reg_operand" "") (const_int 1) (const_int 0))] (const_int 0)))(define_attr "usegroup1" "" (cond [(eq_attr "type" "compare,store,store_store,store_load") (if_then_else (match_operand 0 "group1_mem_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc,load_load,load_store") (if_then_else (match_operand 1 "group1_mem_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "store_store,load_store") (if_then_else (match_operand 2 "group1_mem_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "load_load,store_load") (if_then_else (match_operand 3 "group1_mem_operand" "") (const_int 1) (const_int 0))] (const_int 0)))(define_attr "readarx" "" (cond [(eq_attr "type" "compare") (if_then_else (match_operand 0 "arx_reg_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc") (if_then_else (match_operand 1 "arx_reg_operand" "") (const_int 1) (const_int 0)) (eq_attr "type" "binary,binarycc") (if_then_else (match_operand 2 "arx_reg_operand" "") (const_int 1) (const_int 0))] (const_int 0)));; C4x INSN PATTERNS:;; Note that the movMM and addP patterns can be called during reload; so we need to take special care with theses patterns since; we cannot blindly clobber CC or generate new pseudo registers.;; TWO OPERAND INTEGER INSTRUCTIONS;;; LDP/LDPK;(define_insn "set_ldp" [(set (match_operand:QI 0 "dp_reg_operand" "=z") (high:QI (match_operand:QI 1 "" "")))] "! TARGET_SMALL" "* return (TARGET_C3X) ? \"ldp\\t%A1\" : \"ldpk\\t%A1\";" [(set_attr "type" "ldp")])(define_insn "set_high" [(set (match_operand:QI 0 "std_reg_operand" "=c") (high:QI (match_operand:QI 1 "symbolic_address_operand" "")))] "! TARGET_C3X " "ldhi\\t^%H1,%0" [(set_attr "type" "unary")])(define_insn "set_lo_sum" [(set (match_operand:QI 0 "std_reg_operand" "=c") (lo_sum:QI (match_dup 0) (match_operand:QI 1 "symbolic_address_operand" "")))] "" "or\\t#%H1,%0" [(set_attr "type" "unary")])(define_split [(set (match_operand:QI 0 "std_reg_operand" "") (match_operand:QI 1 "symbolic_address_operand" ""))] "! TARGET_C3X" [(set (match_dup 0) (high:QI (match_dup 1))) (set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))] "")(define_split [(set (match_operand:QI 0 "std_reg_operand" "") (match_operand:QI 1 "const_int_operand" ""))] "! TARGET_C3X && (INTVAL (operands[1]) & ~0xffff) != 0 && (INTVAL (operands[1]) & 0xffff) != 0" [(set (match_dup 0) (match_dup 2)) (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))] "{ operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & ~0xffff); operands[3] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 0xffff);}"); CC has been selected to load a symbolic address. We force the address; into memory and then generate LDP and LDIU insns.; This is also required for the C30 if we pretend that we can ; easily load symbolic addresses into a register.(define_split [(set (match_operand:QI 0 "reg_operand" "") (match_operand:QI 1 "symbolic_address_operand" ""))] "! TARGET_SMALL && (TARGET_C3X || (reload_completed && ! std_reg_operand (operands[0], QImode)))" [(set (match_dup 2) (high:QI (match_dup 3))) (set (match_dup 0) (match_dup 4)) (use (match_dup 1))] "{ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO); operands[2] = dp_reg; operands[3] = force_const_mem (Pmode, operands[1]); operands[4] = change_address (operands[3], QImode, gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (operands[3], 0))); operands[3] = XEXP (operands[3], 0);}"); This pattern is similar to the above but does not emit a LDP; for the small memory model.(define_split [(set (match_operand:QI 0 "reg_operand" "") (match_operand:QI 1 "symbolic_address_operand" ""))] "TARGET_SMALL && (TARGET_C3X || (reload_completed && ! std_reg_operand (operands[0], QImode)))" [(set (match_dup 0) (match_dup 2)) (use (match_dup 1))] "{ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO); operands[2] = force_const_mem (Pmode, operands[1]); operands[2] = change_address (operands[2], QImode, gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (operands[2], 0)));}")(define_insn "load_immed_address" [(set (match_operand:QI 0 "reg_operand" "=a?x?c*r") (match_operand:QI 1 "symbolic_address_operand" ""))] "TARGET_LOAD_ADDRESS" "#" [(set_attr "type" "multi")]);; LDIU/LDA/STI/STIK;; The following moves will not set the condition codes register.;; This must come before the general case(define_insn "*movqi_stik" [(set (match_operand:QI 0 "memory_operand" "=m") (match_operand:QI 1 "stik_const_operand" "K"))] "! TARGET_C3X" "stik\\t%1,%0" [(set_attr "type" "store")]); We must provide an alternative to store to memory in case we have to; spill a register.(define_insn "movqi_noclobber" [(set (match_operand:QI 0 "src_operand" "=d,*c,m,r") (match_operand:QI 1 "src_hi_operand" "rIm,rIm,r,O"))] "(REG_P (operands[0]) || REG_P (operands[1]) || GET_CODE (operands[0]) == SUBREG || GET_CODE (operands[1]) == SUBREG) && ! symbolic_address_operand (operands[1], QImode)" "* if (which_alternative == 2) return \"sti\\t%1,%0\"; if (! TARGET_C3X && which_alternative == 3) { operands[1] = GEN_INT ((INTVAL (operands[1]) >> 16) & 0xffff); return \"ldhi\\t%1,%0\"; } /* The lda instruction cannot use the same register as source and destination. */ if (! TARGET_C3X && which_alternative == 1 && ( IS_ADDR_REG (REGNO (operands[0])) || IS_INDEX_REG (REGNO (operands[0])) || IS_SP_REG (REGNO (operands[0]))) && (REGNO (operands[0]) != REGNO (operands[1]))) return \"lda\\t%1,%0\"; return \"ldiu\\t%1,%0\"; " [(set_attr "type" "unary,lda,store,unary") (set_attr "data" "int16,int16,int16,high_16")]);; LDI;; We shouldn't need these peepholes, but the combiner seems to miss them...(define_peephole [(set (match_operand:QI 0 "ext_reg_operand" "=d") (match_operand:QI 1 "src_operand" "rIm")) (set (reg:CC 21) (compare:CC (match_dup 0) (const_int 0)))] "" "@ ldi\\t%1,%0" [(set_attr "type" "unarycc") (set_attr "data" "int16")])(define_insn "*movqi_set" [(set (reg:CC 21) (compare:CC (match_operand:QI 1 "src_operand" "rIm") (const_int 0))) (set (match_operand:QI 0 "ext_reg_operand" "=d") (match_dup 1))] "" "@ ldi\\t%1,%0" [(set_attr "type" "unarycc") (set_attr "data" "int16")]); This pattern probably gets in the way and requires a scratch register; when a simple compare with zero will suffice.;(define_insn "*movqi_test"; [(set (reg:CC 21); (compare:CC (match_operand:QI 1 "src_operand" "rIm") ; (const_int 0))); (clobber (match_scratch:QI 0 "=d"))]; ""; "@; ldi\\t%1,%0"; [(set_attr "type" "unarycc"); (set_attr "data" "int16")]); If one of the operands is not a register, then we should; emit two insns, using a scratch register. This will produce; better code in loops if the source operand is invariant, since; the source reload can be optimised out. During reload we cannot; use change_address or force_reg which will allocate new pseudo regs.; Unlike most other insns, the move insns can't be split with; different predicates, because register spilling and other parts of; the compiler, have memoized the insn number already.(define_expand "movqi" [(set (match_operand:QI 0 "general_operand" "") (match_operand:QI 1 "general_operand" ""))] "" "{ if (c4x_emit_move_sequence (operands, QImode)) DONE;}")(define_insn "*movqi_update" [(set (match_operand:QI 0 "reg_operand" "=r") (mem:QI (plus:QI (match_operand:QI 1 "addr_reg_operand" "a") (match_operand:QI 2 "index_reg_operand" "x")))) (set (match_dup 1) (plus:QI (match_dup 1) (match_dup 2)))] "" "ldiu\\t*%1++(%2),%0" [(set_attr "type" "unary") (set_attr "data" "int16")])(define_insn "movqi_parallel" [(set (match_operand:QI 0 "parallel_operand" "=q,S<>,q,S<>") (match_operand:QI 1 "parallel_operand" "S<>,q,S<>,q")) (set (match_operand:QI 2 "parallel_operand" "=q,S<>,S<>,q") (match_operand:QI 3 "parallel_operand" "S<>,q,q,S<>"))] "valid_parallel_load_store (operands, QImode)" "@ ldi1\\t%1,%0\\n||\\tldi2\\t%3,%2 sti1\\t%1,%0\\n||\\tsti2\\t%3,%2 ldi\\t%1,%0\\n||\\tsti\\t%3,%2 ldi\\t%3,%2\\n||\\tsti\\t%1,%0" [(set_attr "type" "load_load,store_store,load_store,store_load")]);; PUSH/POP;(define_insn "*pushqi" [(set (mem:QI (pre_inc:QI (reg:QI 20))) (match_operand:QI 0 "reg_operand" "r"))] "" "push\\t%0" [(set_attr "type" "push")])(define_insn "*popqi" [(set (match_operand:QI 0 "reg_operand" "=r") (mem:QI (post_dec:QI (reg:QI 20)))) (clobber (reg:CC 21))]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -