📄 tc-mips.c
字号:
static char *insn_error;static int auto_align = 1;/* When outputting SVR4 PIC code, the assembler needs to know the offset in the stack frame from which to restore the $gp register. This is set by the .cprestore pseudo-op, and saved in this variable. */static offsetT mips_cprestore_offset = -1;/* This is the register which holds the stack frame, as set by the .frame pseudo-op. This is needed to implement .cprestore. */static int mips_frame_reg = SP;/* To output NOP instructions correctly, we need to keep information about the previous two instructions. *//* Whether we are optimizing. The default value of 2 means to remove unneeded NOPs and swap branch instructions when possible. A value of 1 means to not swap branches. A value of 0 means to always insert NOPs. */static int mips_optimize = 2;/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is equivalent to seeing no -g option at all. */static int mips_debug = 0;/* The previous instruction. */static struct mips_cl_insn prev_insn;/* The instruction before prev_insn. */static struct mips_cl_insn prev_prev_insn;/* If we don't want information for prev_insn or prev_prev_insn, we point the insn_mo field at this dummy integer. */static const struct mips_opcode dummy_opcode = { NULL, NULL, 0, 0, 0, 0 };/* Non-zero if prev_insn is valid. */static int prev_insn_valid;/* The frag for the previous instruction. */static struct frag *prev_insn_frag;/* The offset into prev_insn_frag for the previous instruction. */static long prev_insn_where;/* The reloc type for the previous instruction, if any. */static bfd_reloc_code_real_type prev_insn_reloc_type;/* The reloc for the previous instruction, if any. */static fixS *prev_insn_fixp;/* Non-zero if the previous instruction was in a delay slot. */static int prev_insn_is_delay_slot;/* Non-zero if the previous instruction was in a .set noreorder. */static int prev_insn_unreordered;/* Non-zero if the previous instruction uses an extend opcode (if mips16). */static int prev_insn_extended;/* Non-zero if the previous previous instruction was in a .set noreorder. */static int prev_prev_insn_unreordered;/* If this is set, it points to a frag holding nop instructions which were inserted before the start of a noreorder section. If those nops turn out to be unnecessary, the size of the frag can be decreased. */static fragS *prev_nop_frag;/* The number of nop instructions we created in prev_nop_frag. */static int prev_nop_frag_holds;/* The number of nop instructions that we know we need in prev_nop_frag. */static int prev_nop_frag_required;/* The number of instructions we've seen since prev_nop_frag. */static int prev_nop_frag_since;/* For ECOFF and ELF, relocations against symbols are done in two parts, with a HI relocation and a LO relocation. Each relocation has only 16 bits of space to store an addend. This means that in order for the linker to handle carries correctly, it must be able to locate both the HI and the LO relocation. This means that the relocations must appear in order in the relocation table. In order to implement this, we keep track of each unmatched HI relocation. We then sort them so that they immediately precede the corresponding LO relocation. */struct mips_hi_fixup{ /* Next HI fixup. */ struct mips_hi_fixup *next; /* This fixup. */ fixS *fixp; /* The section this fixup is in. */ segT seg;};/* The list of unmatched HI relocs. */static struct mips_hi_fixup *mips_hi_fixup_list;/* Map normal MIPS register numbers to mips16 register numbers. */#define X ILLEGAL_REGstatic const int mips32_to_16_reg_map[] ={ X, X, 2, 3, 4, 5, 6, 7, X, X, X, X, X, X, X, X, 0, 1, X, X, X, X, X, X, X, X, X, X, X, X, X, X};#undef X/* Map mips16 register numbers to normal MIPS register numbers. */static const unsigned int mips16_to_32_reg_map[] ={ 16, 17, 2, 3, 4, 5, 6, 7};/* Since the MIPS does not have multiple forms of PC relative instructions, we do not have to do relaxing as is done on other platforms. However, we do have to handle GP relative addressing correctly, which turns out to be a similar problem. Every macro that refers to a symbol can occur in (at least) two forms, one with GP relative addressing and one without. For example, loading a global variable into a register generally uses a macro instruction like this: lw $4,i If i can be addressed off the GP register (this is true if it is in the .sbss or .sdata section, or if it is known to be smaller than the -G argument) this will generate the following instruction: lw $4,i($gp) This instruction will use a GPREL reloc. If i can not be addressed off the GP register, the following instruction sequence will be used: lui $at,i lw $4,i($at) In this case the first instruction will have a HI16 reloc, and the second reloc will have a LO16 reloc. Both relocs will be against the symbol i. The issue here is that we may not know whether i is GP addressable until after we see the instruction that uses it. Therefore, we want to be able to choose the final instruction sequence only at the end of the assembly. This is similar to the way other platforms choose the size of a PC relative instruction only at the end of assembly. When generating position independent code we do not use GP addressing in quite the same way, but the issue still arises as external symbols and local symbols must be handled differently. We handle these issues by actually generating both possible instruction sequences. The longer one is put in a frag_var with type rs_machine_dependent. We encode what to do with the frag in the subtype field. We encode (1) the number of existing bytes to replace, (2) the number of new bytes to use, (3) the offset from the start of the existing bytes to the first reloc we must generate (that is, the offset is applied from the start of the existing bytes after they are replaced by the new bytes, if any), (4) the offset from the start of the existing bytes to the second reloc, (5) whether a third reloc is needed (the third reloc is always four bytes after the second reloc), and (6) whether to warn if this variant is used (this is sometimes needed if .set nomacro or .set noat is in effect). All these numbers are reasonably small. Generating two instruction sequences must be handled carefully to ensure that delay slots are handled correctly. Fortunately, there are a limited number of cases. When the second instruction sequence is generated, append_insn is directed to maintain the existing delay slot information, so it continues to apply to any code after the second instruction sequence. This means that the second instruction sequence must not impose any requirements not required by the first instruction sequence. These variant frags are then handled in functions called by the machine independent code. md_estimate_size_before_relax returns the final size of the frag. md_convert_frag sets up the final form of the frag. tc_gen_reloc adjust the first reloc and adds a second one if needed. */#define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \ ((relax_substateT) \ (((old) << 23) \ | ((new) << 16) \ | (((reloc1) + 64) << 9) \ | (((reloc2) + 64) << 2) \ | ((reloc3) ? (1 << 1) : 0) \ | ((warn) ? 1 : 0)))#define RELAX_OLD(i) (((i) >> 23) & 0x7f)#define RELAX_NEW(i) (((i) >> 16) & 0x7f)#define RELAX_RELOC1(i) ((bfd_vma) (((i) >> 9) & 0x7f) - 64)#define RELAX_RELOC2(i) ((bfd_vma) (((i) >> 2) & 0x7f) - 64)#define RELAX_RELOC3(i) (((i) >> 1) & 1)#define RELAX_WARN(i) ((i) & 1)/* For mips16 code, we use an entirely different form of relaxation. mips16 supports two versions of most instructions which take immediate values: a small one which takes some small value, and a larger one which takes a 16 bit value. Since branches also follow this pattern, relaxing these values is required. We can assemble both mips16 and normal MIPS code in a single object. Therefore, we need to support this type of relaxation at the same time that we support the relaxation described above. We use the high bit of the subtype field to distinguish these cases. The information we store for this type of relaxation is the argument code found in the opcode file for this relocation, whether the user explicitly requested a small or extended form, and whether the relocation is in a jump or jal delay slot. That tells us the size of the value, and how it should be stored. We also store whether the fragment is considered to be extended or not. We also store whether this is known to be a branch to a different section, whether we have tried to relax this frag yet, and whether we have ever extended a PC relative fragment because of a shift count. */#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \ (0x80000000 \ | ((type) & 0xff) \ | ((small) ? 0x100 : 0) \ | ((ext) ? 0x200 : 0) \ | ((dslot) ? 0x400 : 0) \ | ((jal_dslot) ? 0x800 : 0))#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0)#define RELAX_MIPS16_TYPE(i) ((i) & 0xff)#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0)#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0)#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0)#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000)#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)/* Prototypes for static functions. */#ifdef __STDC__#define internalError() \ as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)#else#define internalError() as_fatal (_("MIPS internal Error"));#endifenum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip, unsigned int reg, enum mips_regclass class));static int reg_needs_delay PARAMS ((unsigned int));static void mips16_mark_labels PARAMS ((void));static void append_insn PARAMS ((char *place, struct mips_cl_insn * ip, expressionS * p, bfd_reloc_code_real_type r, boolean));static void mips_no_prev_insn PARAMS ((int));static void mips_emit_delays PARAMS ((boolean));#ifdef USE_STDARGstatic void macro_build PARAMS ((char *place, int *counter, expressionS * ep, const char *name, const char *fmt, ...));#elsestatic void macro_build ();#endifstatic void mips16_macro_build PARAMS ((char *, int *, expressionS *, const char *, const char *, va_list));static void macro_build_lui PARAMS ((char *place, int *counter, expressionS * ep, int regnum));static void set_at PARAMS ((int *counter, int reg, int unsignedp));static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip, expressionS *));static void load_register PARAMS ((int *, int, expressionS *, int));static void load_address PARAMS ((int *counter, int reg, expressionS *ep));static void macro PARAMS ((struct mips_cl_insn * ip));static void mips16_macro PARAMS ((struct mips_cl_insn * ip));#ifdef LOSING_COMPILERstatic void macro2 PARAMS ((struct mips_cl_insn * ip));#endifstatic void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip));static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip));static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean, boolean, boolean, unsigned long *, boolean *, unsigned short *));static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));static void my_getExpression PARAMS ((expressionS * ep, char *str));static symbolS *get_symbol PARAMS ((void));static void mips_align PARAMS ((int to, int fill, symbolS *label));static void s_align PARAMS ((int));static void s_change_sec PARAMS ((int));static void s_cons PARAMS ((int));static void s_float_cons PARAMS ((int));static void s_mips_globl PARAMS ((int));static void s_option PARAMS ((int));static void s_mipsset PARAMS ((int));static void s_abicalls PARAMS ((int));static void s_cpload PARAMS ((int));static void s_cprestore PARAMS ((int));static void s_gpword PARAMS ((int));static void s_cpadd PARAMS ((int));static void s_insn PARAMS ((int));static void md_obj_begin PARAMS ((void));static void md_obj_end PARAMS ((void));static long get_number PARAMS ((void));static void s_mips_ent PARAMS ((int));static void s_mips_end PARAMS ((int));static void s_mips_frame PARAMS ((int));static void s_mips_mask PARAMS ((int));static void s_mips_stab PARAMS ((int));static void s_mips_weakext PARAMS ((int));static void s_file PARAMS ((int));static int mips16_extended_frag PARAMS ((fragS *, asection *, long));static const char *mips_isa_to_str PARAMS ((int));static const char *mips_cpu_to_str PARAMS ((int));static int validate_mips_insn PARAMS ((const struct mips_opcode *));/* Table and functions used to map between CPU/ISA names, and ISA levels, and CPU numbers. */struct mips_cpu_info{ const char *name; /* CPU or ISA name. */ int is_isa; /* Is this an ISA? (If 0, a CPU.) */ int isa; /* ISA level. */ int cpu; /* CPU number (default CPU if ISA). */};static const struct mips_cpu_info *mips_cpu_info_from_name PARAMS ((const char *));static const struct mips_cpu_info *mips_cpu_info_from_isa PARAMS ((int));static const struct mips_cpu_info *mips_cpu_info_from_cpu PARAMS ((int));/* Pseudo-op table. The following pseudo-ops from the Kane and Heinrich MIPS book should be defined here, but are currently unsupported: .alias, .galive, .gjaldef, .gjrlive, .livereg, .noalias. The following pseudo-ops from the Kane and Heinrich MIPS book are specific to the type of debugging information being generated, and should be defined by the object format: .aent, .begin, .bend, .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp, .vreg. The following pseudo-ops from the Kane and Heinrich MIPS book are not MIPS CPU specific, but are also not specific to the object file format. This file is probably the best place to define them, but they are not currently supported: .asm0, .endr, .lab, .repeat, .struct. */static const pseudo_typeS mips_pseudo_table[] ={ /* MIPS specific pseudo-ops. */ {"option", s_option, 0}, {"set", s_mipsset, 0}, {"rdata", s_change_sec, 'r'}, {"sdata", s_change_sec, 's'}, {"livereg", s_ignore, 0}, {"abicalls", s_abicalls, 0}, {"cpload", s_cpload, 0}, {"cprestore", s_cprestore, 0}, {"gpword", s_gpword, 0}, {"cpadd", s_cpadd, 0}, {"insn", s_insn, 0}, /* Relatively generic pseudo-ops that happen to be used on MIPS chips. */ {"asciiz", stringer, 1}, {"bss", s_change_sec, 'b'}, {"err", s_err, 0}, {"half", s_cons, 1}, {"dword", s_cons, 3}, {"weakext", s_mips_weakext, 0}, /* These pseudo-ops are defined in read.c, but must be overridden here for one reason or another. */ {"align", s_align, 0}, {"byte", s_cons, 0}, {"data", s_change_sec, 'd'}, {"double", s_float_cons, 'd'}, {"float", s_float_cons, 'f'}, {"globl", s_mips_globl, 0}, {"global", s_mips_globl, 0}, {"hword", s_cons, 1}, {"int", s_cons, 2}, {"long", s_cons, 2}, {"octa", s_cons, 4}, {"quad", s_cons, 3}, {"short", s_cons, 1}, {"single", s_float_cons, 'f'},
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -