X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-mips.c;h=a9b46e971d94fc57ca48253bf05eb32dde40daa5;hb=5c111e376966558968049df5839776067729d3c8;hp=7ab846fdca7e5d2cd5e40cd2d81369ea84573454;hpb=335574df3c281b3c469ec272686694b41bad928b;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 7ab846fdca..a9b46e971d 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -1,6 +1,6 @@ /* tc-mips.c -- assemble code for a MIPS chip. Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. Contributed by the OSF and Ralph Campbell. Written by Keith Knowles and Ralph Campbell, working independently. @@ -171,6 +171,10 @@ struct mips_cl_insn /* True if this instruction is complete. */ unsigned int complete_p : 1; + + /* True if this instruction is cleared from history by unconditional + branch. */ + unsigned int cleared_p : 1; }; /* The ABI to use. */ @@ -500,6 +504,10 @@ static int mips_32bitmode = 0; /* True if CPU has seq/sne and seqi/snei instructions. */ #define CPU_HAS_SEQ(CPU) (CPU_IS_OCTEON (CPU)) +/* True, if CPU has support for ldc1 and sdc1. */ +#define CPU_HAS_LDC1_SDC1(CPU) \ + ((mips_opts.isa != ISA_MIPS1) && ((CPU) != CPU_R5900)) + /* True if mflo and mfhi can be immediately followed by instructions which write to the HI and LO registers. @@ -518,6 +526,7 @@ static int mips_32bitmode = 0; || mips_opts.isa == ISA_MIPS64 \ || mips_opts.isa == ISA_MIPS64R2 \ || mips_opts.arch == CPU_R4010 \ + || mips_opts.arch == CPU_R5900 \ || mips_opts.arch == CPU_R10000 \ || mips_opts.arch == CPU_R12000 \ || mips_opts.arch == CPU_R14000 \ @@ -535,6 +544,7 @@ static int mips_32bitmode = 0; #define gpr_interlocks \ (mips_opts.isa != ISA_MIPS1 \ || mips_opts.arch == CPU_R3900 \ + || mips_opts.arch == CPU_R5900 \ || mips_opts.micromips \ ) @@ -1304,9 +1314,6 @@ static struct { /* Prototypes for static functions. */ -#define internalError() \ - as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__) - enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG }; static void append_insn @@ -1679,6 +1686,7 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo) insn->noreorder_p = (mips_opts.noreorder > 0); insn->mips16_absolute_jump_p = 0; insn->complete_p = 0; + insn->cleared_p = 0; } /* Record the current MIPS16/microMIPS mode in now_seg. */ @@ -2301,7 +2309,18 @@ is_size_valid (const struct mips_opcode *mo) } /* Return TRUE if the microMIPS opcode MO is valid for the delay slot - of the preceding instruction. Always TRUE in the standard MIPS mode. */ + of the preceding instruction. Always TRUE in the standard MIPS mode. + + We don't accept macros in 16-bit delay slots to avoid a case where + a macro expansion fails because it relies on a preceding 32-bit real + instruction to have matched and does not handle the operands correctly. + The only macros that may expand to 16-bit instructions are JAL that + cannot be placed in a delay slot anyway, and corner cases of BALIGN + and BGT (that likewise cannot be placed in a delay slot) that decay to + a NOP. In all these cases the macros precede any corresponding real + instruction definitions in the opcode table, so they will match in the + second pass where the size of the delay slot is ignored and therefore + produce correct code. */ static bfd_boolean is_delay_slot_valid (const struct mips_opcode *mo) @@ -2310,7 +2329,7 @@ is_delay_slot_valid (const struct mips_opcode *mo) return TRUE; if (mo->pinfo == INSN_MACRO) - return TRUE; + return (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) == 0; if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0 && micromips_insn_length (mo) != 4) return FALSE; @@ -3724,10 +3743,13 @@ fix_loongson2f (struct mips_cl_insn * ip) /* IP is a branch that has a delay slot, and we need to fill it automatically. Return true if we can do that by swapping IP - with the previous instruction. */ + with the previous instruction. + ADDRESS_EXPR is an operand of the instruction to be used with + RELOC_TYPE. */ static bfd_boolean -can_swap_branch_p (struct mips_cl_insn *ip) +can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr, + bfd_reloc_code_real_type *reloc_type) { unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2; unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write; @@ -3846,13 +3868,64 @@ can_swap_branch_p (struct mips_cl_insn *ip) && insn_length (history) != 4) return FALSE; + /* On R5900 short loops need to be fixed by inserting a nop in + the branch delay slots. + A short loop can be terminated too early. */ + if (mips_opts.arch == CPU_R5900 + /* Check if instruction has a parameter, ignore "j $31". */ + && (address_expr != NULL) + /* Parameter must be 16 bit. */ + && (*reloc_type == BFD_RELOC_16_PCREL_S2) + /* Branch to same segment. */ + && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg) + /* Branch to same code fragment. */ + && (symbol_get_frag(address_expr->X_add_symbol) == frag_now) + /* Can only calculate branch offset if value is known. */ + && symbol_constant_p(address_expr->X_add_symbol) + /* Check if branch is really conditional. */ + && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */ + || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */ + || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */ + { + int distance; + /* Check if loop is shorter than 6 instructions including + branch and delay slot. */ + distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol); + if (distance <= 20) + { + int i; + int rv; + + rv = FALSE; + /* When the loop includes branches or jumps, + it is not a short loop. */ + for (i = 0; i < (distance / 4); i++) + { + if ((history[i].cleared_p) + || delayed_branch_p(&history[i])) + { + rv = TRUE; + break; + } + } + if (rv == FALSE) + { + /* Insert nop after branch to fix short loop. */ + return FALSE; + } + } + } + return TRUE; } -/* Decide how we should add IP to the instruction stream. */ +/* Decide how we should add IP to the instruction stream. + ADDRESS_EXPR is an operand of the instruction to be used with + RELOC_TYPE. */ static enum append_method -get_append_method (struct mips_cl_insn *ip) +get_append_method (struct mips_cl_insn *ip, expressionS *address_expr, + bfd_reloc_code_real_type *reloc_type) { unsigned long pinfo; @@ -3868,7 +3941,8 @@ get_append_method (struct mips_cl_insn *ip) /* Otherwise, it's our responsibility to fill branch delay slots. */ if (delayed_branch_p (ip)) { - if (!branch_likely_p (ip) && can_swap_branch_p (ip)) + if (!branch_likely_p (ip) + && can_swap_branch_p (ip, address_expr, reloc_type)) return APPEND_SWAP; pinfo = ip->insn_mo->pinfo; @@ -4022,6 +4096,52 @@ micromips_map_reloc (bfd_reloc_code_real_type reloc) return reloc; } +/* Try to resolve relocation RELOC against constant OPERAND at assembly time. + Return true on success, storing the resolved value in RESULT. */ + +static bfd_boolean +calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand, + offsetT *result) +{ + switch (reloc) + { + case BFD_RELOC_MIPS_HIGHEST: + case BFD_RELOC_MICROMIPS_HIGHEST: + *result = ((operand + 0x800080008000ull) >> 48) & 0xffff; + return TRUE; + + case BFD_RELOC_MIPS_HIGHER: + case BFD_RELOC_MICROMIPS_HIGHER: + *result = ((operand + 0x80008000ull) >> 32) & 0xffff; + return TRUE; + + case BFD_RELOC_HI16_S: + case BFD_RELOC_MICROMIPS_HI16_S: + case BFD_RELOC_MIPS16_HI16_S: + *result = ((operand + 0x8000) >> 16) & 0xffff; + return TRUE; + + case BFD_RELOC_HI16: + case BFD_RELOC_MICROMIPS_HI16: + case BFD_RELOC_MIPS16_HI16: + *result = (operand >> 16) & 0xffff; + return TRUE; + + case BFD_RELOC_LO16: + case BFD_RELOC_MICROMIPS_LO16: + case BFD_RELOC_MIPS16_LO16: + *result = operand & 0xffff; + return TRUE; + + case BFD_RELOC_UNUSED: + *result = operand; + return TRUE; + + default: + return FALSE; + } +} + /* Output an instruction. IP is the instruction information. ADDRESS_EXPR is an operand of the instruction to be used with RELOC_TYPE. EXPANSIONP is true if the instruction is part of @@ -4057,43 +4177,13 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, if (address_expr == NULL) ip->complete_p = 1; - else if (*reloc_type <= BFD_RELOC_UNUSED + else if (reloc_type[0] <= BFD_RELOC_UNUSED + && reloc_type[1] == BFD_RELOC_UNUSED + && reloc_type[2] == BFD_RELOC_UNUSED && address_expr->X_op == O_constant) { - unsigned int tmp; - - ip->complete_p = 1; switch (*reloc_type) { - case BFD_RELOC_32: - ip->insn_opcode |= address_expr->X_add_number; - break; - - case BFD_RELOC_MIPS_HIGHEST: - tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48; - ip->insn_opcode |= tmp & 0xffff; - break; - - case BFD_RELOC_MIPS_HIGHER: - tmp = (address_expr->X_add_number + 0x80008000ull) >> 32; - ip->insn_opcode |= tmp & 0xffff; - break; - - case BFD_RELOC_HI16_S: - tmp = (address_expr->X_add_number + 0x8000) >> 16; - ip->insn_opcode |= tmp & 0xffff; - break; - - case BFD_RELOC_HI16: - ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff; - break; - - case BFD_RELOC_UNUSED: - case BFD_RELOC_LO16: - case BFD_RELOC_MIPS_GOT_DISP: - ip->insn_opcode |= address_expr->X_add_number & 0xffff; - break; - case BFD_RELOC_MIPS_JMP: { int shift; @@ -4136,13 +4226,22 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, ip->insn_opcode |= ((address_expr->X_add_number >> shift) & 0xffff); } - ip->complete_p = 0; } break; default: - internalError (); - } + { + offsetT value; + + if (calculate_reloc (*reloc_type, address_expr->X_add_number, + &value)) + { + ip->insn_opcode |= value & 0xffff; + ip->complete_p = 1; + } + } + break; + } } if (mips_relax.sequence != 2 && !mips_opts.noreorder) @@ -4224,7 +4323,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, } } - method = get_append_method (ip); + method = get_append_method (ip, address_expr, reloc_type); branch_disp = method == APPEND_SWAP ? insn_length (history) : 0; #ifdef OBJ_ELF @@ -4427,6 +4526,11 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, || lo16_reloc_p (reloc_type[0]))) ip->fixp[0]->fx_no_overflow = 1; + /* These relocations can have an addend that won't fit in 2 octets. */ + if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1 + || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1) + ip->fixp[0]->fx_no_overflow = 1; + if (mips_relax.sequence) { if (mips_relax.first_fixup == 0) @@ -4537,7 +4641,14 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, /* If we have just completed an unconditional branch, clear the history. */ if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1])) || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0]))) - mips_no_prev_insn (); + { + unsigned int i; + + mips_no_prev_insn (); + + for (i = 0; i < ARRAY_SIZE (history); i++) + history[i].cleared_p = 1; + } /* We need to emit a label at the end of branch-likely macros. */ if (emit_branch_likely_macro) @@ -4550,7 +4661,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, mips_clear_insn_labels (); } -/* Forget that there was any previous instruction or label. */ +/* Forget that there was any previous instruction or label. + When BRANCH is true, the branch history is also flushed. */ static void mips_no_prev_insn (void) @@ -4917,7 +5029,7 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) continue; default: - internalError (); + abort (); } continue; @@ -5121,12 +5233,12 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) break; default: - internalError (); + abort (); } continue; default: - internalError (); + abort (); } break; } @@ -5227,14 +5339,15 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt, case 'p': case 'q': { + offsetT value; + gas_assert (ep != NULL); if (ep->X_op != O_constant) *r = (int) BFD_RELOC_UNUSED + c; - else + else if (calculate_reloc (*r, ep->X_add_number, &value)) { - mips16_immed (NULL, 0, c, *r, ep->X_add_number, - 0, &insn.insn_opcode); + mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode); ep = NULL; *r = BFD_RELOC_UNUSED; } @@ -5302,7 +5415,8 @@ macro_build_jalr (expressionS *ep, int cprestore) if (mips_opts.micromips) { jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs"; - if (MIPS_JALR_HINT_P (ep)) + if (MIPS_JALR_HINT_P (ep) + || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT)) macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG); else macro_build (NULL, jalr, "mj", PIC_CALL_REG); @@ -7742,7 +7856,9 @@ macro (struct mips_cl_insn *ip) if (mips_pic == NO_PIC) { s = jals ? "jalrs" : "jalr"; - if (mips_opts.micromips && dreg == RA) + if (mips_opts.micromips + && dreg == RA + && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT)) macro_build (NULL, s, "mj", sreg); else macro_build (NULL, s, JALR_FMT, dreg, sreg); @@ -7757,7 +7873,9 @@ macro (struct mips_cl_insn *ip) s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore) ? "jalrs" : "jalr"); - if (mips_opts.micromips && dreg == RA) + if (mips_opts.micromips + && dreg == RA + && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT)) macro_build (NULL, s, "mj", sreg); else macro_build (NULL, s, JALR_FMT, dreg, sreg); @@ -8811,7 +8929,7 @@ macro (struct mips_cl_insn *ip) s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol)); if (strcmp (s, ".lit8") == 0) { - if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips) + if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips) { macro_build (&offset_expr, "ldc1", "T,o(b)", treg, BFD_RELOC_MIPS_LITERAL, mips_gp_register); @@ -8834,7 +8952,7 @@ macro (struct mips_cl_insn *ip) macro_build_lui (&offset_expr, AT); } - if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips) + if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips) { macro_build (&offset_expr, "ldc1", "T,o(b)", treg, BFD_RELOC_LO16, AT); @@ -8851,7 +8969,7 @@ macro (struct mips_cl_insn *ip) r = BFD_RELOC_LO16; dob: gas_assert (!mips_opts.micromips); - gas_assert (mips_opts.isa == ISA_MIPS1); + gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch)); macro_build (&offset_expr, "lwc1", "T,o(b)", target_big_endian ? treg + 1 : treg, r, breg); /* FIXME: A possible overflow which I don't know how to deal @@ -8863,7 +8981,7 @@ macro (struct mips_cl_insn *ip) case M_S_DOB: gas_assert (!mips_opts.micromips); - gas_assert (mips_opts.isa == ISA_MIPS1); + gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch)); /* Even on a big endian machine $fn comes before $fn+1. We have to adjust when storing to memory. */ macro_build (&offset_expr, "swc1", "T,o(b)", @@ -8889,7 +9007,7 @@ macro (struct mips_cl_insn *ip) /* Itbl support may require additional care here. */ coproc = 1; fmt = "T,o(b)"; - if (mips_opts.isa != ISA_MIPS1) + if (CPU_HAS_LDC1_SDC1 (mips_opts.arch)) { s = "ldc1"; goto ld_st; @@ -8902,7 +9020,7 @@ macro (struct mips_cl_insn *ip) /* Itbl support may require additional care here. */ coproc = 1; fmt = "T,o(b)"; - if (mips_opts.isa != ISA_MIPS1) + if (CPU_HAS_LDC1_SDC1 (mips_opts.arch)) { s = "sdc1"; goto ld_st; @@ -8910,6 +9028,16 @@ macro (struct mips_cl_insn *ip) s = "swc1"; goto ldd_std; + case M_LQ_AB: + fmt = "t,o(b)"; + s = "lq"; + goto ld; + + case M_SQ_AB: + fmt = "t,o(b)"; + s = "sq"; + goto ld_st; + case M_LD_AB: fmt = "t,o(b)"; if (HAVE_64BIT_GPRS) @@ -9222,8 +9350,15 @@ macro (struct mips_cl_insn *ip) case M_DMUL: dbl = 1; case M_MUL: + if (mips_opts.arch == CPU_R5900) + { + macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg); + } + else + { macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg); macro_build (NULL, "mflo", MFHL_FMT, dreg); + } break; case M_DMUL_I: @@ -10013,7 +10148,7 @@ mips16_macro (struct mips_cl_insn *ip) switch (mask) { default: - internalError (); + abort (); case M_DDIV_3: dbl = 1; @@ -10591,7 +10726,7 @@ mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum) /* Let a macro pass, we'll catch it later when it is expanded. */ return 1; - if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa)) + if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || (mips_opts.arch == CPU_R5900)) { /* Allow odd registers for single-precision ops. */ switch (insn->pinfo & (FP_S | FP_D)) @@ -11161,7 +11296,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) while (imm->type && imm->type != *args) ++imm; if (! imm->type) - internalError (); + abort (); my_getExpression (&imm_expr, s); check_absolute_expr (ip, &imm_expr); if ((unsigned long) imm_expr.X_add_number & ~imm->mask) @@ -11742,6 +11877,10 @@ mips_ip (char *str, struct mips_cl_insn *ip) if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1) as_warn (_("Invalid performance register (%lu)"), (unsigned long) imm_expr.X_add_number); + if (imm_expr.X_add_number != 0 && mips_opts.arch == CPU_R5900 + && (!strcmp(insn->name,"mfps") || !strcmp(insn->name,"mtps"))) + as_warn (_("Invalid performance register (%lu)"), + (unsigned long) imm_expr.X_add_number); INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number); imm_expr.X_op = O_absent; s = expr_end; @@ -12366,6 +12505,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) continue; case 'u': /* Upper 16 bits. */ + *imm_reloc = BFD_RELOC_LO16; if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0 && imm_expr.X_op == O_constant && (imm_expr.X_add_number < 0 @@ -12691,7 +12831,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) break; default: - internalError (); + abort (); } if (regno == ILLEGAL_REG) @@ -12764,7 +12904,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) break; default: - internalError (); + abort (); } continue; @@ -13228,7 +13368,7 @@ mips_ip (char *str, struct mips_cl_insn *ip) default: as_bad (_("Bad char = '%c'\n"), *args); - internalError (); + abort (); } break; } @@ -13376,40 +13516,17 @@ mips16_ip (char *str, struct mips_cl_insn *ip) case '\0': if (*s == '\0') { + offsetT value; + /* Stuff the immediate value in now, if we can. */ if (imm_expr.X_op == O_constant && *imm_reloc > BFD_RELOC_UNUSED - && *imm_reloc != BFD_RELOC_MIPS16_GOT16 - && *imm_reloc != BFD_RELOC_MIPS16_CALL16 - && insn->pinfo != INSN_MACRO) + && insn->pinfo != INSN_MACRO + && calculate_reloc (*offset_reloc, + imm_expr.X_add_number, &value)) { - valueT tmp; - - switch (*offset_reloc) - { - case BFD_RELOC_MIPS16_HI16_S: - tmp = (imm_expr.X_add_number + 0x8000) >> 16; - break; - - case BFD_RELOC_MIPS16_HI16: - tmp = imm_expr.X_add_number >> 16; - break; - - case BFD_RELOC_MIPS16_LO16: - tmp = ((imm_expr.X_add_number + 0x8000) & 0xffff) - - 0x8000; - break; - - case BFD_RELOC_UNUSED: - tmp = imm_expr.X_add_number; - break; - - default: - internalError (); - } - mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED, - *offset_reloc, tmp, forced_insn_length, + *offset_reloc, value, forced_insn_length, &ip->insn_opcode); imm_expr.X_op = O_absent; *imm_reloc = BFD_RELOC_UNUSED; @@ -13528,7 +13645,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) break; default: - internalError (); + abort (); } if (regno == ILLEGAL_REG) @@ -13561,7 +13678,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) MIPS16_INSERT_OPERAND (REG32R, *ip, regno); break; default: - internalError (); + abort (); } lastregno = regno; @@ -13920,7 +14037,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) continue; default: - internalError (); + abort (); } break; } @@ -13995,6 +14112,31 @@ static const struct mips16_immed_operand mips16_immed_operands[] = #define MIPS16_NUM_IMMED \ (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0]) +/* Marshal immediate value VAL for an extended MIPS16 instruction. + NBITS is the number of significant bits in VAL. */ + +static unsigned long +mips16_immed_extend (offsetT val, unsigned int nbits) +{ + int extval; + if (nbits == 16) + { + extval = ((val >> 11) & 0x1f) | (val & 0x7e0); + val &= 0x1f; + } + else if (nbits == 15) + { + extval = ((val >> 11) & 0xf) | (val & 0x7f0); + val &= 0xf; + } + else + { + extval = ((val & 0x1f) << 6) | (val & 0x20); + val = 0; + } + return (extval << 16) | val; +} + /* Install immediate value VAL into MIPS16 instruction *INSN, extending it if necessary. The instruction in *INSN may already be extended. @@ -14078,7 +14220,6 @@ mips16_immed (char *file, unsigned int line, int type, else { long minext, maxext; - int extval; if (reloc == BFD_RELOC_UNUSED) { @@ -14097,23 +14238,7 @@ mips16_immed (char *file, unsigned int line, int type, _("operand value out of range for instruction")); } - if (op->extbits == 16) - { - extval = ((val >> 11) & 0x1f) | (val & 0x7e0); - val &= 0x1f; - } - else if (op->extbits == 15) - { - extval = ((val >> 11) & 0xf) | (val & 0x7f0); - val &= 0xf; - } - else - { - extval = ((val & 0x1f) << 6) | (val & 0x20); - val = 0; - } - - *insn |= (extval << 16) | val; + *insn |= mips16_immed_extend (val, op->extbits); } } @@ -15357,9 +15482,11 @@ mips_frob_file (void) gas_assert (reloc_needs_lo_p (l->fixp->fx_r_type)); /* If a GOT16 relocation turns out to be against a global symbol, - there isn't supposed to be a matching LO. */ + there isn't supposed to be a matching LO. Ignore %gots against + constants; we'll report an error for those later. */ if (got16_reloc_p (l->fixp->fx_r_type) - && !pic_need_relax (l->fixp->fx_addsy, l->seg)) + && !(l->fixp->fx_addsy + && pic_need_relax (l->fixp->fx_addsy, l->seg))) continue; /* Check quickly whether the next fixup happens to be a matching %lo. */ @@ -15417,9 +15544,6 @@ mips_frob_file (void) } } -/* We may have combined relocations without symbols in the N32/N64 ABI. - We have to prevent gas from dropping them. */ - int mips_force_relocation (fixS *fixp) { @@ -15433,23 +15557,40 @@ mips_force_relocation (fixS *fixp) || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1) return 1; - if (HAVE_NEWABI - && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr - && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB - || hi16_reloc_p (fixp->fx_r_type) - || lo16_reloc_p (fixp->fx_r_type))) - return 1; - return 0; } +/* Read the instruction associated with RELOC from BUF. */ + +static unsigned int +read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc) +{ + if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc)) + return read_compressed_insn (buf, 4); + else + return read_insn (buf); +} + +/* Write instruction INSN to BUF, given that it has been relocated + by RELOC. */ + +static void +write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc, + unsigned long insn) +{ + if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc)) + write_compressed_insn (buf, insn, 4); + else + write_insn (buf, insn); +} + /* Apply a fixup to the object file. */ void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { char *buf; - long insn; + unsigned long insn; reloc_howto_type *howto; /* We ignore generic BFD relocations we don't know about. */ @@ -15515,6 +15656,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MIPS16_TLS_GOTTPREL: case BFD_RELOC_MIPS16_TLS_TPREL_HI16: case BFD_RELOC_MIPS16_TLS_TPREL_LO16: + if (!fixP->fx_addsy) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("TLS relocation against a constant")); + break; + } S_SET_THREAD_LOCAL (fixP->fx_addsy); /* fall through */ @@ -15536,6 +15683,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MIPS_JALR: case BFD_RELOC_HI16: case BFD_RELOC_HI16_S: + case BFD_RELOC_LO16: case BFD_RELOC_GPREL16: case BFD_RELOC_MIPS_LITERAL: case BFD_RELOC_MIPS_CALL16: @@ -15550,6 +15698,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MIPS16_CALL16: case BFD_RELOC_MIPS16_HI16: case BFD_RELOC_MIPS16_HI16_S: + case BFD_RELOC_MIPS16_LO16: case BFD_RELOC_MIPS16_JMP: case BFD_RELOC_MICROMIPS_JMP: case BFD_RELOC_MICROMIPS_GOT_DISP: @@ -15562,6 +15711,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MICROMIPS_JALR: case BFD_RELOC_MICROMIPS_HI16: case BFD_RELOC_MICROMIPS_HI16_S: + case BFD_RELOC_MICROMIPS_LO16: case BFD_RELOC_MICROMIPS_GPREL16: case BFD_RELOC_MICROMIPS_LITERAL: case BFD_RELOC_MICROMIPS_CALL16: @@ -15570,7 +15720,23 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_MICROMIPS_GOT_LO16: case BFD_RELOC_MICROMIPS_CALL_HI16: case BFD_RELOC_MICROMIPS_CALL_LO16: - /* Nothing needed to do. The value comes from the reloc entry. */ + if (fixP->fx_done) + { + offsetT value; + + if (calculate_reloc (fixP->fx_r_type, *valP, &value)) + { + insn = read_reloc_insn (buf, fixP->fx_r_type); + if (mips16_reloc_p (fixP->fx_r_type)) + insn |= mips16_immed_extend (value, 16); + else + insn |= (value & 0xffff); + write_reloc_insn (buf, fixP->fx_r_type, insn); + } + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Unsupported constant in relocation")); + } break; case BFD_RELOC_64: @@ -15604,27 +15770,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) md_number_to_chars (buf, *valP, fixP->fx_size); break; - case BFD_RELOC_LO16: - case BFD_RELOC_MIPS16_LO16: - case BFD_RELOC_MICROMIPS_LO16: - /* FIXME: Now that embedded-PIC is gone, some of this code/comment - may be safe to remove, but if so it's not obvious. */ - /* When handling an embedded PIC switch statement, we can wind - up deleting a LO16 reloc. See the 'o' case in mips_ip. */ - if (fixP->fx_done) - { - if (*valP + 0x8000 > 0xffff) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("relocation overflow")); - /* 32-bit microMIPS instructions are divided into two halfwords. - Relocations always refer to the second halfword, regardless - of endianness. */ - if (target_big_endian || fixP->fx_r_type == BFD_RELOC_MICROMIPS_LO16) - buf += 2; - md_number_to_chars (buf, *valP, 2); - } - break; - case BFD_RELOC_16_PCREL_S2: if ((*valP & 0x3) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, @@ -15703,7 +15848,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) break; default: - internalError (); + abort (); } /* Remember value for tc_gen_reloc. */ @@ -16327,7 +16472,14 @@ s_mipsset (int x ATTRIBUTE_UNUSED) case ISA_MIPS64: case ISA_MIPS64R2: mips_opts.gp32 = 0; + if (mips_opts.arch == CPU_R5900) + { + mips_opts.fp32 = 1; + } + else + { mips_opts.fp32 = 0; + } break; default: as_bad (_("unknown ISA level %s"), name + 4); @@ -16469,6 +16621,9 @@ s_cpload (int ignore ATTRIBUTE_UNUSED) /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT; + mips_mark_labels (); + mips_assembling_insn = TRUE; + macro_start (); macro_build_lui (&ex, mips_gp_register); macro_build (&ex, "addiu", "t,r,j", mips_gp_register, @@ -16478,6 +16633,7 @@ s_cpload (int ignore ATTRIBUTE_UNUSED) mips_gp_register, reg); macro_end (); + mips_assembling_insn = FALSE; demand_empty_rest_of_line (); } @@ -16554,6 +16710,9 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED) SKIP_WHITESPACE (); expression (&ex_sym); + mips_mark_labels (); + mips_assembling_insn = TRUE; + macro_start (); if (mips_cpreturn_register == -1) { @@ -16601,6 +16760,7 @@ s_cpsetup (int ignore ATTRIBUTE_UNUSED) macro_end (); + mips_assembling_insn = FALSE; demand_empty_rest_of_line (); } @@ -16658,11 +16818,15 @@ s_cprestore (int ignore ATTRIBUTE_UNUSED) ex.X_op_symbol = NULL; ex.X_add_number = mips_cprestore_offset; + mips_mark_labels (); + mips_assembling_insn = TRUE; + macro_start (); macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register, SP, HAVE_64BIT_ADDRESSES); macro_end (); + mips_assembling_insn = FALSE; demand_empty_rest_of_line (); } @@ -16693,6 +16857,9 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED) return; } + mips_mark_labels (); + mips_assembling_insn = TRUE; + macro_start (); if (mips_cpreturn_register == -1) { @@ -16708,6 +16875,7 @@ s_cpreturn (int ignore ATTRIBUTE_UNUSED) mips_cpreturn_register, 0); macro_end (); + mips_assembling_insn = FALSE; demand_empty_rest_of_line (); } @@ -16887,12 +17055,16 @@ s_cpadd (int ignore ATTRIBUTE_UNUSED) return; } + mips_mark_labels (); + mips_assembling_insn = TRUE; + /* Add $gp to the register named as an argument. */ macro_start (); reg = tc_get_register (0); macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register); macro_end (); + mips_assembling_insn = FALSE; demand_empty_rest_of_line (); } @@ -19029,6 +19201,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] = { "r4600", 0, ISA_MIPS3, CPU_R4600 }, { "orion", 0, ISA_MIPS3, CPU_R4600 }, { "r4650", 0, ISA_MIPS3, CPU_R4650 }, + { "r5900", 0, ISA_MIPS3, CPU_R5900 }, /* ST Microelectronics Loongson 2E and 2F cores */ { "loongson2e", 0, ISA_MIPS3, CPU_LOONGSON_2E }, { "loongson2f", 0, ISA_MIPS3, CPU_LOONGSON_2F },