diff options
Diffstat (limited to 'gas/config/tc-nios2.c')
-rw-r--r-- | gas/config/tc-nios2.c | 1513 |
1 files changed, 1406 insertions, 107 deletions
diff --git a/gas/config/tc-nios2.c b/gas/config/tc-nios2.c index 549ca98..4488a47 100644 --- a/gas/config/tc-nios2.c +++ b/gas/config/tc-nios2.c @@ -210,6 +210,11 @@ static int nios2_auto_align_on = 1; labels preceeding instructions. */ static symbolS *nios2_last_label; +/* If we saw a 16-bit CDX instruction, we can align on 2-byte boundaries + instead of 4-bytes. Use this to keep track of the minimum power-of-2 + alignment. */ +static int nios2_min_align = 2; + #ifdef OBJ_ELF /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ symbolS *GOT_symbol; @@ -322,8 +327,12 @@ nios2_special_relocation_p (const char *str) } -/* nop fill pattern for text section. */ -static char const nop[4] = { 0x3a, 0x88, 0x01, 0x00 }; +/* nop fill patterns for text section. */ +static char const nop_r1[4] = { 0x3a, 0x88, 0x01, 0x00 }; +static char const nop_r2[4] = { 0x20, 0x00, 0x00, 0xc4 }; +static char const nop_r2_cdx[2] = { 0x3b, 0x00 }; +static char const *nop32 = nop_r1; +static char const *nop16 = NULL; /* Handles all machine-dependent alignment needs. */ static void @@ -350,16 +359,26 @@ nios2_align (int log_size, const char *pfill, symbolS *label) if (align != 0) { - if (subseg_text_p (now_seg) && align >= 2) + if (subseg_text_p (now_seg) && align >= nios2_min_align) { - /* First, make sure we're on a four-byte boundary, in case + /* First, make sure we're on the minimum boundary, in case someone has been putting .byte values the text section. */ - if (nios2_current_align < 2 || switched_seg_p) - frag_align (2, 0, 0); + if (nios2_current_align < nios2_min_align || switched_seg_p) + frag_align (nios2_min_align, 0, 0); + + /* If we might be on a 2-byte boundary, first align to a + 4-byte boundary using the 2-byte nop as fill. */ + if (nios2_min_align == 1 + && align > nios2_min_align + && pfill == nop32 ) + { + gas_assert (nop16); + frag_align_pattern (2, nop16, 2, 0); + } /* Now fill in the alignment pattern. */ if (pfill != NULL) - frag_align_pattern (align, pfill, sizeof nop, 0); + frag_align_pattern (align, pfill, 4, 0); else frag_align (align, 0, 0); } @@ -470,7 +489,7 @@ s_nios2_align (int ignore ATTRIBUTE_UNUSED) pfill = (const char *) &fill; } else if (subseg_text_p (now_seg)) - pfill = (const char *) &nop; + pfill = (const char *) nop32; else { pfill = NULL; @@ -669,6 +688,11 @@ const pseudo_typeS md_pseudo_table[] = { jmp at skip: respectively. + + 16-bit CDX branch instructions are relaxed first into equivalent + 32-bit branches and then the above transformations are applied + if necessary. + */ /* Arbitrarily limit the number of addis we can insert; we need to be able @@ -685,7 +709,7 @@ const pseudo_typeS md_pseudo_table[] = { /* The fr_subtype field represents the target-specific relocation state. It has type relax_substateT (unsigned int). We use it to track the number of addis necessary, plus a bit to track whether this is a - conditional branch. + conditional branch and a bit for 16-bit CDX instructions. Regardless of the smaller RELAX_MAX_ADDI limit, we reserve 16 bits in the fr_subtype to encode the number of addis so that the whole theoretically-valid range is representable. @@ -693,10 +717,14 @@ const pseudo_typeS md_pseudo_table[] = { represents a branch that needs to be relaxed. */ #define UBRANCH (0 << 16) #define CBRANCH (1 << 16) +#define CDXBRANCH (1 << 17) #define IS_CBRANCH(SUBTYPE) ((SUBTYPE) & CBRANCH) #define IS_UBRANCH(SUBTYPE) (!IS_CBRANCH (SUBTYPE)) +#define IS_CDXBRANCH(SUBTYPE) ((SUBTYPE) & CDXBRANCH) #define UBRANCH_SUBTYPE(N) (UBRANCH | (N)) #define CBRANCH_SUBTYPE(N) (CBRANCH | (N)) +#define CDX_UBRANCH_SUBTYPE(N) (CDXBRANCH | UBRANCH | (N)) +#define CDX_CBRANCH_SUBTYPE(N) (CDXBRANCH | CBRANCH | (N)) #define SUBTYPE_ADDIS(SUBTYPE) ((SUBTYPE) & 0xffff) /* For the -relax-section mode, unconditional branches require 2 extra i @@ -730,7 +758,7 @@ nios2_relax_subtype_size (relax_substateT subtype) int n = SUBTYPE_ADDIS (subtype); if (n == 0) /* Regular conditional/unconditional branch instruction. */ - return 4; + return (IS_CDXBRANCH (subtype) ? 2 : 4); else if (nios2_as_options.relax == relax_all) return (IS_CBRANCH (subtype) ? CBRANCH_JUMP_SIZE : UBRANCH_JUMP_SIZE); else if (IS_CBRANCH (subtype)) @@ -761,6 +789,7 @@ nios2_relax_frag (segT segment, fragS *fragp, long stretch) fragS *sym_frag = symbol_get_frag (symbolp); offsetT offset; int n; + bfd_boolean is_cdx = FALSE; target += S_GET_VALUE (symbolp); @@ -777,7 +806,15 @@ nios2_relax_frag (segT segment, fragS *fragp, long stretch) /* We subtract fr_var (4 for 32-bit insns) because all pc relative branches are from the next instruction. */ offset = target - fragp->fr_address - fragp->fr_fix - fragp->fr_var; - if (offset >= -32768 && offset <= 32764) + if (IS_CDXBRANCH (subtype) && IS_UBRANCH (subtype) + && offset >= -1024 && offset < 1024) + /* PC-relative CDX branch with 11-bit offset. */ + is_cdx = TRUE; + else if (IS_CDXBRANCH (subtype) && IS_CBRANCH (subtype) + && offset >= -128 && offset < 128) + /* PC-relative CDX branch with 8-bit offset. */ + is_cdx = TRUE; + else if (offset >= -32768 && offset < 32768) /* Fits in PC-relative branch. */ n = 0; else if (nios2_as_options.relax == relax_all) @@ -816,7 +853,9 @@ nios2_relax_frag (segT segment, fragS *fragp, long stretch) /* We cannot handle this case, diagnose overflow later. */ return 0; - if (IS_CBRANCH (subtype)) + if (is_cdx) + fragp->fr_subtype = subtype; + else if (IS_CBRANCH (subtype)) fragp->fr_subtype = CBRANCH_SUBTYPE (n); else fragp->fr_subtype = UBRANCH_SUBTYPE (n); @@ -841,16 +880,50 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, addressT target = fragp->fr_offset; symbolS *symbolp = fragp->fr_symbol; offsetT offset; - unsigned int addend_mask, addi_mask; + unsigned int addend_mask, addi_mask, op; offsetT addend, remainder; int i; + bfd_boolean is_r2 = (bfd_get_mach (stdoutput) == bfd_mach_nios2r2); + + /* If this is a CDX branch we're not relaxing, just generate the fixup. */ + if (IS_CDXBRANCH (subtype)) + { + gas_assert (is_r2); + fix_new (fragp, fragp->fr_fix, 2, fragp->fr_symbol, + fragp->fr_offset, 1, + (IS_UBRANCH (subtype) + ? BFD_RELOC_NIOS2_R2_I10_1_PCREL + : BFD_RELOC_NIOS2_R2_T1I7_1_PCREL)); + fragp->fr_fix += 2; + return; + } + + /* If this is a CDX branch we are relaxing, turn it into an equivalent + 32-bit branch and then fall through to the normal non-CDX cases. */ + if (fragp->fr_var == 2) + { + unsigned int opcode = md_chars_to_number (buffer, 2); + gas_assert (is_r2); + if (IS_CBRANCH (subtype)) + { + unsigned int reg = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (opcode)]; + if (GET_IW_R2_OP (opcode) == R2_OP_BNEZ_N) + opcode = MATCH_R2_BNE | SET_IW_F2I16_A (reg); + else + opcode = MATCH_R2_BEQ | SET_IW_F2I16_A (reg); + } + else + opcode = MATCH_R2_BR; + md_number_to_chars (buffer, opcode, 4); + fragp->fr_var = 4; + } /* If we didn't or can't relax, this is a regular branch instruction. We just need to generate the fixup for the symbol and offset. */ if (n == 0) { - fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 1, - BFD_RELOC_16_PCREL); + fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, + fragp->fr_offset, 1, BFD_RELOC_16_PCREL); fragp->fr_fix += 4; return; } @@ -871,35 +944,66 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, nbytes = 12; br_opcode = md_chars_to_number (buffer, 4); - old_op = GET_IW_R1_OP (br_opcode); - switch (old_op) + if (is_r2) { - case R1_OP_BEQ: - new_op = R1_OP_BNE; - break; - case R1_OP_BNE: - new_op = R1_OP_BEQ; - break; - case R1_OP_BGE: - new_op = R1_OP_BLT; - break; - case R1_OP_BGEU: - new_op = R1_OP_BLTU; - break; - case R1_OP_BLT: - new_op = R1_OP_BGE; - break; - case R1_OP_BLTU: - new_op = R1_OP_BGEU; - break; - default: - as_bad_where (fragp->fr_file, fragp->fr_line, - _("expecting conditional branch for relaxation\n")); - abort (); + old_op = GET_IW_R2_OP (br_opcode); + switch (old_op) + { + case R2_OP_BEQ: + new_op = R2_OP_BNE; + break; + case R2_OP_BNE: + new_op = R2_OP_BEQ; + break; + case R2_OP_BGE: + new_op = R2_OP_BLT; + break; + case R2_OP_BGEU: + new_op = R2_OP_BLTU; + break; + case R2_OP_BLT: + new_op = R2_OP_BGE; + break; + case R2_OP_BLTU: + new_op = R2_OP_BGEU; + break; + default: + abort (); + } + br_opcode = ((br_opcode & ~IW_R2_OP_SHIFTED_MASK) + | SET_IW_R2_OP (new_op)); + br_opcode = br_opcode | SET_IW_F2I16_IMM16 (nbytes); + } + else + { + old_op = GET_IW_R1_OP (br_opcode); + switch (old_op) + { + case R1_OP_BEQ: + new_op = R1_OP_BNE; + break; + case R1_OP_BNE: + new_op = R1_OP_BEQ; + break; + case R1_OP_BGE: + new_op = R1_OP_BLT; + break; + case R1_OP_BGEU: + new_op = R1_OP_BLTU; + break; + case R1_OP_BLT: + new_op = R1_OP_BGE; + break; + case R1_OP_BLTU: + new_op = R1_OP_BGEU; + break; + default: + abort (); + } + br_opcode = ((br_opcode & ~IW_R1_OP_SHIFTED_MASK) + | SET_IW_R1_OP (new_op)); + br_opcode = br_opcode | SET_IW_I_IMM16 (nbytes); } - - br_opcode = (br_opcode & ~IW_R1_OP_SHIFTED_MASK) | SET_IW_R1_OP (new_op); - br_opcode = br_opcode | SET_IW_I_IMM16 (nbytes); md_number_to_chars (buffer, br_opcode, 4); fragp->fr_fix += 4; buffer += 4; @@ -909,8 +1013,11 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, if (nios2_as_options.relax == relax_section) { /* Insert the nextpc instruction. */ - md_number_to_chars (buffer, - MATCH_R1_NEXTPC | SET_IW_R_C (AT_REGNUM), 4); + if (is_r2) + op = MATCH_R2_NEXTPC | SET_IW_F3X6L5_C (AT_REGNUM); + else + op = MATCH_R1_NEXTPC | SET_IW_R_C (AT_REGNUM); + md_number_to_chars (buffer, op, 4); fragp->fr_fix += 4; buffer += 4; @@ -921,12 +1028,20 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, addend = 32767; else addend = -32768; - addend_mask = SET_IW_I_IMM16 ((unsigned int)addend); + if (is_r2) + addend_mask = SET_IW_F2I16_IMM16 ((unsigned int)addend); + else + addend_mask = SET_IW_I_IMM16 ((unsigned int)addend); /* Insert n-1 addi instructions. */ - addi_mask = (MATCH_R1_ADDI - | SET_IW_I_B (AT_REGNUM) - | SET_IW_I_A (AT_REGNUM)); + if (is_r2) + addi_mask = (MATCH_R2_ADDI + | SET_IW_F2I16_B (AT_REGNUM) + | SET_IW_F2I16_A (AT_REGNUM)); + else + addi_mask = (MATCH_R1_ADDI + | SET_IW_I_B (AT_REGNUM) + | SET_IW_I_A (AT_REGNUM)); for (i = 0; i < n - 1; i ++) { md_number_to_chars (buffer, addi_mask | addend_mask, 4); @@ -937,7 +1052,10 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, /* Insert the last addi instruction to hold the remainder. */ remainder = offset - addend * (n - 1); gas_assert (remainder >= -32768 && remainder <= 32767); - addend_mask = SET_IW_I_IMM16 ((unsigned int)remainder); + if (is_r2) + addend_mask = SET_IW_F2I16_IMM16 ((unsigned int)remainder); + else + addend_mask = SET_IW_I_IMM16 ((unsigned int)remainder); md_number_to_chars (buffer, addi_mask | addend_mask, 4); fragp->fr_fix += 4; buffer += 4; @@ -946,18 +1064,22 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, /* Load at for the absolute case. */ else { - md_number_to_chars (buffer, - (MATCH_R1_ORHI | SET_IW_I_B (AT_REGNUM) - | SET_IW_I_A (0)), - 4); + if (is_r2) + op = MATCH_R2_ORHI | SET_IW_F2I16_B (AT_REGNUM) | SET_IW_F2I16_A (0); + else + op = MATCH_R1_ORHI | SET_IW_I_B (AT_REGNUM) | SET_IW_I_A (0); + md_number_to_chars (buffer, op, 4); fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 0, BFD_RELOC_NIOS2_HI16); fragp->fr_fix += 4; buffer += 4; - md_number_to_chars (buffer, - (MATCH_R1_ORI | SET_IW_I_B (AT_REGNUM) - | SET_IW_I_A (AT_REGNUM)), - 4); + if (is_r2) + op = (MATCH_R2_ORI | SET_IW_F2I16_B (AT_REGNUM) + | SET_IW_F2I16_A (AT_REGNUM)); + else + op = (MATCH_R1_ORI | SET_IW_I_B (AT_REGNUM) + | SET_IW_I_A (AT_REGNUM)); + md_number_to_chars (buffer, op, 4); fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 0, BFD_RELOC_NIOS2_LO16); fragp->fr_fix += 4; @@ -965,7 +1087,11 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, } /* Insert the jmp instruction. */ - md_number_to_chars (buffer, MATCH_R1_JMP | SET_IW_R_A (AT_REGNUM), 4); + if (is_r2) + op = MATCH_R2_JMP | SET_IW_F3X6L5_A (AT_REGNUM); + else + op = MATCH_R1_JMP | SET_IW_R_A (AT_REGNUM); + md_number_to_chars (buffer, op, 4); fragp->fr_fix += 4; buffer += 4; } @@ -977,8 +1103,15 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, static bfd_boolean nios2_check_overflow (valueT fixup, reloc_howto_type *howto) { - /* Apply the rightshift before checking for overflow. */ - fixup = ((signed)fixup) >> howto->rightshift; + /* If there is a rightshift, check that the low-order bits are + zero before applying it. */ + if (howto->rightshift) + { + if ((~(~((valueT) 0) << howto->rightshift) & fixup) + && howto->complain_on_overflow != complain_overflow_dont) + return TRUE; + fixup = ((signed)fixup) >> howto->rightshift; + } /* Check for overflow - return TRUE if overflow, FALSE if not. */ switch (howto->complain_on_overflow) @@ -994,7 +1127,7 @@ nios2_check_overflow (valueT fixup, reloc_howto_type *howto) if ((fixup & 0x80000000) > 0) { /* Check for negative overflow. */ - if ((signed) fixup < ((signed) 0x80000000 >> howto->bitsize)) + if ((signed) fixup < ((signed) ~0 << (howto->bitsize-1))) return TRUE; } else @@ -1056,14 +1189,24 @@ nios2_diagnose_overflow (valueT fixup, reloc_howto_type *howto, address, range_min, range_max); break; case branch_target_overflow: - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch offset %d out of range %d to %d"), - (int)fixup, -32768, 32767); + if (opcode->format == iw_i_type || opcode->format == iw_F2I16_type) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("branch offset %d out of range %d to %d"), + (int)fixup, -32768, 32767); + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("branch offset %d out of range"), + (int)fixup); break; case address_offset_overflow: - as_bad_where (fixP->fx_file, fixP->fx_line, - _("%s offset %d out of range %d to %d"), - opcode->name, (int)fixup, -32768, 32767); + if (opcode->format == iw_i_type || opcode->format == iw_F2I16_type) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("%s offset %d out of range %d to %d"), + opcode->name, (int)fixup, -32768, 32767); + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("%s offset %d out of range"), + opcode->name, (int)fixup); break; case signed_immed16_overflow: as_bad_where (fixP->fx_file, fixP->fx_line, @@ -1080,6 +1223,11 @@ nios2_diagnose_overflow (valueT fixup, reloc_howto_type *howto, _("immediate value %u out of range %u to %u"), (unsigned int)fixup, 0, 31); break; + case signed_immed12_overflow: + as_bad_where (fixP->fx_file, fixP->fx_line, + _("immediate value %d out of range %d to %d"), + (int)fixup, -2048, 2047); + break; case custom_opcode_overflow: as_bad_where (fixP->fx_file, fixP->fx_line, _("custom instruction opcode %u out of range %u to %u"), @@ -1137,6 +1285,19 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) || fixP->fx_r_type == BFD_RELOC_NIOS2_GOT_HA || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL_LO || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL_HA + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_S12 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_I10_1_PCREL + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T1I7_1_PCREL + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T1I7_2 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T2I4 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T2I4_1 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T2I4_2 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_X1I7_2 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_X2L5 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_F1I5_2 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_L5I4X1 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T1X1I6 + || fixP->fx_r_type == BFD_RELOC_NIOS2_R2_T1X1I6_2 /* Add other relocs here as we generate them. */ )); @@ -1208,8 +1369,11 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) FIXME : for some reason fixP->fx_pcrel isn't 1 when it should be so I'm using the howto structure instead to determine this. */ if (howto->pc_relative == 1) - fixup = fixup - (fixP->fx_frag->fr_address + fixP->fx_where - + fixP->fx_size); + { + fixup = (fixup - (fixP->fx_frag->fr_address + fixP->fx_where + + fixP->fx_size)); + *valP = fixup; + } /* Get the instruction or data to be fixed up. */ buf = fixP->fx_frag->fr_literal + fixP->fx_where; @@ -1390,6 +1554,198 @@ nios2_parse_reg (const char *token, unsigned long regtype) return reg; } +/* This function parses a reglist for ldwm/stwm and push.n/pop.n + instructions, given as a brace-enclosed register list. The tokenizer + has replaced commas in the token with spaces. + The return value is a bitmask of registers in the set. It also + sets nios2_reglist_mask and nios2_reglist_dir to allow error checking + when parsing the base register. */ + +static unsigned long nios2_reglist_mask; +static int nios2_reglist_dir; + +static unsigned long +nios2_parse_reglist (char *token, const struct nios2_opcode *op) +{ + unsigned long mask = 0; + int dir = 0; + unsigned long regtype = 0; + int last = -1; + const char *regname; + + nios2_reglist_mask = 0; + nios2_reglist_dir = 0; + + if (op->match == MATCH_R2_LDWM || op->match == MATCH_R2_STWM) + { + regtype = REG_LDWM; + dir = 0; + } + else if (op->match == MATCH_R2_PUSH_N) + { + regtype = REG_POP; + dir = -1; + } + else if (op->match == MATCH_R2_POP_N) + { + regtype = REG_POP; + dir = 1; + } + else + bad_opcode (op); + + for (regname = strtok (token, "{ }"); + regname; + regname = strtok (NULL, "{ }")) + { + int regno; + struct nios2_reg *reg = nios2_parse_reg (regname, regtype); + + if (!reg) + break; + regno = reg->index; + + /* Make sure registers are listed in proper sequence. */ + if (last >= 0) + { + if (regno == last) + { + as_bad ("duplicate register %s\n", reg->name); + return 0; + } + else if (dir == 0) + dir = (regno < last ? -1 : 1); + else if ((dir > 0 && regno < last) + || (dir < 0 && regno > last) + || (op->match == MATCH_R2_PUSH_N + && ! ((last == 31 && regno == 28) + || (last == 31 && regno <= 23) + || (last == 28 && regno <= 23) + || (regno < 23 && regno == last - 1))) + || (op->match == MATCH_R2_POP_N + && ! ((regno == 31 && last == 28) + || (regno == 31 && last <= 23) + || (regno == 28 && last <= 23) + || (last < 23 && last == regno - 1)))) + { + as_bad ("invalid register order"); + return 0; + } + } + + mask |= 1 << regno; + last = regno; + } + + /* Check that all ldwm/stwm regs belong to the same set. */ + if ((op->match == MATCH_R2_LDWM || op->match == MATCH_R2_STWM) + && (mask & 0x00003ffc) && (mask & 0x90ffc000)) + { + as_bad ("invalid register set in reglist"); + return 0; + } + + /* Check that push.n/pop.n regs include RA. */ + if ((op->match == MATCH_R2_PUSH_N || op->match == MATCH_R2_POP_N) + && ((mask & 0x80000000) == 0)) + { + as_bad ("reglist must include ra (r31)"); + return 0; + } + + /* Check that there is at least one register in the set. */ + if (!mask) + { + as_bad ("reglist must include at least one register"); + return 0; + } + + /* OK, reglist passed validation. */ + nios2_reglist_mask = mask; + nios2_reglist_dir = dir; + return mask; +} + +/* This function parses the base register and options used by the ldwm/stwm + instructions. Returns the base register and sets the option arguments + accordingly. On failure, returns NULL. */ +static struct nios2_reg * +nios2_parse_base_register (char *str, int *direction, int *writeback, int *ret) +{ + char *regname; + struct nios2_reg *reg; + + *direction = 0; + *writeback = 0; + *ret = 0; + + /* Check for --. */ + if (strncmp (str, "--", 2) == 0) + { + str += 2; + *direction -= 1; + } + + /* Extract the base register. */ + if (*str != '(') + { + as_bad ("expected '(' before base register"); + return NULL; + } + str++; + regname = str; + str = strchr (str, ')'); + if (!str) + { + as_bad ("expected ')' after base register"); + return NULL; + } + *str = '\0'; + str++; + reg = nios2_parse_reg (regname, REG_NORMAL); + if (reg == NULL) + return NULL; + + /* Check for ++. */ + if (strncmp (str, "++", 2) == 0) + { + str += 2; + *direction += 1; + } + + /* Ensure that either -- or ++ is specified, but not both. */ + if (*direction == 0) + { + as_bad ("invalid base register syntax"); + return NULL;; + } + + /* Check for options. The tokenizer has replaced commas with spaces. */ + while (*str) + { + while (*str == ' ') + str++; + if (strncmp (str, "writeback", 9) == 0) + { + *writeback = 1; + str += 9; + } + else if (strncmp (str, "ret", 3) == 0) + { + *ret = 1; + str += 3; + } + else if (*str) + { + as_bad ("invalid option syntax"); + return NULL; + } + } + + return reg; +} + + /* The various nios2_assemble_* functions call this function to generate an expression from a string representing an expression. It then tries to evaluate the expression, and if it can, returns its value. @@ -1398,13 +1754,14 @@ nios2_parse_reg (const char *token, unsigned long regtype) static unsigned long nios2_assemble_expression (const char *exprstr, nios2_insn_infoS *insn, - bfd_reloc_code_real_type reloc_type, + bfd_reloc_code_real_type orig_reloc_type, unsigned int pcrel) { nios2_insn_relocS *reloc; char *saved_line_ptr; - unsigned short value; + unsigned long value = 0; int i; + bfd_reloc_code_real_type reloc_type = orig_reloc_type; gas_assert (exprstr != NULL); gas_assert (insn != NULL); @@ -1431,6 +1788,25 @@ nios2_assemble_expression (const char *exprstr, break; } + /* No relocation allowed; we must have a constant expression. */ + if (orig_reloc_type == BFD_RELOC_NONE) + { + expressionS exp; + + /* Parse the expression string. */ + saved_line_ptr = input_line_pointer; + input_line_pointer = (char *) exprstr; + expression (&exp); + input_line_pointer = saved_line_ptr; + + /* If we don't have a constant, give an error. */ + if (reloc_type != orig_reloc_type || exp.X_op != O_constant) + as_bad (_("expression must be constant")); + else + value = exp.X_add_number; + return (unsigned long) value; + } + /* We potentially have a relocation. */ reloc = nios2_insn_reloc_new (reloc_type, pcrel); reloc->reloc_next = insn->insn_reloc; @@ -1445,7 +1821,6 @@ nios2_assemble_expression (const char *exprstr, /* This is redundant as the fixup will put this into the instruction, but it is included here so that self-test mode (-r) works. */ - value = 0; if (nios2_mode == NIOS2_MODE_TEST && reloc->reloc_expression.X_op == O_constant) value = reloc->reloc_expression.X_add_number; @@ -1453,8 +1828,29 @@ nios2_assemble_expression (const char *exprstr, return (unsigned long) value; } +/* Encode a 3-bit register number, giving an error if this is not possible. */ +static unsigned int +nios2_assemble_reg3 (const char *token) +{ + struct nios2_reg *reg = nios2_parse_reg (token, REG_3BIT); + int j; + + if (reg == NULL) + return 0; + + for (j = 0; j < nios2_num_r2_reg3_mappings; j++) + if (nios2_r2_reg3_mappings[j] == reg->index) + return j; + + /* Should never get here if we passed validation. */ + as_bad (_("invalid register %s"), token); + return 0; +} /* Argument assemble functions. */ + + +/* Control register index. */ static void nios2_assemble_arg_c (const char *token, nios2_insn_infoS *insn) { @@ -1469,11 +1865,15 @@ nios2_assemble_arg_c (const char *token, nios2_insn_infoS *insn) case iw_r_type: insn->insn_code |= SET_IW_R_IMM5 (reg->index); break; + case iw_F3X6L5_type: + insn->insn_code |= SET_IW_F3X6L5_IMM5 (reg->index); + break; default: bad_opcode (op); } } +/* Destination register. */ static void nios2_assemble_arg_d (const char *token, nios2_insn_infoS *insn) { @@ -1481,7 +1881,7 @@ nios2_assemble_arg_d (const char *token, nios2_insn_infoS *insn) unsigned long regtype = REG_NORMAL; struct nios2_reg *reg; - if (op->format == iw_custom_type) + if (op->format == iw_custom_type || op->format == iw_F3X8_type) regtype |= REG_COPROCESSOR; reg = nios2_parse_reg (token, regtype); if (reg == NULL) @@ -1499,11 +1899,26 @@ nios2_assemble_arg_d (const char *token, nios2_insn_infoS *insn) else insn->insn_code |= SET_IW_CUSTOM_READC (1); break; + case iw_F3X6L5_type: + case iw_F3X6_type: + insn->insn_code |= SET_IW_F3X6L5_C (reg->index); + break; + case iw_F3X8_type: + insn->insn_code |= SET_IW_F3X8_C (reg->index); + if (reg->regtype & REG_COPROCESSOR) + insn->insn_code |= SET_IW_F3X8_READC (0); + else + insn->insn_code |= SET_IW_F3X8_READC (1); + break; + case iw_F2_type: + insn->insn_code |= SET_IW_F2_B (reg->index); + break; default: bad_opcode (op); } } +/* Source register 1. */ static void nios2_assemble_arg_s (const char *token, nios2_insn_infoS *insn) { @@ -1511,7 +1926,7 @@ nios2_assemble_arg_s (const char *token, nios2_insn_infoS *insn) unsigned long regtype = REG_NORMAL; struct nios2_reg *reg; - if (op->format == iw_custom_type) + if (op->format == iw_custom_type || op->format == iw_F3X8_type) regtype |= REG_COPROCESSOR; reg = nios2_parse_reg (token, regtype); if (reg == NULL) @@ -1520,6 +1935,8 @@ nios2_assemble_arg_s (const char *token, nios2_insn_infoS *insn) switch (op->format) { case iw_r_type: + if (op->match == MATCH_R1_JMP && reg->index == 31) + as_bad (_("r31 cannot be used with jmp; use ret instead")); insn->insn_code |= SET_IW_R_A (reg->index); break; case iw_i_type: @@ -1532,11 +1949,53 @@ nios2_assemble_arg_s (const char *token, nios2_insn_infoS *insn) else insn->insn_code |= SET_IW_CUSTOM_READA (1); break; + case iw_F2I16_type: + insn->insn_code |= SET_IW_F2I16_A (reg->index); + break; + case iw_F2X4I12_type: + insn->insn_code |= SET_IW_F2X4I12_A (reg->index); + break; + case iw_F1X4I12_type: + insn->insn_code |= SET_IW_F1X4I12_A (reg->index); + break; + case iw_F1X4L17_type: + insn->insn_code |= SET_IW_F1X4L17_A (reg->index); + break; + case iw_F3X6L5_type: + case iw_F3X6_type: + if (op->match == MATCH_R2_JMP && reg->index == 31) + as_bad (_("r31 cannot be used with jmp; use ret instead")); + insn->insn_code |= SET_IW_F3X6L5_A (reg->index); + break; + case iw_F2X6L10_type: + insn->insn_code |= SET_IW_F2X6L10_A (reg->index); + break; + case iw_F3X8_type: + insn->insn_code |= SET_IW_F3X8_A (reg->index); + if (reg->regtype & REG_COPROCESSOR) + insn->insn_code |= SET_IW_F3X8_READA (0); + else + insn->insn_code |= SET_IW_F3X8_READA (1); + break; + case iw_F1X1_type: + if (op->match == MATCH_R2_JMPR_N && reg->index == 31) + as_bad (_("r31 cannot be used with jmpr.n; use ret.n instead")); + insn->insn_code |= SET_IW_F1X1_A (reg->index); + break; + case iw_F1I5_type: + /* Implicit stack pointer reference. */ + if (reg->index != 27) + as_bad (_("invalid register %s"), token); + break; + case iw_F2_type: + insn->insn_code |= SET_IW_F2_A (reg->index); + break; default: bad_opcode (op); } } +/* Source register 2. */ static void nios2_assemble_arg_t (const char *token, nios2_insn_infoS *insn) { @@ -1544,7 +2003,7 @@ nios2_assemble_arg_t (const char *token, nios2_insn_infoS *insn) unsigned long regtype = REG_NORMAL; struct nios2_reg *reg; - if (op->format == iw_custom_type) + if (op->format == iw_custom_type || op->format == iw_F3X8_type) regtype |= REG_COPROCESSOR; reg = nios2_parse_reg (token, regtype); if (reg == NULL) @@ -1565,11 +2024,147 @@ nios2_assemble_arg_t (const char *token, nios2_insn_infoS *insn) else insn->insn_code |= SET_IW_CUSTOM_READB (1); break; + case iw_F2I16_type: + insn->insn_code |= SET_IW_F2I16_B (reg->index); + break; + case iw_F2X4I12_type: + insn->insn_code |= SET_IW_F2X4I12_B (reg->index); + break; + case iw_F3X6L5_type: + case iw_F3X6_type: + insn->insn_code |= SET_IW_F3X6L5_B (reg->index); + break; + case iw_F2X6L10_type: + insn->insn_code |= SET_IW_F2X6L10_B (reg->index); + break; + case iw_F3X8_type: + insn->insn_code |= SET_IW_F3X8_B (reg->index); + if (reg->regtype & REG_COPROCESSOR) + insn->insn_code |= SET_IW_F3X8_READB (0); + else + insn->insn_code |= SET_IW_F3X8_READB (1); + break; + case iw_F1I5_type: + insn->insn_code |= SET_IW_F1I5_B (reg->index); + break; + case iw_F2_type: + insn->insn_code |= SET_IW_F2_B (reg->index); + break; + case iw_T1X1I6_type: + /* Implicit zero register reference. */ + if (reg->index != 0) + as_bad (_("invalid register %s"), token); + break; + + default: + bad_opcode (op); + } +} + +/* Destination register w/3-bit encoding. */ +static void +nios2_assemble_arg_D (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + int reg = nios2_assemble_reg3 (token); + + switch (op->format) + { + case iw_T1I7_type: + insn->insn_code |= SET_IW_T1I7_A3 (reg); + break; + case iw_T2X1L3_type: + insn->insn_code |= SET_IW_T2X1L3_B3 (reg); + break; + case iw_T2X1I3_type: + insn->insn_code |= SET_IW_T2X1I3_B3 (reg); + break; + case iw_T3X1_type: + insn->insn_code |= SET_IW_T3X1_C3 (reg); + break; + case iw_T2X3_type: + /* Some instructions using this encoding take 3 register arguments, + requiring the destination register to be the same as the first + source register. */ + if (op->num_args == 3) + insn->insn_code |= SET_IW_T2X3_A3 (reg); + else + insn->insn_code |= SET_IW_T2X3_B3 (reg); + break; + default: + bad_opcode (op); + } +} + +/* Source register w/3-bit encoding. */ +static void +nios2_assemble_arg_S (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + int reg = nios2_assemble_reg3 (token); + + switch (op->format) + { + case iw_T1I7_type: + insn->insn_code |= SET_IW_T1I7_A3 (reg); + break; + case iw_T2I4_type: + insn->insn_code |= SET_IW_T2I4_A3 (reg); + break; + case iw_T2X1L3_type: + insn->insn_code |= SET_IW_T2X1L3_A3 (reg); + break; + case iw_T2X1I3_type: + insn->insn_code |= SET_IW_T2X1I3_A3 (reg); + break; + case iw_T3X1_type: + insn->insn_code |= SET_IW_T3X1_A3 (reg); + break; + case iw_T2X3_type: + /* Some instructions using this encoding take 3 register arguments, + requiring the destination register to be the same as the first + source register. */ + if (op->num_args == 3) + { + int dreg = GET_IW_T2X3_A3 (insn->insn_code); + if (dreg != reg) + as_bad ("source and destination registers must be the same"); + } + else + insn->insn_code |= SET_IW_T2X3_A3 (reg); + break; + case iw_T1X1I6_type: + insn->insn_code |= SET_IW_T1X1I6_A3 (reg); + break; + default: + bad_opcode (op); + } +} + +/* Source register 2 w/3-bit encoding. */ +static void +nios2_assemble_arg_T (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + int reg = nios2_assemble_reg3 (token); + + switch (op->format) + { + case iw_T2I4_type: + insn->insn_code |= SET_IW_T2I4_B3 (reg); + break; + case iw_T3X1_type: + insn->insn_code |= SET_IW_T3X1_B3 (reg); + break; + case iw_T2X3_type: + insn->insn_code |= SET_IW_T2X3_B3 (reg); + break; default: bad_opcode (op); } } +/* 16-bit signed immediate. */ static void nios2_assemble_arg_i (const char *token, nios2_insn_infoS *insn) { @@ -1583,11 +2178,41 @@ nios2_assemble_arg_i (const char *token, nios2_insn_infoS *insn) BFD_RELOC_NIOS2_S16, 0); insn->constant_bits |= SET_IW_I_IMM16 (val); break; + case iw_F2I16_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_S16, 0); + insn->constant_bits |= SET_IW_F2I16_IMM16 (val); + break; + default: + bad_opcode (op); + } +} + +/* 12-bit signed immediate. */ +static void +nios2_assemble_arg_I (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_F2X4I12_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_S12, 0); + insn->constant_bits |= SET_IW_F2X4I12_IMM12 (val); + break; + case iw_F1X4I12_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_S12, 0); + insn->constant_bits |= SET_IW_F2X4I12_IMM12 (val); + break; default: bad_opcode (op); } } +/* 16-bit unsigned immediate. */ static void nios2_assemble_arg_u (const char *token, nios2_insn_infoS *insn) { @@ -1601,11 +2226,128 @@ nios2_assemble_arg_u (const char *token, nios2_insn_infoS *insn) BFD_RELOC_NIOS2_U16, 0); insn->constant_bits |= SET_IW_I_IMM16 (val); break; + case iw_F2I16_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_U16, 0); + insn->constant_bits |= SET_IW_F2I16_IMM16 (val); + break; + default: + bad_opcode (op); + } +} + +/* 7-bit unsigned immediate with 2-bit shift. */ +static void +nios2_assemble_arg_U (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T1I7_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T1I7_2, 0); + insn->constant_bits |= SET_IW_T1I7_IMM7 (val >> 2); + break; + case iw_X1I7_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_X1I7_2, 0); + insn->constant_bits |= SET_IW_X1I7_IMM7 (val >> 2); + break; + default: + bad_opcode (op); + } +} + +/* 5-bit unsigned immediate with 2-bit shift. */ +static void +nios2_assemble_arg_V (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_F1I5_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_F1I5_2, 0); + insn->constant_bits |= SET_IW_F1I5_IMM5 (val >> 2); + break; + default: + bad_opcode (op); + } +} + +/* 4-bit unsigned immediate with 2-bit shift. */ +static void +nios2_assemble_arg_W (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T2I4_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T2I4_2, 0); + insn->constant_bits |= SET_IW_T2I4_IMM4 (val >> 2); + break; + case iw_L5I4X1_type: + /* This argument is optional for push.n/pop.n, and defaults to + zero if unspecified. */ + if (token == NULL) + return; + + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_L5I4X1, 0); + insn->constant_bits |= SET_IW_L5I4X1_IMM4 (val >> 2); + break; + default: + bad_opcode (op); + } +} + +/* 4-bit unsigned immediate with 1-bit shift. */ +static void +nios2_assemble_arg_X (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T2I4_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T2I4_1, 0); + insn->constant_bits |= SET_IW_T2I4_IMM4 (val >> 1); + break; default: bad_opcode (op); } } +/* 4-bit unsigned immediate without shift. */ +static void +nios2_assemble_arg_Y (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T2I4_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T2I4, 0); + insn->constant_bits |= SET_IW_T2I4_IMM4 (val); + break; + default: + bad_opcode (op); + } +} + + +/* 16-bit signed immediate address offset. */ static void nios2_assemble_arg_o (const char *token, nios2_insn_infoS *insn) { @@ -1619,11 +2361,55 @@ nios2_assemble_arg_o (const char *token, nios2_insn_infoS *insn) BFD_RELOC_16_PCREL, 1); insn->constant_bits |= SET_IW_I_IMM16 (val); break; + case iw_F2I16_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_16_PCREL, 1); + insn->constant_bits |= SET_IW_F2I16_IMM16 (val); + break; + default: + bad_opcode (op); + } +} + +/* 10-bit signed address offset with 1-bit shift. */ +static void +nios2_assemble_arg_O (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_I10_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_I10_1_PCREL, 1); + insn->constant_bits |= SET_IW_I10_IMM10 (val >> 1); + break; + default: + bad_opcode (op); + } +} + +/* 7-bit signed address offset with 1-bit shift. */ +static void +nios2_assemble_arg_P (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T1I7_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T1I7_1_PCREL, 1); + insn->constant_bits |= SET_IW_T1I7_IMM7 (val >> 1); + break; default: bad_opcode (op); } } +/* 5-bit unsigned immediate. */ static void nios2_assemble_arg_j (const char *token, nios2_insn_infoS *insn) { @@ -1637,11 +2423,66 @@ nios2_assemble_arg_j (const char *token, nios2_insn_infoS *insn) BFD_RELOC_NIOS2_IMM5, 0); insn->constant_bits |= SET_IW_R_IMM5 (val); break; + case iw_F3X6L5_type: + if (op->match == MATCH_R2_ENI) + /* Value must be constant 0 or 1. */ + { + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + if (val != 0 && val != 1) + as_bad ("invalid eni argument %u", val); + insn->insn_code |= SET_IW_F3X6L5_IMM5 (val); + } + else + { + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_IMM5, 0); + insn->constant_bits |= SET_IW_F3X6L5_IMM5 (val); + } + break; + case iw_F2X6L10_type: + /* Only constant expression without relocation permitted for + bit position. */ + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + if (val > 31) + as_bad ("invalid bit position %u", val); + insn->insn_code |= SET_IW_F2X6L10_MSB (val); + break; + case iw_X2L5_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_X2L5, 0); + insn->constant_bits |= SET_IW_X2L5_IMM5 (val); + break; default: bad_opcode (op); } } +/* Second 5-bit unsigned immediate field. */ +static void +nios2_assemble_arg_k (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_F2X6L10_type: + /* Only constant expression without relocation permitted for + bit position. */ + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NONE, 0); + if (val > 31) + as_bad ("invalid bit position %u", val); + else if (GET_IW_F2X6L10_MSB (insn->insn_code) < val) + as_bad ("MSB must be greater than or equal to LSB"); + insn->insn_code |= SET_IW_F2X6L10_LSB (val); + break; + default: + bad_opcode (op); + } +} + +/* 8-bit unsigned immediate. */ static void nios2_assemble_arg_l (const char *token, nios2_insn_infoS *insn) { @@ -1655,11 +2496,17 @@ nios2_assemble_arg_l (const char *token, nios2_insn_infoS *insn) BFD_RELOC_NIOS2_IMM8, 0); insn->constant_bits |= SET_IW_CUSTOM_N (val); break; + case iw_F3X8_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_IMM8, 0); + insn->constant_bits |= SET_IW_F3X8_N (val); + break; default: bad_opcode (op); } } +/* 26-bit unsigned immediate. */ static void nios2_assemble_arg_m (const char *token, nios2_insn_infoS *insn) { @@ -1676,6 +2523,277 @@ nios2_assemble_arg_m (const char *token, nios2_insn_infoS *insn) 0); insn->constant_bits |= SET_IW_J_IMM26 (val); break; + case iw_L26_type: + val = nios2_assemble_expression (token, insn, + (nios2_as_options.noat + ? BFD_RELOC_NIOS2_CALL26_NOAT + : BFD_RELOC_NIOS2_CALL26), + 0); + insn->constant_bits |= SET_IW_L26_IMM26 (val); + break; + default: + bad_opcode (op); + } +} + +/* 6-bit unsigned immediate with no shifting. */ +static void +nios2_assemble_arg_M (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T1X1I6_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T1X1I6, 0); + insn->constant_bits |= SET_IW_T1X1I6_IMM6 (val); + break; + default: + bad_opcode (op); + } +} + +/* 6-bit unsigned immediate with 2-bit shift. */ +static void +nios2_assemble_arg_N (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_T1X1I6_type: + val = nios2_assemble_expression (token, insn, + BFD_RELOC_NIOS2_R2_T1X1I6_2, 0); + insn->constant_bits |= SET_IW_T1X1I6_IMM6 (val >> 2); + break; + default: + bad_opcode (op); + } +} + + +/* Encoded enumeration for addi.n/subi.n. */ +static void +nios2_assemble_arg_e (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + int i; + + switch (op->format) + { + case iw_T2X1I3_type: + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + for (i = 0; i < nios2_num_r2_asi_n_mappings; i++) + if (val == nios2_r2_asi_n_mappings[i]) + break; + if (i == nios2_num_r2_asi_n_mappings) + { + as_bad (_("Invalid constant operand %s"), token); + return; + } + insn->insn_code |= SET_IW_T2X1I3_IMM3 ((unsigned)i); + break; + default: + bad_opcode (op); + } +} + +/* Encoded enumeration for slli.n/srli.n. */ +static void +nios2_assemble_arg_f (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + int i; + + switch (op->format) + { + case iw_T2X1L3_type: + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + for (i = 0; i < nios2_num_r2_shi_n_mappings; i++) + if (val == nios2_r2_shi_n_mappings[i]) + break; + if (i == nios2_num_r2_shi_n_mappings) + { + as_bad (_("Invalid constant operand %s"), token); + return; + } + insn->insn_code |= SET_IW_T2X1L3_SHAMT ((unsigned)i); + break; + default: + bad_opcode (op); + } +} + +/* Encoded enumeration for andi.n. */ +static void +nios2_assemble_arg_g (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + int i; + + switch (op->format) + { + case iw_T2I4_type: + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + for (i = 0; i < nios2_num_r2_andi_n_mappings; i++) + if (val == nios2_r2_andi_n_mappings[i]) + break; + if (i == nios2_num_r2_andi_n_mappings) + { + as_bad (_("Invalid constant operand %s"), token); + return; + } + insn->insn_code |= SET_IW_T2I4_IMM4 ((unsigned)i); + break; + default: + bad_opcode (op); + } +} + +/* Encoded enumeration for movi.n. */ +static void +nios2_assemble_arg_h (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + int i; + + switch (op->format) + { + case iw_T1I7_type: + val = nios2_assemble_expression (token, insn, BFD_RELOC_NONE, 0); + i = (signed) val; + if ((signed) i == -1) + val = 127; + else if (i == -2) + val = 126; + else if (i == 0xff) + val = 125; + else if (i < 0 || i > 125) + { + as_bad (_("Invalid constant operand %s"), token); + return; + } + insn->insn_code |= SET_IW_T1I7_IMM7 (val); + break; + default: + bad_opcode (op); + } +} + +/* Encoded REGMASK for ldwm/stwm or push.n/pop.n. */ +static void +nios2_assemble_arg_R (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned long mask; + char *buf = strdup (token); + unsigned long reglist = nios2_parse_reglist (buf, op); + free (buf); + + if (reglist == 0) + return; + + switch (op->format) + { + case iw_F1X4L17_type: + /* Encoding for ldwm/stwm. */ + if (reglist & 0x00003ffc) + mask = reglist >> 2; + else + { + insn->insn_code |= SET_IW_F1X4L17_RS (1); + mask = (reglist & 0x00ffc000) >> 14; + if (reglist & (1 << 28)) + mask |= 1 << 10; + if (reglist & (1 << 31)) + mask |= 1 << 11; + } + insn->insn_code |= SET_IW_F1X4L17_REGMASK (mask); + break; + + case iw_L5I4X1_type: + /* Encoding for push.n/pop.n. */ + if (reglist & (1 << 28)) + insn->insn_code |= SET_IW_L5I4X1_FP (1); + mask = reglist & 0x00ff0000; + if (mask) + { + int i; + + for (i = 0; i < nios2_num_r2_reg_range_mappings; i++) + if (nios2_r2_reg_range_mappings[i] == mask) + break; + if (i == nios2_num_r2_reg_range_mappings) + { + as_bad ("invalid reglist"); + return; + } + insn->insn_code |= SET_IW_L5I4X1_REGRANGE (i); + insn->insn_code |= SET_IW_L5I4X1_CS (1); + } + break; + + default: + bad_opcode (op); + } +} + +/* Base register for ldwm/stwm. */ +static void +nios2_assemble_arg_B (const char *token, nios2_insn_infoS *insn) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + int direction, writeback, ret; + char *str = strdup (token); + struct nios2_reg *reg + = nios2_parse_base_register (str, &direction, &writeback, &ret); + + free (str); + if (!reg) + return; + + switch (op->format) + { + case iw_F1X4L17_type: + /* For ldwm, check to see if the base register is already inside the + register list. */ + if (op->match == MATCH_R2_LDWM + && (nios2_reglist_mask & (1 << reg->index))) + { + as_bad ("invalid base register; %s is inside the reglist", reg->name); + return; + } + + /* For stwm, ret option is not allowed. */ + if (op->match == MATCH_R2_STWM && ret) + { + as_bad ("invalid option syntax"); + return; + } + + /* Check that the direction matches the ordering of the reglist. */ + if (nios2_reglist_dir && direction != nios2_reglist_dir) + { + as_bad ("reglist order does not match increment/decrement mode"); + return; + } + + insn->insn_code |= SET_IW_F1X4L17_A (reg->index); + if (direction > 0) + insn->insn_code |= SET_IW_F1X4L17_ID (1); + if (writeback) + insn->insn_code |= SET_IW_F1X4L17_WB (1); + if (ret) + insn->insn_code |= SET_IW_F1X4L17_PC (1); + break; + default: bad_opcode (op); } @@ -1723,22 +2841,70 @@ nios2_assemble_args (nios2_insn_infoS *insn) nios2_assemble_arg_t (insn->insn_tokens[tokidx++], insn); break; + case 'D': + nios2_assemble_arg_D (insn->insn_tokens[tokidx++], insn); + break; + + case 'S': + nios2_assemble_arg_S (insn->insn_tokens[tokidx++], insn); + break; + + case 'T': + nios2_assemble_arg_T (insn->insn_tokens[tokidx++], insn); + break; + case 'i': nios2_assemble_arg_i (insn->insn_tokens[tokidx++], insn); break; + case 'I': + nios2_assemble_arg_I (insn->insn_tokens[tokidx++], insn); + break; + case 'u': nios2_assemble_arg_u (insn->insn_tokens[tokidx++], insn); break; + case 'U': + nios2_assemble_arg_U (insn->insn_tokens[tokidx++], insn); + break; + + case 'V': + nios2_assemble_arg_V (insn->insn_tokens[tokidx++], insn); + break; + + case 'W': + nios2_assemble_arg_W (insn->insn_tokens[tokidx++], insn); + break; + + case 'X': + nios2_assemble_arg_X (insn->insn_tokens[tokidx++], insn); + break; + + case 'Y': + nios2_assemble_arg_Y (insn->insn_tokens[tokidx++], insn); + break; + case 'o': nios2_assemble_arg_o (insn->insn_tokens[tokidx++], insn); break; + case 'O': + nios2_assemble_arg_O (insn->insn_tokens[tokidx++], insn); + break; + + case 'P': + nios2_assemble_arg_P (insn->insn_tokens[tokidx++], insn); + break; + case 'j': nios2_assemble_arg_j (insn->insn_tokens[tokidx++], insn); break; + case 'k': + nios2_assemble_arg_k (insn->insn_tokens[tokidx++], insn); + break; + case 'l': nios2_assemble_arg_l (insn->insn_tokens[tokidx++], insn); break; @@ -1746,7 +2912,39 @@ nios2_assemble_args (nios2_insn_infoS *insn) case 'm': nios2_assemble_arg_m (insn->insn_tokens[tokidx++], insn); break; + + case 'M': + nios2_assemble_arg_M (insn->insn_tokens[tokidx++], insn); + break; + + case 'N': + nios2_assemble_arg_N (insn->insn_tokens[tokidx++], insn); + break; + + case 'e': + nios2_assemble_arg_e (insn->insn_tokens[tokidx++], insn); + break; + + case 'f': + nios2_assemble_arg_f (insn->insn_tokens[tokidx++], insn); + break; + case 'g': + nios2_assemble_arg_g (insn->insn_tokens[tokidx++], insn); + break; + + case 'h': + nios2_assemble_arg_h (insn->insn_tokens[tokidx++], insn); + break; + + case 'R': + nios2_assemble_arg_R (insn->insn_tokens[tokidx++], insn); + break; + + case 'B': + nios2_assemble_arg_B (insn->insn_tokens[tokidx++], insn); + break; + default: bad_opcode (op); break; @@ -1775,6 +2973,9 @@ nios2_consume_arg (char *argstr, const char *parsestr) case 'd': case 's': case 't': + case 'D': + case 'S': + case 'T': break; case 'i': @@ -1798,11 +2999,60 @@ nios2_consume_arg (char *argstr, const char *parsestr) break; case 'm': case 'j': + case 'k': case 'l': + case 'I': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'O': + case 'P': + case 'e': + case 'f': + case 'g': + case 'h': + case 'M': + case 'N': + /* We can't have %hi, %lo or %hiadj here. */ if (*argstr == '%') as_bad (_("badly formed expression near %s"), argstr); break; + + case 'R': + /* Register list for ldwm/stwm or push.n/pop.n. Replace the commas + in the list with spaces so we don't confuse them with separators. */ + if (*argstr != '{') + { + as_bad ("missing '{' in register list"); + break; + } + for (temp = argstr + 1; *temp; temp++) + { + if (*temp == '}') + break; + else if (*temp == ',') + *temp = ' '; + } + if (!*temp) + { + as_bad ("missing '}' in register list"); + break; + } + break; + + case 'B': + /* Base register and options for ldwm/stwm. This is the final argument + and consumes the rest of the argument string; replace commas + with spaces so that the token splitter doesn't think they are + separate arguments. */ + for (temp = argstr; *temp; temp++) + if (*temp == ',') + *temp = ' '; + break; + case 'o': case 'E': break; @@ -1839,7 +3089,6 @@ nios2_consume_separator (char *argstr, const char *separator) return p; } - /* The principal argument parsing function which takes a string argstr representing the instruction arguments for insn, and extracts the argument tokens matching parsestr into parsed_args. */ @@ -2022,7 +3271,7 @@ nios2_translate_pseudo_insn (nios2_insn_infoS *insn) else /* we cannot recover from this. */ as_fatal (_("unrecognized pseudo-instruction %s"), - ps_insn->pseudo_insn); + insn->insn_nios2_opcode->name); return ps_insn; } @@ -2056,7 +3305,8 @@ const nios2_ps_insn_infoS nios2_ps_insn_info_structs[] = { {"cmpgtui", "cmpgeui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, {"cmplei", "cmplti", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, {"cmpleui", "cmpltui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg}, - {"subi", "addi", nios2_negate_arg, "", 0, 3, nios2_free_arg} + {"subi", "addi", nios2_negate_arg, "", 0, 3, nios2_free_arg}, + {"nop.n", "mov.n", nios2_append_arg, "zero", 2, 1, NULL} /* Add further pseudo-ops here. */ }; @@ -2101,6 +3351,7 @@ output_ubranch (nios2_insn_infoS *insn) symbolS *symp = reloc->reloc_expression.X_add_symbol; offsetT offset = reloc->reloc_expression.X_add_number; char *f; + bfd_boolean is_cdx = (insn->insn_nios2_opcode->size == 2); /* Tag dwarf2 debug info to the address at the start of the insn. We must do it before frag_var() below closes off the frag. */ @@ -2111,7 +3362,8 @@ output_ubranch (nios2_insn_infoS *insn) this may generate. */ f = frag_var (rs_machine_dependent, UBRANCH_MAX_SIZE, insn->insn_nios2_opcode->size, - UBRANCH_SUBTYPE (0), symp, offset, NULL); + (is_cdx ? CDX_UBRANCH_SUBTYPE (0) : UBRANCH_SUBTYPE (0)), + symp, offset, NULL); md_number_to_chars (f, insn->insn_code, insn->insn_nios2_opcode->size); @@ -2131,6 +3383,7 @@ output_cbranch (nios2_insn_infoS *insn) symbolS *symp = reloc->reloc_expression.X_add_symbol; offsetT offset = reloc->reloc_expression.X_add_number; char *f; + bfd_boolean is_cdx = (insn->insn_nios2_opcode->size == 2); /* Tag dwarf2 debug info to the address at the start of the insn. We must do it before frag_var() below closes off the frag. */ @@ -2141,7 +3394,8 @@ output_cbranch (nios2_insn_infoS *insn) this may generate. */ f = frag_var (rs_machine_dependent, CBRANCH_MAX_SIZE, insn->insn_nios2_opcode->size, - CBRANCH_SUBTYPE (0), symp, offset, NULL); + (is_cdx ? CDX_CBRANCH_SUBTYPE (0) : CBRANCH_SUBTYPE (0)), + symp, offset, NULL); md_number_to_chars (f, insn->insn_code, insn->insn_nios2_opcode->size); @@ -2162,23 +3416,50 @@ output_call (nios2_insn_infoS *insn) and puts it in the current frag. */ char *f = frag_more (12); nios2_insn_relocS *reloc = insn->insn_reloc; + const struct nios2_opcode *op = insn->insn_nios2_opcode; - md_number_to_chars (f, - (MATCH_R1_ORHI | SET_IW_I_B (AT_REGNUM) - | SET_IW_I_A (0)), - 4); - dwarf2_emit_insn (4); - fix_new_exp (frag_now, f - frag_now->fr_literal, 4, - &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_HI16); - md_number_to_chars (f + 4, - (MATCH_R1_ORI | SET_IW_I_B (AT_REGNUM) - | SET_IW_I_A (AT_REGNUM)), - 4); - dwarf2_emit_insn (4); - fix_new_exp (frag_now, f - frag_now->fr_literal + 4, 4, - &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_LO16); - md_number_to_chars (f + 8, MATCH_R1_CALLR | SET_IW_R_A (AT_REGNUM), 4); - dwarf2_emit_insn (4); + switch (op->format) + { + case iw_j_type: + md_number_to_chars (f, + (MATCH_R1_ORHI | SET_IW_I_B (AT_REGNUM) + | SET_IW_I_A (0)), + 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_HI16); + md_number_to_chars (f + 4, + (MATCH_R1_ORI | SET_IW_I_B (AT_REGNUM) + | SET_IW_I_A (AT_REGNUM)), + 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal + 4, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_LO16); + md_number_to_chars (f + 8, MATCH_R1_CALLR | SET_IW_R_A (AT_REGNUM), 4); + dwarf2_emit_insn (4); + break; + case iw_L26_type: + md_number_to_chars (f, + (MATCH_R2_ORHI | SET_IW_F2I16_B (AT_REGNUM) + | SET_IW_F2I16_A (0)), + 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_HI16); + md_number_to_chars (f + 4, + (MATCH_R2_ORI | SET_IW_F2I16_B (AT_REGNUM) + | SET_IW_F2I16_A (AT_REGNUM)), + 4); + dwarf2_emit_insn (4); + fix_new_exp (frag_now, f - frag_now->fr_literal + 4, 4, + &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_LO16); + md_number_to_chars (f + 8, MATCH_R2_CALLR | SET_IW_F3X6L5_A (AT_REGNUM), + 4); + dwarf2_emit_insn (4); + break; + default: + bad_opcode (op); + } } /* Output a movhi/addi pair for the movia pseudo-op. */ @@ -2189,21 +3470,33 @@ output_movia (nios2_insn_infoS *insn) and puts it in the current frag. */ char *f = frag_more (8); nios2_insn_relocS *reloc = insn->insn_reloc; - unsigned long reg_index = GET_IW_I_B (insn->insn_code); + unsigned long reg, code; + const struct nios2_opcode *op = insn->insn_nios2_opcode; /* If the reloc is NULL, there was an error assembling the movia. */ if (reloc != NULL) { + switch (op->format) + { + case iw_i_type: + reg = GET_IW_I_B (insn->insn_code); + code = MATCH_R1_ADDI | SET_IW_I_A (reg) | SET_IW_I_B (reg); + break; + case iw_F2I16_type: + reg = GET_IW_F2I16_B (insn->insn_code); + code = MATCH_R2_ADDI | SET_IW_F2I16_A (reg) | SET_IW_F2I16_B (reg); + break; + default: + bad_opcode (op); + } + md_number_to_chars (f, insn->insn_code, 4); dwarf2_emit_insn (4); fix_new (frag_now, f - frag_now->fr_literal, 4, reloc->reloc_expression.X_add_symbol, reloc->reloc_expression.X_add_number, 0, BFD_RELOC_NIOS2_HIADJ16); - md_number_to_chars (f + 4, - (MATCH_R1_ADDI | SET_IW_I_A (reg_index) - | SET_IW_I_B (reg_index)), - 4); + md_number_to_chars (f + 4, code, 4); dwarf2_emit_insn (4); fix_new (frag_now, f + 4 - frag_now->fr_literal, 4, reloc->reloc_expression.X_add_symbol, @@ -2387,6 +3680,7 @@ md_begin (void) nios2_current_align_seg = now_seg; nios2_last_label = NULL; nios2_current_align = 0; + nios2_min_align = 2; } @@ -2400,11 +3694,11 @@ md_assemble (char *op_str) nios2_insn_infoS thisinsn; nios2_insn_infoS *insn = &thisinsn; - /* Make sure we are aligned on a 4-byte boundary. */ - if (nios2_current_align < 2) - nios2_align (2, NULL, nios2_last_label); - else if (nios2_current_align > 2) - nios2_current_align = 2; + /* Make sure we are aligned on an appropriate boundary. */ + if (nios2_current_align < nios2_min_align) + nios2_align (nios2_min_align, NULL, nios2_last_label); + else if (nios2_current_align > nios2_min_align) + nios2_current_align = nios2_min_align; nios2_last_label = NULL; /* We don't want to clobber to op_str @@ -2420,6 +3714,11 @@ md_assemble (char *op_str) if (insn->insn_nios2_opcode != NULL) { nios2_ps_insn_infoS *ps_insn = NULL; + + /* Note if we've seen a 16-bit instruction. */ + if (insn->insn_nios2_opcode->size == 2) + nios2_min_align = 1; + /* Set the opcode for the instruction. */ insn->insn_code = insn->insn_nios2_opcode->match; insn->constant_bits = 0; @@ -2667,7 +3966,7 @@ nios2_cons_align (int size) ++log_size; if (subseg_text_p (now_seg)) - pfill = (const char *) &nop; + pfill = (const char *) nop32; else pfill = NULL; |