X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-arm.c;h=77606a3bb6282bdf9f8140076919bd3a1b9ddb11;hb=877807f8c481a4c5780b3d7bba7963c068910434;hp=1ffc27302d42089717e5eeb274c78c1a9ac73d90;hpb=21d799b5c48956e71980143cb33035262984ed67;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 1ffc27302d..77606a3bb6 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -1,6 +1,6 @@ /* tc-arm.c -- Assemble for the ARM Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006, 2007, 2008, 2009 + 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modified by David Taylor (dtaylor@armltd.co.uk) @@ -97,13 +97,12 @@ enum arm_float_abi /* Types of processor to assemble for. */ #ifndef CPU_DEFAULT -#if defined __XSCALE__ -#define CPU_DEFAULT ARM_ARCH_XSCALE -#else -#if defined __thumb__ -#define CPU_DEFAULT ARM_ARCH_V5T -#endif -#endif +/* The code that was here used to select a default CPU depending on compiler + pre-defines which were only present when doing native builds, thus + changing gas' default behaviour depending upon the build host. + + If you have a target that requires a default CPU option then the you + should define CPU_DEFAULT here. */ #endif #ifndef FPU_DEFAULT @@ -185,22 +184,30 @@ static const arm_feature_set arm_ext_v5exp = ARM_FEATURE (ARM_EXT_V5ExP, 0); static const arm_feature_set arm_ext_v5j = ARM_FEATURE (ARM_EXT_V5J, 0); static const arm_feature_set arm_ext_v6 = ARM_FEATURE (ARM_EXT_V6, 0); static const arm_feature_set arm_ext_v6k = ARM_FEATURE (ARM_EXT_V6K, 0); -static const arm_feature_set arm_ext_v6z = ARM_FEATURE (ARM_EXT_V6Z, 0); static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE (ARM_EXT_V6T2, 0); +static const arm_feature_set arm_ext_v6m = ARM_FEATURE (ARM_EXT_V6M, 0); static const arm_feature_set arm_ext_v6_notm = ARM_FEATURE (ARM_EXT_V6_NOTM, 0); +static const arm_feature_set arm_ext_v6_dsp = ARM_FEATURE (ARM_EXT_V6_DSP, 0); static const arm_feature_set arm_ext_barrier = ARM_FEATURE (ARM_EXT_BARRIER, 0); static const arm_feature_set arm_ext_msr = ARM_FEATURE (ARM_EXT_THUMB_MSR, 0); static const arm_feature_set arm_ext_div = ARM_FEATURE (ARM_EXT_DIV, 0); static const arm_feature_set arm_ext_v7 = ARM_FEATURE (ARM_EXT_V7, 0); static const arm_feature_set arm_ext_v7a = ARM_FEATURE (ARM_EXT_V7A, 0); static const arm_feature_set arm_ext_v7r = ARM_FEATURE (ARM_EXT_V7R, 0); +static const arm_feature_set arm_ext_v7m = ARM_FEATURE (ARM_EXT_V7M, 0); static const arm_feature_set arm_ext_m = - ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_V7M, 0); + ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, 0); +static const arm_feature_set arm_ext_mp = ARM_FEATURE (ARM_EXT_MP, 0); +static const arm_feature_set arm_ext_sec = ARM_FEATURE (ARM_EXT_SEC, 0); +static const arm_feature_set arm_ext_os = ARM_FEATURE (ARM_EXT_OS, 0); +static const arm_feature_set arm_ext_adiv = ARM_FEATURE (ARM_EXT_ADIV, 0); +static const arm_feature_set arm_ext_virt = ARM_FEATURE (ARM_EXT_VIRT, 0); static const arm_feature_set arm_arch_any = ARM_ANY; static const arm_feature_set arm_arch_full = ARM_FEATURE (-1, -1); static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2; static const arm_feature_set arm_arch_none = ARM_ARCH_NONE; +static const arm_feature_set arm_arch_v6m_only = ARM_ARCH_V6M_ONLY; static const arm_feature_set arm_cext_iwmmxt2 = ARM_FEATURE (0, ARM_CEXT_IWMMXT2); @@ -216,19 +223,31 @@ static const arm_feature_set fpu_vfp_ext_v1xd = ARM_FEATURE (0, FPU_VFP_EXT_V1xD); static const arm_feature_set fpu_vfp_ext_v1 = ARM_FEATURE (0, FPU_VFP_EXT_V1); static const arm_feature_set fpu_vfp_ext_v2 = ARM_FEATURE (0, FPU_VFP_EXT_V2); +static const arm_feature_set fpu_vfp_ext_v3xd = ARM_FEATURE (0, FPU_VFP_EXT_V3xD); static const arm_feature_set fpu_vfp_ext_v3 = ARM_FEATURE (0, FPU_VFP_EXT_V3); static const arm_feature_set fpu_vfp_ext_d32 = ARM_FEATURE (0, FPU_VFP_EXT_D32); static const arm_feature_set fpu_neon_ext_v1 = ARM_FEATURE (0, FPU_NEON_EXT_V1); static const arm_feature_set fpu_vfp_v3_or_neon_ext = ARM_FEATURE (0, FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3); -static const arm_feature_set fpu_neon_fp16 = ARM_FEATURE (0, FPU_NEON_FP16); +static const arm_feature_set fpu_vfp_fp16 = ARM_FEATURE (0, FPU_VFP_EXT_FP16); +static const arm_feature_set fpu_neon_ext_fma = ARM_FEATURE (0, FPU_NEON_EXT_FMA); +static const arm_feature_set fpu_vfp_ext_fma = ARM_FEATURE (0, FPU_VFP_EXT_FMA); static int mfloat_abi_opt = -1; /* Record user cpu selection for object attributes. */ static arm_feature_set selected_cpu = ARM_ARCH_NONE; /* Must be long enough to hold any of the names in arm_cpus. */ static char selected_cpu_name[16]; + +/* Return if no cpu was selected on command-line. */ +static bfd_boolean +no_cpu_selected (void) +{ + return selected_cpu.core == arm_arch_none.core + && selected_cpu.coproc == arm_arch_none.coproc; +} + #ifdef OBJ_ELF # ifdef EABI_DEFAULT static int meabi_flags = EABI_DEFAULT; @@ -344,6 +363,9 @@ struct arm_it appropriate. */ int uncond_value; struct neon_type vectype; + /* This does not indicate an actual NEON instruction, only that + the mnemonic accepts neon-style type suffixes. */ + int is_neon; /* Set to the opcode if the instruction needs relaxation. Zero if the instruction is not relaxed. */ unsigned long relax; @@ -495,6 +517,7 @@ enum arm_reg_type REG_TYPE_MMXWC, REG_TYPE_MMXWCG, REG_TYPE_XSCALE, + REG_TYPE_RNB }; /* Structure for a hash table entry for a register. @@ -504,7 +527,7 @@ enum arm_reg_type struct reg_entry { const char * name; - unsigned char number; + unsigned int number; unsigned char type; unsigned char builtin; struct neon_typed_alias * neon; @@ -551,7 +574,7 @@ struct asm_opcode const char * template_name; /* Parameters to instruction. */ - unsigned char operands[8]; + unsigned int operands[8]; /* Conditional tag - see opcode_lookup. */ unsigned int tag : 4; @@ -697,6 +720,11 @@ struct asm_opcode #define BAD_IT_COND _("incorrect condition in IT block") #define BAD_IT_IT _("IT falling in the range of a previous IT block") #define MISSING_FNSTART _("missing .fnstart before unwinding directive") +#define BAD_PC_ADDRESSING \ + _("cannot use register index with PC-relative addressing") +#define BAD_PC_WRITEBACK \ + _("cannot use writeback with PC-relative addressing") +#define BAD_RANGE _("branch out of range") static struct hash_control * arm_ops_hsh; static struct hash_control * arm_cond_hsh; @@ -731,6 +759,9 @@ typedef struct literal_pool symbolS * symbol; segT section; subsegT sub_section; +#ifdef OBJ_ELF + struct dwarf2_line_info locs [MAX_LITERAL_POOL_SIZE]; +#endif struct literal_pool * next; } literal_pool; @@ -931,6 +962,8 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode) input_line_pointer = save_in; return 1; } +#else + (void) seg; #endif /* Get rid of any bignums now, so that we don't generate an error for which @@ -1047,10 +1080,10 @@ md_atof (int type, char * litP, int * sizeP) /* We handle all bad expressions here, so that we can report the faulty instruction in the error message. */ void -md_operand (expressionS * expr) +md_operand (expressionS * exp) { if (in_my_get_expression) - expr->X_op = O_illegal; + exp->X_op = O_illegal; } /* Immediate values. */ @@ -1452,6 +1485,10 @@ arm_typed_reg_parse (char **ccp, enum arm_reg_type type, if (reg == FAIL) return FAIL; + /* Do not allow regname(... to parse as a register. */ + if (*str == '(') + return FAIL; + /* Do not allow a scalar (reg+index) to parse as a register. */ if ((atype.defined & NTA_HASINDEX) != 0) { @@ -1578,23 +1615,23 @@ parse_reg_list (char ** strp) } else { - expressionS expr; + expressionS exp; - if (my_get_expression (&expr, &str, GE_NO_PREFIX)) + if (my_get_expression (&exp, &str, GE_NO_PREFIX)) return FAIL; - if (expr.X_op == O_constant) + if (exp.X_op == O_constant) { - if (expr.X_add_number - != (expr.X_add_number & 0x0000ffff)) + if (exp.X_add_number + != (exp.X_add_number & 0x0000ffff)) { inst.error = _("invalid register mask"); return FAIL; } - if ((range & expr.X_add_number) != 0) + if ((range & exp.X_add_number) != 0) { - int regno = range & expr.X_add_number; + int regno = range & exp.X_add_number; regno &= -regno; regno = (1 << regno) - 1; @@ -1603,7 +1640,7 @@ parse_reg_list (char ** strp) regno); } - range |= expr.X_add_number; + range |= exp.X_add_number; } else { @@ -1613,7 +1650,7 @@ parse_reg_list (char ** strp) return FAIL; } - memcpy (&inst.reloc.exp, &expr, sizeof (expressionS)); + memcpy (&inst.reloc.exp, &exp, sizeof (expressionS)); inst.reloc.type = BFD_RELOC_ARM_MULTI; inst.reloc.pc_rel = 0; } @@ -1870,7 +1907,6 @@ parse_neon_el_struct_list (char **str, unsigned *pbase, int lane = -1; int leading_brace = 0; enum arm_reg_type rtype = REG_TYPE_NDQ; - int addregs = 1; const char *const incr_error = _("register stride must be 1 or 2"); const char *const type_error = _("mismatched element/structure types in list"); struct neon_typed_alias firsttype; @@ -1895,7 +1931,6 @@ parse_neon_el_struct_list (char **str, unsigned *pbase, if (rtype == REG_TYPE_NQ) { reg_incr = 1; - addregs = 2; } firsttype = atype; } @@ -2047,7 +2082,7 @@ parse_reloc (char **str) /* Directives: register aliases. */ static struct reg_entry * -insert_reg_alias (char *str, int number, int type) +insert_reg_alias (char *str, unsigned number, int type) { struct reg_entry *new_reg; const char *name; @@ -2196,7 +2231,7 @@ create_neon_reg_alias (char *newname, char *p) struct reg_entry mybasereg; struct neon_type ntype; struct neon_typed_alias typeinfo; - char *namebuf, *nameend; + char *namebuf, *nameend ATTRIBUTE_UNUSED; int namelen; typeinfo.defined = 0; @@ -2292,7 +2327,16 @@ create_neon_reg_alias (char *newname, char *p) } } + /* If TC_CASE_SENSITIVE is defined, then newname already points to + the desired alias name, and p points to its end. If not, then + the desired alias name is in the global original_case_string. */ +#ifdef TC_CASE_SENSITIVE namelen = nameend - newname; +#else + newname = original_case_string; + namelen = strlen (newname); +#endif + namebuf = (char *) alloca (namelen + 1); strncpy (namebuf, newname, namelen); namebuf[namelen] = '\0'; @@ -2372,7 +2416,7 @@ s_unreq (int a ATTRIBUTE_UNUSED) if (!reg) as_bad (_("unknown register alias '%s'"), name); else if (reg->builtin) - as_warn (_("ignoring attempt to undefine built-in register '%s'"), + as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"), name); else { @@ -2482,14 +2526,27 @@ make_mapping_symbol (enum mstate state, valueT value, fragS *frag) /* Save the mapping symbols for future reference. Also check that we do not place two mapping symbols at the same offset within a frag. We'll handle overlap between frags in - check_mapping_symbols. */ + check_mapping_symbols. + + If .fill or other data filling directive generates zero sized data, + the mapping symbol for the following code will have the same value + as the one generated for the data filling directive. In this case, + we replace the old symbol with the new one at the same address. */ if (value == 0) { - know (frag->tc_frag_data.first_map == NULL); + if (frag->tc_frag_data.first_map != NULL) + { + know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0); + symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP, &symbol_lastP); + } frag->tc_frag_data.first_map = symbolP; } if (frag->tc_frag_data.last_map != NULL) - know (S_GET_VALUE (frag->tc_frag_data.last_map) < S_GET_VALUE (symbolP)); + { + know (S_GET_VALUE (frag->tc_frag_data.last_map) <= S_GET_VALUE (symbolP)); + if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP)) + symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP, &symbol_lastP); + } frag->tc_frag_data.last_map = symbolP; } @@ -2536,7 +2593,24 @@ mapping_state (enum mstate state) /* The mapping symbol has already been emitted. There is nothing else to do. */ return; - else if (TRANSITION (MAP_UNDEFINED, MAP_DATA)) + + if (state == MAP_ARM || state == MAP_THUMB) + /* PR gas/12931 + All ARM instructions require 4-byte alignment. + (Almost) all Thumb instructions require 2-byte alignment. + + When emitting instructions into any section, mark the section + appropriately. + + Some Thumb instructions are alignment-sensitive modulo 4 bytes, + but themselves require 2-byte alignment; this applies to some + PC- relative forms. However, these cases will invovle implicit + literal pool generation or an explicit .align >=2, both of + which will cause the section to me marked with sufficient + alignment. Thus, we don't handle those cases here. */ + record_alignment (now_seg, state == MAP_ARM ? 2 : 1); + + if (TRANSITION (MAP_UNDEFINED, MAP_DATA)) /* This case will be evaluated later in the next else. */ return; else if (TRANSITION (MAP_UNDEFINED, MAP_ARM) @@ -2577,8 +2651,8 @@ mapping_state_2 (enum mstate state, int max_chars) make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now); } #else -#define mapping_state(x) /* nothing */ -#define mapping_state_2(x, y) /* nothing */ +#define mapping_state(x) ((void)0) +#define mapping_state_2(x, y) ((void)0) #endif /* Find the real, Thumb encoded start of a Thumb function. */ @@ -3003,6 +3077,14 @@ add_to_lit_pool (void) } pool->literals[entry] = inst.reloc.exp; +#ifdef OBJ_ELF + /* PR ld/12974: Record the location of the first source line to reference + this entry in the literal pool. If it turns out during linking that the + symbol does not exist we will be able to give an accurate line number for + the (first use of the) missing reference. */ + if (debug_type == DEBUG_DWARF2) + dwarf2_where (pool->locs + entry); +#endif pool->next_free_entry += 1; } @@ -3100,8 +3182,14 @@ s_ltorg (int ignored ATTRIBUTE_UNUSED) #endif for (entry = 0; entry < pool->next_free_entry; entry ++) - /* First output the expression in the instruction to the pool. */ - emit_expr (&(pool->literals[entry]), 4); /* .word */ + { +#ifdef OBJ_ELF + if (debug_type == DEBUG_DWARF2) + dwarf2_gen_line_info (frag_now_fix (), pool->locs + entry); +#endif + /* First output the expression in the instruction to the pool. */ + emit_expr (&(pool->literals[entry]), 4); /* .word */ + } /* Mark the pool as empty. */ pool->next_free_entry = 0; @@ -4231,12 +4319,37 @@ s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED) if (tag < NUM_KNOWN_OBJ_ATTRIBUTES) attributes_set_explicitly[tag] = 1; } + +/* Emit a tls fix for the symbol. */ + +static void +s_arm_tls_descseq (int ignored ATTRIBUTE_UNUSED) +{ + char *p; + expressionS exp; +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (4); +#endif + + /* Since we're just labelling the code, there's no need to define a + mapping symbol. */ + expression (&exp); + p = obstack_next_free (&frchain_now->frch_obstack); + fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 0, + thumb_mode ? BFD_RELOC_ARM_THM_TLS_DESCSEQ + : BFD_RELOC_ARM_TLS_DESCSEQ); +} #endif /* OBJ_ELF */ static void s_arm_arch (int); static void s_arm_object_arch (int); static void s_arm_cpu (int); static void s_arm_fpu (int); +static void s_arm_arch_extension (int); #ifdef TE_PE @@ -4290,6 +4403,7 @@ const pseudo_typeS md_pseudo_table[] = { "arch", s_arm_arch, 0 }, { "object_arch", s_arm_object_arch, 0 }, { "fpu", s_arm_fpu, 0 }, + { "arch_extension", s_arm_arch_extension, 0 }, #ifdef OBJ_ELF { "word", s_arm_elf_cons, 4 }, { "long", s_arm_elf_cons, 4 }, @@ -4310,6 +4424,7 @@ const pseudo_typeS md_pseudo_table[] = { "setfp", s_arm_unwind_setfp, 0 }, { "unwind_raw", s_arm_unwind_raw, 0 }, { "eabi_attribute", s_arm_eabi_attribute, 0 }, + { "tlsdescseq", s_arm_tls_descseq, 0 }, #else { "word", cons, 4}, @@ -4379,7 +4494,7 @@ parse_big_immediate (char **str, int i) /* If we're on a 64-bit host, then a 64-bit number can be returned using O_constant. We have to be careful not to break compilation for 32-bit X_add_number, though. */ - if ((exp.X_add_number & ~0xffffffffl) != 0) + if ((exp.X_add_number & ~(offsetT)(0xffffffffU)) != 0) { /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */ inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff; @@ -4387,14 +4502,32 @@ parse_big_immediate (char **str, int i) } } else if (exp.X_op == O_big - && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32 - && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number <= 64) + && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32) { unsigned parts = 32 / LITTLENUM_NUMBER_OF_BITS, j, idx = 0; + /* Bignums have their least significant bits in generic_bignum[0]. Make sure we put 32 bits in imm and 32 bits in reg, in a (hopefully) portable way. */ gas_assert (parts != 0); + + /* Make sure that the number is not too big. + PR 11972: Bignums can now be sign-extended to the + size of a .octa so check that the out of range bits + are all zero or all one. */ + if (LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 64) + { + LITTLENUM_TYPE m = -1; + + if (generic_bignum[parts * 2] != 0 + && generic_bignum[parts * 2] != m) + return FAIL; + + for (j = parts * 2 + 1; j < (unsigned) exp.X_add_number; j++) + if (generic_bignum[j] != generic_bignum[j-1]) + return FAIL; + } + inst.operands[i].imm = 0; for (j = 0; j < parts; j++, idx++) inst.operands[i].imm |= generic_bignum[idx] @@ -4706,7 +4839,7 @@ static int parse_shifter_operand (char **str, int i) { int value; - expressionS expr; + expressionS exp; if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL) { @@ -4730,16 +4863,16 @@ parse_shifter_operand (char **str, int i) if (skip_past_comma (str) == SUCCESS) { /* #x, y -- ie explicit rotation by Y. */ - if (my_get_expression (&expr, str, GE_NO_PREFIX)) + if (my_get_expression (&exp, str, GE_NO_PREFIX)) return FAIL; - if (expr.X_op != O_constant || inst.reloc.exp.X_op != O_constant) + if (exp.X_op != O_constant || inst.reloc.exp.X_op != O_constant) { inst.error = _("constant expression expected"); return FAIL; } - value = expr.X_add_number; + value = exp.X_add_number; if (value < 0 || value > 30 || value % 2 != 0) { inst.error = _("invalid rotation"); @@ -4924,6 +5057,33 @@ parse_shifter_operand_group_reloc (char **str, int i) /* Never reached. */ } +/* Parse a Neon alignment expression. Information is written to + inst.operands[i]. We assume the initial ':' has been skipped. + + align .imm = align << 8, .immisalign=1, .preind=0 */ +static parse_operand_result +parse_neon_alignment (char **str, int i) +{ + char *p = *str; + expressionS exp; + + my_get_expression (&exp, &p, GE_NO_PREFIX); + + if (exp.X_op != O_constant) + { + inst.error = _("alignment must be constant"); + return PARSE_OPERAND_FAIL; + } + + inst.operands[i].imm = exp.X_add_number << 8; + inst.operands[i].immisalign = 1; + /* Alignments are not pre-indexes. */ + inst.operands[i].preind = 0; + + *str = p; + return PARSE_OPERAND_SUCCESS; +} + /* Parse all forms of an ARM address expression. Information is written to inst.operands[i] and/or inst.reloc. @@ -4967,13 +5127,13 @@ parse_address_main (char **str, int i, int group_relocations, { if (skip_past_char (&p, '=') == FAIL) { - /* bare address - translate to PC-relative offset */ + /* Bare address - translate to PC-relative offset. */ inst.reloc.pc_rel = 1; inst.operands[i].reg = REG_PC; inst.operands[i].isreg = 1; inst.operands[i].preind = 1; } - /* else a load-constant pseudo op, no special treatment needed here */ + /* Otherwise a load-constant pseudo op, no special treatment needed here. */ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) return PARSE_OPERAND_FAIL; @@ -5007,22 +5167,15 @@ parse_address_main (char **str, int i, int group_relocations, return PARSE_OPERAND_FAIL; } else if (skip_past_char (&p, ':') == SUCCESS) - { - /* FIXME: '@' should be used here, but it's filtered out by generic - code before we get to see it here. This may be subject to - change. */ - expressionS exp; - my_get_expression (&exp, &p, GE_NO_PREFIX); - if (exp.X_op != O_constant) - { - inst.error = _("alignment must be constant"); - return PARSE_OPERAND_FAIL; - } - inst.operands[i].imm = exp.X_add_number << 8; - inst.operands[i].immisalign = 1; - /* Alignments are not pre-indexes. */ - inst.operands[i].preind = 0; - } + { + /* FIXME: '@' should be used here, but it's filtered out by generic + code before we get to see it here. This may be subject to + change. */ + parse_operand_result result = parse_neon_alignment (&p, i); + + if (result != PARSE_OPERAND_SUCCESS) + return result; + } else { if (inst.operands[i].negative) @@ -5082,10 +5235,35 @@ parse_address_main (char **str, int i, int group_relocations, } } else - if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) - return PARSE_OPERAND_FAIL; + { + char *q = p; + if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) + return PARSE_OPERAND_FAIL; + /* If the offset is 0, find out if it's a +0 or -0. */ + if (inst.reloc.exp.X_op == O_constant + && inst.reloc.exp.X_add_number == 0) + { + skip_whitespace (q); + if (*q == '#') + { + q++; + skip_whitespace (q); + } + if (*q == '-') + inst.operands[i].negative = 1; + } + } } } + else if (skip_past_char (&p, ':') == SUCCESS) + { + /* FIXME: '@' should be used here, but it's filtered out by generic code + before we get to see it here. This may be subject to change. */ + parse_operand_result result = parse_neon_alignment (&p, i); + + if (result != PARSE_OPERAND_SUCCESS) + return result; + } if (skip_past_char (&p, ']') == FAIL) { @@ -5148,6 +5326,7 @@ parse_address_main (char **str, int i, int group_relocations, } else { + char *q = p; if (inst.operands[i].negative) { inst.operands[i].negative = 0; @@ -5155,6 +5334,19 @@ parse_address_main (char **str, int i, int group_relocations, } if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) return PARSE_OPERAND_FAIL; + /* If the offset is 0, find out if it's a +0 or -0. */ + if (inst.reloc.exp.X_op == O_constant + && inst.reloc.exp.X_add_number == 0) + { + skip_whitespace (q); + if (*q == '#') + { + q++; + skip_whitespace (q); + } + if (*q == '-') + inst.operands[i].negative = 1; + } } } } @@ -5229,37 +5421,85 @@ parse_half (char **str) /* Parse a PSR flag operand. The value returned is FAIL on syntax error, or a bitmask suitable to be or-ed into the ARM msr instruction. */ static int -parse_psr (char **str) +parse_psr (char **str, bfd_boolean lhs) { char *p; unsigned long psr_field; const struct asm_psr *psr; char *start; + bfd_boolean is_apsr = FALSE; + bfd_boolean m_profile = ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m); + + /* PR gas/12698: If the user has specified -march=all then m_profile will + be TRUE, but we want to ignore it in this case as we are building for any + CPU type, including non-m variants. */ + if (selected_cpu.core == arm_arch_any.core) + m_profile = FALSE; /* CPSR's and SPSR's can now be lowercase. This is just a convenience feature for ease of use and backwards compatibility. */ p = *str; if (strncasecmp (p, "SPSR", 4) == 0) - psr_field = SPSR_BIT; + { + if (m_profile) + goto unsupported_psr; + + psr_field = SPSR_BIT; + } else if (strncasecmp (p, "CPSR", 4) == 0) - psr_field = 0; - else + { + if (m_profile) + goto unsupported_psr; + + psr_field = 0; + } + else if (strncasecmp (p, "APSR", 4) == 0) + { + /* APSR[_] can be used as a synonym for CPSR[_] on ARMv7-A + and ARMv7-R architecture CPUs. */ + is_apsr = TRUE; + psr_field = 0; + } + else if (m_profile) { start = p; do p++; while (ISALNUM (*p) || *p == '_'); + if (strncasecmp (start, "iapsr", 5) == 0 + || strncasecmp (start, "eapsr", 5) == 0 + || strncasecmp (start, "xpsr", 4) == 0 + || strncasecmp (start, "psr", 3) == 0) + p = start + strcspn (start, "rR") + 1; + psr = (const struct asm_psr *) hash_find_n (arm_v7m_psr_hsh, start, p - start); + if (!psr) return FAIL; + /* If APSR is being written, a bitfield may be specified. Note that + APSR itself is handled above. */ + if (psr->field <= 3) + { + psr_field = psr->field; + is_apsr = TRUE; + goto check_suffix; + } + *str = p; - return psr->field; + /* M-profile MSR instructions have the mask field set to "10", except + *PSR variants which modify APSR, which may use a different mask (and + have been handled already). Do that by setting the PSR_f field + here. */ + return psr->field | (lhs ? PSR_f : 0); } + else + goto unsupported_psr; p += 4; +check_suffix: if (*p == '_') { /* A suffix follows. */ @@ -5270,23 +5510,106 @@ parse_psr (char **str) p++; while (ISALNUM (*p) || *p == '_'); - psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start, - p - start); - if (!psr) - goto error; + if (is_apsr) + { + /* APSR uses a notation for bits, rather than fields. */ + unsigned int nzcvq_bits = 0; + unsigned int g_bit = 0; + char *bit; + + for (bit = start; bit != p; bit++) + { + switch (TOLOWER (*bit)) + { + case 'n': + nzcvq_bits |= (nzcvq_bits & 0x01) ? 0x20 : 0x01; + break; + + case 'z': + nzcvq_bits |= (nzcvq_bits & 0x02) ? 0x20 : 0x02; + break; + + case 'c': + nzcvq_bits |= (nzcvq_bits & 0x04) ? 0x20 : 0x04; + break; + + case 'v': + nzcvq_bits |= (nzcvq_bits & 0x08) ? 0x20 : 0x08; + break; + + case 'q': + nzcvq_bits |= (nzcvq_bits & 0x10) ? 0x20 : 0x10; + break; + + case 'g': + g_bit |= (g_bit & 0x1) ? 0x2 : 0x1; + break; + + default: + inst.error = _("unexpected bit specified after APSR"); + return FAIL; + } + } + + if (nzcvq_bits == 0x1f) + psr_field |= PSR_f; + + if (g_bit == 0x1) + { + if (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)) + { + inst.error = _("selected processor does not " + "support DSP extension"); + return FAIL; + } - psr_field |= psr->field; + psr_field |= PSR_s; + } + + if ((nzcvq_bits & 0x20) != 0 + || (nzcvq_bits != 0x1f && nzcvq_bits != 0) + || (g_bit & 0x2) != 0) + { + inst.error = _("bad bitmask specified after APSR"); + return FAIL; + } + } + else + { + psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start, + p - start); + if (!psr) + goto error; + + psr_field |= psr->field; + } } else { if (ISALNUM (*p)) goto error; /* Garbage after "[CS]PSR". */ - psr_field |= (PSR_c | PSR_f); + /* Unadorned APSR is equivalent to APSR_nzcvq/CPSR_f (for writes). This + is deprecated, but allow it anyway. */ + if (is_apsr && lhs) + { + psr_field |= PSR_f; + as_tsktsk (_("writing to APSR without specifying a bitmask is " + "deprecated")); + } + else if (!m_profile) + /* These bits are never right for M-profile devices: don't set them + (only code paths which read/write APSR reach here). */ + psr_field |= (PSR_c | PSR_f); } *str = p; return psr_field; + unsupported_psr: + inst.error = _("selected processor does not support requested special " + "purpose register"); + return FAIL; + error: inst.error = _("flag for {c}psr instruction expected"); return FAIL; @@ -5721,6 +6044,11 @@ parse_neon_mov (char **str, int *which_operand) return FAIL; } +/* Use this macro when the operand constraints are different + for ARM and THUMB (e.g. ldrd). */ +#define MIX_ARM_THUMB_OPERANDS(arm_operand, thumb_operand) \ + ((arm_operand) | ((thumb_operand) << 16)) + /* Matcher codes for parse_operands. */ enum operand_parse_code { @@ -5728,7 +6056,10 @@ enum operand_parse_code OP_RR, /* ARM register */ OP_RRnpc, /* ARM register, not r15 */ + OP_RRnpcsp, /* ARM register, neither r15 nor r13 (a.k.a. 'BadReg') */ OP_RRnpcb, /* ARM register, not r15, in square brackets */ + OP_RRnpctw, /* ARM register, not r15 in Thumb-state or with writeback, + optional trailing ! */ OP_RRw, /* ARM register, not r15, optional trailing ! */ OP_RCP, /* Coprocessor number */ OP_RCN, /* Coprocessor register */ @@ -5760,7 +6091,6 @@ enum operand_parse_code OP_NRDLST, /* Neon double-precision register list (d0-d31, qN aliases) */ OP_NSTRLST, /* Neon element/structure list */ - OP_NILO, /* Neon immediate/logic operands 2 or 2+3. (VBIC, VORR...) */ OP_RNDQ_I0, /* Neon D or Q reg, or immediate zero. */ OP_RVSD_I0, /* VFP S or D reg, or immediate zero. */ OP_RR_RNSC, /* ARM reg or Neon scalar. */ @@ -5768,7 +6098,7 @@ enum operand_parse_code OP_RNDQ_RNSC, /* Neon D or Q reg, or Neon scalar. */ OP_RND_RNSC, /* Neon D reg, or Neon scalar. */ OP_VMOV, /* Neon VMOV operands. */ - OP_RNDQ_IMVNb,/* Neon D or Q reg, or immediate good for VMVN. */ + OP_RNDQ_Ibig, /* Neon D or Q reg, or big immediate for logic and VMVN. */ OP_RNDQ_I63b, /* Neon D or Q reg, or immediate for shift. */ OP_RIWR_I32z, /* iWMMXt wR register, or immediate 0 .. 32 for iWMMXt2. */ @@ -5805,11 +6135,11 @@ enum operand_parse_code OP_CPSF, /* CPS flags */ OP_ENDI, /* Endianness specifier */ - OP_PSR, /* CPSR/SPSR mask for msr */ + OP_wPSR, /* CPSR/SPSR/APSR mask for msr (writing). */ + OP_rPSR, /* CPSR/SPSR/APSR mask for msr (reading). */ OP_COND, /* conditional code */ OP_TB, /* Table branch. */ - OP_RVC_PSR, /* CPSR/SPSR mask for msr, or VFP control register. */ OP_APSR_RR, /* ARM register or "APSR_nzcv". */ OP_RRnpc_I0, /* ARM register or literal 0 */ @@ -5823,11 +6153,13 @@ enum operand_parse_code OP_oI7b, /* immediate, prefix optional, 0 .. 7 */ OP_oI31b, /* 0 .. 31 */ OP_oI32b, /* 1 .. 32 */ + OP_oI32z, /* 0 .. 32 */ OP_oIffffb, /* 0 .. 65535 */ OP_oI255c, /* curly-brace enclosed, 0 .. 255 */ OP_oRR, /* ARM register */ OP_oRRnpc, /* ARM register, not the PC */ + OP_oRRnpcsp, /* ARM register, neither the PC nor the SP (a.k.a. BadReg) */ OP_oRRw, /* ARM register, not r15, optional trailing ! */ OP_oRND, /* Optional Neon double precision register */ OP_oRNQ, /* Optional Neon quad precision register */ @@ -5837,7 +6169,12 @@ enum operand_parse_code OP_oSHar, /* ASR immediate */ OP_oSHllar, /* LSL or ASR immediate */ OP_oROR, /* ROR 0/8/16/24 */ - OP_oBARRIER, /* Option argument for a barrier instruction. */ + OP_oBARRIER_I15, /* Option argument for a barrier instruction. */ + + /* Some pre-defined mixed (ARM/THUMB) operands. */ + OP_RR_npcsp = MIX_ARM_THUMB_OPERANDS (OP_RR, OP_RRnpcsp), + OP_RRnpc_npcsp = MIX_ARM_THUMB_OPERANDS (OP_RRnpc, OP_RRnpcsp), + OP_oRRnpc_npcsp = MIX_ARM_THUMB_OPERANDS (OP_oRRnpc, OP_oRRnpcsp), OP_FIRST_OPTIONAL = OP_oI7b }; @@ -5847,14 +6184,15 @@ enum operand_parse_code structure. Returns SUCCESS or FAIL depending on whether the specified grammar matched. */ static int -parse_operands (char *str, const unsigned char *pattern) +parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) { - unsigned const char *upat = pattern; + unsigned const int *upat = pattern; char *backtrack_pos = 0; const char *backtrack_error = 0; int i, val, backtrack_index = 0; enum arm_reg_type rtype; parse_operand_result result; + unsigned int op_parse_code; #define po_char_or_fail(chr) \ do \ @@ -5941,11 +6279,40 @@ parse_operands (char *str, const unsigned char *pattern) } \ while (0) +#define po_barrier_or_imm(str) \ + do \ + { \ + val = parse_barrier (&str); \ + if (val == FAIL) \ + { \ + if (ISALPHA (*str)) \ + goto failure; \ + else \ + goto immediate; \ + } \ + else \ + { \ + if ((inst.instruction & 0xf0) == 0x60 \ + && val != 0xf) \ + { \ + /* ISB can only take SY as an option. */ \ + inst.error = _("invalid barrier type"); \ + goto failure; \ + } \ + } \ + } \ + while (0) + skip_whitespace (str); for (i = 0; upat[i] != OP_stop; i++) { - if (upat[i] >= OP_FIRST_OPTIONAL) + op_parse_code = upat[i]; + if (op_parse_code >= 1<<16) + op_parse_code = thumb ? (op_parse_code >> 16) + : (op_parse_code & ((1<<16)-1)); + + if (op_parse_code >= OP_FIRST_OPTIONAL) { /* Remember where we are in case we need to backtrack. */ gas_assert (!backtrack_pos); @@ -5957,11 +6324,13 @@ parse_operands (char *str, const unsigned char *pattern) if (i > 0 && (i > 1 || inst.operands[0].present)) po_char_or_fail (','); - switch (upat[i]) + switch (op_parse_code) { /* Registers */ case OP_oRRnpc: + case OP_oRRnpcsp: case OP_RRnpc: + case OP_RRnpcsp: case OP_oRR: case OP_RR: po_reg_or_fail (REG_TYPE_RN); break; case OP_RCP: po_reg_or_fail (REG_TYPE_CP); break; @@ -6000,36 +6369,6 @@ parse_operands (char *str, const unsigned char *pattern) scalars are accepted here, so deal with those in later code. */ case OP_RNSC: po_scalar_or_goto (8, failure); break; - /* WARNING: We can expand to two operands here. This has the potential - to totally confuse the backtracking mechanism! It will be OK at - least as long as we don't try to use optional args as well, - though. */ - case OP_NILO: - { - po_reg_or_goto (REG_TYPE_NDQ, try_imm); - inst.operands[i].present = 1; - i++; - skip_past_comma (&str); - po_reg_or_goto (REG_TYPE_NDQ, one_reg_only); - break; - one_reg_only: - /* Optional register operand was omitted. Unfortunately, it's in - operands[i-1] and we need it to be in inst.operands[i]. Fix that - here (this is a bit grotty). */ - inst.operands[i] = inst.operands[i-1]; - inst.operands[i-1].present = 0; - break; - try_imm: - /* There's a possibility of getting a 64-bit immediate here, so - we need special handling. */ - if (parse_big_immediate (&str, i) == FAIL) - { - inst.error = _("immediate value is out of range"); - goto failure; - } - } - break; - case OP_RNDQ_I0: { po_reg_or_goto (REG_TYPE_NDQ, try_imm0); @@ -6085,11 +6424,11 @@ parse_operands (char *str, const unsigned char *pattern) po_misc_or_fail (parse_neon_mov (&str, &i) == FAIL); break; - case OP_RNDQ_IMVNb: + case OP_RNDQ_Ibig: { - po_reg_or_goto (REG_TYPE_NDQ, try_mvnimm); + po_reg_or_goto (REG_TYPE_NDQ, try_immbig); break; - try_mvnimm: + try_immbig: /* There's a possibility of getting a 64-bit immediate here, so we need special handling. */ if (parse_big_immediate (&str, i) == FAIL) @@ -6115,6 +6454,7 @@ parse_operands (char *str, const unsigned char *pattern) po_char_or_fail (']'); break; + case OP_RRnpctw: case OP_RRw: case OP_oRRw: po_reg_or_fail (REG_TYPE_RN); @@ -6143,6 +6483,7 @@ parse_operands (char *str, const unsigned char *pattern) case OP_oI31b: case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break; case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break; + case OP_oI32z: po_imm_or_fail ( 0, 32, TRUE); break; case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break; /* Immediate variants */ @@ -6266,17 +6607,27 @@ parse_operands (char *str, const unsigned char *pattern) case OP_CPSF: val = parse_cps_flags (&str); break; case OP_ENDI: val = parse_endian_specifier (&str); break; case OP_oROR: val = parse_ror (&str); break; - case OP_PSR: val = parse_psr (&str); break; case OP_COND: val = parse_cond (&str); break; - case OP_oBARRIER:val = parse_barrier (&str); break; + case OP_oBARRIER_I15: + po_barrier_or_imm (str); break; + immediate: + if (parse_immediate (&str, &val, 0, 15, TRUE) == FAIL) + goto failure; + break; - case OP_RVC_PSR: - po_reg_or_goto (REG_TYPE_VFC, try_psr); - inst.operands[i].isvec = 1; /* Mark VFP control reg as vector. */ - break; - try_psr: - val = parse_psr (&str); - break; + case OP_wPSR: + case OP_rPSR: + po_reg_or_goto (REG_TYPE_RNB, try_psr); + if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_virt)) + { + inst.error = _("Banked registers are not available with this " + "architecture."); + goto failure; + } + break; + try_psr: + val = parse_psr (&str, op_parse_code == OP_wPSR); + break; case OP_APSR_RR: po_reg_or_goto (REG_TYPE_RN, try_apsr); @@ -6300,6 +6651,8 @@ parse_operands (char *str, const unsigned char *pattern) if (found != 15) goto failure; inst.operands[i].isvec = 1; + /* APSR_nzcv is encoded in instructions as if it were the REG_PC. */ + inst.operands[i].reg = REG_PC; } else goto failure; @@ -6392,13 +6745,13 @@ parse_operands (char *str, const unsigned char *pattern) break; default: - as_fatal (_("unhandled operand code %d"), upat[i]); + as_fatal (_("unhandled operand code %d"), op_parse_code); } /* Various value-based sanity checks and shared operations. We do not signal immediate failures for the register constraints; this allows a syntax error to take precedence. */ - switch (upat[i]) + switch (op_parse_code) { case OP_oRRnpc: case OP_RRnpc: @@ -6410,13 +6763,31 @@ parse_operands (char *str, const unsigned char *pattern) inst.error = BAD_PC; break; + case OP_oRRnpcsp: + case OP_RRnpcsp: + if (inst.operands[i].isreg) + { + if (inst.operands[i].reg == REG_PC) + inst.error = BAD_PC; + else if (inst.operands[i].reg == REG_SP) + inst.error = BAD_SP; + } + break; + + case OP_RRnpctw: + if (inst.operands[i].isreg + && inst.operands[i].reg == REG_PC + && (inst.operands[i].writeback || thumb)) + inst.error = BAD_PC; + break; + case OP_CPSF: case OP_ENDI: case OP_oROR: - case OP_PSR: - case OP_RVC_PSR: + case OP_wPSR: + case OP_rPSR: case OP_COND: - case OP_oBARRIER: + case OP_oBARRIER_I15: case OP_REGLST: case OP_VRSLST: case OP_VRDLST: @@ -6481,6 +6852,7 @@ parse_operands (char *str, const unsigned char *pattern) #undef po_reg_or_goto #undef po_imm_or_fail #undef po_scalar_or_fail +#undef po_barrier_or_imm /* Shorthand macro for instruction encoding functions issuing errors. */ #define constraint(expr, err) \ @@ -6695,10 +7067,15 @@ encode_arm_addr_mode_common (int i, bfd_boolean is_t) static void encode_arm_addr_mode_2 (int i, bfd_boolean is_t) { + const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC); + encode_arm_addr_mode_common (i, is_t); if (inst.operands[i].immisreg) { + constraint ((inst.operands[i].imm == REG_PC + || (is_pc && inst.operands[i].writeback)), + BAD_PC_ADDRESSING); inst.instruction |= INST_IMMEDIATE; /* yes, this is backwards */ inst.instruction |= inst.operands[i].imm; if (!inst.operands[i].negative) @@ -6716,8 +7093,30 @@ encode_arm_addr_mode_2 (int i, bfd_boolean is_t) } else /* immediate offset in inst.reloc */ { + if (is_pc && !inst.reloc.pc_rel) + { + const bfd_boolean is_load = ((inst.instruction & LOAD_BIT) != 0); + + /* If is_t is TRUE, it's called from do_ldstt. ldrt/strt + cannot use PC in addressing. + PC cannot be used in writeback addressing, either. */ + constraint ((is_t || inst.operands[i].writeback), + BAD_PC_ADDRESSING); + + /* Use of PC in str is deprecated for ARMv7. */ + if (warn_on_deprecated + && !is_load + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7)) + as_warn (_("use of PC in this instruction is deprecated")); + } + if (inst.reloc.type == BFD_RELOC_UNUSED) - inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; + { + /* Prefer + for zero encoded value. */ + if (!inst.operands[i].negative) + inst.instruction |= INDEX_UP; + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM; + } } } @@ -6739,15 +7138,27 @@ encode_arm_addr_mode_3 (int i, bfd_boolean is_t) if (inst.operands[i].immisreg) { + constraint ((inst.operands[i].imm == REG_PC + || inst.operands[i].reg == REG_PC), + BAD_PC_ADDRESSING); inst.instruction |= inst.operands[i].imm; if (!inst.operands[i].negative) inst.instruction |= INDEX_UP; } else /* immediate offset in inst.reloc */ { + constraint ((inst.operands[i].reg == REG_PC && !inst.reloc.pc_rel + && inst.operands[i].writeback), + BAD_PC_WRITEBACK); inst.instruction |= HWOFFSET_IMM; if (inst.reloc.type == BFD_RELOC_UNUSED) - inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; + { + /* Prefer + for zero encoded value. */ + if (!inst.operands[i].negative) + inst.instruction |= INDEX_UP; + + inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8; + } } } @@ -6809,6 +7220,10 @@ encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override) inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; } + /* Prefer + for zero encoded value. */ + if (!inst.operands[i].negative) + inst.instruction |= INDEX_UP; + return SUCCESS; } @@ -6936,8 +7351,16 @@ do_rd_rm_rn (void) unsigned Rn = inst.operands[2].reg; /* Enforce restrictions on SWP instruction. */ if ((inst.instruction & 0x0fbfffff) == 0x01000090) - constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg, - _("Rn must not overlap other operands")); + { + constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg, + _("Rn must not overlap other operands")); + + /* SWP{b} is deprecated for ARMv6* and ARMv7. */ + if (warn_on_deprecated + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)) + as_warn (_("swp{b} use is deprecated for this architecture")); + + } inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg; inst.instruction |= Rn << 16; @@ -6954,6 +7377,11 @@ do_rd_rn_rm (void) static void do_rm_rd_rn (void) { + constraint ((inst.operands[2].reg == REG_PC), BAD_PC); + constraint (((inst.reloc.exp.X_op != O_constant + && inst.reloc.exp.X_op != O_illegal) + || inst.reloc.exp.X_add_number != 0), + BAD_ADDR_MODE); inst.instruction |= inst.operands[0].reg; inst.instruction |= inst.operands[1].reg << 12; inst.instruction |= inst.operands[2].reg << 16; @@ -7025,7 +7453,8 @@ do_barrier (void) if (inst.operands[0].present) { constraint ((inst.instruction & 0xf0) != 0x40 - && inst.operands[0].imm != 0xf, + && inst.operands[0].imm > 0xf + && inst.operands[0].imm < 0x0, _("bad barrier type")); inst.instruction |= inst.operands[0].imm; } @@ -7097,14 +7526,15 @@ encode_branch (int default_reloc) { if (inst.operands[0].hasreloc) { - constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32, - _("the only suffix valid here is '(plt)'")); - inst.reloc.type = BFD_RELOC_ARM_PLT32; + constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32 + && inst.operands[0].imm != BFD_RELOC_ARM_TLS_CALL, + _("the only valid suffixes here are '(plt)' and '(tlscall)'")); + inst.reloc.type = inst.operands[0].imm == BFD_RELOC_ARM_PLT32 + ? BFD_RELOC_ARM_PLT32 + : thumb_mode ? BFD_RELOC_ARM_THM_TLS_CALL : BFD_RELOC_ARM_TLS_CALL; } else - { - inst.reloc.type = (bfd_reloc_code_real_type) default_reloc; - } + inst.reloc.type = (bfd_reloc_code_real_type) default_reloc; inst.reloc.pc_rel = 1; } @@ -7320,6 +7750,25 @@ do_dbg (void) inst.instruction |= inst.operands[0].imm; } +static void +do_div (void) +{ + unsigned Rd, Rn, Rm; + + Rd = inst.operands[0].reg; + Rn = (inst.operands[1].present + ? inst.operands[1].reg : Rd); + Rm = inst.operands[2].reg; + + constraint ((Rd == REG_PC), BAD_PC); + constraint ((Rn == REG_PC), BAD_PC); + constraint ((Rm == REG_PC), BAD_PC); + + inst.instruction |= Rd << 16; + inst.instruction |= Rn << 0; + inst.instruction |= Rm << 8; +} + static void do_it (void) { @@ -7386,35 +7835,34 @@ static void do_ldrd (void) { constraint (inst.operands[0].reg % 2 != 0, - _("first destination register must be even")); + _("first transfer register must be even")); constraint (inst.operands[1].present && inst.operands[1].reg != inst.operands[0].reg + 1, - _("can only load two consecutive registers")); + _("can only transfer two consecutive registers")); constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here")); constraint (!inst.operands[2].isreg, _("'[' expected")); if (!inst.operands[1].present) inst.operands[1].reg = inst.operands[0].reg + 1; - if (inst.instruction & LOAD_BIT) - { - /* encode_arm_addr_mode_3 will diagnose overlap between the base - register and the first register written; we have to diagnose - overlap between the base and the second register written here. */ + /* encode_arm_addr_mode_3 will diagnose overlap between the base + register and the first register written; we have to diagnose + overlap between the base and the second register written here. */ - if (inst.operands[2].reg == inst.operands[1].reg - && (inst.operands[2].writeback || inst.operands[2].postind)) - as_warn (_("base register written back, and overlaps " - "second destination register")); + if (inst.operands[2].reg == inst.operands[1].reg + && (inst.operands[2].writeback || inst.operands[2].postind)) + as_warn (_("base register written back, and overlaps " + "second transfer register")); + if (!(inst.instruction & V4_STR_BIT)) + { /* For an index-register load, the index register must not overlap the - destination (even if not write-back). */ - else if (inst.operands[2].immisreg - && ((unsigned) inst.operands[2].imm == inst.operands[0].reg - || (unsigned) inst.operands[2].imm == inst.operands[1].reg)) - as_warn (_("index register overlaps destination register")); + destination (even if not write-back). */ + if (inst.operands[2].immisreg + && ((unsigned) inst.operands[2].imm == inst.operands[0].reg + || (unsigned) inst.operands[2].imm == inst.operands[1].reg)) + as_warn (_("index register overlaps transfer register")); } - inst.instruction |= inst.operands[0].reg << 12; encode_arm_addr_mode_3 (2, /*is_t=*/FALSE); } @@ -7444,6 +7892,8 @@ do_ldrex (void) || inst.reloc.exp.X_add_number != 0, _("offset must be zero in ARM encoding")); + constraint ((inst.operands[1].reg == REG_PC), BAD_PC); + inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg << 16; inst.reloc.type = BFD_RELOC_UNUSED; @@ -7499,6 +7949,7 @@ do_ldstt (void) static void do_ldstv4 (void) { + constraint (inst.operands[0].reg == REG_PC, BAD_PC); inst.instruction |= inst.operands[0].reg << 12; if (!inst.operands[1].isreg) if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE)) @@ -7611,18 +8062,76 @@ do_vfp_nsyn_msr (void) return SUCCESS; } +static void +do_vmrs (void) +{ + unsigned Rt = inst.operands[0].reg; + + if (thumb_mode && inst.operands[0].reg == REG_SP) + { + inst.error = BAD_SP; + return; + } + + /* APSR_ sets isvec. All other refs to PC are illegal. */ + if (!inst.operands[0].isvec && inst.operands[0].reg == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (inst.operands[1].reg != 1) + first_error (_("operand 1 must be FPSCR")); + + inst.instruction |= (Rt << 12); +} + +static void +do_vmsr (void) +{ + unsigned Rt = inst.operands[1].reg; + + if (thumb_mode) + reject_bad_reg (Rt); + else if (Rt == REG_PC) + { + inst.error = BAD_PC; + return; + } + + if (inst.operands[0].reg != 1) + first_error (_("operand 0 must be FPSCR")); + + inst.instruction |= (Rt << 12); +} + static void do_mrs (void) { + unsigned br; + if (do_vfp_nsyn_mrs () == SUCCESS) return; - /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ - constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f)) - != (PSR_c|PSR_f), - _("'CPSR' or 'SPSR' expected")); + constraint (inst.operands[0].reg == REG_PC, BAD_PC); inst.instruction |= inst.operands[0].reg << 12; - inst.instruction |= (inst.operands[1].imm & SPSR_BIT); + + if (inst.operands[1].isreg) + { + br = inst.operands[1].reg; + if (((br & 0x200) == 0) && ((br & 0xf0000) != 0xf000)) + as_bad (_("bad register for mrs")); + } + else + { + /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ + constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f)) + != (PSR_c|PSR_f), + _("'APSR', 'CPSR' or 'SPSR' expected")); + br = (15<<16) | (inst.operands[1].imm & SPSR_BIT); + } + + inst.instruction |= br; } /* Two possible forms: @@ -7649,6 +8158,8 @@ do_msr (void) static void do_mul (void) { + constraint (inst.operands[2].reg == REG_PC, BAD_PC); + if (!inst.operands[2].present) inst.operands[2].reg = inst.operands[0].reg; inst.instruction |= inst.operands[0].reg << 16; @@ -7738,8 +8249,9 @@ do_pkhtb (void) } /* ARMv5TE: Preload-Cache + MP Extensions: Preload for write - PLD + PLD(W) Syntactically, like LDR with B=1, W=0, L=1. */ @@ -7868,6 +8380,9 @@ do_shift (void) { inst.instruction |= inst.operands[2].reg << 8; inst.instruction |= SHIFT_BY_REG; + /* PR 12854: Error on extraneous shifts. */ + constraint (inst.operands[2].shifted, + _("extraneous shift as part of operand to shift insn")); } else inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM; @@ -7880,6 +8395,13 @@ do_smc (void) inst.reloc.pc_rel = 0; } +static void +do_hvc (void) +{ + inst.reloc.type = BFD_RELOC_ARM_HVC; + inst.reloc.pc_rel = 0; +} + static void do_swi (void) { @@ -7978,6 +8500,21 @@ do_strex (void) inst.reloc.type = BFD_RELOC_UNUSED; } +static void +do_t_strexbh (void) +{ + constraint (!inst.operands[2].isreg || !inst.operands[2].preind + || inst.operands[2].postind || inst.operands[2].writeback + || inst.operands[2].immisreg || inst.operands[2].shifted + || inst.operands[2].negative, + BAD_ADDR_MODE); + + constraint (inst.operands[0].reg == inst.operands[1].reg + || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP); + + do_rm_rd_rn (); +} + static void do_strexd (void) { @@ -8251,7 +8788,23 @@ do_vfp_dp_const (void) static void vfp_conv (int srcsize) { - unsigned immbits = srcsize - inst.operands[1].imm; + int immbits = srcsize - inst.operands[1].imm; + + if (srcsize == 16 && !(immbits >= 0 && immbits <= srcsize)) + { + /* If srcsize is 16, inst.operands[1].imm must be in the range 0-16. + i.e. immbits must be in range 0 - 16. */ + inst.error = _("immediate value out of range, expected range [0, 16]"); + return; + } + else if (srcsize == 32 && !(immbits >= 0 && immbits < srcsize)) + { + /* If srcsize is 32, inst.operands[1].imm must be in the range 1-32. + i.e. immbits must be in range 0 - 31. */ + inst.error = _("immediate value out of range, expected range [1, 32]"); + return; + } + inst.instruction |= (immbits & 1) << 5; inst.instruction |= (immbits >> 1); } @@ -8646,12 +9199,13 @@ encode_thumb32_shifted_operand (int i) Thumb32 format load or store instruction. Reject forms that cannot be used with such instructions. If is_t is true, reject forms that cannot be used with a T instruction; if is_d is true, reject forms - that cannot be used with a D instruction. */ + that cannot be used with a D instruction. If it is a store insn, + reject PC in Rn. */ static void encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) { - bfd_boolean is_pc = (inst.operands[i].reg == REG_PC); + const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC); constraint (!inst.operands[i].isreg, _("Instruction does not support =N addresses")); @@ -8659,7 +9213,7 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) inst.instruction |= inst.operands[i].reg << 16; if (inst.operands[i].immisreg) { - constraint (is_pc, _("cannot use register index with PC-relative addressing")); + constraint (is_pc, BAD_PC_ADDRESSING); constraint (is_t || is_d, _("cannot use register index with this instruction")); constraint (inst.operands[i].negative, _("Thumb does not support negative register indexing")); @@ -8684,10 +9238,11 @@ encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d) } else if (inst.operands[i].preind) { - constraint (is_pc && inst.operands[i].writeback, - _("cannot use writeback with PC-relative addressing")); + constraint (is_pc && inst.operands[i].writeback, BAD_PC_WRITEBACK); constraint (is_t && inst.operands[i].writeback, _("cannot use writeback with this instruction")); + constraint (is_pc && ((inst.instruction & THUMB2_LOAD_BIT) == 0) + && !inst.reloc.pc_rel, BAD_PC_ADDRESSING); if (is_d) { @@ -8951,6 +9506,9 @@ do_t_add_sub (void) } else { + unsigned int value = inst.reloc.exp.X_add_number; + unsigned int shift = inst.operands[2].shift_kind; + Rn = inst.operands[2].reg; /* See if we can do this with a 16-bit instruction. */ if (!inst.operands[2].shifted && inst.size_req != 4) @@ -9001,6 +9559,10 @@ do_t_add_sub (void) inst.instruction = THUMB_OP32 (inst.instruction); inst.instruction |= Rd << 8; inst.instruction |= Rs << 16; + constraint (Rd == REG_SP && Rs == REG_SP && value > 3, + _("shift value over 3 not allowed in thumb mode")); + constraint (Rd == REG_SP && Rs == REG_SP && shift != SHIFT_LSL, + _("only LSL shift allowed in thumb mode")); encode_thumb32_shifted_operand (2); } } @@ -9281,7 +9843,8 @@ do_t_barrier (void) if (inst.operands[0].present) { constraint ((inst.instruction & 0xf0) != 0x40 - && inst.operands[0].imm != 0xf, + && inst.operands[0].imm > 0xf + && inst.operands[0].imm < 0x0, _("bad barrier type")); inst.instruction |= inst.operands[0].imm; } @@ -9380,8 +9943,7 @@ do_t_blx (void) { /* No register. This must be BLX(1). */ inst.instruction = 0xf000e800; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX; - inst.reloc.pc_rel = 1; + encode_branch (BFD_RELOC_THUMB_PCREL_BLX); } } @@ -9390,6 +9952,7 @@ do_t_branch (void) { int opcode; int cond; + int reloc; cond = inst.cond; set_it_insn_type (IF_INSIDE_IT_LAST_INSN); @@ -9408,33 +9971,37 @@ do_t_branch (void) else opcode = inst.instruction; - if (unified_syntax && inst.size_req == 4) + if (unified_syntax + && (inst.size_req == 4 + || (inst.size_req != 2 + && (inst.operands[0].hasreloc + || inst.reloc.exp.X_op == O_constant)))) { inst.instruction = THUMB_OP32(opcode); if (cond == COND_ALWAYS) - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH25; else { gas_assert (cond != 0xF); inst.instruction |= cond << 22; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH20; } } else { inst.instruction = THUMB_OP16(opcode); if (cond == COND_ALWAYS) - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH12; else { inst.instruction |= cond << 8; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH9; } /* Allow section relaxation. */ if (unified_syntax && inst.size_req != 2) inst.relax = opcode; } - + inst.reloc.type = reloc; inst.reloc.pc_rel = 1; } @@ -9456,8 +10023,15 @@ static void do_t_branch23 (void) { set_it_insn_type_last (); - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; - inst.reloc.pc_rel = 1; + encode_branch (BFD_RELOC_THUMB_PCREL_BRANCH23); + + /* md_apply_fix blows up with 'bl foo(PLT)' where foo is defined in + this file. We used to simply ignore the PLT reloc type here -- + the branch encoding is now needed to deal with TLSCALL relocs. + So if we see a PLT reloc now, put it back to how it used to be to + keep the preexisting behaviour. */ + if (inst.reloc.type == BFD_RELOC_ARM_PLT32) + inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; #if defined(OBJ_COFF) /* If the destination of the branch is a defined symbol which does not have @@ -9649,6 +10223,12 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) if (mask & (1 << 13)) inst.error = _("SP not allowed in register list"); + + if ((mask & (1 << base)) != 0 + && writeback) + inst.error = _("having the base register in the register list when " + "using write back is UNPREDICTABLE"); + if (load) { if (mask & (1 << 15)) @@ -9658,19 +10238,11 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) else set_it_insn_type_last (); } - - if ((mask & (1 << base)) != 0 - && writeback) - as_warn (_("base register should not be in register list " - "when written back")); } else { if (mask & (1 << 15)) inst.error = _("PC not allowed in register list"); - - if (mask & (1 << base)) - as_warn (_("value stored for r%d is UNPREDICTABLE"), base); } if ((mask & (mask - 1)) == 0) @@ -9727,30 +10299,68 @@ do_t_ldmstm (void) { mask = 1 << inst.operands[0].reg; - if (inst.operands[0].reg <= 7 - && (inst.instruction == T_MNEM_stmia - ? inst.operands[0].writeback - : (inst.operands[0].writeback - == !(inst.operands[1].imm & mask)))) + if (inst.operands[0].reg <= 7) { if (inst.instruction == T_MNEM_stmia - && (inst.operands[1].imm & mask) - && (inst.operands[1].imm & (mask - 1))) - as_warn (_("value stored for r%d is UNPREDICTABLE"), - inst.operands[0].reg); + ? inst.operands[0].writeback + : (inst.operands[0].writeback + == !(inst.operands[1].imm & mask))) + { + if (inst.instruction == T_MNEM_stmia + && (inst.operands[1].imm & mask) + && (inst.operands[1].imm & (mask - 1))) + as_warn (_("value stored for r%d is UNKNOWN"), + inst.operands[0].reg); - inst.instruction = THUMB_OP16 (inst.instruction); - inst.instruction |= inst.operands[0].reg << 8; - inst.instruction |= inst.operands[1].imm; - narrow = TRUE; + inst.instruction = THUMB_OP16 (inst.instruction); + inst.instruction |= inst.operands[0].reg << 8; + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; + } + else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0) + { + /* This means 1 register in reg list one of 3 situations: + 1. Instruction is stmia, but without writeback. + 2. lmdia without writeback, but with Rn not in + reglist. + 3. ldmia with writeback, but with Rn in reglist. + Case 3 is UNPREDICTABLE behaviour, so we handle + case 1 and 2 which can be converted into a 16-bit + str or ldr. The SP cases are handled below. */ + unsigned long opcode; + /* First, record an error for Case 3. */ + if (inst.operands[1].imm & mask + && inst.operands[0].writeback) + inst.error = + _("having the base register in the register list when " + "using write back is UNPREDICTABLE"); + + opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str + : T_MNEM_ldr); + inst.instruction = THUMB_OP16 (opcode); + inst.instruction |= inst.operands[0].reg << 3; + inst.instruction |= (ffs (inst.operands[1].imm)-1); + narrow = TRUE; + } } - else if (inst.operands[0] .reg == REG_SP - && inst.operands[0].writeback) + else if (inst.operands[0] .reg == REG_SP) { - inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia - ? T_MNEM_push : T_MNEM_pop); - inst.instruction |= inst.operands[1].imm; - narrow = TRUE; + if (inst.operands[0].writeback) + { + inst.instruction = + THUMB_OP16 (inst.instruction == T_MNEM_stmia + ? T_MNEM_push : T_MNEM_pop); + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; + } + else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0) + { + inst.instruction = + THUMB_OP16 (inst.instruction == T_MNEM_stmia + ? T_MNEM_str_sp : T_MNEM_ldr_sp); + inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8); + narrow = TRUE; + } } } @@ -9776,7 +10386,7 @@ do_t_ldmstm (void) as_warn (_("this instruction will write back the base register")); if ((inst.operands[1].imm & (1 << inst.operands[0].reg)) && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1))) - as_warn (_("value stored for r%d is UNPREDICTABLE"), + as_warn (_("value stored for r%d is UNKNOWN"), inst.operands[0].reg); } else @@ -9804,6 +10414,8 @@ do_t_ldrex (void) || inst.operands[1].negative, BAD_ADDR_MODE); + constraint ((inst.operands[1].reg == REG_PC), BAD_PC); + inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg << 16; inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8; @@ -9863,6 +10475,8 @@ do_t_ldst (void) /* [Rn, Rik] */ if (Rn <= 7 && inst.operands[1].imm <= 7) goto op16; + else if (opcode != T_MNEM_ldr && opcode != T_MNEM_str) + reject_bad_reg (inst.operands[1].imm); } else if ((Rn <= 7 && opcode != T_MNEM_ldrsh && opcode != T_MNEM_ldrsb) @@ -9902,6 +10516,27 @@ do_t_ldst (void) } } /* Definitely a 32-bit variant. */ + + /* Warning for Erratum 752419. */ + if (opcode == T_MNEM_ldr + && inst.operands[0].reg == REG_SP + && inst.operands[1].writeback == 1 + && !inst.operands[1].immisreg) + { + if (no_cpu_selected () + || (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7) + && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a) + && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7r))) + as_warn (_("This instruction may be unpredictable " + "if executed on M-profile cores " + "with interrupts enabled.")); + } + + /* Do some validations regarding addressing modes. */ + if (inst.operands[1].immisreg && opcode != T_MNEM_ldr + && opcode != T_MNEM_str) + reject_bad_reg (inst.operands[1].imm); + inst.instruction = THUMB_OP32 (opcode); inst.instruction |= inst.operands[0].reg << 12; encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE); @@ -10115,9 +10750,24 @@ do_t_mov_cmp (void) reject_bad_reg (Rn); reject_bad_reg (Rm); } - else if ((Rn == REG_SP || Rn == REG_PC) - && (Rm == REG_SP || Rm == REG_PC)) - reject_bad_reg (Rm); + else if (narrow) + { + /* This is mov.n. */ + if ((Rn == REG_SP || Rn == REG_PC) + && (Rm == REG_SP || Rm == REG_PC)) + { + as_warn (_("Use of r%u as a source register is " + "deprecated when r%u is the destination " + "register."), Rm, Rn); + } + } + else + { + /* This is mov.w. */ + constraint (Rn == REG_PC, BAD_PC); + constraint (Rm == REG_PC, BAD_PC); + constraint (Rn == REG_SP && Rm == REG_SP, BAD_SP); + } } else reject_bad_reg (Rn); @@ -10250,8 +10900,8 @@ do_t_mov_cmp (void) case T_MNEM_movs: /* We know we have low registers at this point. - Generate ADD Rd, Rs, #0. */ - inst.instruction = T_OPCODE_ADD_I3; + Generate LSLS Rd, Rs, #0. */ + inst.instruction = T_OPCODE_LSL_I; inst.instruction |= Rn; inst.instruction |= Rm << 3; break; @@ -10429,34 +11079,41 @@ static void do_t_mrs (void) { unsigned Rd; - int flags; if (do_vfp_nsyn_mrs () == SUCCESS) return; - flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); - if (flags == 0) + Rd = inst.operands[0].reg; + reject_bad_reg (Rd); + inst.instruction |= Rd << 8; + + if (inst.operands[1].isreg) { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); + unsigned br = inst.operands[1].reg; + if (((br & 0x200) == 0) && ((br & 0xf000) != 0xf000)) + as_bad (_("bad register for mrs")); + + inst.instruction |= br & (0xf << 16); + inst.instruction |= (br & 0x300) >> 4; + inst.instruction |= (br & SPSR_BIT) >> 2; } else { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1), - _("selected processor does not support " - "requested special purpose register")); - /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ - constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f), - _("'CPSR' or 'SPSR' expected")); - } + int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); - Rd = inst.operands[0].reg; - reject_bad_reg (Rd); + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) + constraint (flags != 0, _("selected processor does not support " + "requested special purpose register")); + else + /* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile + devices). */ + constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f), + _("'APSR', 'CPSR' or 'SPSR' expected")); - inst.instruction |= Rd << 8; - inst.instruction |= (flags & SPSR_BIT) >> 2; - inst.instruction |= inst.operands[1].imm & 0xff; + inst.instruction |= (flags & SPSR_BIT) >> 2; + inst.instruction |= inst.operands[1].imm & 0xff; + inst.instruction |= 0xf0000; + } } static void @@ -10470,26 +11127,33 @@ do_t_msr (void) constraint (!inst.operands[1].isreg, _("Thumb encoding does not support an immediate here")); - flags = inst.operands[0].imm; - if (flags & ~0xff) - { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1), - _("selected processor does not support " - "requested special purpose register")); - } + + if (inst.operands[0].isreg) + flags = (int)(inst.operands[0].reg); else + flags = inst.operands[0].imm; + + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); - flags |= PSR_f; + int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); + + constraint ((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp) + && (bits & ~(PSR_s | PSR_f)) != 0) + || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp) + && bits != PSR_f), + _("selected processor does not support requested special " + "purpose register")); } + else + constraint ((flags & 0xff) != 0, _("selected processor does not support " + "requested special purpose register")); Rn = inst.operands[1].reg; reject_bad_reg (Rn); inst.instruction |= (flags & SPSR_BIT) >> 2; - inst.instruction |= (flags & ~SPSR_BIT) >> 8; + inst.instruction |= (flags & 0xf0000) >> 8; + inst.instruction |= (flags & 0x300) >> 4; inst.instruction |= (flags & 0xff); inst.instruction |= Rn << 16; } @@ -10597,7 +11261,7 @@ do_t_nop (void) { /* PR9722: Check for Thumb2 availability before generating a thumb2 nop instruction. */ - if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)) + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2)) { inst.instruction = THUMB_OP16 (inst.instruction); inst.instruction |= inst.operands[0].imm << 4; @@ -10949,6 +11613,10 @@ do_t_shift (void) inst.instruction |= inst.operands[0].reg << 8; inst.instruction |= inst.operands[1].reg << 16; inst.instruction |= inst.operands[2].reg; + + /* PR 12854: Error on extraneous shifts. */ + constraint (inst.operands[2].shifted, + _("extraneous shift as part of operand to shift insn")); } else { @@ -10977,6 +11645,10 @@ do_t_shift (void) inst.instruction |= inst.operands[0].reg; inst.instruction |= inst.operands[2].reg << 3; + + /* PR 12854: Error on extraneous shifts. */ + constraint (inst.operands[2].shifted, + _("extraneous shift as part of operand to shift insn")); } else { @@ -11016,6 +11688,10 @@ do_t_shift (void) inst.instruction |= inst.operands[0].reg; inst.instruction |= inst.operands[2].reg << 3; + + /* PR 12854: Error on extraneous shifts. */ + constraint (inst.operands[2].shifted, + _("extraneous shift as part of operand to shift insn")); } else { @@ -11052,10 +11728,30 @@ do_t_simd (void) inst.instruction |= Rm; } +static void +do_t_simd2 (void) +{ + unsigned Rd, Rn, Rm; + + Rd = inst.operands[0].reg; + Rm = inst.operands[1].reg; + Rn = inst.operands[2].reg; + + reject_bad_reg (Rd); + reject_bad_reg (Rn); + reject_bad_reg (Rm); + + inst.instruction |= Rd << 8; + inst.instruction |= Rn << 16; + inst.instruction |= Rm; +} + static void do_t_smc (void) { unsigned int value = inst.reloc.exp.X_add_number; + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a), + _("SMC is not permitted on this architecture")); constraint (inst.reloc.exp.X_op != O_constant, _("expression too complex")); inst.reloc.type = BFD_RELOC_UNUSED; @@ -11064,6 +11760,16 @@ do_t_smc (void) inst.instruction |= (value & 0x000f) << 16; } +static void +do_t_hvc (void) +{ + unsigned int value = inst.reloc.exp.X_add_number; + + inst.reloc.type = BFD_RELOC_UNUSED; + inst.instruction |= (value & 0x0fff); + inst.instruction |= (value & 0xf000) << 4; +} + static void do_t_ssat_usat (int bias) { @@ -11133,6 +11839,8 @@ do_t_strex (void) || inst.operands[2].negative, BAD_ADDR_MODE); + constraint (inst.operands[2].reg == REG_PC, BAD_PC); + inst.instruction |= inst.operands[0].reg << 8; inst.instruction |= inst.operands[1].reg << 12; inst.instruction |= inst.operands[2].reg << 16; @@ -11147,8 +11855,7 @@ do_t_strexd (void) constraint (inst.operands[0].reg == inst.operands[1].reg || inst.operands[0].reg == inst.operands[2].reg - || inst.operands[0].reg == inst.operands[3].reg - || inst.operands[1].reg == inst.operands[2].reg, + || inst.operands[0].reg == inst.operands[3].reg, BAD_OVERLAP); inst.instruction |= inst.operands[0].reg; @@ -11215,6 +11922,17 @@ do_t_sxth (void) static void do_t_swi (void) { + /* We have to do the following check manually as ARM_EXT_OS only applies + to ARM_EXT_V6M. */ + if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m)) + { + if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os) + /* This only applies to the v6m howver, not later architectures. */ + && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7)) + as_bad (_("SVC is not permitted on this architecture")); + ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_os); + } + inst.reloc.type = BFD_RELOC_ARM_SWI; } @@ -11293,6 +12011,8 @@ struct neon_tab_entry vcge / vcgt with the operands reversed. */ \ X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \ X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \ + X(vfma, N_INV, 0x0000c10, N_INV), \ + X(vfms, N_INV, 0x0200c10, N_INV), \ X(vmla, 0x0000900, 0x0000d10, 0x0800040), \ X(vmls, 0x1000900, 0x0200d10, 0x0800440), \ X(vmul, 0x0000910, 0x1000d10, 0x0800840), \ @@ -11328,8 +12048,10 @@ struct neon_tab_entry X(vqmovn, 0x1b20200, N_INV, N_INV), \ X(vqmovun, 0x1b20240, N_INV, N_INV), \ X(vnmul, 0xe200a40, 0xe200b40, N_INV), \ - X(vnmla, 0xe000a40, 0xe000b40, N_INV), \ - X(vnmls, 0xe100a40, 0xe100b40, N_INV), \ + X(vnmla, 0xe100a40, 0xe100b40, N_INV), \ + X(vnmls, 0xe100a00, 0xe100b00, N_INV), \ + X(vfnma, 0xe900a40, 0xe900b40, N_INV), \ + X(vfnms, 0xe900a00, 0xe900b00, N_INV), \ X(vcmp, 0xeb40a40, 0xeb40b40, N_INV), \ X(vcmpz, 0xeb50a40, 0xeb50b40, N_INV), \ X(vcmpe, 0xeb40ac0, 0xeb40bc0, N_INV), \ @@ -11349,20 +12071,40 @@ NEON_ENC_TAB #undef X }; -#define NEON_ENC_INTEGER(X) (neon_enc_tab[(X) & 0x0fffffff].integer) -#define NEON_ENC_ARMREG(X) (neon_enc_tab[(X) & 0x0fffffff].integer) -#define NEON_ENC_POLY(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) -#define NEON_ENC_FLOAT(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) -#define NEON_ENC_SCALAR(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) -#define NEON_ENC_IMMED(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) -#define NEON_ENC_INTERLV(X) (neon_enc_tab[(X) & 0x0fffffff].integer) -#define NEON_ENC_LANE(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) -#define NEON_ENC_DUP(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) -#define NEON_ENC_SINGLE(X) \ +/* Do not use these macros; instead, use NEON_ENCODE defined below. */ +#define NEON_ENC_INTEGER_(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_ARMREG_(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_POLY_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_FLOAT_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_SCALAR_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_IMMED_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_INTERLV_(X) (neon_enc_tab[(X) & 0x0fffffff].integer) +#define NEON_ENC_LANE_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly) +#define NEON_ENC_DUP_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm) +#define NEON_ENC_SINGLE_(X) \ ((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf0000000)) -#define NEON_ENC_DOUBLE(X) \ +#define NEON_ENC_DOUBLE_(X) \ ((neon_enc_tab[(X) & 0x0fffffff].float_or_poly) | ((X) & 0xf0000000)) +#define NEON_ENCODE(type, inst) \ + do \ + { \ + inst.instruction = NEON_ENC_##type##_ (inst.instruction); \ + inst.is_neon = 1; \ + } \ + while (0) + +#define check_neon_suffixes \ + do \ + { \ + if (!inst.error && inst.vectype.elems > 0 && !inst.is_neon) \ + { \ + as_bad (_("invalid neon suffix for non neon instruction")); \ + return; \ + } \ + } \ + while (0) + /* Define shapes for instruction operands. The following mnemonic characters are used in this table: @@ -11638,6 +12380,8 @@ neon_select_shape (enum neon_shape shape, ...) case SE_L: break; } + if (!matches) + break; } if (matches) break; @@ -11975,8 +12719,17 @@ neon_check_type (unsigned els, enum neon_shape ns, ...) { if ((thisarg & N_VFP) != 0) { - enum neon_shape_el regshape = neon_shape_tab[ns].el[i]; - unsigned regwidth = neon_shape_el_size[regshape], match; + enum neon_shape_el regshape; + unsigned regwidth, match; + + /* PR 11136: Catch the case where we are passed a shape of NS_NULL. */ + if (ns == NS_NULL) + { + first_error (_("invalid instruction shape")); + return badtype; + } + regshape = neon_shape_tab[ns].el[i]; + regwidth = neon_shape_el_size[regshape]; /* In VFP mode, operands must match register widths. If we have a key operand, use its width, else use the width of @@ -12028,6 +12781,8 @@ neon_check_type (unsigned els, enum neon_shape ns, ...) static void do_vfp_cond_or_thumb (void) { + inst.is_neon = 1; + if (thumb_mode) inst.instruction |= 0xe0000000; else @@ -12056,6 +12811,8 @@ do_vfp_nsyn_opcode (const char *opname) thumb_mode ? *opcode->tvariant : *opcode->avariant), _(BAD_FPU)); + inst.is_neon = 1; + if (thumb_mode) { inst.instruction = opcode->tvalue; @@ -12121,9 +12878,8 @@ try_vfp_nsyn (int args, void (*pfn) (enum neon_shape)) pfn (rs); return SUCCESS; } - else - inst.error = NULL; + inst.error = NULL; return FAIL; } @@ -12137,14 +12893,35 @@ do_vfp_nsyn_mla_mls (enum neon_shape rs) if (is_mla) do_vfp_nsyn_opcode ("fmacs"); else - do_vfp_nsyn_opcode ("fmscs"); + do_vfp_nsyn_opcode ("fnmacs"); } else { if (is_mla) do_vfp_nsyn_opcode ("fmacd"); else - do_vfp_nsyn_opcode ("fmscd"); + do_vfp_nsyn_opcode ("fnmacd"); + } +} + +static void +do_vfp_nsyn_fma_fms (enum neon_shape rs) +{ + int is_fma = (inst.instruction & 0x0fffffff) == N_MNEM_vfma; + + if (rs == NS_FFF) + { + if (is_fma) + do_vfp_nsyn_opcode ("ffmas"); + else + do_vfp_nsyn_opcode ("ffnmas"); + } + else + { + if (is_fma) + do_vfp_nsyn_opcode ("ffmad"); + else + do_vfp_nsyn_opcode ("ffnmad"); } } @@ -12236,12 +13013,12 @@ do_vfp_nsyn_nmul (void) if (rs == NS_FFF) { - inst.instruction = NEON_ENC_SINGLE (inst.instruction); + NEON_ENCODE (SINGLE, inst); do_vfp_sp_dyadic (); } else { - inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + NEON_ENCODE (DOUBLE, inst); do_vfp_dp_rd_rn_rm (); } do_vfp_cond_or_thumb (); @@ -12257,12 +13034,12 @@ do_vfp_nsyn_cmp (void) if (rs == NS_FF) { - inst.instruction = NEON_ENC_SINGLE (inst.instruction); + NEON_ENCODE (SINGLE, inst); do_vfp_sp_monadic (); } else { - inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + NEON_ENCODE (DOUBLE, inst); do_vfp_dp_rd_rm (); } } @@ -12285,12 +13062,12 @@ do_vfp_nsyn_cmp (void) if (rs == NS_FI) { - inst.instruction = NEON_ENC_SINGLE (inst.instruction); + NEON_ENCODE (SINGLE, inst); do_vfp_sp_compare_z (); } else { - inst.instruction = NEON_ENC_DOUBLE (inst.instruction); + NEON_ENCODE (DOUBLE, inst); do_vfp_dp_rd (); } } @@ -12331,9 +13108,12 @@ do_vfp_nsyn_pop (void) /* Fix up Neon data-processing instructions, ORing in the correct bits for ARM mode or Thumb mode and moving the encoded bit 24 to bit 28. */ -static unsigned -neon_dp_fixup (unsigned i) +static void +neon_dp_fixup (struct arm_it* insn) { + unsigned int i = insn->instruction; + insn->is_neon = 1; + if (thumb_mode) { /* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */ @@ -12347,7 +13127,7 @@ neon_dp_fixup (unsigned i) else i |= 0xf2000000; - return i; + insn->instruction = i; } /* Turn a size (8, 16, 32, 64) into the respective bit number minus 3 @@ -12384,7 +13164,7 @@ neon_three_same (int isquad, int ubit, int size) if (size != -1) inst.instruction |= neon_logbits (size) << 20; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } /* Encode instructions of the form: @@ -12407,7 +13187,7 @@ neon_two_same (int qbit, int ubit, int size) if (size != -1) inst.instruction |= neon_logbits (size) << 18; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } /* Neon instruction encoders, in approximate order of appearance. */ @@ -12446,7 +13226,7 @@ neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et, if (write_ubit) inst.instruction |= (uval != 0) << 24; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -12456,7 +13236,7 @@ do_neon_shl_imm (void) { enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_KEY | N_I_ALL); - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); neon_imm_shift (FALSE, 0, neon_quad (rs), et, inst.operands[2].imm); } else @@ -12476,7 +13256,7 @@ do_neon_shl_imm (void) tmp = inst.operands[2].reg; inst.operands[2].reg = inst.operands[1].reg; inst.operands[1].reg = tmp; - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); } } @@ -12489,7 +13269,7 @@ do_neon_qshl_imm (void) enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL); struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_ALL | N_KEY); - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et, inst.operands[2].imm); } @@ -12504,7 +13284,7 @@ do_neon_qshl_imm (void) tmp = inst.operands[2].reg; inst.operands[2].reg = inst.operands[1].reg; inst.operands[1].reg = tmp; - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size); } } @@ -12778,12 +13558,17 @@ do_neon_logic (void) enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); neon_check_type (3, rs, N_IGNORE_TYPE); /* U bit and size field were set as part of the bitmask. */ - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_three_same (neon_quad (rs), 0, -1); } else { - enum neon_shape rs = neon_select_shape (NS_DI, NS_QI, NS_NULL); + const int three_ops_form = (inst.operands[2].present + && !inst.operands[2].isreg); + const int immoperand = (three_ops_form ? 2 : 1); + enum neon_shape rs = (three_ops_form + ? neon_select_shape (NS_DDI, NS_QQI, NS_NULL) + : neon_select_shape (NS_DI, NS_QI, NS_NULL)); struct neon_type_el et = neon_check_type (2, rs, N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK); enum neon_opc opcode = (enum neon_opc) inst.instruction & 0x0fffffff; @@ -12793,15 +13578,19 @@ do_neon_logic (void) if (et.type == NT_invtype) return; - inst.instruction = NEON_ENC_IMMED (inst.instruction); + if (three_ops_form) + constraint (inst.operands[0].reg != inst.operands[1].reg, + _("first and second operands shall be the same register")); - immbits = inst.operands[1].imm; + NEON_ENCODE (IMMED, inst); + + immbits = inst.operands[immoperand].imm; if (et.size == 64) { /* .i64 is a pseudo-op, so the immediate must be a repeating pattern. */ - if (immbits != (inst.operands[1].regisimm ? - inst.operands[1].reg : 0)) + if (immbits != (inst.operands[immoperand].regisimm ? + inst.operands[immoperand].reg : 0)) { /* Set immbits to an invalid constant. */ immbits = 0xdeadbeef; @@ -12843,7 +13632,7 @@ do_neon_logic (void) inst.instruction |= cmode << 8; neon_write_immbits (immbits); - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } } @@ -12864,12 +13653,12 @@ neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types, types | N_KEY); if (et.type == NT_float) { - inst.instruction = NEON_ENC_FLOAT (inst.instruction); + NEON_ENCODE (FLOAT, inst); neon_three_same (neon_quad (rs), 0, -1); } else { - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_three_same (neon_quad (rs), et.type == ubit_meaning, et.size); } } @@ -13002,7 +13791,7 @@ neon_compare (unsigned regtypes, unsigned immtypes, int invert) struct neon_type_el et = neon_check_type (2, rs, N_EQK | N_SIZ, immtypes | N_KEY); - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); inst.instruction |= LOW4 (inst.operands[0].reg) << 12; inst.instruction |= HI1 (inst.operands[0].reg) << 22; inst.instruction |= LOW4 (inst.operands[1].reg); @@ -13011,7 +13800,7 @@ neon_compare (unsigned regtypes, unsigned immtypes, int invert) inst.instruction |= (et.type == NT_float) << 10; inst.instruction |= neon_logbits (et.size) << 18; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } } @@ -13087,7 +13876,7 @@ neon_mul_mac (struct neon_type_el et, int ubit) inst.instruction |= neon_logbits (et.size) << 20; inst.instruction |= (ubit != 0) << 24; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -13104,7 +13893,7 @@ do_neon_mac_maybe_scalar (void) enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL); struct neon_type_el et = neon_check_type (3, rs, N_EQK, N_EQK, N_I16 | N_I32 | N_F32 | N_KEY); - inst.instruction = NEON_ENC_SCALAR (inst.instruction); + NEON_ENCODE (SCALAR, inst); neon_mul_mac (et, neon_quad (rs)); } else @@ -13115,6 +13904,18 @@ do_neon_mac_maybe_scalar (void) } } +static void +do_neon_fmac (void) +{ + if (try_vfp_nsyn (3, do_vfp_nsyn_fma_fms) == SUCCESS) + return; + + if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) + return; + + neon_dyadic_misc (NT_untyped, N_IF_32, 0); +} + static void do_neon_tst (void) { @@ -13151,7 +13952,7 @@ do_neon_qdmulh (void) enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL); struct neon_type_el et = neon_check_type (3, rs, N_EQK, N_EQK, N_S16 | N_S32 | N_KEY); - inst.instruction = NEON_ENC_SCALAR (inst.instruction); + NEON_ENCODE (SCALAR, inst); neon_mul_mac (et, neon_quad (rs)); } else @@ -13159,7 +13960,7 @@ do_neon_qdmulh (void) enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL); struct neon_type_el et = neon_check_type (3, rs, N_EQK, N_EQK, N_S16 | N_S32 | N_KEY); - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); /* The U bit (rounding) comes from bit mask. */ neon_three_same (neon_quad (rs), 0, et.size); } @@ -13212,7 +14013,7 @@ do_neon_abs_neg (void) inst.instruction |= (et.type == NT_float) << 10; inst.instruction |= neon_logbits (et.size) << 18; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -13263,7 +14064,7 @@ do_neon_qmovn (void) N_EQK | N_HLF, N_SU_16_64 | N_KEY); /* Saturating move where operands can be signed or unsigned, and the destination has the same signedness. */ - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); if (et.type == NT_unsigned) inst.instruction |= 0xc0; else @@ -13277,7 +14078,7 @@ do_neon_qmovun (void) struct neon_type_el et = neon_check_type (2, NS_DQ, N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY); /* Saturating move with unsigned results. Operands must be signed. */ - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_two_same (0, 1, et.size / 2); } @@ -13343,7 +14144,7 @@ do_neon_movn (void) { struct neon_type_el et = neon_check_type (2, NS_DQ, N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY); - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_two_same (0, 1, et.size / 2); } @@ -13383,21 +14184,21 @@ do_neon_shll (void) if (imm == et.size) { /* Maximum shift variant. */ - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); inst.instruction |= LOW4 (inst.operands[0].reg) << 12; inst.instruction |= HI1 (inst.operands[0].reg) << 22; inst.instruction |= LOW4 (inst.operands[1].reg); inst.instruction |= HI1 (inst.operands[1].reg) << 5; inst.instruction |= neon_logbits (et.size) << 18; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } else { /* A more-specific type check for non-max versions. */ et = neon_check_type (2, NS_QDI, N_EQK | N_DBL, N_SU_32 | N_KEY); - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm); } } @@ -13549,12 +14350,22 @@ do_vfp_nsyn_cvtz (void) } static void -do_neon_cvt (void) +do_neon_cvt_1 (bfd_boolean round_to_zero ATTRIBUTE_UNUSED) { enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_FFI, NS_DD, NS_QQ, NS_FD, NS_DF, NS_FF, NS_QD, NS_DQ, NS_NULL); int flavour = neon_cvt_flavour (rs); + /* PR11109: Handle round-to-zero for VCVT conversions. */ + if (round_to_zero + && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_vfp_v2) + && (flavour == 0 || flavour == 1 || flavour == 8 || flavour == 9) + && (rs == NS_FD || rs == NS_FF)) + { + do_vfp_nsyn_cvtz (); + return; + } + /* VFP rather than Neon conversions. */ if (flavour >= 6) { @@ -13578,7 +14389,7 @@ do_neon_cvt (void) if (inst.operands[2].present && inst.operands[2].imm == 0) goto int_encode; immbits = 32 - inst.operands[2].imm; - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); if (flavour != -1) inst.instruction |= enctab[flavour]; inst.instruction |= LOW4 (inst.operands[0].reg) << 12; @@ -13589,7 +14400,7 @@ do_neon_cvt (void) inst.instruction |= 1 << 21; inst.instruction |= immbits << 16; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } break; @@ -13599,7 +14410,7 @@ do_neon_cvt (void) { unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 }; - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL) return; @@ -13614,7 +14425,7 @@ do_neon_cvt (void) inst.instruction |= neon_quad (rs) << 6; inst.instruction |= 2 << 18; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } break; @@ -13645,7 +14456,7 @@ do_neon_cvt (void) inst.instruction |= HI1 (inst.operands[0].reg) << 22; inst.instruction |= LOW4 (inst.operands[1].reg); inst.instruction |= HI1 (inst.operands[1].reg) << 5; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); break; default: @@ -13654,6 +14465,18 @@ do_neon_cvt (void) } } +static void +do_neon_cvtr (void) +{ + do_neon_cvt_1 (FALSE); +} + +static void +do_neon_cvt (void) +{ + do_neon_cvt_1 (TRUE); +} + static void do_neon_cvtb (void) { @@ -13741,7 +14564,7 @@ do_neon_mvn (void) { enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); inst.instruction |= LOW4 (inst.operands[0].reg) << 12; inst.instruction |= HI1 (inst.operands[0].reg) << 22; inst.instruction |= LOW4 (inst.operands[1].reg); @@ -13750,11 +14573,11 @@ do_neon_mvn (void) } else { - inst.instruction = NEON_ENC_IMMED (inst.instruction); + NEON_ENCODE (IMMED, inst); neon_move_immediate (); } - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } /* Encode instructions of form: @@ -13774,7 +14597,7 @@ neon_mixed_length (struct neon_type_el et, unsigned size) inst.instruction |= (et.type == NT_unsigned) << 24; inst.instruction |= neon_logbits (size) << 20; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -13801,14 +14624,14 @@ neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes) { struct neon_type_el et = neon_check_type (3, NS_QDS, N_EQK | N_DBL, N_EQK, regtypes | N_KEY); - inst.instruction = NEON_ENC_SCALAR (inst.instruction); + NEON_ENCODE (SCALAR, inst); neon_mul_mac (et, et.type == NT_unsigned); } else { struct neon_type_el et = neon_check_type (3, NS_QDD, N_EQK | N_DBL, N_EQK, scalartypes | N_KEY); - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_mixed_length (et, et.size); } } @@ -13854,9 +14677,9 @@ do_neon_vmull (void) struct neon_type_el et = neon_check_type (3, NS_QDD, N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_KEY); if (et.type == NT_poly) - inst.instruction = NEON_ENC_POLY (inst.instruction); + NEON_ENCODE (POLY, inst); else - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); /* For polynomial encoding, size field must be 0b00 and the U bit must be zero. Should be OK as-is. */ neon_mixed_length (et, et.size); @@ -13882,7 +14705,7 @@ do_neon_ext (void) inst.instruction |= neon_quad (rs) << 6; inst.instruction |= imm << 8; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -13918,7 +14741,7 @@ do_neon_dup (void) if (vfp_or_neon_is_neon (NEON_CHECK_CC) == FAIL) return; - inst.instruction = NEON_ENC_SCALAR (inst.instruction); + NEON_ENCODE (SCALAR, inst); inst.instruction |= LOW4 (inst.operands[0].reg) << 12; inst.instruction |= HI1 (inst.operands[0].reg) << 22; inst.instruction |= LOW4 (dm); @@ -13927,7 +14750,7 @@ do_neon_dup (void) inst.instruction |= x << 17; inst.instruction |= sizebits << 16; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } else { @@ -13935,7 +14758,7 @@ do_neon_dup (void) struct neon_type_el et = neon_check_type (2, rs, N_8 | N_16 | N_32 | N_KEY, N_EQK); /* Duplicate ARM register to lanes of vector. */ - inst.instruction = NEON_ENC_ARMREG (inst.instruction); + NEON_ENCODE (ARMREG, inst); switch (et.size) { case 8: inst.instruction |= 0x400000; break; @@ -14032,7 +14855,7 @@ do_neon_mov (void) inst.instruction |= HI1 (inst.operands[1].reg) << 7; inst.instruction |= neon_quad (rs) << 6; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } break; @@ -14052,18 +14875,19 @@ do_neon_mov (void) return; inst.instruction = 0x0800010; neon_move_immediate (); - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); break; case NS_SR: /* case 4. */ { unsigned bcdebits = 0; - struct neon_type_el et = neon_check_type (2, NS_NULL, - N_8 | N_16 | N_32 | N_KEY, N_EQK); - int logsize = neon_logbits (et.size); + int logsize; unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg); unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg); + et = neon_check_type (2, NS_NULL, N_8 | N_16 | N_32 | N_KEY, N_EQK); + logsize = neon_logbits (et.size); + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1), _(BAD_FPU)); constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1) @@ -14105,13 +14929,15 @@ do_neon_mov (void) case NS_RS: /* case 6. */ { - struct neon_type_el et = neon_check_type (2, NS_NULL, - N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY); - unsigned logsize = neon_logbits (et.size); + unsigned logsize; unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg); unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg); unsigned abcdebits = 0; + et = neon_check_type (2, NS_NULL, + N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY); + logsize = neon_logbits (et.size); + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1), _(BAD_FPU)); constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1) @@ -14238,7 +15064,7 @@ do_neon_trn (void) enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL); struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_8 | N_16 | N_32 | N_KEY); - inst.instruction = NEON_ENC_INTEGER (inst.instruction); + NEON_ENCODE (INTEGER, inst); neon_two_same (neon_quad (rs), 1, et.size); } @@ -14342,7 +15168,7 @@ do_neon_tbl_tbx (void) inst.instruction |= HI1 (inst.operands[2].reg) << 5; inst.instruction |= listlenbits << 8; - inst.instruction = neon_dp_fixup (inst.instruction); + neon_dp_fixup (&inst); } static void @@ -14380,6 +15206,18 @@ do_neon_ldr_str (void) { int is_ldr = (inst.instruction & (1 << 20)) != 0; + /* Use of PC in vstr in ARM mode is deprecated in ARMv7. + And is UNPREDICTABLE in thumb mode. */ + if (!is_ldr + && inst.operands[1].reg == REG_PC + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7)) + { + if (!thumb_mode && warn_on_deprecated) + as_warn (_("Use of PC here is deprecated")); + else + inst.error = _("Use of PC here is UNPREDICTABLE"); + } + if (inst.operands[0].issingle) { if (is_ldr) @@ -14428,12 +15266,13 @@ do_neon_ld_st_interleave (void) { case 64: alignbits = 1; break; case 128: - if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2 + && NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4) goto bad_alignment; alignbits = 2; break; case 256: - if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4) goto bad_alignment; alignbits = 3; break; @@ -14668,20 +15507,23 @@ do_neon_ld_dup (void) static void do_neon_ldx_stx (void) { + if (inst.operands[1].isreg) + constraint (inst.operands[1].reg == REG_PC, BAD_PC); + switch (NEON_LANE (inst.operands[0].imm)) { case NEON_INTERLEAVE_LANES: - inst.instruction = NEON_ENC_INTERLV (inst.instruction); + NEON_ENCODE (INTERLV, inst); do_neon_ld_st_interleave (); break; case NEON_ALL_LANES: - inst.instruction = NEON_ENC_DUP (inst.instruction); + NEON_ENCODE (DUP, inst); do_neon_ld_dup (); break; default: - inst.instruction = NEON_ENC_LANE (inst.instruction); + NEON_ENCODE (LANE, inst); do_neon_ld_st_lane (); } @@ -14734,6 +15576,29 @@ fix_new_arm (fragS * frag, switch (exp->X_op) { case O_constant: + if (pc_rel) + { + /* Create an absolute valued symbol, so we have something to + refer to in the object file. Unfortunately for us, gas's + generic expression parsing will already have folded out + any use of .set foo/.type foo %function that may have + been used to set type information of the target location, + that's being specified symbolically. We have to presume + the user knows what they are doing. */ + char name[16 + 8]; + symbolS *symbol; + + sprintf (name, "*ABS*0x%lx", (unsigned long)exp->X_add_number); + + symbol = symbol_find_or_make (name); + S_SET_SEGMENT (symbol, absolute_section); + symbol_set_frag (symbol, &zero_address_frag); + S_SET_VALUE (symbol, exp->X_add_number); + exp->X_op = O_symbol; + exp->X_add_symbol = symbol; + exp->X_add_number = 0; + } + /* FALLTHROUGH */ case O_symbol: case O_add: case O_subtract: @@ -14955,14 +15820,11 @@ opcode_lookup (char **str) const struct asm_opcode *opcode; const struct asm_cond *cond; char save[2]; - bfd_boolean neon_supported; - - neon_supported = ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1); /* Scan up to the end of the mnemonic, which must end in white space, - '.' (in unified mode, or for Neon instructions), or end of string. */ + '.' (in unified mode, or for Neon/VFP instructions), or end of string. */ for (base = end = *str; *end != '\0'; end++) - if (*end == ' ' || ((unified_syntax || neon_supported) && *end == '.')) + if (*end == ' ' || *end == '.') break; if (end == base) @@ -15470,7 +16332,7 @@ md_assemble (char *str) || (thumb_mode == 1 && !ARM_CPU_HAS_FEATURE (variant, *opcode->tvariant))) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support Thumb mode `%s'"), str); return; } if (inst.cond != COND_ALWAYS && !unified_syntax @@ -15495,7 +16357,7 @@ md_assemble (char *str) inst.size_req = 2; else if (inst.size_req == 4) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support Thumb-2 mode `%s'"), str); return; } } @@ -15503,7 +16365,7 @@ md_assemble (char *str) inst.instruction = opcode->tvalue; - if (!parse_operands (p, opcode->operands)) + if (!parse_operands (p, opcode->operands, /*thumb=*/TRUE)) { /* Prepare the it_insn_type for those encodings that don't set it. */ @@ -15542,6 +16404,8 @@ md_assemble (char *str) ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_v6t2); + check_neon_suffixes; + if (!inst.error) { mapping_state (MAP_THUMB); @@ -15559,7 +16423,7 @@ md_assemble (char *str) && !(opcode->avariant && ARM_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant))) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support ARM mode `%s'"), str); return; } if (inst.size_req) @@ -15574,7 +16438,7 @@ md_assemble (char *str) else inst.instruction |= inst.cond << 28; inst.size = INSN_SIZE; - if (!parse_operands (p, opcode->operands)) + if (!parse_operands (p, opcode->operands, /*thumb=*/FALSE)) { it_fsm_pre_encode (); opcode->aencode (); @@ -15587,6 +16451,9 @@ md_assemble (char *str) else ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *opcode->avariant); + + check_neon_suffixes; + if (!inst.error) { mapping_state (MAP_ARM); @@ -15736,6 +16603,13 @@ arm_canonicalize_symbol_name (char * name) REGNUM2(p, 4,t), REGNUM2(p, 5,t), REGNUM2(p, 6,t), REGNUM2(p, 7,t), \ REGNUM2(p, 8,t), REGNUM2(p, 9,t), REGNUM2(p,10,t), REGNUM2(p,11,t), \ REGNUM2(p,12,t), REGNUM2(p,13,t), REGNUM2(p,14,t), REGNUM2(p,15,t) +#define SPLRBANK(base,bank,t) \ + REGDEF(lr_##bank, 768|((base+0)<<16), t), \ + REGDEF(sp_##bank, 768|((base+1)<<16), t), \ + REGDEF(spsr_##bank, 768|(base<<16)|SPSR_BIT, t), \ + REGDEF(LR_##bank, 768|((base+0)<<16), t), \ + REGDEF(SP_##bank, 768|((base+1)<<16), t), \ + REGDEF(SPSR_##bank, 768|(base<<16)|SPSR_BIT, t) static const struct reg_entry reg_names[] = { @@ -15766,6 +16640,34 @@ static const struct reg_entry reg_names[] = REGSET(c, CN), REGSET(C, CN), REGSET(cr, CN), REGSET(CR, CN), + /* ARM banked registers. */ + REGDEF(R8_usr,512|(0<<16),RNB), REGDEF(r8_usr,512|(0<<16),RNB), + REGDEF(R9_usr,512|(1<<16),RNB), REGDEF(r9_usr,512|(1<<16),RNB), + REGDEF(R10_usr,512|(2<<16),RNB), REGDEF(r10_usr,512|(2<<16),RNB), + REGDEF(R11_usr,512|(3<<16),RNB), REGDEF(r11_usr,512|(3<<16),RNB), + REGDEF(R12_usr,512|(4<<16),RNB), REGDEF(r12_usr,512|(4<<16),RNB), + REGDEF(SP_usr,512|(5<<16),RNB), REGDEF(sp_usr,512|(5<<16),RNB), + REGDEF(LR_usr,512|(6<<16),RNB), REGDEF(lr_usr,512|(6<<16),RNB), + + REGDEF(R8_fiq,512|(8<<16),RNB), REGDEF(r8_fiq,512|(8<<16),RNB), + REGDEF(R9_fiq,512|(9<<16),RNB), REGDEF(r9_fiq,512|(9<<16),RNB), + REGDEF(R10_fiq,512|(10<<16),RNB), REGDEF(r10_fiq,512|(10<<16),RNB), + REGDEF(R11_fiq,512|(11<<16),RNB), REGDEF(r11_fiq,512|(11<<16),RNB), + REGDEF(R12_fiq,512|(12<<16),RNB), REGDEF(r12_fiq,512|(12<<16),RNB), + REGDEF(SP_fiq,512|(13<<16),RNB), REGDEF(SP_fiq,512|(13<<16),RNB), + REGDEF(LR_fiq,512|(14<<16),RNB), REGDEF(lr_fiq,512|(14<<16),RNB), + REGDEF(SPSR_fiq,512|(14<<16)|SPSR_BIT,RNB), REGDEF(spsr_fiq,512|(14<<16)|SPSR_BIT,RNB), + + SPLRBANK(0,IRQ,RNB), SPLRBANK(0,irq,RNB), + SPLRBANK(2,SVC,RNB), SPLRBANK(2,svc,RNB), + SPLRBANK(4,ABT,RNB), SPLRBANK(4,abt,RNB), + SPLRBANK(6,UND,RNB), SPLRBANK(6,und,RNB), + SPLRBANK(12,MON,RNB), SPLRBANK(12,mon,RNB), + REGDEF(elr_hyp,768|(14<<16),RNB), REGDEF(ELR_hyp,768|(14<<16),RNB), + REGDEF(sp_hyp,768|(15<<16),RNB), REGDEF(SP_hyp,768|(15<<16),RNB), + REGDEF(spsr_hyp,768|(14<<16)|SPSR_BIT,RNB), + REGDEF(SPSR_hyp,768|(14<<16)|SPSR_BIT,RNB), + /* FPA registers. */ REGNUM(f,0,FN), REGNUM(f,1,FN), REGNUM(f,2,FN), REGNUM(f,3,FN), REGNUM(f,4,FN), REGNUM(f,5,FN), REGNUM(f,6,FN), REGNUM(f,7, FN), @@ -15842,6 +16744,7 @@ static const struct asm_psr psrs[] = {"c", PSR_c}, {"x", PSR_x}, {"s", PSR_s}, + /* Combinations of flags. */ {"fs", PSR_f | PSR_s}, {"fx", PSR_f | PSR_x}, @@ -15921,6 +16824,7 @@ static const struct asm_psr v7m_psrs[] = {"primask", 16}, {"PRIMASK", 16}, {"basepri", 17}, {"BASEPRI", 17}, {"basepri_max", 18}, {"BASEPRI_MAX", 18}, + {"basepri_max", 18}, {"BASEPRI_MASK", 18}, /* Typo, preserved for backwards compatibility. */ {"faultmask", 19}, {"FAULTMASK", 19}, {"control", 20}, {"CONTROL", 20} }; @@ -15950,7 +16854,14 @@ static struct reloc_entry reloc_names[] = { "tlsldm", BFD_RELOC_ARM_TLS_LDM32}, { "TLSLDM", BFD_RELOC_ARM_TLS_LDM32}, { "tlsldo", BFD_RELOC_ARM_TLS_LDO32}, { "TLSLDO", BFD_RELOC_ARM_TLS_LDO32}, { "gottpoff",BFD_RELOC_ARM_TLS_IE32}, { "GOTTPOFF",BFD_RELOC_ARM_TLS_IE32}, - { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32} + { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32}, + { "got_prel", BFD_RELOC_ARM_GOT_PREL}, { "GOT_PREL", BFD_RELOC_ARM_GOT_PREL}, + { "tlsdesc", BFD_RELOC_ARM_TLS_GOTDESC}, + { "TLSDESC", BFD_RELOC_ARM_TLS_GOTDESC}, + { "tlscall", BFD_RELOC_ARM_TLS_CALL}, + { "TLSCALL", BFD_RELOC_ARM_TLS_CALL}, + { "tlsdescseq", BFD_RELOC_ARM_TLS_DESCSEQ}, + { "TLSDESCSEQ", BFD_RELOC_ARM_TLS_DESCSEQ} }; #endif @@ -15976,10 +16887,18 @@ static const struct asm_cond conds[] = static struct asm_barrier_opt barrier_opt_names[] = { - { "sy", 0xf }, - { "un", 0x7 }, - { "st", 0xe }, - { "unst", 0x6 } + { "sy", 0xf }, { "SY", 0xf }, + { "un", 0x7 }, { "UN", 0x7 }, + { "st", 0xe }, { "ST", 0xe }, + { "unst", 0x6 }, { "UNST", 0x6 }, + { "ish", 0xb }, { "ISH", 0xb }, + { "sh", 0xb }, { "SH", 0xb }, + { "ishst", 0xa }, { "ISHST", 0xa }, + { "shst", 0xa }, { "SHST", 0xa }, + { "nsh", 0x7 }, { "NSH", 0x7 }, + { "nshst", 0x6 }, { "NSHST", 0x6 }, + { "osh", 0x3 }, { "OSH", 0x3 }, + { "oshst", 0x2 }, { "OSHST", 0x2 } }; /* Table of ARM-format instructions. */ @@ -15995,6 +16914,18 @@ static struct asm_barrier_opt barrier_opt_names[] = #define OPS5(a,b,c,d,e) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e, } #define OPS6(a,b,c,d,e,f) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e,OP_##f, } +/* These macros are similar to the OPSn, but do not prepend the OP_ prefix. + This is useful when mixing operands for ARM and THUMB, i.e. using the + MIX_ARM_THUMB_OPERANDS macro. + In order to use these macros, prefix the number of operands with _ + e.g. _3. */ +#define OPS_1(a) { a, } +#define OPS_2(a,b) { a,b, } +#define OPS_3(a,b,c) { a,b,c, } +#define OPS_4(a,b,c,d) { a,b,c,d, } +#define OPS_5(a,b,c,d,e) { a,b,c,d,e, } +#define OPS_6(a,b,c,d,e,f) { a,b,c,d,e,f, } + /* These macros abstract out the exact format of the mnemonic table and save some repeated characters. */ @@ -16172,9 +17103,6 @@ static struct asm_barrier_opt barrier_opt_names[] = #define do_0 0 -/* Thumb-only, unconditional. */ -#define UT(mnem, op, nops, ops, te) TUE (mnem, 0, op, nops, ops, 0, te) - static const struct asm_opcode insns[] = { #define ARM_VARIANT &arm_ext_v1 /* Core ARM Instructions. */ @@ -16215,9 +17143,11 @@ static const struct asm_opcode insns[] = tC3("mvns", 1f00000, _mvns, 2, (RR, SH), mov, t_mvn_tst), tCE("ldr", 4100000, _ldr, 2, (RR, ADDRGLDR),ldst, t_ldst), - tC3("ldrb", 4500000, _ldrb, 2, (RR, ADDRGLDR),ldst, t_ldst), - tCE("str", 4000000, _str, 2, (RR, ADDRGLDR),ldst, t_ldst), - tC3("strb", 4400000, _strb, 2, (RR, ADDRGLDR),ldst, t_ldst), + tC3("ldrb", 4500000, _ldrb, 2, (RRnpc_npcsp, ADDRGLDR),ldst, t_ldst), + tCE("str", 4000000, _str, _2, (MIX_ARM_THUMB_OPERANDS (OP_RR, + OP_RRnpc), + OP_ADDRGLDR),ldst, t_ldst), + tC3("strb", 4400000, _strb, 2, (RRnpc_npcsp, ADDRGLDR),ldst, t_ldst), tCE("stm", 8800000, _stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm), tC3("stmia", 8800000, _stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm), @@ -16267,10 +17197,10 @@ static const struct asm_opcode insns[] = TC3w("teqs", 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst), CL("teqp", 130f000, 2, (RR, SH), cmp), - TC3("ldrt", 4300000, f8500e00, 2, (RR, ADDR), ldstt, t_ldstt), - TC3("ldrbt", 4700000, f8100e00, 2, (RR, ADDR), ldstt, t_ldstt), - TC3("strt", 4200000, f8400e00, 2, (RR, ADDR), ldstt, t_ldstt), - TC3("strbt", 4600000, f8000e00, 2, (RR, ADDR), ldstt, t_ldstt), + TC3("ldrt", 4300000, f8500e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt), + TC3("ldrbt", 4700000, f8100e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt), + TC3("strt", 4200000, f8400e00, 2, (RR_npcsp, ADDR), ldstt, t_ldstt), + TC3("strbt", 4600000, f8000e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt), TC3("stmdb", 9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm), TC3("stmfd", 9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm), @@ -16312,7 +17242,7 @@ static const struct asm_opcode insns[] = TCE("stc", c000000, ec000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TC3("stcl", c400000, ec400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TCE("mcr", e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), - TCE("mrc", e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), + TCE("mrc", e100010, ee100010, 6, (RCP, I7b, APSR_RR, RCN, RCN, oI7b), co_reg, co_reg), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v2s /* ARM 3 - swp instructions. */ @@ -16325,8 +17255,8 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_msr - TCE("mrs", 10f0000, f3ef8000, 2, (APSR_RR, RVC_PSR), mrs, t_mrs), - TCE("msr", 120f000, f3808000, 2, (RVC_PSR, RR_EXi), msr, t_msr), + TCE("mrs", 1000000, f3e08000, 2, (RRnpc, rPSR), mrs, t_mrs), + TCE("msr", 120f000, f3808000, 2, (wPSR, RR_EXi), msr, t_msr), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v3m /* ARM 7M long multiplies. */ @@ -16347,12 +17277,12 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_v4t - tC3("ldrh", 01000b0, _ldrh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), - tC3("strh", 00000b0, _strh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), - tC3("ldrsh", 01000f0, _ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), - tC3("ldrsb", 01000d0, _ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), - tCM("ld","sh", 01000f0, _ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), - tCM("ld","sb", 01000d0, _ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst), + tC3("ldrh", 01000b0, _ldrh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), + tC3("strh", 00000b0, _strh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), + tC3("ldrsh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), + tC3("ldrsb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), + tCM("ld","sh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), + tCM("ld","sb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v4t_5 @@ -16386,6 +17316,8 @@ static const struct asm_opcode insns[] = #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v5exp /* ARM Architecture 5TExP. */ +#undef THUMB_VARIANT +#define THUMB_VARIANT &arm_ext_v5exp TCE("smlabb", 1000080, fb100000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla), TCE("smlatb", 10000a0, fb100020, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla), @@ -16408,17 +17340,21 @@ static const struct asm_opcode insns[] = TCE("smulwb", 12000a0, fb30f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE("smulwt", 12000e0, fb30f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), - TCE("qadd", 1000050, fa80f080, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd), - TCE("qdadd", 1400050, fa80f090, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd), - TCE("qsub", 1200050, fa80f0a0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd), - TCE("qdsub", 1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd), + TCE("qadd", 1000050, fa80f080, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2), + TCE("qdadd", 1400050, fa80f090, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2), + TCE("qsub", 1200050, fa80f0a0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2), + TCE("qdsub", 1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v5e /* ARM Architecture 5TE. */ +#undef THUMB_VARIANT +#define THUMB_VARIANT &arm_ext_v6t2 TUF("pld", 450f000, f810f000, 1, (ADDR), pld, t_pld), - TC3("ldrd", 00000d0, e8500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd), - TC3("strd", 00000f0, e8400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd), + TC3("ldrd", 00000d0, e8500000, 3, (RRnpc_npcsp, oRRnpc_npcsp, ADDRGLDRS), + ldrd, t_ldstd), + TC3("strd", 00000f0, e8400000, 3, (RRnpc_npcsp, oRRnpc_npcsp, + ADDRGLDRS), ldrd, t_ldstd), TCE("mcrr", c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), TCE("mrrc", c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), @@ -16447,18 +17383,34 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_v6t2 - TCE("ldrex", 1900f9f, e8500f00, 2, (RRnpc, ADDR), ldrex, t_ldrex), - TCE("strex", 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex), + TCE("ldrex", 1900f9f, e8500f00, 2, (RRnpc_npcsp, ADDR), ldrex, t_ldrex), + TCE("strex", 1800f90, e8400000, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR), + strex, t_strex), TUF("mcrr2", c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), TUF("mrrc2", c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c), TCE("ssat", 6a00010, f3000000, 4, (RRnpc, I32, RRnpc, oSHllar),ssat, t_ssat), TCE("usat", 6e00010, f3800000, 4, (RRnpc, I31, RRnpc, oSHllar),usat, t_usat), -/* ARM V6 not included in V7M (eg. integer SIMD). */ +/* ARM V6 not included in V7M. */ #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_v6_notm + TUF("rfeia", 8900a00, e990c000, 1, (RRw), rfe, rfe), + UF(rfeib, 9900a00, 1, (RRw), rfe), + UF(rfeda, 8100a00, 1, (RRw), rfe), + TUF("rfedb", 9100a00, e810c000, 1, (RRw), rfe, rfe), + TUF("rfefd", 8900a00, e990c000, 1, (RRw), rfe, rfe), + UF(rfefa, 9900a00, 1, (RRw), rfe), + UF(rfeea, 8100a00, 1, (RRw), rfe), + TUF("rfeed", 9100a00, e810c000, 1, (RRw), rfe, rfe), + TUF("srsia", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs), + UF(srsib, 9c00500, 2, (oRRw, I31w), srs), + UF(srsda, 8400500, 2, (oRRw, I31w), srs), + TUF("srsdb", 9400500, e800c000, 2, (oRRw, I31w), srs, srs), +/* ARM V6 not included in V7M (eg. integer SIMD). */ +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_v6_dsp TUF("cps", 1020000, f3af8100, 1, (I31b), imm0, t_cps), TCE("pkhbt", 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt), TCE("pkhtb", 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb), @@ -16522,14 +17474,6 @@ static const struct asm_opcode insns[] = /* Old name for USAX. */ TCE("usubaddx", 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd), TCE("usub8", 6500ff0, fac0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd), - TUF("rfeia", 8900a00, e990c000, 1, (RRw), rfe, rfe), - UF(rfeib, 9900a00, 1, (RRw), rfe), - UF(rfeda, 8100a00, 1, (RRw), rfe), - TUF("rfedb", 9100a00, e810c000, 1, (RRw), rfe, rfe), - TUF("rfefd", 8900a00, e990c000, 1, (RRw), rfe, rfe), - UF(rfefa, 9900a00, 1, (RRw), rfe), - UF(rfeea, 8100a00, 1, (RRw), rfe), - TUF("rfeed", 9100a00, e810c000, 1, (RRw), rfe, rfe), TCE("sxtah", 6b00070, fa00f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah), TCE("sxtab16", 6800070, fa20f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah), TCE("sxtab", 6a00070, fa40f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah), @@ -16557,10 +17501,6 @@ static const struct asm_opcode insns[] = TCE("smuadx", 700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE("smusd", 700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), TCE("smusdx", 700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), - TUF("srsia", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs), - UF(srsib, 9c00500, 2, (oRRw, I31w), srs), - UF(srsda, 8400500, 2, (oRRw, I31w), srs), - TUF("srsdb", 9400500, e800c000, 2, (oRRw, I31w), srs, srs), TCE("ssat16", 6a00f30, f3200000, 3, (RRnpc, I16, RRnpc), ssat16, t_ssat16), TCE("umaal", 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal), TCE("usad8", 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd), @@ -16579,26 +17519,42 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_v6_notm - - TCE("ldrexd", 1b00f9f, e8d0007f, 3, (RRnpc, oRRnpc, RRnpcb), ldrexd, t_ldrexd), - TCE("strexd", 1a00f90, e8c00070, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb), strexd, t_strexd), + TCE("ldrexd", 1b00f9f, e8d0007f, 3, (RRnpc_npcsp, oRRnpc_npcsp, RRnpcb), + ldrexd, t_ldrexd), + TCE("strexd", 1a00f90, e8c00070, 4, (RRnpc_npcsp, RRnpc_npcsp, oRRnpc_npcsp, + RRnpcb), strexd, t_strexd), #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_v6t2 - - TCE("ldrexb", 1d00f9f, e8d00f4f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn), - TCE("ldrexh", 1f00f9f, e8d00f5f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn), - TCE("strexb", 1c00f90, e8c00f40, 3, (RRnpc, RRnpc, ADDR), strex, rm_rd_rn), - TCE("strexh", 1e00f90, e8c00f50, 3, (RRnpc, RRnpc, ADDR), strex, rm_rd_rn), + TCE("ldrexb", 1d00f9f, e8d00f4f, 2, (RRnpc_npcsp,RRnpcb), + rd_rn, rd_rn), + TCE("ldrexh", 1f00f9f, e8d00f5f, 2, (RRnpc_npcsp, RRnpcb), + rd_rn, rd_rn), + TCE("strexb", 1c00f90, e8c00f40, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR), + strex, t_strexbh), + TCE("strexh", 1e00f90, e8c00f50, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR), + strex, t_strexbh), TUF("clrex", 57ff01f, f3bf8f2f, 0, (), noargs, noargs), #undef ARM_VARIANT -#define ARM_VARIANT & arm_ext_v6z +#define ARM_VARIANT & arm_ext_sec +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_sec TCE("smc", 1600070, f7f08000, 1, (EXPi), smc, t_smc), +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_virt +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_virt + + TCE("hvc", 1400070, f7e08000, 1, (EXPi), hvc, t_hvc), + TCE("eret", 160006e, f3de8f00, 0, (), noargs, noargs), + #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v6t2 +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_v6t2 TCE("bfc", 7c0001f, f36f0000, 3, (RRnpc, I31, I32), bfc, t_bfc), TCE("bfi", 7c00010, f3600000, 4, (RRnpc, RRnpc_I0, I31, I32), bfi, t_bfi), @@ -16610,13 +17566,16 @@ static const struct asm_opcode insns[] = TCE("movt", 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16), TCE("rbit", 6ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit), - TC3("ldrht", 03000b0, f8300e00, 2, (RR, ADDR), ldsttv4, t_ldstt), - TC3("ldrsht", 03000f0, f9300e00, 2, (RR, ADDR), ldsttv4, t_ldstt), - TC3("ldrsbt", 03000d0, f9100e00, 2, (RR, ADDR), ldsttv4, t_ldstt), - TC3("strht", 02000b0, f8200e00, 2, (RR, ADDR), ldsttv4, t_ldstt), + TC3("ldrht", 03000b0, f8300e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), + TC3("ldrsht", 03000f0, f9300e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), + TC3("ldrsbt", 03000d0, f9100e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), + TC3("strht", 02000b0, f8200e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), - UT("cbnz", b900, 2, (RR, EXP), t_cbz), - UT("cbz", b100, 2, (RR, EXP), t_cbz), + /* Thumb-only instructions. */ +#undef ARM_VARIANT +#define ARM_VARIANT NULL + TUE("cbnz", 0, b900, 2, (RR, EXP), 0, t_cbz), + TUE("cbz", 0, b100, 2, (RR, EXP), 0, t_cbz), /* ARM does not really have an IT instruction, so always allow it. The opcode is copied from Thumb in order to allow warnings in @@ -16654,12 +17613,14 @@ static const struct asm_opcode insns[] = TCE("tbb", 0, e8d0f000, 1, (TB), 0, t_tb), TCE("tbh", 0, e8d0f010, 1, (TB), 0, t_tb), - /* Thumb-2 hardware division instructions (R and M profiles only). */ + /* Hardware division instructions. */ +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_adiv #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_div - TCE("sdiv", 0, fb90f0f0, 3, (RR, oRR, RR), 0, t_div), - TCE("udiv", 0, fbb0f0f0, 3, (RR, oRR, RR), 0, t_div), + TCE("sdiv", 710f010, fb90f0f0, 3, (RR, oRR, RR), div, t_div), + TCE("udiv", 730f010, fbb0f0f0, 3, (RR, oRR, RR), div, t_div), /* ARM V6M/V7 instructions. */ #undef ARM_VARIANT @@ -16667,9 +17628,9 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_barrier - TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER), barrier, t_barrier), - TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER), barrier, t_barrier), - TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER), barrier, t_barrier), + TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER_I15), barrier, t_barrier), + TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER_I15), barrier, t_barrier), + TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER_I15), barrier, t_barrier), /* ARM V7 instructions. */ #undef ARM_VARIANT @@ -16680,6 +17641,13 @@ static const struct asm_opcode insns[] = TUF("pli", 450f000, f910f000, 1, (ADDR), pli, t_pld), TCE("dbg", 320f0f0, f3af80f0, 1, (I15), dbg, t_dbg), +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_mp +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_mp + + TUF("pldw", 410f000, f830f000, 1, (ADDR), pld, t_pld), + #undef ARM_VARIANT #define ARM_VARIANT & fpu_fpa_ext_v1 /* Core FPA instruction set (V1). */ @@ -17131,6 +18099,8 @@ static const struct asm_opcode insns[] = cCE("fmrs", e100a10, 2, (RR, RVS), vfp_reg_from_sp), cCE("fmsr", e000a10, 2, (RVS, RR), vfp_sp_from_reg), cCE("fmstat", ef1fa10, 0, (), noargs), + cCE("vmrs", ef10a10, 2, (APSR_RR, RVC), vmrs), + cCE("vmsr", ee10a10, 2, (RVC, RR), vmsr), cCE("fsitos", eb80ac0, 2, (RVS, RVS), vfp_sp_monadic), cCE("fuitos", eb80a40, 2, (RVS, RVS), vfp_sp_monadic), cCE("ftosis", ebd0a40, 2, (RVS, RVS), vfp_sp_monadic), @@ -17143,22 +18113,22 @@ static const struct asm_opcode insns[] = /* Memory operations. */ cCE("flds", d100a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), cCE("fsts", d000a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), - cCE("fldmias", c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fldmfds", c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fldmdbs", d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fldmeas", d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fldmiax", c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fldmfdx", c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fldmdbx", d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fldmeax", d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fstmias", c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fstmeas", c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fstmdbs", d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fstmfds", d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fstmiax", c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fstmeax", c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fstmdbx", d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fstmfdx", d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), + cCE("fldmias", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fldmfds", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fldmdbs", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fldmeas", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fldmiax", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fldmfdx", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fldmdbx", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fldmeax", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fstmias", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fstmeas", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fstmdbs", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fstmfds", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fstmiax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fstmeax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fstmdbx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fstmfdx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), /* Monadic operations. */ cCE("fabss", eb00ac0, 2, (RVS, RVS), vfp_sp_monadic), @@ -17182,6 +18152,19 @@ static const struct asm_opcode insns[] = cCE("fcmpes", eb40ac0, 2, (RVS, RVS), vfp_sp_monadic), cCE("fcmpezs", eb50ac0, 1, (RVS), vfp_sp_compare_z), + /* Double precision load/store are still present on single precision + implementations. */ + cCE("fldd", d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), + cCE("fstd", d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), + cCE("fldmiad", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fldmfdd", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fldmdbd", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fldmead", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fstmiad", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fstmead", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fstmdbd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fstmfdd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + #undef ARM_VARIANT #define ARM_VARIANT & fpu_vfp_ext_v1 /* VFP V1 (Double precision). */ @@ -17200,18 +18183,6 @@ static const struct asm_opcode insns[] = cCE("ftouid", ebc0b40, 2, (RVS, RVD), vfp_sp_dp_cvt), cCE("ftouizd", ebc0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt), - /* Memory operations. */ - cCE("fldd", d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), - cCE("fstd", d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), - cCE("fldmiad", c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fldmfdd", c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fldmdbd", d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fldmead", d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fstmiad", c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fstmead", c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fstmdbd", d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fstmfdd", d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - /* Monadic operations. */ cCE("fabsd", eb00bc0, 2, (RVD, RVD), vfp_dp_rd_rm), cCE("fnegd", eb10b40, 2, (RVD, RVD), vfp_dp_rd_rm), @@ -17272,16 +18243,17 @@ static const struct asm_opcode insns[] = NCEF(vabs, 1b10300, 2, (RNSDQ, RNSDQ), neon_abs_neg), NCEF(vneg, 1b10380, 2, (RNSDQ, RNSDQ), neon_abs_neg), - NCE(vldm, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vldmia, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vldmdb, d100b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstm, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstmia, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstmdb, d000b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vldm, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vldmia, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vldmdb, d100b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstm, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstmia, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstmdb, d000b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str), NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str), - nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32b), neon_cvt), + nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32z), neon_cvt), + nCEF(vcvtr, _vcvt, 2, (RNSDQ, RNSDQ), neon_cvtr), nCEF(vcvtb, _vcvt, 2, (RVS, RVS), neon_cvtb), nCEF(vcvtt, _vcvt, 2, (RVS, RVS), neon_cvtt), @@ -17322,16 +18294,16 @@ static const struct asm_opcode insns[] = nUF(vqshl, _vqshl, 3, (RNDQ, oRNDQ, RNDQ_I63b), neon_qshl_imm), nUF(vqshlq, _vqshl, 3, (RNQ, oRNQ, RNDQ_I63b), neon_qshl_imm), /* Logic ops, types optional & ignored. */ - nUF(vand, _vand, 2, (RNDQ, NILO), neon_logic), - nUF(vandq, _vand, 2, (RNQ, NILO), neon_logic), - nUF(vbic, _vbic, 2, (RNDQ, NILO), neon_logic), - nUF(vbicq, _vbic, 2, (RNQ, NILO), neon_logic), - nUF(vorr, _vorr, 2, (RNDQ, NILO), neon_logic), - nUF(vorrq, _vorr, 2, (RNQ, NILO), neon_logic), - nUF(vorn, _vorn, 2, (RNDQ, NILO), neon_logic), - nUF(vornq, _vorn, 2, (RNQ, NILO), neon_logic), - nUF(veor, _veor, 3, (RNDQ, oRNDQ, RNDQ), neon_logic), - nUF(veorq, _veor, 3, (RNQ, oRNQ, RNQ), neon_logic), + nUF(vand, _vand, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic), + nUF(vandq, _vand, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic), + nUF(vbic, _vbic, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic), + nUF(vbicq, _vbic, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic), + nUF(vorr, _vorr, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic), + nUF(vorrq, _vorr, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic), + nUF(vorn, _vorn, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic), + nUF(vornq, _vorn, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic), + nUF(veor, _veor, 3, (RNDQ, oRNDQ, RNDQ), neon_logic), + nUF(veorq, _veor, 3, (RNQ, oRNQ, RNQ), neon_logic), /* Bitfield ops, untyped. */ NUF(vbsl, 1100110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield), NUF(vbslq, 1100110, 3, (RNQ, RNQ, RNQ), neon_bitfield), @@ -17430,8 +18402,8 @@ static const struct asm_opcode insns[] = /* CVT with optional immediate for fixed-point variant. */ nUF(vcvtq, _vcvt, 3, (RNQ, RNQ, oI32b), neon_cvt), - nUF(vmvn, _vmvn, 2, (RNDQ, RNDQ_IMVNb), neon_mvn), - nUF(vmvnq, _vmvn, 2, (RNQ, RNDQ_IMVNb), neon_mvn), + nUF(vmvn, _vmvn, 2, (RNDQ, RNDQ_Ibig), neon_mvn), + nUF(vmvnq, _vmvn, 2, (RNQ, RNDQ_Ibig), neon_mvn), /* Data processing, three registers of different lengths. */ /* Dyadic, long insns. Types S8 S16 S32 U8 U16 U32. */ @@ -17538,29 +18510,52 @@ static const struct asm_opcode insns[] = nUF(vst4, _vst4, 2, (NSTRLST, ADDR), neon_ldx_stx), #undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_vfp_ext_v3xd +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_vfp_ext_v3xd + cCE("fconsts", eb00a00, 2, (RVS, I255), vfp_sp_const), + cCE("fshtos", eba0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE("fsltos", eba0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE("fuhtos", ebb0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE("fultos", ebb0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE("ftoshs", ebe0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE("ftosls", ebe0ac0, 2, (RVS, I32), vfp_sp_conv_32), + cCE("ftouhs", ebf0a40, 2, (RVS, I16z), vfp_sp_conv_16), + cCE("ftouls", ebf0ac0, 2, (RVS, I32), vfp_sp_conv_32), + +#undef THUMB_VARIANT #define THUMB_VARIANT & fpu_vfp_ext_v3 #undef ARM_VARIANT #define ARM_VARIANT & fpu_vfp_ext_v3 - cCE("fconsts", eb00a00, 2, (RVS, I255), vfp_sp_const), cCE("fconstd", eb00b00, 2, (RVD, I255), vfp_dp_const), - cCE("fshtos", eba0a40, 2, (RVS, I16z), vfp_sp_conv_16), cCE("fshtod", eba0b40, 2, (RVD, I16z), vfp_dp_conv_16), - cCE("fsltos", eba0ac0, 2, (RVS, I32), vfp_sp_conv_32), cCE("fsltod", eba0bc0, 2, (RVD, I32), vfp_dp_conv_32), - cCE("fuhtos", ebb0a40, 2, (RVS, I16z), vfp_sp_conv_16), cCE("fuhtod", ebb0b40, 2, (RVD, I16z), vfp_dp_conv_16), - cCE("fultos", ebb0ac0, 2, (RVS, I32), vfp_sp_conv_32), cCE("fultod", ebb0bc0, 2, (RVD, I32), vfp_dp_conv_32), - cCE("ftoshs", ebe0a40, 2, (RVS, I16z), vfp_sp_conv_16), cCE("ftoshd", ebe0b40, 2, (RVD, I16z), vfp_dp_conv_16), - cCE("ftosls", ebe0ac0, 2, (RVS, I32), vfp_sp_conv_32), cCE("ftosld", ebe0bc0, 2, (RVD, I32), vfp_dp_conv_32), - cCE("ftouhs", ebf0a40, 2, (RVS, I16z), vfp_sp_conv_16), cCE("ftouhd", ebf0b40, 2, (RVD, I16z), vfp_dp_conv_16), - cCE("ftouls", ebf0ac0, 2, (RVS, I32), vfp_sp_conv_32), cCE("ftould", ebf0bc0, 2, (RVD, I32), vfp_dp_conv_32), +#undef ARM_VARIANT +#define ARM_VARIANT &fpu_vfp_ext_fma +#undef THUMB_VARIANT +#define THUMB_VARIANT &fpu_vfp_ext_fma + /* Mnemonics shared by Neon and VFP. These are included in the + VFP FMA variant; NEON and VFP FMA always includes the NEON + FMA instructions. */ + nCEF(vfma, _vfma, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_fmac), + nCEF(vfms, _vfms, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_fmac), + /* ffmas/ffmad/ffmss/ffmsd are dummy mnemonics to satisfy gas; + the v form should always be used. */ + cCE("ffmas", ea00a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic), + cCE("ffnmas", ea00a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic), + cCE("ffmad", ea00b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + cCE("ffnmad", ea00b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm), + nCE(vfnma, _vfnma, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul), + nCE(vfnms, _vfnms, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul), + #undef THUMB_VARIANT #undef ARM_VARIANT #define ARM_VARIANT & arm_cext_xscale /* Intel XScale extensions. */ @@ -18218,8 +19213,10 @@ relax_adr (fragS *fragp, asection *sec, long stretch) offsetT val; /* Assume worst case for symbols not known to be in the same section. */ - if (!S_IS_DEFINED (fragp->fr_symbol) - || sec != S_GET_SEGMENT (fragp->fr_symbol)) + if (fragp->fr_symbol == NULL + || !S_IS_DEFINED (fragp->fr_symbol) + || sec != S_GET_SEGMENT (fragp->fr_symbol) + || S_IS_WEAK (fragp->fr_symbol)) return 4; val = relaxed_symbol_addr (fragp, stretch); @@ -18262,13 +19259,20 @@ relax_branch (fragS *fragp, asection *sec, int bits, long stretch) /* Assume worst case for symbols not known to be in the same section. */ if (!S_IS_DEFINED (fragp->fr_symbol) - || sec != S_GET_SEGMENT (fragp->fr_symbol)) + || sec != S_GET_SEGMENT (fragp->fr_symbol) + || S_IS_WEAK (fragp->fr_symbol)) return 4; #ifdef OBJ_ELF if (S_IS_DEFINED (fragp->fr_symbol) && ARM_IS_FUNC (fragp->fr_symbol)) return 4; + + /* PR 12532. Global symbols with default visibility might + be preempted, so do not relax relocations to them. */ + if ((ELF_ST_VISIBILITY (S_GET_OTHER (fragp->fr_symbol)) == STV_DEFAULT) + && (! S_IS_LOCAL (fragp->fr_symbol))) + return 4; #endif val = relaxed_symbol_addr (fragp, stretch); @@ -18438,9 +19442,7 @@ arm_handle_align (fragS * fragP) if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE) bytes &= MAX_MEM_FOR_RS_ALIGN_CODE; -#ifdef OBJ_ELF gas_assert ((fragP->tc_frag_data.thumb_mode & MODE_RECORDED) != 0); -#endif if (fragP->tc_frag_data.thumb_mode & (~ MODE_RECORDED)) { @@ -18546,7 +19548,7 @@ void arm_init_frag (fragS * fragP, int max_chars ATTRIBUTE_UNUSED) { /* Record whether this frag is in an ARM or a THUMB area. */ - fragP->tc_frag_data.thumb_mode = thumb_mode; + fragP->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED; } #else /* OBJ_ELF is defined. */ @@ -18990,12 +19992,12 @@ tc_arm_regname_to_dw2regnum (char *regname) void tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size) { - expressionS expr; + expressionS exp; - expr.X_op = O_secrel; - expr.X_add_symbol = symbol; - expr.X_add_number = 0; - emit_expr (&expr, size); + exp.X_op = O_secrel; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); } #endif @@ -19052,7 +20054,9 @@ md_pcrel_from_section (fixS * fixP, segT seg) return base + 4; case BFD_RELOC_THUMB_PCREL_BRANCH23: - if (fixP->fx_addsy + if (fixP->fx_addsy + && (S_GET_SEGMENT (fixP->fx_addsy) == seg) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19060,8 +20064,10 @@ md_pcrel_from_section (fixS * fixP, segT seg) /* BLX is like branches above, but forces the low two bits of PC to zero. */ - case BFD_RELOC_THUMB_PCREL_BLX: - if (fixP->fx_addsy + case BFD_RELOC_THUMB_PCREL_BLX: + if (fixP->fx_addsy + && (S_GET_SEGMENT (fixP->fx_addsy) == seg) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && THUMB_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19070,18 +20076,22 @@ md_pcrel_from_section (fixS * fixP, segT seg) /* ARM mode branches are offset by +8. However, the Windows CE loader expects the relocation not to take this into account. */ case BFD_RELOC_ARM_PCREL_BLX: - if (fixP->fx_addsy + if (fixP->fx_addsy + && (S_GET_SEGMENT (fixP->fx_addsy) == seg) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; - return base + 8; + return base + 8; - case BFD_RELOC_ARM_PCREL_CALL: - if (fixP->fx_addsy + case BFD_RELOC_ARM_PCREL_CALL: + if (fixP->fx_addsy + && (S_GET_SEGMENT (fixP->fx_addsy) == seg) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && THUMB_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; - return base + 8; + return base + 8; case BFD_RELOC_ARM_PCREL_BRANCH: case BFD_RELOC_ARM_PCREL_JUMP: @@ -19395,6 +20405,31 @@ arm_optimize_expr (expressionS *l, operatorT op, expressionS *r) return FALSE; } +/* Encode Thumb2 unconditional branches and calls. The encoding + for the 2 are identical for the immediate values. */ + +static void +encode_thumb2_b_bl_offset (char * buf, offsetT value) +{ +#define T2I1I2MASK ((1 << 13) | (1 << 11)) + offsetT newval; + offsetT newval2; + addressT S, I1, I2, lo, hi; + + S = (value >> 24) & 0x01; + I1 = (value >> 23) & 0x01; + I2 = (value >> 22) & 0x01; + hi = (value >> 12) & 0x3ff; + lo = (value >> 1) & 0x7ff; + newval = md_chars_to_number (buf, THUMB_SIZE); + newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); + newval |= (S << 10) | hi; + newval2 &= ~T2I1I2MASK; + newval2 |= (((I1 ^ S) << 13) | ((I2 ^ S) << 11) | lo) ^ T2I1I2MASK; + md_number_to_chars (buf, newval, THUMB_SIZE); + md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE); +} + void md_apply_fix (fixS * fixP, valueT * valP, @@ -19442,22 +20477,23 @@ md_apply_fix (fixS * fixP, not have a reloc for it, so tc_gen_reloc will reject it. */ fixP->fx_done = 1; - if (fixP->fx_addsy - && ! S_IS_DEFINED (fixP->fx_addsy)) + if (fixP->fx_addsy) { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("undefined symbol %s used as an immediate value"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + const char *msg = 0; - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) != seg) - { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("symbol %s is in a different section"), - S_GET_NAME (fixP->fx_addsy)); - break; + if (! S_IS_DEFINED (fixP->fx_addsy)) + msg = _("undefined symbol %s used as an immediate value"); + else if (S_GET_SEGMENT (fixP->fx_addsy) != seg) + msg = _("symbol %s is in a different section"); + else if (S_IS_WEAK (fixP->fx_addsy)) + msg = _("symbol %s is weak and may be overridden later"); + + if (msg) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + msg, S_GET_NAME (fixP->fx_addsy)); + break; + } } newimm = encode_arm_immediate (value); @@ -19483,24 +20519,25 @@ md_apply_fix (fixS * fixP, unsigned int highpart = 0; unsigned int newinsn = 0xe1a00000; /* nop. */ - if (fixP->fx_addsy - && ! S_IS_DEFINED (fixP->fx_addsy)) + if (fixP->fx_addsy) { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("undefined symbol %s used as an immediate value"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + const char *msg = 0; - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) != seg) - { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("symbol %s is in a different section"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + if (! S_IS_DEFINED (fixP->fx_addsy)) + msg = _("undefined symbol %s used as an immediate value"); + else if (S_GET_SEGMENT (fixP->fx_addsy) != seg) + msg = _("symbol %s is in a different section"); + else if (S_IS_WEAK (fixP->fx_addsy)) + msg = _("symbol %s is weak and may be overridden later"); + if (msg) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + msg, S_GET_NAME (fixP->fx_addsy)); + break; + } + } + newimm = encode_arm_immediate (value); temp = md_chars_to_number (buf, INSN_SIZE); @@ -19550,7 +20587,7 @@ md_apply_fix (fixS * fixP, value = 0; case BFD_RELOC_ARM_LITERAL: - sign = value >= 0; + sign = value > 0; if (value < 0) value = - value; @@ -19568,14 +20605,19 @@ md_apply_fix (fixS * fixP, } newval = md_chars_to_number (buf, INSN_SIZE); - newval &= 0xff7ff000; - newval |= value | (sign ? INDEX_UP : 0); + if (value == 0) + newval &= 0xfffff000; + else + { + newval &= 0xff7ff000; + newval |= value | (sign ? INDEX_UP : 0); + } md_number_to_chars (buf, newval, INSN_SIZE); break; case BFD_RELOC_ARM_OFFSET_IMM8: case BFD_RELOC_ARM_HWLITERAL: - sign = value >= 0; + sign = value > 0; if (value < 0) value = - value; @@ -19592,8 +20634,13 @@ md_apply_fix (fixS * fixP, } newval = md_chars_to_number (buf, INSN_SIZE); - newval &= 0xff7ff0f0; - newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0); + if (value == 0) + newval &= 0xfffff0f0; + else + { + newval &= 0xff7ff0f0; + newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0); + } md_number_to_chars (buf, newval, INSN_SIZE); break; @@ -19780,17 +20827,20 @@ md_apply_fix (fixS * fixP, /* Turn add/sum into addw/subw. */ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM) newval = (newval & 0xfeffffff) | 0x02000000; - - /* 12 bit immediate for addw/subw. */ - if (value < 0) + /* No flat 12-bit imm encoding for addsw/subsw. */ + if ((newval & 0x00100000) == 0) { - value = -value; - newval ^= 0x00a00000; + /* 12 bit immediate for addw/subw. */ + if (value < 0) + { + value = -value; + newval ^= 0x00a00000; + } + if (value > 0xfff) + newimm = (unsigned int) FAIL; + else + newimm = value; } - if (value > 0xfff) - newimm = (unsigned int) FAIL; - else - newimm = value; } if (newimm == (unsigned int)FAIL) @@ -19818,6 +20868,15 @@ md_apply_fix (fixS * fixP, md_number_to_chars (buf, newval, INSN_SIZE); break; + case BFD_RELOC_ARM_HVC: + if (((unsigned long) value) > 0xffff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid hvc expression")); + newval = md_chars_to_number (buf, INSN_SIZE); + newval |= (value & 0xf) | ((value & 0xfff0) << 4); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + case BFD_RELOC_ARM_SWI: if (fixP->tc_fix_data != 0) { @@ -19852,7 +20911,7 @@ md_apply_fix (fixS * fixP, if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) /* Flip the bl to blx. This is a simple flip @@ -19872,7 +20931,7 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_ARM_PCREL_JUMP: if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) { @@ -19895,7 +20954,7 @@ md_apply_fix (fixS * fixP, temp = 1; if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && ARM_IS_FUNC (fixP->fx_addsy)) { @@ -19925,8 +20984,7 @@ md_apply_fix (fixS * fixP, _("misaligned branch destination")); if ((value & (offsetT)0xfe000000) != (offsetT)0 && (value & (offsetT)0xfe000000) != (offsetT)0xfe000000) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); if (fixP->fx_done || !seg->use_rela_p) { @@ -19962,8 +21020,7 @@ md_apply_fix (fixS * fixP, else { if (value & ~0x7e) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); if (fixP->fx_done || !seg->use_rela_p) { @@ -19976,8 +21033,7 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */ if ((value & ~0xff) && ((value & ~0xff) != ~0xff)) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); if (fixP->fx_done || !seg->use_rela_p) { @@ -19989,8 +21045,7 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch. */ if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff)) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); if (fixP->fx_done || !seg->use_rela_p) { @@ -20003,15 +21058,14 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_THUMB_PCREL_BRANCH20: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && !S_IS_EXTERNAL (fixP->fx_addsy) - && S_IS_DEFINED (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) { /* Force a relocation for a branch 20 bits wide. */ fixP->fx_done = 0; } - if ((value & ~0x1fffff) && ((value & ~0x1fffff) != ~0x1fffff)) + if ((value & ~0x1fffff) && ((value & ~0x0fffff) != ~0x0fffff)) as_bad_where (fixP->fx_file, fixP->fx_line, _("conditional branch out of range")); @@ -20036,14 +21090,12 @@ md_apply_fix (fixS * fixP, break; case BFD_RELOC_THUMB_PCREL_BLX: - /* If there is a blx from a thumb state function to another thumb function flip this to a bl and warn about it. */ if (fixP->fx_addsy - && S_IS_DEFINED (fixP->fx_addsy) - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) { @@ -20062,13 +21114,11 @@ md_apply_fix (fixS * fixP, goto thumb_bl_common; case BFD_RELOC_THUMB_PCREL_BRANCH23: - /* A bl from Thumb state ISA to an internal ARM state function is converted to a blx. */ if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && !S_IS_EXTERNAL (fixP->fx_addsy) - && S_IS_DEFINED (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) { @@ -20087,10 +21137,6 @@ md_apply_fix (fixS * fixP, fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23; #endif - if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff)) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); - if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX) /* For a BLX instruction, make sure that the relocation is rounded up to a word boundary. This follows the semantics of the instruction @@ -20098,45 +21144,28 @@ md_apply_fix (fixS * fixP, 1 of the base address. */ value = (value + 1) & ~ 1; + if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff)) + { + if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2))) + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); + else if ((value & ~0x1ffffff) + && ((value & ~0x1ffffff) != ~0x1ffffff)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Thumb2 branch out of range")); + } + if (fixP->fx_done || !seg->use_rela_p) - { - offsetT newval2; + encode_thumb2_b_bl_offset (buf, value); - newval = md_chars_to_number (buf, THUMB_SIZE); - newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); - newval |= (value & 0x7fffff) >> 12; - newval2 |= (value & 0xfff) >> 1; - md_number_to_chars (buf, newval, THUMB_SIZE); - md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE); - } break; case BFD_RELOC_THUMB_PCREL_BRANCH25: - if ((value & ~0x1ffffff) && ((value & ~0x1ffffff) != ~0x1ffffff)) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("branch out of range")); + if ((value & ~0x0ffffff) && ((value & ~0x0ffffff) != ~0x0ffffff)) + as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE); if (fixP->fx_done || !seg->use_rela_p) - { - offsetT newval2; - addressT S, I1, I2, lo, hi; - - S = (value & 0x01000000) >> 24; - I1 = (value & 0x00800000) >> 23; - I2 = (value & 0x00400000) >> 22; - hi = (value & 0x003ff000) >> 12; - lo = (value & 0x00000ffe) >> 1; - - I1 = !(I1 ^ S); - I2 = !(I2 ^ S); + encode_thumb2_b_bl_offset (buf, value); - newval = md_chars_to_number (buf, THUMB_SIZE); - newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); - newval |= (S << 10) | hi; - newval2 |= (I1 << 13) | (I2 << 11) | lo; - md_number_to_chars (buf, newval, THUMB_SIZE); - md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE); - } break; case BFD_RELOC_8: @@ -20150,6 +21179,14 @@ md_apply_fix (fixS * fixP, break; #ifdef OBJ_ELF + case BFD_RELOC_ARM_TLS_CALL: + case BFD_RELOC_ARM_THM_TLS_CALL: + case BFD_RELOC_ARM_TLS_DESCSEQ: + case BFD_RELOC_ARM_THM_TLS_DESCSEQ: + S_SET_THREAD_LOCAL (fixP->fx_addsy); + break; + + case BFD_RELOC_ARM_TLS_GOTDESC: case BFD_RELOC_ARM_TLS_GD32: case BFD_RELOC_ARM_TLS_LE32: case BFD_RELOC_ARM_TLS_IE32: @@ -20163,7 +21200,12 @@ md_apply_fix (fixS * fixP, if (fixP->fx_done || !seg->use_rela_p) md_number_to_chars (buf, 0, 4); break; - + + case BFD_RELOC_ARM_GOT_PREL: + if (fixP->fx_done || !seg->use_rela_p) + md_number_to_chars (buf, value, 4); + break; + case BFD_RELOC_ARM_TARGET2: /* TARGET2 is not partial-inplace, so we need to write the addend here for REL targets, because it won't be written out @@ -20212,7 +21254,7 @@ md_apply_fix (fixS * fixP, as_bad_where (fixP->fx_file, fixP->fx_line, _("co-processor offset out of range")); cp_off_common: - sign = value >= 0; + sign = value > 0; if (value < 0) value = -value; if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM @@ -20220,8 +21262,13 @@ md_apply_fix (fixS * fixP, newval = md_chars_to_number (buf, INSN_SIZE); else newval = get_thumb32_insn (buf); - newval &= 0xff7fff00; - newval |= (value >> 2) | (sign ? INDEX_UP : 0); + if (value == 0) + newval &= 0xffffff00; + else + { + newval &= 0xff7fff00; + newval |= (value >> 2) | (sign ? INDEX_UP : 0); + } if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM || fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2) md_number_to_chars (buf, newval, INSN_SIZE); @@ -20746,8 +21793,13 @@ tc_gen_reloc (asection *section, fixS *fixp) return NULL; #ifdef OBJ_ELF + case BFD_RELOC_ARM_TLS_CALL: + case BFD_RELOC_ARM_THM_TLS_CALL: + case BFD_RELOC_ARM_TLS_DESCSEQ: + case BFD_RELOC_ARM_THM_TLS_DESCSEQ: case BFD_RELOC_ARM_GOT32: case BFD_RELOC_ARM_GOTOFF: + case BFD_RELOC_ARM_GOT_PREL: case BFD_RELOC_ARM_PLT32: case BFD_RELOC_ARM_TARGET1: case BFD_RELOC_ARM_ROSEGREL32: @@ -20790,6 +21842,7 @@ tc_gen_reloc (asection *section, fixS *fixp) code = fixp->fx_r_type; break; + case BFD_RELOC_ARM_TLS_GOTDESC: case BFD_RELOC_ARM_TLS_GD32: case BFD_RELOC_ARM_TLS_IE32: case BFD_RELOC_ARM_TLS_LDM32: @@ -20845,6 +21898,7 @@ tc_gen_reloc (asection *section, fixS *fixp) case BFD_RELOC_ARM_SWI: type = "SWI"; break; case BFD_RELOC_ARM_MULTI: type = "MULTI"; break; case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break; + case BFD_RELOC_ARM_T32_OFFSET_IMM: type = "T32_OFFSET_IMM"; break; case BFD_RELOC_ARM_T32_CP_OFF_IMM: type = "T32_CP_OFF_IMM"; break; case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break; case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break; @@ -20983,14 +22037,25 @@ arm_force_relocation (struct fix * fixp) } #endif - /* Resolve these relocations even if the symbol is extern or weak. */ + /* Resolve these relocations even if the symbol is extern or weak. + Technically this is probably wrong due to symbol preemption. + In practice these relocations do not have enough range to be useful + at dynamic link time, and some code (e.g. in the Linux kernel) + expects these references to be resolved. */ if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM + || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM8 || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE + || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM + || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2 + || fixp->fx_r_type == BFD_RELOC_ARM_THUMB_OFFSET || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12 - || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12) + || fixp->fx_r_type == BFD_RELOC_ARM_T32_OFFSET_IMM + || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12 + || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM + || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM_S2) return 0; /* Always leave these relocations for the linker. */ @@ -21050,6 +22115,11 @@ arm_fix_adjustable (fixS * fixP) || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32 || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32 || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GOTDESC + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_CALL + || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_CALL + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_DESCSEQ + || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_DESCSEQ || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2) return FALSE; @@ -21257,8 +22327,8 @@ arm_adjust_symtab (void) /* If it's a .thumb_func, declare it as so, otherwise tag label as .code 16. */ if (THUMB_IS_FUNC (sym)) - elf_sym->internal_elf_sym.st_info = - ELF_ST_INFO (bind, STT_ARM_TFUNC); + elf_sym->internal_elf_sym.st_target_internal + = ST_BRANCH_TO_THUMB; else if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4) elf_sym->internal_elf_sym.st_info = ELF_ST_INFO (bind, STT_ARM_16BIT); @@ -21268,6 +22338,8 @@ arm_adjust_symtab (void) /* Remove any overlapping mapping symbols generated by alignment frags. */ bfd_map_over_sections (stdoutput, check_mapping_symbols, (char *) 0); + /* Now do generic ELF adjustments. */ + elf_adjust_symtab (); #endif } @@ -21844,29 +22916,43 @@ static const struct arm_cpu_option_table arm_cpus[] = {"arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM1026EJ-S"}, {"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL}, - {"fa626te", ARM_ARCH_V5TE, FPU_NONE, NULL}, + {"fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"}, {"arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL}, {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2, "ARM1136JF-S"}, {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL}, - {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, NULL}, - {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, NULL}, + {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"}, + {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"}, {"arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL}, {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL}, {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL}, {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL}, - {"cortex-a8", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 + {"cortex-a5", ARM_ARCH_V7A_MP_SEC, + FPU_NONE, "Cortex-A5"}, + {"cortex-a8", ARM_ARCH_V7A_SEC, + ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), - NULL}, - {"cortex-a9", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 + "Cortex-A8"}, + {"cortex-a9", ARM_ARCH_V7A_MP_SEC, + ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), - NULL}, - {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, NULL}, - {"cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16, NULL}, - {"cortex-m3", ARM_ARCH_V7M, FPU_NONE, NULL}, - {"cortex-m1", ARM_ARCH_V6M, FPU_NONE, NULL}, - {"cortex-m0", ARM_ARCH_V6M, FPU_NONE, NULL}, + "Cortex-A9"}, + {"cortex-a15", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT, + FPU_ARCH_NEON_VFP_V4, + "Cortex-A15"}, + {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"}, + {"cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16, + "Cortex-R4F"}, + {"cortex-r5", ARM_ARCH_V7R_IDIV, + FPU_NONE, "Cortex-R5"}, + {"cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"}, + {"cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"}, + {"cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"}, + {"cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"}, /* ??? XSCALE is really an architecture. */ {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, /* ??? iwmmxt is not a processor. */ @@ -21916,6 +23002,7 @@ static const struct arm_arch_option_table arm_archs[] = {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP}, {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP}, {"armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP}, + {"armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP}, {"armv7", ARM_ARCH_V7, FPU_ARCH_VFP}, /* The official spelling of the ARMv7 profile variants is the dashed form. Accept the non-dashed form for compatibility with old toolchains. */ @@ -21925,31 +23012,52 @@ static const struct arm_arch_option_table arm_archs[] = {"armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP}, {"armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP}, {"armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP}, + {"armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP}, {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP}, {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP}, {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP}, {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} }; -/* ISA extensions in the co-processor space. */ -struct arm_option_cpu_value_table +/* ISA extensions in the co-processor and main instruction set space. */ +struct arm_option_extension_value_table { char *name; const arm_feature_set value; + const arm_feature_set allowed_archs; }; -static const struct arm_option_cpu_value_table arm_extensions[] = +/* The following table must be in alphabetical order with a NULL last entry. + */ +static const struct arm_option_extension_value_table arm_extensions[] = +{ + {"idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0), + ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)}, + {"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY}, + {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY}, + {"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY}, + {"mp", ARM_FEATURE (ARM_EXT_MP, 0), + ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)}, + {"os", ARM_FEATURE (ARM_EXT_OS, 0), + ARM_FEATURE (ARM_EXT_V6M, 0)}, + {"sec", ARM_FEATURE (ARM_EXT_SEC, 0), + ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)}, + {"virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV | ARM_EXT_DIV, 0), + ARM_FEATURE (ARM_EXT_V7A, 0)}, + {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY}, + {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} +}; + +/* ISA floating-point and Advanced SIMD extensions. */ +struct arm_option_fpu_value_table { - {"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK)}, - {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE)}, - {"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT)}, - {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2)}, - {NULL, ARM_ARCH_NONE} + char *name; + const arm_feature_set value; }; /* This list should, at a minimum, contain all the fpu names recognized by GCC. */ -static const struct arm_option_cpu_value_table arm_fpus[] = +static const struct arm_option_fpu_value_table arm_fpus[] = { {"softfpa", FPU_NONE}, {"fpe", FPU_ARCH_FPE}, @@ -21969,7 +23077,11 @@ static const struct arm_option_cpu_value_table arm_fpus[] = {"vfpxd", FPU_ARCH_VFP_V1xD}, {"vfpv2", FPU_ARCH_VFP_V2}, {"vfpv3", FPU_ARCH_VFP_V3}, + {"vfpv3-fp16", FPU_ARCH_VFP_V3_FP16}, {"vfpv3-d16", FPU_ARCH_VFP_V3D16}, + {"vfpv3-d16-fp16", FPU_ARCH_VFP_V3D16_FP16}, + {"vfpv3xd", FPU_ARCH_VFP_V3xD}, + {"vfpv3xd-fp16", FPU_ARCH_VFP_V3xD_FP16}, {"arm1020t", FPU_ARCH_VFP_V1}, {"arm1020e", FPU_ARCH_VFP_V2}, {"arm1136jfs", FPU_ARCH_VFP_V2}, @@ -21977,6 +23089,10 @@ static const struct arm_option_cpu_value_table arm_fpus[] = {"maverick", FPU_ARCH_MAVERICK}, {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1}, {"neon-fp16", FPU_ARCH_NEON_FP16}, + {"vfpv4", FPU_ARCH_VFP_V4}, + {"vfpv4-d16", FPU_ARCH_VFP_V4D16}, + {"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16}, + {"neon-vfpv4", FPU_ARCH_NEON_VFP_V4}, {NULL, ARM_ARCH_NONE} }; @@ -22019,15 +23135,23 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) arm_feature_set *ext_set = (arm_feature_set *) xmalloc (sizeof (arm_feature_set)); + /* We insist on extensions being specified in alphabetical order, and with + extensions being added before being removed. We achieve this by having + the global ARM_EXTENSIONS table in alphabetical order, and using the + ADDING_VALUE variable to indicate whether we are adding an extension (1) + or removing it (0) and only allowing it to change in the order + -1 -> 1 -> 0. */ + const struct arm_option_extension_value_table * opt = NULL; + int adding_value = -1; + /* Copy the feature set, so that we can modify it. */ *ext_set = **opt_p; *opt_p = ext_set; while (str != NULL && *str != 0) { - const struct arm_option_cpu_value_table * opt; char * ext; - int optlen; + size_t optlen; if (*str != '+') { @@ -22043,24 +23167,86 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) else optlen = strlen (str); + if (optlen >= 2 + && strncmp (str, "no", 2) == 0) + { + if (adding_value != 0) + { + adding_value = 0; + opt = arm_extensions; + } + + optlen -= 2; + str += 2; + } + else if (optlen > 0) + { + if (adding_value == -1) + { + adding_value = 1; + opt = arm_extensions; + } + else if (adding_value != 1) + { + as_bad (_("must specify extensions to add before specifying " + "those to remove")); + return FALSE; + } + } + if (optlen == 0) { as_bad (_("missing architectural extension")); return FALSE; } - for (opt = arm_extensions; opt->name != NULL; opt++) - if (strncmp (opt->name, str, optlen) == 0) + gas_assert (adding_value != -1); + gas_assert (opt != NULL); + + /* Scan over the options table trying to find an exact match. */ + for (; opt->name != NULL; opt++) + if (strncmp (opt->name, str, optlen) == 0 + && strlen (opt->name) == optlen) { - ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value); + /* Check we can apply the extension to this architecture. */ + if (!ARM_CPU_HAS_FEATURE (*ext_set, opt->allowed_archs)) + { + as_bad (_("extension does not apply to the base architecture")); + return FALSE; + } + + /* Add or remove the extension. */ + if (adding_value) + ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value); + else + ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->value); + break; } if (opt->name == NULL) { - as_bad (_("unknown architectural extension `%s'"), str); + /* Did we fail to find an extension because it wasn't specified in + alphabetical order, or because it does not exist? */ + + for (opt = arm_extensions; opt->name != NULL; opt++) + if (strncmp (opt->name, str, optlen) == 0) + break; + + if (opt->name == NULL) + as_bad (_("unknown architectural extension `%s'"), str); + else + as_bad (_("architectural extensions must be specified in " + "alphabetical order")); + return FALSE; } + else + { + /* We should skip the extension we've just matched the next time + round. */ + opt++; + } str = ext; }; @@ -22131,7 +23317,7 @@ arm_parse_arch (char * str) } for (opt = arm_archs; opt->name != NULL; opt++) - if (streq (opt->name, str)) + if (strncmp (opt->name, str, optlen) == 0) { march_cpu_opt = &opt->value; march_fpu_opt = &opt->default_fpu; @@ -22150,7 +23336,7 @@ arm_parse_arch (char * str) static bfd_boolean arm_parse_fpu (char * str) { - const struct arm_option_cpu_value_table * opt; + const struct arm_option_fpu_value_table * opt; for (opt = arm_fpus; opt->name != NULL; opt++) if (streq (opt->name, str)) @@ -22378,9 +23564,10 @@ static const cpu_arch_ver_table cpu_arch_ver[] = {4, ARM_ARCH_V5TE}, {5, ARM_ARCH_V5TEJ}, {6, ARM_ARCH_V6}, - {7, ARM_ARCH_V6Z}, {9, ARM_ARCH_V6K}, + {7, ARM_ARCH_V6Z}, {11, ARM_ARCH_V6M}, + {12, ARM_ARCH_V6SM}, {8, ARM_ARCH_V6T2}, {10, ARM_ARCH_V7A}, {10, ARM_ARCH_V7R}, @@ -22412,6 +23599,7 @@ static void aeabi_set_public_attributes (void) { int arch; + int virt_sec = 0; arm_feature_set flags; arm_feature_set tmp; const cpu_arch_ver_table *p; @@ -22428,6 +23616,12 @@ aeabi_set_public_attributes (void) ARM_MERGE_FEATURE_SETS (flags, flags, *object_arch); } + /* We need to make sure that the attributes do not identify us as v6S-M + when the only v6S-M feature in use is the Operating System Extensions. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_os)) + if (!ARM_CPU_HAS_FEATURE (flags, arm_arch_v6m_only)) + ARM_CLEAR_FEATURE (flags, flags, arm_ext_os); + tmp = flags; arch = 0; for (p = cpu_arch_ver; p->val; p++) @@ -22439,24 +23633,40 @@ aeabi_set_public_attributes (void) } } + /* The table lookup above finds the last architecture to contribute + a new feature. Unfortunately, Tag13 is a subset of the union of + v6T2 and v7-M, so it is never seen as contributing a new feature. + We can not search for the last entry which is entirely used, + because if no CPU is specified we build up only those flags + actually used. Perhaps we should separate out the specified + and implicit cases. Avoid taking this path for -march=all by + checking for contradictory v7-A / v7-M features. */ + if (arch == 10 + && !ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a) + && ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m) + && ARM_CPU_HAS_FEATURE (flags, arm_ext_v6_dsp)) + arch = 13; + /* Tag_CPU_name. */ if (selected_cpu_name[0]) { - char *p; + char *q; - p = selected_cpu_name; - if (strncmp (p, "armv", 4) == 0) + q = selected_cpu_name; + if (strncmp (q, "armv", 4) == 0) { int i; - p += 4; - for (i = 0; p[i]; i++) - p[i] = TOUPPER (p[i]); + q += 4; + for (i = 0; q[i]; i++) + q[i] = TOUPPER (q[i]); } - aeabi_set_attribute_string (Tag_CPU_name, p); + aeabi_set_attribute_string (Tag_CPU_name, q); } + /* Tag_CPU_arch. */ aeabi_set_attribute_int (Tag_CPU_arch, arch); + /* Tag_CPU_arch_profile. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)) aeabi_set_attribute_int (Tag_CPU_arch_profile, 'A'); @@ -22464,36 +23674,73 @@ aeabi_set_public_attributes (void) aeabi_set_attribute_int (Tag_CPU_arch_profile, 'R'); else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_m)) aeabi_set_attribute_int (Tag_CPU_arch_profile, 'M'); + /* Tag_ARM_ISA_use. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v1) || arch == 0) aeabi_set_attribute_int (Tag_ARM_ISA_use, 1); + /* Tag_THUMB_ISA_use. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v4t) || arch == 0) aeabi_set_attribute_int (Tag_THUMB_ISA_use, ARM_CPU_HAS_FEATURE (flags, arm_arch_t2) ? 2 : 1); + /* Tag_VFP_arch. */ - if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)) + if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma)) + aeabi_set_attribute_int (Tag_VFP_arch, + ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32) + ? 5 : 6); + else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)) aeabi_set_attribute_int (Tag_VFP_arch, 3); - else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v3)) + else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v3xd)) aeabi_set_attribute_int (Tag_VFP_arch, 4); else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v2)) aeabi_set_attribute_int (Tag_VFP_arch, 2); else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1) || ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd)) aeabi_set_attribute_int (Tag_VFP_arch, 1); + + /* Tag_ABI_HardFP_use. */ + if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd) + && !ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1)) + aeabi_set_attribute_int (Tag_ABI_HardFP_use, 1); + /* Tag_WMMX_arch. */ if (ARM_CPU_HAS_FEATURE (flags, arm_cext_iwmmxt2)) aeabi_set_attribute_int (Tag_WMMX_arch, 2); else if (ARM_CPU_HAS_FEATURE (flags, arm_cext_iwmmxt)) aeabi_set_attribute_int (Tag_WMMX_arch, 1); + /* Tag_Advanced_SIMD_arch (formerly Tag_NEON_arch). */ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1)) - aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 1); + aeabi_set_attribute_int + (Tag_Advanced_SIMD_arch, (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma) + ? 2 : 1)); + /* Tag_VFP_HP_extension (formerly Tag_NEON_FP16_arch). */ - if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_fp16)) + if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16)) aeabi_set_attribute_int (Tag_VFP_HP_extension, 1); + + /* Tag_DIV_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)) + aeabi_set_attribute_int (Tag_DIV_use, 2); + else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_div)) + aeabi_set_attribute_int (Tag_DIV_use, 0); + else + aeabi_set_attribute_int (Tag_DIV_use, 1); + + /* Tag_MP_extension_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_mp)) + aeabi_set_attribute_int (Tag_MPextension_use, 1); + + /* Tag Virtualization_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_sec)) + virt_sec |= 1; + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_virt)) + virt_sec |= 2; + if (virt_sec != 0) + aeabi_set_attribute_int (Tag_Virtualization_use, virt_sec); } /* Add the default contents for the .ARM.attributes section. */ @@ -22613,12 +23860,64 @@ s_arm_object_arch (int ignored ATTRIBUTE_UNUSED) ignore_rest_of_line (); } +/* Parse a .arch_extension directive. */ + +static void +s_arm_arch_extension (int ignored ATTRIBUTE_UNUSED) +{ + const struct arm_option_extension_value_table *opt; + char saved_char; + char *name; + int adding_value = 1; + + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE (*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + + if (strlen (name) >= 2 + && strncmp (name, "no", 2) == 0) + { + adding_value = 0; + name += 2; + } + + for (opt = arm_extensions; opt->name != NULL; opt++) + if (streq (opt->name, name)) + { + if (!ARM_CPU_HAS_FEATURE (*mcpu_cpu_opt, opt->allowed_archs)) + { + as_bad (_("architectural extension `%s' is not allowed for the " + "current base architecture"), name); + break; + } + + if (adding_value) + ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu, opt->value); + else + ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->value); + + mcpu_cpu_opt = &selected_cpu; + ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt); + *input_line_pointer = saved_char; + demand_empty_rest_of_line (); + return; + } + + if (opt->name == NULL) + as_bad (_("unknown architecture `%s'\n"), name); + + *input_line_pointer = saved_char; + ignore_rest_of_line (); +} + /* Parse a .fpu directive. */ static void s_arm_fpu (int ignored ATTRIBUTE_UNUSED) { - const struct arm_option_cpu_value_table *opt; + const struct arm_option_fpu_value_table *opt; char saved_char; char *name; @@ -22674,6 +23973,7 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_CPU_arch_profile), T (Tag_ARM_ISA_use), T (Tag_THUMB_ISA_use), + T (Tag_FP_arch), T (Tag_VFP_arch), T (Tag_WMMX_arch), T (Tag_Advanced_SIMD_arch), @@ -22688,7 +23988,9 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_ABI_FP_exceptions), T (Tag_ABI_FP_user_exceptions), T (Tag_ABI_FP_number_model), + T (Tag_ABI_align_needed), T (Tag_ABI_align8_needed), + T (Tag_ABI_align_preserved), T (Tag_ABI_align8_preserved), T (Tag_ABI_enum_size), T (Tag_ABI_HardFP_use), @@ -22698,14 +24000,17 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_ABI_FP_optimization_goals), T (Tag_compatibility), T (Tag_CPU_unaligned_access), + T (Tag_FP_HP_extension), T (Tag_VFP_HP_extension), T (Tag_ABI_FP_16bit_format), + T (Tag_MPextension_use), + T (Tag_DIV_use), T (Tag_nodefaults), T (Tag_also_compatible_with), T (Tag_conformance), T (Tag_T2EE_use), T (Tag_Virtualization_use), - T (Tag_MPextension_use) + /* We deliberately do not include Tag_MPextension_use_legacy. */ #undef T }; unsigned int i; @@ -22729,7 +24034,7 @@ arm_apply_sym_value (struct fix * fixP) { if (fixP->fx_addsy && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) - && !S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)) { switch (fixP->fx_r_type) {