📄 tc-mips.c
字号:
{ sec = subseg_new (".mdebug", (subsegT) 0); (void) bfd_set_section_flags (stdoutput, sec, SEC_HAS_CONTENTS | SEC_READONLY); (void) bfd_set_section_alignment (stdoutput, sec, 2); }#ifdef MIPS_STABS_ELF pdr_seg = subseg_new (".pdr", (subsegT) 0); (void) bfd_set_section_flags (stdoutput, pdr_seg, SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);#endif subseg_set (seg, subseg); } } if (! ECOFF_DEBUGGING) md_obj_begin ();}voidmd_mips_end (){ if (! ECOFF_DEBUGGING) md_obj_end ();}voidmd_assemble (str) char *str;{ struct mips_cl_insn insn; imm_expr.X_op = O_absent; imm_reloc = BFD_RELOC_UNUSED; imm_unmatched_hi = false; offset_expr.X_op = O_absent; offset_reloc = BFD_RELOC_UNUSED; if (mips_opts.mips16) mips16_ip (str, &insn); else { mips_ip (str, &insn); DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"), str, insn.insn_opcode)); } if (insn_error) { as_bad ("%s `%s'", insn_error, str); return; } if (insn.insn_mo->pinfo == INSN_MACRO) { if (mips_opts.mips16) mips16_macro (&insn); else macro (&insn); } else { if (imm_expr.X_op != O_absent) append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc, imm_unmatched_hi); else if (offset_expr.X_op != O_absent) append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false); else append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false); }}/* See whether instruction IP reads register REG. CLASS is the type of register. */static intinsn_uses_reg (ip, reg, class) struct mips_cl_insn *ip; unsigned int reg; enum mips_regclass class;{ if (class == MIPS16_REG) { assert (mips_opts.mips16); reg = mips16_to_32_reg_map[reg]; class = MIPS_GR_REG; } /* Don't report on general register 0, since it never changes. */ if (class == MIPS_GR_REG && reg == 0) return 0; if (class == MIPS_FP_REG) { assert (! mips_opts.mips16); /* If we are called with either $f0 or $f1, we must check $f0. This is not optimal, because it will introduce an unnecessary NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would need to distinguish reading both $f0 and $f1 or just one of them. Note that we don't have to check the other way, because there is no instruction that sets both $f0 and $f1 and requires a delay. */ if ((ip->insn_mo->pinfo & INSN_READ_FPR_S) && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1) == (reg &~ (unsigned) 1))) return 1; if ((ip->insn_mo->pinfo & INSN_READ_FPR_T) && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1) == (reg &~ (unsigned) 1))) return 1; } else if (! mips_opts.mips16) { if ((ip->insn_mo->pinfo & INSN_READ_GPR_S) && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg) return 1; if ((ip->insn_mo->pinfo & INSN_READ_GPR_T) && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg) return 1; } else { if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X) && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)] == reg)) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y) && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY)] == reg)) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z) && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z) & MIPS16OP_MASK_MOVE32Z)] == reg)) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA) return 1; if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X) && ((ip->insn_opcode >> MIPS16OP_SH_REGR32) & MIPS16OP_MASK_REGR32) == reg) return 1; } return 0;}/* This function returns true if modifying a register requires a delay. */static intreg_needs_delay (reg) unsigned int reg;{ unsigned long prev_pinfo; prev_pinfo = prev_insn.insn_mo->pinfo; if (! mips_opts.noreorder && ISA_HAS_COPROC_DELAYS (mips_opts.isa) && ((prev_pinfo & INSN_LOAD_COPROC_DELAY) || (! gpr_interlocks && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) { /* A load from a coprocessor or from memory. All load delays delay the use of general register rt for one instruction on the r3000. The r6000 and r4000 use interlocks. */ /* Itbl support may require additional care here. */ know (prev_pinfo & INSN_WRITE_GPR_T); if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)) return 1; } return 0;}/* Mark instruction labels in mips16 mode. This permits the linker to handle them specially, such as generating jalx instructions when needed. We also make them odd for the duration of the assembly, in order to generate the right sort of code. We will make them even in the adjust_symtab routine, while leaving them marked. This is convenient for the debugger and the disassembler. The linker knows to make them odd again. */static voidmips16_mark_labels (){ if (mips_opts.mips16) { struct insn_label_list *l; valueT val; for (l = insn_labels; l != NULL; l = l->next) {#ifdef OBJ_ELF if (OUTPUT_FLAVOR == bfd_target_elf_flavour) S_SET_OTHER (l->label, STO_MIPS16);#endif val = S_GET_VALUE (l->label); if ((val & 1) == 0) S_SET_VALUE (l->label, val + 1); } }}/* Output an instruction. PLACE is where to put the instruction; if it is NULL, this uses frag_more to get room. IP is the instruction information. ADDRESS_EXPR is an operand of the instruction to be used with RELOC_TYPE. */static voidappend_insn (place, ip, address_expr, reloc_type, unmatched_hi) char *place; struct mips_cl_insn *ip; expressionS *address_expr; bfd_reloc_code_real_type reloc_type; boolean unmatched_hi;{ register unsigned long prev_pinfo, pinfo; char *f; fixS *fixp; int nops = 0; /* Mark instruction labels in mips16 mode. */ if (mips_opts.mips16) mips16_mark_labels (); prev_pinfo = prev_insn.insn_mo->pinfo; pinfo = ip->insn_mo->pinfo; if (place == NULL && (! mips_opts.noreorder || prev_nop_frag != NULL)) { int prev_prev_nop; /* If the previous insn required any delay slots, see if we need to insert a NOP or two. There are eight kinds of possible hazards, of which an instruction can have at most one type. (1) a load from memory delay (2) a load from a coprocessor delay (3) an unconditional branch delay (4) a conditional branch delay (5) a move to coprocessor register delay (6) a load coprocessor register from memory delay (7) a coprocessor condition code delay (8) a HI/LO special register delay There are a lot of optimizations we could do that we don't. In particular, we do not, in general, reorder instructions. If you use gcc with optimization, it will reorder instructions and generally do much more optimization then we do here; repeating all that work in the assembler would only benefit hand written assembly code, and does not seem worth it. */ /* This is how a NOP is emitted. */#define emit_nop() \ (mips_opts.mips16 \ ? md_number_to_chars (frag_more (2), 0x6500, 2) \ : md_number_to_chars (frag_more (4), 0, 4)) /* The previous insn might require a delay slot, depending upon the contents of the current insn. */ if (! mips_opts.mips16 && ISA_HAS_COPROC_DELAYS (mips_opts.isa) && (((prev_pinfo & INSN_LOAD_COPROC_DELAY) && ! cop_interlocks) || (! gpr_interlocks && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))) { /* A load from a coprocessor or from memory. All load delays delay the use of general register rt for one instruction on the r3000. The r6000 and r4000 use interlocks. */ /* Itbl support may require additional care here. */ know (prev_pinfo & INSN_WRITE_GPR_T); if (mips_optimize == 0 || insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT), MIPS_GR_REG)) ++nops; } else if (! mips_opts.mips16 && ISA_HAS_COPROC_DELAYS (mips_opts.isa) && (((prev_pinfo & INSN_COPROC_MOVE_DELAY) && ! cop_interlocks) || (mips_opts.isa == ISA_MIPS1 && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)))) { /* A generic coprocessor delay. The previous instruction modified a coprocessor general or control register. If it modified a control register, we need to avoid any coprocessor instruction (this is probably not always required, but it sometimes is). If it modified a general register, we avoid using that register. On the r6000 and r4000 loading a coprocessor register from memory is interlocked, and does not require a delay. This case is not handled very well. There is no special knowledge of CP0 handling, and the coprocessors other than the floating point unit are not distinguished at all. */ /* Itbl support may require additional care here. FIXME! Need to modify this to include knowledge about user specified delays! */ if (prev_pinfo & INSN_WRITE_FPR_T) { if (mips_optimize == 0 || insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_FT) & OP_MASK_FT), MIPS_FP_REG)) ++nops; } else if (prev_pinfo & INSN_WRITE_FPR_S) { if (mips_optimize == 0 || insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_FS) & OP_MASK_FS), MIPS_FP_REG)) ++nops; } else { /* We don't know exactly what the previous instruction does. If the current instruction uses a coprocessor register, we must insert a NOP. If previous instruction may set the condition codes, and the current instruction uses them, we must insert two NOPS. */ /* Itbl support may require additional care here. */ if (mips_optimize == 0 || ((prev_pinfo & INSN_WRITE_COND_CODE) && (pinfo & INSN_READ_COND_CODE))) nops += 2; else if (pinfo & INSN_COP) ++nops; } } else if (! mips_opts.mips16 && ISA_HAS_COPROC_DELAYS (mips_opts.isa) && (prev_pinfo & INSN_WRITE_COND_CODE) && ! cop_interlocks) { /* The previous instruction sets the coprocessor condition codes, but does not require a general coprocessor delay (this means it is a floating point comparison instruction). If this instruction uses the condition codes, we need to insert a single NOP. */ /* Itbl support may require additional care here. */ if (mips_optimize == 0 || (pinfo & INSN_READ_COND_CODE)) ++nops; } /* If we're fixing up mfhi/mflo for the r7000 and the previous insn was an mfhi/mflo and the current insn reads the register that the mfhi/mflo wrote to, then insert two nops. */ else if (mips_7000_hilo_fix && MF_HILO_INSN (prev_pinfo) && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD), MIPS_GR_REG)) { nops += 2; } /* If we're fixing up mfhi/mflo for the r7000 and the 2nd previous insn was an mfhi/mflo and the current insn reads the register that the mfhi/mflo wrote to, then insert one nop. */ else if (mips_7000_hilo_fix && MF_HILO_INSN (prev_prev_insn.insn_opcode) && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD), MIPS_GR_REG)) { nops += 1; } else if (prev_pinfo & INSN_READ_LO) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -