X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-nios2.c;h=7e2a3985c29cfd4c275328806c3558d4e9407266;hb=5b7c81bd8cc80253a48c7045e4d2c8d2bf178eb9;hp=2fb26b82c4bfc3e19dfc4c9585b6013cebfc62c1;hpb=b90efa5b79ac1524ec260f8eb89d1be37e0219a7;p=binutils-gdb.git diff --git a/gas/config/tc-nios2.c b/gas/config/tc-nios2.c index 2fb26b82c4b..7e2a3985c29 100644 --- a/gas/config/tc-nios2.c +++ b/gas/config/tc-nios2.c @@ -1,5 +1,5 @@ /* Altera Nios II assembler. - Copyright (C) 2012-2015 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Contributed by Nigel Gray (ngray@altera.com). Contributed by Mentor Graphics, Inc. @@ -80,7 +80,9 @@ struct option md_longopts[] = { #define OPTION_EB (OPTION_MD_BASE + 3) {"EB", no_argument, NULL, OPTION_EB}, #define OPTION_EL (OPTION_MD_BASE + 4) - {"EL", no_argument, NULL, OPTION_EL} + {"EL", no_argument, NULL, OPTION_EL}, +#define OPTION_MARCH (OPTION_MD_BASE + 5) + {"march", required_argument, NULL, OPTION_MARCH} }; size_t md_longopts_size = sizeof (md_longopts); @@ -95,18 +97,18 @@ typedef enum } relax_optionT; /* Struct contains all assembler options set with .set. */ -struct +static struct { /* .set noat -> noat = 1 allows assembly code to use at without warning and macro expansions generate a warning. .set at -> noat = 0, assembly code using at warn but macro expansions do not generate warnings. */ - bfd_boolean noat; + bool noat; - /* .set nobreak -> nobreak = 1 allows assembly code to use ba,bt without + /* .set nobreak -> nobreak = 1 allows assembly code to use ba,bt without warning. .set break -> nobreak = 0, assembly code using ba,bt warns. */ - bfd_boolean nobreak; + bool nobreak; /* .cmd line option -relax-all allows all branches and calls to be replaced with longer versions. @@ -115,7 +117,7 @@ struct a section. */ relax_optionT relax; -} nios2_as_options = {FALSE, FALSE, relax_section}; +} nios2_as_options = {false, false, relax_section}; typedef struct nios2_insn_reloc @@ -142,7 +144,7 @@ typedef struct nios2_insn_info /* Constant bits masked into insn_code for self-check mode. */ unsigned long constant_bits; - + /* Pointer to the relevant bit of the opcode table. */ const struct nios2_opcode *insn_nios2_opcode; /* After parsing ptrs to the tokens in the instruction fill this array @@ -182,20 +184,20 @@ typedef struct nios2_ps_insn_info } nios2_ps_insn_infoS; /* Opcode hash table. */ -static struct hash_control *nios2_opcode_hash = NULL; +static htab_t nios2_opcode_hash = NULL; #define nios2_opcode_lookup(NAME) \ - ((struct nios2_opcode *) hash_find (nios2_opcode_hash, (NAME))) + ((struct nios2_opcode *) str_hash_find (nios2_opcode_hash, (NAME))) /* Register hash table. */ -static struct hash_control *nios2_reg_hash = NULL; +static htab_t nios2_reg_hash = NULL; #define nios2_reg_lookup(NAME) \ - ((struct nios2_reg *) hash_find (nios2_reg_hash, (NAME))) + ((struct nios2_reg *) str_hash_find (nios2_reg_hash, (NAME))) /* Pseudo-op hash table. */ -static struct hash_control *nios2_ps_hash = NULL; +static htab_t nios2_ps_hash = NULL; #define nios2_ps_lookup(NAME) \ - ((nios2_ps_insn_infoS *) hash_find (nios2_ps_hash, (NAME))) + ((nios2_ps_insn_infoS *) str_hash_find (nios2_ps_hash, (NAME))) /* The known current alignment of the current section. */ static int nios2_current_align; @@ -204,14 +206,22 @@ static segT nios2_current_align_seg; static int nios2_auto_align_on = 1; /* The last seen label in the current section. This is used to auto-align - labels preceeding instructions. */ + labels preceding 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; #endif +/* The processor architecture value, EF_NIOS2_ARCH_R1 by default. */ +static int nios2_architecture = EF_NIOS2_ARCH_R1; + /** Utility routines. */ /* Function md_chars_to_number takes the sequence of @@ -228,10 +238,10 @@ md_chars_to_number (char *buf, int n) val = 0; if (target_big_endian) for (i = 0; i < n; ++i) - val = val | ((buf[i] & 0xff) << 8 * (n - (i + 1))); + val = val | ((valueT) (buf[i] & 0xff) << 8 * (n - (i + 1))); else for (i = 0; i < n; ++i) - val = val | ((buf[i] & 0xff) << 8 * i); + val = val | ((valueT) (buf[i] & 0xff) << 8 * i); return val; } @@ -253,7 +263,7 @@ md_number_to_chars (char *buf, valueT val, int n) of type TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ -char * +const char * md_atof (int type, char *litP, int *sizeP) { int prec; @@ -316,8 +326,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 @@ -344,16 +358,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); } @@ -367,7 +391,7 @@ nios2_align (int log_size, const char *pfill, symbolS *label) if (label != NULL && !switched_seg_p) { symbolS *sym; - int label_seen = FALSE; + int label_seen = false; struct frag *old_frag; valueT old_value; valueT new_value; @@ -393,7 +417,7 @@ nios2_align (int log_size, const char *pfill, symbolS *label) if (symbol_get_frag (sym) == old_frag && S_GET_VALUE (sym) == old_value) { - label_seen = TRUE; + label_seen = true; symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, new_value); } @@ -464,7 +488,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; @@ -541,29 +565,31 @@ s_nios2_sdata (int ignore ATTRIBUTE_UNUSED) static void s_nios2_set (int equiv) { - char *directive = input_line_pointer; - char delim = get_symbol_end (); + char *save = input_line_pointer; + char *directive; + char delim = get_symbol_name (&directive); char *endline = input_line_pointer; - *endline = delim; + + (void) restore_line_pointer (delim); /* We only want to handle ".set XXX" if the user has tried ".set XXX, YYY" they are not trying a directive. This prevents us from polluting the name space. */ SKIP_WHITESPACE (); - if (is_end_of_line[(unsigned char) *input_line_pointer]) + if (is_end_of_line[(unsigned char) *input_line_pointer]) { - bfd_boolean done = TRUE; + bool done = true; *endline = 0; - + if (!strcmp (directive, "noat")) - nios2_as_options.noat = TRUE; + nios2_as_options.noat = true; else if (!strcmp (directive, "at")) - nios2_as_options.noat = FALSE; + nios2_as_options.noat = false; else if (!strcmp (directive, "nobreak")) - nios2_as_options.nobreak = TRUE; + nios2_as_options.nobreak = true; else if (!strcmp (directive, "break")) - nios2_as_options.nobreak = FALSE; + nios2_as_options.nobreak = false; else if (!strcmp (directive, "norelax")) nios2_as_options.relax = relax_none; else if (!strcmp (directive, "relaxsection")) @@ -571,8 +597,8 @@ s_nios2_set (int equiv) else if (!strcmp (directive, "relaxall")) nios2_as_options.relax = relax_all; else - done = FALSE; - + done = false; + if (done) { *endline = delim; @@ -582,10 +608,9 @@ s_nios2_set (int equiv) } /* If we fall through to here, either we have ".set XXX, YYY" - or we have ".set XXX" where XXX is unknown or we have + or we have ".set XXX" where XXX is unknown or we have a syntax error. */ - input_line_pointer = directive; - *endline = delim; + input_line_pointer = save; s_set (equiv); } @@ -627,7 +652,7 @@ const pseudo_typeS md_pseudo_table[] = { Nios II PC-relative branch instructions only support 16-bit offsets. And, there's no good way to add a 32-bit constant to the PC without using two registers. - + To deal with this, for the pc-relative relaxation mode we convert br label into a series of 16-bit adds, like: @@ -663,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 @@ -679,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. @@ -687,14 +717,18 @@ 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 - nstructions besides the addis, conditional branches require 3. */ +/* For the -relax-section mode, unconditional branches require 2 extra + instructions besides the addis, conditional branches require 3. */ #define UBRANCH_ADDIS_TO_SIZE(N) (((N) + 2) * 4) #define CBRANCH_ADDIS_TO_SIZE(N) (((N) + 3) * 4) @@ -724,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)) @@ -755,6 +789,7 @@ nios2_relax_frag (segT segment, fragS *fragp, long stretch) fragS *sym_frag = symbol_get_frag (symbolp); offsetT offset; int n; + bool is_cdx = false; target += S_GET_VALUE (symbolp); @@ -771,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) @@ -810,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); @@ -835,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; + bool 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; } @@ -865,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; @@ -903,11 +1013,14 @@ 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; - + /* We need to know whether the offset is positive or negative. */ target += S_GET_VALUE (symbolp); offset = target - fragp->fr_address - fragp->fr_fix; @@ -915,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); @@ -931,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; @@ -940,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; @@ -959,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; } @@ -968,11 +1100,18 @@ md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED, /** Fixups and overflow checking. */ /* Check a fixup for overflow. */ -static bfd_boolean +static bool 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) @@ -982,31 +1121,31 @@ nios2_check_overflow (valueT fixup, reloc_howto_type *howto) case complain_overflow_bitfield: if ((fixup >> howto->bitsize) != 0 && ((signed) fixup >> howto->bitsize) != -1) - return TRUE; + return true; break; case complain_overflow_signed: if ((fixup & 0x80000000) > 0) { /* Check for negative overflow. */ - if ((signed) fixup < ((signed) 0x80000000 >> howto->bitsize)) - return TRUE; + if ((signed) fixup < (signed) (~0U << (howto->bitsize - 1))) + return true; } else { /* Check for positive overflow. */ if (fixup >= ((unsigned) 1 << (howto->bitsize - 1))) - return TRUE; + return true; } break; case complain_overflow_unsigned: if ((fixup >> howto->bitsize) != 0) - return TRUE; + return true; break; default: as_bad (_("error checking for overflow - broken assembler")); break; } - return FALSE; + return false; } /* Emit diagnostic for fixup overflow. */ @@ -1044,20 +1183,30 @@ nios2_diagnose_overflow (valueT fixup, reloc_howto_type *howto, = ((fixP->fx_frag->fr_address + fixP->fx_where) & 0xf0000000); range_max = range_min + 0x0fffffff; address = fixup | range_min; - + as_bad_where (fixP->fx_file, fixP->fx_line, _("call target address 0x%08x out of range 0x%08x to 0x%08x"), 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, @@ -1074,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"), @@ -1131,13 +1285,26 @@ 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. */ )); if (fixP->fx_r_type == BFD_RELOC_64) { /* We may reach here due to .8byte directives, but we never output - BFD_RELOC_64; it must be resolved. */ + BFD_RELOC_64; it must be resolved. */ if (fixP->fx_addsy != NULL) as_bad_where (fixP->fx_file, fixP->fx_line, _("cannot create 64-bit relocation")); @@ -1202,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; @@ -1214,7 +1384,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) nios2_diagnose_overflow (fixup, howto, fixP, value); /* Apply the right shift. */ - fixup = ((signed)fixup) >> howto->rightshift; + fixup = (offsetT) fixup >> howto->rightshift; /* Truncate the fixup to right size. */ switch (fixP->fx_r_type) @@ -1226,13 +1396,11 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) fixup = fixup & 0xFFFF; break; case BFD_RELOC_NIOS2_HIADJ16: - fixup = ((((fixup >> 16) & 0xFFFF) + ((fixup >> 15) & 0x01)) - & 0xFFFF); + fixup = ((fixup + 0x8000) >> 16) & 0xFFFF; break; default: { - int n = sizeof (fixup) * 8 - howto->bitsize; - fixup = (fixup << n) >> n; + fixup &= ((valueT) 2 << (howto->bitsize - 1)) - 1; break; } } @@ -1311,7 +1479,7 @@ static nios2_insn_relocS * nios2_insn_reloc_new (bfd_reloc_code_real_type reloc_type, unsigned int pcrel) { nios2_insn_relocS *retval; - retval = (nios2_insn_relocS *) malloc (sizeof (nios2_insn_relocS)); + retval = XNEW (nios2_insn_relocS); if (retval == NULL) { as_bad (_("can't create relocation")); @@ -1384,21 +1552,214 @@ 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 |= 1UL << 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. - If not, it creates a new nios2_insn_relocS and stores the expression and + If not, it creates a new nios2_insn_relocS and stores the expression and reloc_type for future use. */ 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); @@ -1411,7 +1772,7 @@ nios2_assemble_expression (const char *exprstr, { reloc_type = nios2_special_reloc[i].reloc_type; exprstr += strlen (nios2_special_reloc[i].string) + 1; - + /* %lo and %hiadj have different meanings for PC-relative expressions. */ if (pcrel) @@ -1421,10 +1782,29 @@ nios2_assemble_expression (const char *exprstr, if (reloc_type == BFD_RELOC_NIOS2_HIADJ16) reloc_type = BFD_RELOC_NIOS2_PCREL_HA; } - + 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; @@ -1439,7 +1819,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; @@ -1447,9 +1826,30 @@ 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. */ -static void + + +/* Control register index. */ +static void nios2_assemble_arg_c (const char *token, nios2_insn_infoS *insn) { struct nios2_reg *reg = nios2_parse_reg (token, REG_CONTROL); @@ -1463,19 +1863,23 @@ 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); } } -static void +/* Destination register. */ +static void nios2_assemble_arg_d (const char *token, nios2_insn_infoS *insn) { const struct nios2_opcode *op = insn->insn_nios2_opcode; 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) @@ -1493,19 +1897,34 @@ 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); } } -static void +/* Source register 1. */ +static void nios2_assemble_arg_s (const char *token, nios2_insn_infoS *insn) { const struct nios2_opcode *op = insn->insn_nios2_opcode; 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) @@ -1514,6 +1933,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: @@ -1526,19 +1947,61 @@ 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); } } -static void +/* Source register 2. */ +static void nios2_assemble_arg_t (const char *token, nios2_insn_infoS *insn) { const struct nios2_opcode *op = insn->insn_nios2_opcode; 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) @@ -1559,117 +2022,776 @@ 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); } } -static void -nios2_assemble_arg_i (const char *token, nios2_insn_infoS *insn) +/* 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; - unsigned int val; + int reg = nios2_assemble_reg3 (token); switch (op->format) { - case iw_i_type: - val = nios2_assemble_expression (token, insn, - BFD_RELOC_NIOS2_S16, 0); - insn->constant_bits |= SET_IW_I_IMM16 (val); + 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); } } -static void -nios2_assemble_arg_u (const char *token, nios2_insn_infoS *insn) +/* 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; - unsigned int val; + int reg = nios2_assemble_reg3 (token); switch (op->format) { - case iw_i_type: - val = nios2_assemble_expression (token, insn, - BFD_RELOC_NIOS2_U16, 0); - insn->constant_bits |= SET_IW_I_IMM16 (val); + 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); } } -static void -nios2_assemble_arg_o (const char *token, nios2_insn_infoS *insn) +/* 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; - unsigned int val; + int reg = nios2_assemble_reg3 (token); switch (op->format) { - case iw_i_type: - val = nios2_assemble_expression (token, insn, - BFD_RELOC_16_PCREL, 1); - insn->constant_bits |= SET_IW_I_IMM16 (val); + 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); } } -static void -nios2_assemble_arg_j (const char *token, nios2_insn_infoS *insn) +/* 16-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_r_type: + case iw_i_type: val = nios2_assemble_expression (token, insn, - BFD_RELOC_NIOS2_IMM5, 0); - insn->constant_bits |= SET_IW_R_IMM5 (val); - break; + 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) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_i_type: + val = nios2_assemble_expression (token, 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) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_i_type: + val = nios2_assemble_expression (token, 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) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_r_type: + val = nios2_assemble_expression (token, 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) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_custom_type: + val = nios2_assemble_expression (token, 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) +{ + const struct nios2_opcode *op = insn->insn_nios2_opcode; + unsigned int val; + + switch (op->format) + { + case iw_j_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_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); } } -static void -nios2_assemble_arg_l (const char *token, nios2_insn_infoS *insn) +/* 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_custom_type: - val = nios2_assemble_expression (token, insn, - BFD_RELOC_NIOS2_IMM8, 0); - insn->constant_bits |= SET_IW_CUSTOM_N (val); + 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); } } -static void -nios2_assemble_arg_m (const char *token, nios2_insn_infoS *insn) +/* 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_j_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_J_IMM26 (val); + 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 & (1u << 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); } @@ -1704,49 +2826,129 @@ nios2_assemble_args (nios2_insn_infoS *insn) case 'c': nios2_assemble_arg_c (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 '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; - + 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; } - /* Perform argument checking. */ + /* Perform argument checking. */ nios2_check_assembly (insn->insn_code | insn->constant_bits, insn->insn_tokens[tokidx]); } @@ -1762,13 +2964,16 @@ static char * nios2_consume_arg (char *argstr, const char *parsestr) { char *temp; - + switch (*parsestr) { case 'c': case 'd': case 's': case 't': + case 'D': + case 'S': + case 'T': break; case 'i': @@ -1792,11 +2997,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; @@ -1833,7 +3087,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. */ @@ -1846,8 +3099,8 @@ nios2_parse_args (nios2_insn_infoS *insn, char *argstr, int i; p = argstr; i = 0; - bfd_boolean terminate = FALSE; - + bool terminate = false; + /* This rest of this function is it too fragile and it mostly works, therefore special case this one. */ if (*parsestr == 0 && argstr != 0) @@ -1856,7 +3109,7 @@ nios2_parse_args (nios2_insn_infoS *insn, char *argstr, parsed_args[0] = NULL; return; } - + while (p != NULL && !terminate && i < NIOS2_MAX_INSN_TOKENS) { parsed_args[i] = nios2_consume_arg (p, parsestr); @@ -1883,7 +3136,7 @@ nios2_parse_args (nios2_insn_infoS *insn, char *argstr, } if (*parsestr == '\0' || (p != NULL && *p == '\0')) - terminate = TRUE; + terminate = true; ++i; } @@ -1903,10 +3156,7 @@ nios2_modify_arg (char **parsed_args, const char *modifier, { char *tmp = parsed_args[ndx]; - parsed_args[ndx] - = (char *) malloc (strlen (parsed_args[ndx]) + strlen (modifier) + 1); - strcpy (parsed_args[ndx], tmp); - strcat (parsed_args[ndx], modifier); + parsed_args[ndx] = concat (tmp, modifier, (char *) NULL); } /* Modify parsed_args[ndx] by negating that argument. */ @@ -1916,13 +3166,7 @@ nios2_negate_arg (char **parsed_args, const char *modifier ATTRIBUTE_UNUSED, { char *tmp = parsed_args[ndx]; - parsed_args[ndx] - = (char *) malloc (strlen ("~(") + strlen (parsed_args[ndx]) + - strlen (")+1") + 1); - - strcpy (parsed_args[ndx], "~("); - strcat (parsed_args[ndx], tmp); - strcat (parsed_args[ndx], ")+1"); + parsed_args[ndx] = concat ("~(", tmp, ")+1", (char *) NULL); } /* The function nios2_swap_args swaps the pointers at indices index_1 and @@ -1964,7 +3208,7 @@ nios2_append_arg (char **parsed_args, const char *appnd, int num, parsed_args[i + 1] = NULL; } -/* This function inserts the string insert num times in the array +/* This function inserts the string insert num times in the array parsed_args, starting at the index start. */ static void nios2_insert_arg (char **parsed_args, const char *insert, int num, @@ -1986,11 +3230,8 @@ nios2_insert_arg (char **parsed_args, const char *insert, int num, static void nios2_free_arg (char **parsed_args, int num ATTRIBUTE_UNUSED, int start) { - if (parsed_args[start]) - { - free (parsed_args[start]); - parsed_args[start] = NULL; - } + free (parsed_args[start]); + parsed_args[start] = NULL; } /* This function swaps the pseudo-op for a real op. */ @@ -1998,16 +3239,29 @@ static nios2_ps_insn_infoS* nios2_translate_pseudo_insn (nios2_insn_infoS *insn) { + const struct nios2_opcode *op = insn->insn_nios2_opcode; nios2_ps_insn_infoS *ps_insn; + unsigned int tokidx, ntok; - /* Find which real insn the pseudo-op transates to and + /* Find which real insn the pseudo-op translates to and switch the insn_info ptr to point to it. */ - ps_insn = nios2_ps_lookup (insn->insn_nios2_opcode->name); + ps_insn = nios2_ps_lookup (op->name); if (ps_insn != NULL) { insn->insn_nios2_opcode = nios2_opcode_lookup (ps_insn->insn); insn->insn_tokens[0] = insn->insn_nios2_opcode->name; + + /* Make sure there are enough arguments. */ + ntok = ((op->pinfo & NIOS2_INSN_OPTARG) + ? op->num_args - 1 : op->num_args); + for (tokidx = 1; tokidx <= ntok; tokidx++) + if (insn->insn_tokens[tokidx] == NULL) + { + as_bad ("missing argument"); + return NULL; + } + /* Modify the args so they work with the real insn. */ ps_insn->arg_modifer_func ((char **) insn->insn_tokens, ps_insn->arg_modifier, ps_insn->num, @@ -2016,7 +3270,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; } @@ -2050,7 +3304,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. */ }; @@ -2067,7 +3322,7 @@ static void output_insn (nios2_insn_infoS *insn) { char *f; - nios2_insn_relocS *reloc; + nios2_insn_relocS *reloc; f = frag_more (insn->insn_nios2_opcode->size); /* This allocates enough space for the instruction and puts it in the current frag. */ @@ -2095,6 +3350,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; + bool 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. */ @@ -2105,7 +3361,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); @@ -2125,6 +3382,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; + bool 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. */ @@ -2135,7 +3393,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); @@ -2156,23 +3415,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. */ @@ -2183,21 +3469,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 = 0; + 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, @@ -2209,10 +3507,38 @@ output_movia (nios2_insn_infoS *insn) /** External interfaces. */ +/* Update the selected architecture based on ARCH, giving an error if + ARCH is an invalid value. */ + +static void +nios2_use_arch (const char *arch) +{ + if (strcmp (arch, "nios2") == 0 || strcmp (arch, "r1") == 0) + { + nios2_architecture |= EF_NIOS2_ARCH_R1; + nios2_opcodes = (struct nios2_opcode *) nios2_r1_opcodes; + nios2_num_opcodes = nios2_num_r1_opcodes; + nop32 = nop_r1; + nop16 = NULL; + return; + } + else if (strcmp (arch, "r2") == 0) + { + nios2_architecture |= EF_NIOS2_ARCH_R2; + nios2_opcodes = (struct nios2_opcode *) nios2_r2_opcodes; + nios2_num_opcodes = nios2_num_r2_opcodes; + nop32 = nop_r2; + nop16 = nop_r2_cdx; + return; + } + + as_bad (_("unknown architecture '%s'"), arch); +} + /* The following functions are called by machine-independent parts of the assembler. */ int -md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) +md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED) { switch (c) { @@ -2235,6 +3561,9 @@ md_parse_option (int c, char *arg ATTRIBUTE_UNUSED) case OPTION_EL: target_big_endian = 0; break; + case OPTION_MARCH: + nios2_use_arch (arg); + break; default: return 0; break; @@ -2262,9 +3591,11 @@ md_show_usage (FILE *stream) "branches with jmp sequences (default)\n" " -no-relax do not replace any branches or calls\n" " -EB force big-endian byte ordering\n" - " -EL force little-endian byte ordering\n"); + " -EL force little-endian byte ordering\n" + " -march=ARCH enable instructions from architecture ARCH\n"); } + /* This function is called once, at assembler startup time. It should set up all the tables, etc. that the MD part of the assembler will need. */ @@ -2272,69 +3603,51 @@ void md_begin (void) { int i; - const char *inserted; - /* Create and fill a hashtable for the Nios II opcodes, registers and + switch (nios2_architecture) + { + default: + case EF_NIOS2_ARCH_R1: + bfd_default_set_arch_mach (stdoutput, bfd_arch_nios2, bfd_mach_nios2r1); + break; + case EF_NIOS2_ARCH_R2: + if (target_big_endian) + as_fatal (_("Big-endian R2 is not supported.")); + bfd_default_set_arch_mach (stdoutput, bfd_arch_nios2, bfd_mach_nios2r2); + break; + } + + /* Create and fill a hashtable for the Nios II opcodes, registers and arguments. */ - nios2_opcode_hash = hash_new (); - nios2_reg_hash = hash_new (); - nios2_ps_hash = hash_new (); + nios2_opcode_hash = str_htab_create (); + nios2_reg_hash = str_htab_create (); + nios2_ps_hash = str_htab_create (); for (i = 0; i < nios2_num_opcodes; ++i) - { - inserted - = hash_insert (nios2_opcode_hash, nios2_opcodes[i].name, - (PTR) & nios2_opcodes[i]); - if (inserted != NULL) - { - fprintf (stderr, _("internal error: can't hash `%s': %s\n"), - nios2_opcodes[i].name, inserted); - /* Probably a memory allocation problem? Give up now. */ - as_fatal (_("Broken assembler. No assembly attempted.")); - } - } + if (str_hash_insert (nios2_opcode_hash, nios2_opcodes[i].name, + &nios2_opcodes[i], 0) != NULL) + as_fatal (_("duplicate %s"), nios2_opcodes[i].name); for (i = 0; i < nios2_num_regs; ++i) - { - inserted - = hash_insert (nios2_reg_hash, nios2_regs[i].name, - (PTR) & nios2_regs[i]); - if (inserted != NULL) - { - fprintf (stderr, _("internal error: can't hash `%s': %s\n"), - nios2_regs[i].name, inserted); - /* Probably a memory allocation problem? Give up now. */ - as_fatal (_("Broken assembler. No assembly attempted.")); - } - - } + if (str_hash_insert (nios2_reg_hash, nios2_regs[i].name, + &nios2_regs[i], 0) != NULL) + as_fatal (_("duplicate %s"), nios2_regs[i].name); for (i = 0; i < nios2_num_ps_insn_info_structs; ++i) - { - inserted - = hash_insert (nios2_ps_hash, nios2_ps_insn_info_structs[i].pseudo_insn, - (PTR) & nios2_ps_insn_info_structs[i]); - if (inserted != NULL) - { - fprintf (stderr, _("internal error: can't hash `%s': %s\n"), - nios2_ps_insn_info_structs[i].pseudo_insn, inserted); - /* Probably a memory allocation problem? Give up now. */ - as_fatal (_("Broken assembler. No assembly attempted.")); - } - } + if (str_hash_insert (nios2_ps_hash, + nios2_ps_insn_info_structs[i].pseudo_insn, + &nios2_ps_insn_info_structs[i], 0) != NULL) + as_fatal (_("duplicate %s"), nios2_ps_insn_info_structs[i].pseudo_insn); /* Assembler option defaults. */ - nios2_as_options.noat = FALSE; - nios2_as_options.nobreak = FALSE; - - /* Debug information is incompatible with relaxation. */ - if (debug_type != DEBUG_UNSPECIFIED) - nios2_as_options.relax = relax_none; + nios2_as_options.noat = false; + nios2_as_options.nobreak = false; /* Initialize the alignment data. */ nios2_current_align_seg = now_seg; nios2_last_label = NULL; nios2_current_align = 0; + nios2_min_align = 2; } @@ -2342,17 +3655,18 @@ md_begin (void) void md_assemble (char *op_str) { - char *argstr; + char *argstr; char *op_strdup = NULL; unsigned long saved_pinfo = 0; nios2_insn_infoS thisinsn; nios2_insn_infoS *insn = &thisinsn; + bool ps_error = false; - /* 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 @@ -2368,6 +3682,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; @@ -2380,43 +3699,53 @@ md_assemble (char *op_str) nios2_parse_args (insn, argstr, insn->insn_nios2_opcode->args_test, (char **) &insn->insn_tokens[1]); - /* We need to preserve the MOVIA macro as this is clobbered by + /* We need to preserve the MOVIA macro as this is clobbered by translate_pseudo_insn. */ if (insn->insn_nios2_opcode->pinfo == NIOS2_INSN_MACRO_MOVIA) saved_pinfo = NIOS2_INSN_MACRO_MOVIA; - /* If the instruction is an pseudo-instruction, we want to replace it + /* If the instruction is an pseudo-instruction, we want to replace it with its real equivalent, and then continue. */ if ((insn->insn_nios2_opcode->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO) - ps_insn = nios2_translate_pseudo_insn (insn); - - /* Assemble the parsed arguments into the instruction word. */ - nios2_assemble_args (insn); - - /* Handle relaxation and other transformations. */ - if (nios2_as_options.relax != relax_none - && !nios2_as_options.noat - && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_UBRANCH) - output_ubranch (insn); - else if (nios2_as_options.relax != relax_none - && !nios2_as_options.noat - && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CBRANCH) - output_cbranch (insn); - else if (nios2_as_options.relax == relax_all - && !nios2_as_options.noat - && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CALL - && insn->insn_reloc - && ((insn->insn_reloc->reloc_type - == BFD_RELOC_NIOS2_CALL26) - || (insn->insn_reloc->reloc_type - == BFD_RELOC_NIOS2_CALL26_NOAT))) - output_call (insn); - else if (saved_pinfo == NIOS2_INSN_MACRO_MOVIA) - output_movia (insn); - else - output_insn (insn); - if (ps_insn) - nios2_cleanup_pseudo_insn (insn, ps_insn); + { + ps_insn = nios2_translate_pseudo_insn (insn); + if (!ps_insn) + ps_error = true; + } + + /* If we found invalid pseudo-instruction syntax, the error's already + been diagnosed in nios2_translate_pseudo_insn, so skip + remaining processing. */ + if (!ps_error) + { + /* Assemble the parsed arguments into the instruction word. */ + nios2_assemble_args (insn); + + /* Handle relaxation and other transformations. */ + if (nios2_as_options.relax != relax_none + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_UBRANCH) + output_ubranch (insn); + else if (nios2_as_options.relax != relax_none + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CBRANCH) + output_cbranch (insn); + else if (nios2_as_options.relax == relax_all + && !nios2_as_options.noat + && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CALL + && insn->insn_reloc + && ((insn->insn_reloc->reloc_type + == BFD_RELOC_NIOS2_CALL26) + || (insn->insn_reloc->reloc_type + == BFD_RELOC_NIOS2_CALL26_NOAT))) + output_call (insn); + else if (saved_pinfo == NIOS2_INSN_MACRO_MOVIA) + output_movia (insn); + else + output_insn (insn); + if (ps_insn) + nios2_cleanup_pseudo_insn (insn, ps_insn); + } } else /* Unrecognised instruction - error. */ @@ -2511,8 +3840,8 @@ nios2_frob_symbol (symbolS *symp) arelent * tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) { - arelent *reloc = (arelent *) xmalloc (sizeof (arelent)); - reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + arelent *reloc = XNEW (arelent); + reloc->sym_ptr_ptr = XNEW (asymbol *); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; @@ -2558,7 +3887,7 @@ md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED) /* Called just before the assembler exits. */ void -md_end () +md_end (void) { /* FIXME - not yet implemented */ } @@ -2578,7 +3907,7 @@ md_undefined_symbol (char *name ATTRIBUTE_UNUSED) as_bad ("GOT already in the symbol table"); GOT_symbol = symbol_new (name, undefined_section, - (valueT) 0, &zero_address_frag); + &zero_address_frag, 0); } return GOT_symbol; @@ -2615,7 +3944,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; @@ -2628,7 +3957,7 @@ nios2_cons_align (int size) /* Map 's' to SHF_NIOS2_GPREL. */ /* This is from the Alpha code tc-alpha.c. */ int -nios2_elf_section_letter (int letter, char **ptr_msg) +nios2_elf_section_letter (int letter, const char **ptr_msg) { if (letter == 's') return SHF_NIOS2_GPREL; @@ -2647,31 +3976,48 @@ nios2_elf_section_flags (flagword flags, int attr, int type ATTRIBUTE_UNUSED) return flags; } -/* Implement TC_PARSE_CONS_EXPRESSION to handle %tls_ldo(...) */ +/* Implement TC_PARSE_CONS_EXPRESSION to handle %tls_ldo(...) and + %gotoff(...). */ bfd_reloc_code_real_type nios2_cons (expressionS *exp, int size) { - bfd_reloc_code_real_type nios2_tls_ldo_reloc = BFD_RELOC_NONE; + bfd_reloc_code_real_type explicit_reloc = BFD_RELOC_NONE; + const char *reloc_name = NULL; SKIP_WHITESPACE (); if (input_line_pointer[0] == '%') { if (strprefix (input_line_pointer + 1, "tls_ldo")) { + reloc_name = "%tls_ldo"; if (size != 4) as_bad (_("Illegal operands: %%tls_ldo in %d-byte data field"), size); else { input_line_pointer += 8; - nios2_tls_ldo_reloc = BFD_RELOC_NIOS2_TLS_DTPREL; + explicit_reloc = BFD_RELOC_NIOS2_TLS_DTPREL; + } + } + else if (strprefix (input_line_pointer + 1, "gotoff")) + { + reloc_name = "%gotoff"; + if (size != 4) + as_bad (_("Illegal operands: %%gotoff in %d-byte data field"), + size); + else + { + input_line_pointer += 7; + explicit_reloc = BFD_RELOC_NIOS2_GOTOFF; } } - if (nios2_tls_ldo_reloc != BFD_RELOC_NONE) + + if (explicit_reloc != BFD_RELOC_NONE) { SKIP_WHITESPACE (); if (input_line_pointer[0] != '(') - as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + as_bad (_("Illegal operands: %s requires arguments in ()"), + reloc_name); else { int c; @@ -2689,29 +4035,32 @@ nios2_cons (expressionS *exp, int size) } if (c != ')') - as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + as_bad (_("Illegal operands: %s requires arguments in ()"), + reloc_name); else { *end = '\0'; expression (exp); *end = c; if (input_line_pointer != end) - as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()")); + as_bad (_("Illegal operands: %s requires arguments in ()"), + reloc_name); else { input_line_pointer++; SKIP_WHITESPACE (); c = *input_line_pointer; if (! is_end_of_line[c] && c != ',') - as_bad (_("Illegal operands: garbage after %%tls_ldo()")); + as_bad (_("Illegal operands: garbage after %s()"), + reloc_name); } } } } } - if (nios2_tls_ldo_reloc == BFD_RELOC_NONE) + if (explicit_reloc == BFD_RELOC_NONE) expression (exp); - return nios2_tls_ldo_reloc; + return explicit_reloc; } /* Implement HANDLE_ALIGN. */ @@ -2747,3 +4096,13 @@ nios2_frame_initial_instructions (void) { cfi_add_CFA_def_cfa (27, 0); } + +#ifdef OBJ_ELF +/* Some special processing for a Nios II ELF file. */ + +void +nios2_elf_final_processing (void) +{ + elf_elfheader (stdoutput)->e_flags = nios2_architecture; +} +#endif