📄 sh.c
字号:
prepare_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; } 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)) || 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, 18), 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. */char *output_movedouble (insn, operands, mode) rtx insn; 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 = REGNO (SUBREG_REG (inside)) + SUBREG_WORD (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 requires r0 for the other operand, which is not currently supported, so we can't use the 'o' constraint. Thus we must check for and handle r0+REG addresses here. We punt for now, since this is likely very rare. */ if (GET_CODE (XEXP (inside, 1)) == REG) abort (); } else if (GET_CODE (inside) == LABEL_REF) return "mov.l %1,%0\n\tmov.l %1+4,%T0"; else if (GET_CODE (inside) == POST_INC) return "mov.l %1,%0\n\tmov.l %1,%T0"; else abort (); /* Work out the safe way to copy. Copy into the second half first. */ if (dreg == ptrreg) return "mov.l %T1,%T0\n\tmov.l %1,%0"; } return "mov.l %1,%0\n\tmov.l %T1,%T0";}/* Print an instruction which would have gone into a delay slot after another instruction, but couldn't because the other instruction expanded into a sequence where putting the slot insn at the end wouldn't work. */static voidprint_slot (insn) rtx insn;{ final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1); INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;}char *output_far_jump (insn, op) rtx insn; rtx op;{ struct { rtx lab, reg, op; } this; char *jump; int far; int offset = branch_dest (insn) - insn_addresses[INSN_UID (insn)]; this.lab = gen_label_rtx (); if (TARGET_SH2 && offset >= -32764 && offset - get_attr_length (insn) <= 32766) { far = 0; jump = "mov.w %O0,%1;braf %1"; } else { far = 1; jump = "mov.l %O0,%1;jmp @%1"; } /* If we have a scratch register available, use it. */ if (GET_CODE (PREV_INSN (insn)) == INSN && INSN_CODE (PREV_INSN (insn)) == CODE_FOR_indirect_jump_scratch) { this.reg = SET_DEST (PATTERN (PREV_INSN (insn))); output_asm_insn (jump, &this.lab); if (dbr_sequence_length ()) print_slot (final_sequence); else output_asm_insn ("nop", 0); } else { /* Output the delay slot insn first if any. */ if (dbr_sequence_length ()) print_slot (final_sequence); this.reg = gen_rtx (REG, SImode, 13); output_asm_insn ("mov.l r13,@-r15", 0); output_asm_insn (jump, &this.lab); output_asm_insn ("mov.l @r15+,r13", 0); } if (far) output_asm_insn (".align 2", 0); ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (this.lab)); this.op = op; output_asm_insn (far ? ".long %O2" : ".word %O2-%O0", &this.lab); return "";}/* Local label counter, used for constants in the pool and inside pattern branches. */static int lf = 100;/* Output code for ordinary branches. */char *output_branch (logic, insn, operands) int logic; rtx insn; rtx *operands;{ switch (get_attr_length (insn)) { case 6: /* This can happen if filling the delay slot has caused a forward branch to exceed its range (we could reverse it, but only when we know we won't overextend other branches; this should best be handled by relaxation). It can also happen when other condbranches hoist delay slot insn from their destination, thus leading to code size increase. But the branch will still be in the range -4092..+4098 bytes. */ if (! TARGET_RELAX) { int label = lf++; /* The call to print_slot will clobber the operands. */ rtx op0 = operands[0]; /* If the instruction in the delay slot is annulled (true), then there is no delay slot where we can put it now. The only safe place for it is after the label. final will do that by default. */ if (final_sequence && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))) { asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t", ASSEMBLER_DIALECT ? "/" : ".", label); print_slot (final_sequence); } else asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label); output_asm_insn ("bra\t%l0", &op0); fprintf (asm_out_file, "\tnop\n"); ASM_OUTPUT_INTERNAL_LABEL(asm_out_file, "LF", label); return ""; } /* When relaxing, handle this like a short branch. The linker will fix it up if it still doesn't fit after relaxation. */ case 2: return logic ? "bt%.\t%l0" : "bf%.\t%l0"; default: abort (); }}char *output_branchy_insn (code, template, insn, operands) char *template; enum rtx_code code; rtx insn; rtx *operands;{ rtx next_insn = NEXT_INSN (insn); int label_nr; if (next_insn && GET_CODE (next_insn) == JUMP_INSN && condjump_p (next_insn)) { rtx src = SET_SRC (PATTERN (next_insn)); if (GET_CODE (src) == IF_THEN_ELSE && GET_CODE (XEXP (src, 0)) != code) { /* Following branch not taken */ operands[9] = gen_label_rtx (); emit_label_after (operands[9], next_insn); return template; } else { int offset = (branch_dest (next_insn) - insn_addresses[INSN_UID (next_insn)] + 4); if (offset >= -252 && offset <= 258) { if (GET_CODE (src) == IF_THEN_ELSE) /* branch_true */ src = XEXP (src, 1); operands[9] = src; return template; } } } operands[9] = gen_label_rtx (); emit_label_after (operands[9], insn); return template;}char *output_ieee_ccmpeq (insn, operands) rtx insn, operands;{ output_branchy_insn (NE, "bt\t%l9\\;fcmp/eq\t%1,%0", insn, operands);}/* Output to FILE the start of the assembler file. */voidoutput_file_start (file) FILE *file;{ register int pos; output_file_directive (file, main_input_filename); /* Switch to the data section so that the coffsem symbol and the gcc2_compiled. symbol aren't in the text section. */ data_section (); if (TARGET_LITTLE_ENDIAN) fprintf (file, "\t.little\n");}/* Actual number of instructions used to make a shift by N. */static char ashiftrt_insns[] = { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};/* Left shift and logical right shift are the same. */static char shift_insns[] = { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,3,3,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};/* Individual shift amounts needed to get the above length sequences. One bit right shifts clobber the T bit, so when possible, put one bit shifts in the middle of the sequence, so the ends are eligible for branch delay slots. */static short shift_amounts[32][5] = { {0}, {1}, {2}, {2, 1}, {2, 2}, {2, 1, 2}, {2, 2, 2}, {2, 2, 1, 2}, {8}, {8, 1}, {8, 2}, {8, 1, 2}, {8, 2, 2}, {8, 2, 1, 2}, {8, -2, 8}, {8, -1, 8}, {16}, {16, 1}, {16, 2}, {16, 1, 2}, {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8}, {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2}, {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};/* Likewise, but for shift amounts < 16, up to three highmost bits might be clobbered. This is typically used when combined with some kind of sign or zero extension. */ static char ext_shift_insns[] = { 0,1,1,2,2,3,2,2,1,2,2,3,3,3,2,2,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};static short ext_shift_amounts[32][4] = { {0}, {1}, {2}, {2, 1}, {2, 2}, {2, 1, 2}, {8, -2}, {8, -1}, {8}, {8, 1}, {8, 2}, {8, 1, 2}, {8, 2, 2}, {16, -2, -1}, {16, -2}, {16, -1}, {16}, {16, 1}, {16, 2}, {16, 1, 2}, {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8}, {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2}, {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};/* Assuming we have a value that has been sign-extended by at least one bit, can we use the ext_shift_amounts with the last shift turned to an arithmetic shift to shift it by N without data loss, and quicker than by other means? */#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)/* This is used in length attributes in sh.md to help compute the length of arbitrary constant shift instructions. */intshift_insns_rtx (insn) rtx insn;{ rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0)); int shift_count = INTVAL (XEXP (set_src, 1)); enum rtx_code shift_code = GET_CODE (set_src); switch (shift_code) { case ASHIFTRT: return ashiftrt_insns[shift_count]; case LSHIFTRT: case ASHIFT: return shift_insns[shift_count]; default: abort(); }}/* Return the cost of a shift. */intshiftcosts (x) rtx x;{ int value = INTVAL (XEXP (x, 1)); /* If shift by a non constant, then this will be expensive. */ if (GET_CODE (XEXP (x, 1)) != CONST_INT) return SH_DYNAMIC_SHIFT_COST; /* Otherwise, return the true cost in instructions. */ if (GET_CODE (x) == ASHIFTRT) { int cost = ashiftrt_insns[value]; /* If SH3, then we put the constant in a reg and use shad. */ if (cost > 1 + SH_DYNAMIC_SHIFT_COST) cost = 1 + SH_DYNAMIC_SHIFT_COST; return cost; } else return shift_insns[value];}/* Return the cost of an AND operation. */intandcosts (x) rtx x;{ int i; /* Anding with a register is a single cycle and instruction. */ if (GET_CODE (XEXP (x, 1)) != CONST_INT) return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -