📄 sh.c
字号:
if (x == CONST0_RTX (GET_MODE (x))) { fprintf ((stream), "r63"); break; } goto default_output; case 'u': if (GET_CODE (x) == CONST_INT) { fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1)); break; } /* Fall through. */ default_output: default: switch (GET_CODE (x)) { /* FIXME: We need this on SHmedia32 because reload generates some sign-extended HI or QI loads into DImode registers but, because Pmode is SImode, the address ends up with a subreg:SI of the DImode register. Maybe reload should be fixed so as to apply alter_subreg to such loads? */ case SUBREG: if (SUBREG_BYTE (x) != 0 || GET_CODE (SUBREG_REG (x)) != REG) abort (); x = SUBREG_REG (x); /* Fall through. */ case REG: if (FP_REGISTER_P (REGNO (x)) && GET_MODE (x) == V16SFmode) fprintf ((stream), "mtrx%s", reg_names[REGNO (x)] + 2); else if (FP_REGISTER_P (REGNO (x)) && GET_MODE (x) == V4SFmode) fprintf ((stream), "fv%s", reg_names[REGNO (x)] + 2); else if (GET_CODE (x) == REG && GET_MODE (x) == V2SFmode) fprintf ((stream), "fp%s", reg_names[REGNO (x)] + 2); else if (FP_REGISTER_P (REGNO (x)) && GET_MODE_SIZE (GET_MODE (x)) > 4) fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1); else fputs (reg_names[REGNO (x)], (stream)); break; case MEM: output_address (XEXP (x, 0)); break; case CONST: if (TARGET_SHMEDIA && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND && GET_MODE (XEXP (x, 0)) == DImode && GET_CODE (XEXP (XEXP (x, 0), 0)) == TRUNCATE && GET_MODE (XEXP (XEXP (x, 0), 0)) == HImode) { rtx val = XEXP (XEXP (XEXP (x, 0), 0), 0); fputc ('(', stream); if (GET_CODE (val) == ASHIFTRT) { fputc ('(', stream); if (GET_CODE (XEXP (val, 0)) == CONST) fputc ('(', stream); output_addr_const (stream, XEXP (val, 0)); if (GET_CODE (XEXP (val, 0)) == CONST) fputc (')', stream); fputs (" >> ", stream); output_addr_const (stream, XEXP (val, 1)); fputc (')', stream); } else { if (GET_CODE (val) == CONST) fputc ('(', stream); output_addr_const (stream, val); if (GET_CODE (val) == CONST) fputc (')', stream); } fputs (" & 65535)", stream); break; } /* Fall through. */ default: if (TARGET_SH1) fputc ('#', stream); output_addr_const (stream, x); break; } break; }}/* Like force_operand, but guarantees that VALUE ends up in TARGET. */static voidforce_into (value, target) rtx value, target;{ value = force_operand (value, target); if (! rtx_equal_p (value, target)) emit_insn (gen_move_insn (target, value));}/* Emit code to perform a block move. Choose the best method. OPERANDS[0] is the destination. OPERANDS[1] is the source. OPERANDS[2] is the size. OPERANDS[3] is the alignment safe to use. */intexpand_block_move (operands) rtx *operands;{ int align = INTVAL (operands[3]); int constp = (GET_CODE (operands[2]) == CONST_INT); int bytes = (constp ? INTVAL (operands[2]) : 0); /* If it isn't a constant number of bytes, or if it doesn't have 4 byte alignment, or if it isn't a multiple of 4 bytes, then fail. */ if (! constp || align < 4 || (bytes % 4 != 0)) return 0; if (TARGET_HARD_SH4) { if (bytes < 12) return 0; else if (bytes == 12) { tree entry_name; rtx sym; rtx func_addr_rtx; rtx r4 = gen_rtx (REG, SImode, 4); rtx r5 = gen_rtx (REG, SImode, 5); entry_name = get_identifier ("__movstrSI12_i4"); sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); emit_insn (gen_block_move_real_i4 (func_addr_rtx)); return 1; } else if (! TARGET_SMALLCODE) { tree entry_name; rtx sym; rtx func_addr_rtx; int dwords; rtx r4 = gen_rtx (REG, SImode, 4); rtx r5 = gen_rtx (REG, SImode, 5); rtx r6 = gen_rtx (REG, SImode, 6); entry_name = get_identifier (bytes & 4 ? "__movstr_i4_odd" : "__movstr_i4_even"); sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); dwords = bytes >> 3; emit_insn (gen_move_insn (r6, GEN_INT (dwords - 1))); emit_insn (gen_block_lump_real_i4 (func_addr_rtx)); return 1; } else return 0; } if (bytes < 64) { char entry[30]; tree entry_name; rtx sym; rtx func_addr_rtx; rtx r4 = gen_rtx_REG (SImode, 4); rtx r5 = gen_rtx_REG (SImode, 5); sprintf (entry, "__movstrSI%d", bytes); entry_name = get_identifier (entry); sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); emit_insn (gen_block_move_real (func_addr_rtx)); return 1; } /* This is the same number of bytes as a memcpy call, but to a different less common function name, so this will occasionally use more space. */ if (! TARGET_SMALLCODE) { tree entry_name; rtx sym; rtx func_addr_rtx; int final_switch, while_loop; rtx r4 = gen_rtx_REG (SImode, 4); rtx r5 = gen_rtx_REG (SImode, 5); rtx r6 = gen_rtx_REG (SImode, 6); entry_name = get_identifier ("__movstr"); sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name)); func_addr_rtx = copy_to_mode_reg (Pmode, sym); force_into (XEXP (operands[0], 0), r4); force_into (XEXP (operands[1], 0), r5); /* r6 controls the size of the move. 16 is decremented from it for each 64 bytes moved. Then the negative bit left over is used as an index into a list of move instructions. e.g., a 72 byte move would be set up with size(r6) = 14, for one iteration through the big while loop, and a switch of -2 for the last part. */ final_switch = 16 - ((bytes / 4) % 16); while_loop = ((bytes / 4) / 16 - 1) * 16; emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch))); emit_insn (gen_block_lump_real (func_addr_rtx)); return 1; } return 0;}/* Prepare operands for a move define_expand; specifically, one of the operands must be in a register. */intprepare_move_operands (operands, mode) rtx operands[]; enum machine_mode mode;{ if ((mode == SImode || mode == DImode) && flag_pic) { rtx temp; if (SYMBOLIC_CONST_P (operands[1])) { if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (Pmode, operands[1]); else if (TARGET_SHMEDIA && GET_CODE (operands[1]) == LABEL_REF && target_reg_operand (operands[0], mode)) /* It's ok. */; else { temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); operands[1] = legitimize_pic_address (operands[1], mode, temp); } } else if (GET_CODE (operands[1]) == CONST && GET_CODE (XEXP (operands[1], 0)) == PLUS && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0))) { temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode); temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0), mode, temp); operands[1] = expand_binop (mode, add_optab, temp, XEXP (XEXP (operands[1], 0), 1), no_new_pseudos ? temp : gen_reg_rtx (Pmode), 0, OPTAB_LIB_WIDEN); } } if (! reload_in_progress && ! reload_completed) { /* Copy the source to a register if both operands aren't registers. */ if (! register_operand (operands[0], mode) && ! sh_register_operand (operands[1], mode)) operands[1] = copy_to_mode_reg (mode, operands[1]); /* This case can happen while generating code to move the result of a library call to the target. Reject `st r0,@(rX,rY)' because reload will fail to find a spill register for rX, since r0 is already being used for the source. */ else if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 0 && GET_CODE (operands[0]) == MEM && GET_CODE (XEXP (operands[0], 0)) == PLUS && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG) operands[1] = copy_to_mode_reg (mode, operands[1]); } return 0;}/* Prepare the operands for an scc instruction; make sure that the compare has been done. */rtxprepare_scc_operands (code) enum rtx_code code;{ rtx t_reg = gen_rtx_REG (SImode, T_REG); enum rtx_code oldcode = code; enum machine_mode mode; /* First need a compare insn. */ switch (code) { case NE: /* It isn't possible to handle this case. */ abort (); case LT: code = GT; break; case LE: code = GE; break; case LTU: code = GTU; break; case LEU: code = GEU; break; default: break; } if (code != oldcode) { rtx tmp = sh_compare_op0; sh_compare_op0 = sh_compare_op1; sh_compare_op1 = tmp; } mode = GET_MODE (sh_compare_op0); if (mode == VOIDmode) mode = GET_MODE (sh_compare_op1); sh_compare_op0 = force_reg (mode, sh_compare_op0); if ((code != EQ && code != NE && (sh_compare_op1 != const0_rtx || code == GTU || code == GEU || code == LTU || code == LEU)) || (mode == DImode && sh_compare_op1 != const0_rtx) || (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT)) sh_compare_op1 = force_reg (mode, sh_compare_op1); if (TARGET_SH4 && GET_MODE_CLASS (mode) == MODE_FLOAT) (mode == SFmode ? emit_sf_insn : emit_df_insn) (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, gen_rtx (SET, VOIDmode, t_reg, gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1)), gen_rtx (USE, VOIDmode, get_fpscr_rtx ())))); else emit_insn (gen_rtx (SET, VOIDmode, t_reg, gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1))); return t_reg;}/* Called from the md file, set up the operands of a compare instruction. */voidfrom_compare (operands, code) rtx *operands; int code;{ enum machine_mode mode = GET_MODE (sh_compare_op0); rtx insn; if (mode == VOIDmode) mode = GET_MODE (sh_compare_op1); if (code != EQ || mode == DImode || (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT)) { /* Force args into regs, since we can't use constants here. */ sh_compare_op0 = force_reg (mode, sh_compare_op0); if (sh_compare_op1 != const0_rtx || code == GTU || code == GEU || (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT)) sh_compare_op1 = force_reg (mode, sh_compare_op1); } if (TARGET_SH3E && GET_MODE_CLASS (mode) == MODE_FLOAT && code == GE) { from_compare (operands, GT); insn = gen_ieee_ccmpeqsf_t (sh_compare_op0, sh_compare_op1); } else insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG), gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1)); if (TARGET_SH4 && GET_MODE_CLASS (mode) == MODE_FLOAT) { insn = gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, insn, gen_rtx (USE, VOIDmode, get_fpscr_rtx ()))); (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn); } else emit_insn (insn);}/* Functions to output assembly code. *//* Return a sequence of instructions to perform DI or DF move. Since the SH cannot move a DI or DF in one instruction, we have to take care when we see overlapping source and dest registers. */const char *output_movedouble (insn, operands, mode) rtx insn ATTRIBUTE_UNUSED; rtx operands[]; enum machine_mode mode;{ rtx dst = operands[0]; rtx src = operands[1]; if (GET_CODE (dst) == MEM && GET_CODE (XEXP (dst, 0)) == PRE_DEC) return "mov.l %T1,%0\n\tmov.l %1,%0"; if (register_operand (dst, mode) && register_operand (src, mode)) { if (REGNO (src) == MACH_REG) return "sts mach,%S0\n\tsts macl,%R0"; /* When mov.d r1,r2 do r2->r3 then r1->r2; when mov.d r1,r0 do r1->r0 then r2->r1. */ if (REGNO (src) + 1 == REGNO (dst)) return "mov %T1,%T0\n\tmov %1,%0"; else return "mov %1,%0\n\tmov %T1,%T0"; } else if (GET_CODE (src) == CONST_INT) { if (INTVAL (src) < 0) output_asm_insn ("mov #-1,%S0", operands); else output_asm_insn ("mov #0,%S0", operands); return "mov %1,%R0"; } else if (GET_CODE (src) == MEM) { int ptrreg = -1; int dreg = REGNO (dst); rtx inside = XEXP (src, 0); if (GET_CODE (inside) == REG) ptrreg = REGNO (inside); else if (GET_CODE (inside) == SUBREG) ptrreg = subreg_regno (inside); else if (GET_CODE (inside) == PLUS) { ptrreg = REGNO (XEXP (inside, 0)); /* ??? A r0+REG address shouldn't be possible here, because it isn't an offsettable address. Unfortunately, offsettable addresses use QImode to check the offset, and a QImode offsettable address
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -