/* tc-arm.c -- Assemble for the ARM
Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006
+ 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
else if (new->number != number || new->type != type)
as_warn (_("ignoring redefinition of register alias '%s'"), str);
- return 0;
+ return NULL;
}
name = xstrdup (str);
new_register_name .req existing_register_name
If we find one, or if it looks sufficiently like one that we want to
- handle any error here, return non-zero. Otherwise return zero. */
+ handle any error here, return TRUE. Otherwise return FALSE. */
-static int
+static bfd_boolean
create_register_alias (char * newname, char *p)
{
struct reg_entry *old;
collapsed to single spaces. */
oldname = p;
if (strncmp (oldname, " .req ", 6) != 0)
- return 0;
+ return FALSE;
oldname += 6;
if (*oldname == '\0')
- return 0;
+ return FALSE;
old = hash_find (arm_reg_hsh, oldname);
if (!old)
{
as_warn (_("unknown register '%s' -- .req ignored"), oldname);
- return 1;
+ return TRUE;
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
/* Create aliases under the new name as stated; an all-lowercase
version of the new name; and an all-uppercase version of the new
name. */
- insert_reg_alias (nbuf, old->number, old->type);
-
- for (p = nbuf; *p; p++)
- *p = TOUPPER (*p);
+ if (insert_reg_alias (nbuf, old->number, old->type) != NULL)
+ {
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
- if (strncmp (nbuf, newname, nlen))
- insert_reg_alias (nbuf, old->number, old->type);
+ if (strncmp (nbuf, newname, nlen))
+ {
+ /* If this attempt to create an additional alias fails, do not bother
+ trying to create the all-lower case alias. We will fail and issue
+ a second, duplicate error message. This situation arises when the
+ programmer does something like:
+ foo .req r0
+ Foo .req r1
+ The second .req creates the "Foo" alias but then fails to create
+ the artifical FOO alias because it has already been created by the
+ first .req. */
+ if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
+ return TRUE;
+ }
- for (p = nbuf; *p; p++)
- *p = TOLOWER (*p);
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
- if (strncmp (nbuf, newname, nlen))
- insert_reg_alias (nbuf, old->number, old->type);
+ if (strncmp (nbuf, newname, nlen))
+ insert_reg_alias (nbuf, old->number, old->type);
+ }
- return 1;
+ return TRUE;
}
/* Create a Neon typed/indexed register alias using directives, e.g.:
name);
else
{
+ char * p;
+ char * nbuf;
+
hash_delete (arm_reg_hsh, name);
free ((char *) reg->name);
if (reg->neon)
free (reg->neon);
free (reg);
+
+ /* Also locate the all upper case and all lower case versions.
+ Do not complain if we cannot find one or the other as it
+ was probably deleted above. */
+
+ nbuf = strdup (name);
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+ reg = hash_find (arm_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (arm_reg_hsh, nbuf);
+ free ((char *) reg->name);
+ if (reg->neon)
+ free (reg->neon);
+ free (reg);
+ }
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+ reg = hash_find (arm_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (arm_reg_hsh, nbuf);
+ free ((char *) reg->name);
+ if (reg->neon)
+ free (reg->neon);
+ free (reg);
+ }
+
+ free (nbuf);
}
}
s_align (int unused ATTRIBUTE_UNUSED)
{
int temp;
+ bfd_boolean fill_p;
long temp_fill;
long max_alignment = 15;
{
input_line_pointer++;
temp_fill = get_absolute_expression ();
+ fill_p = TRUE;
}
else
- temp_fill = 0;
+ {
+ fill_p = FALSE;
+ temp_fill = 0;
+ }
if (!temp)
temp = 2;
/* Only make a frag if we HAVE to. */
if (temp && !need_pass_2)
- frag_align (temp, (int) temp_fill, 0);
+ {
+ if (!fill_p && subseg_text_p (now_seg))
+ frag_align_code (temp, 0);
+ else
+ frag_align (temp, (int) temp_fill, 0);
+ }
demand_empty_rest_of_line ();
record_alignment (now_seg, temp);
static void
s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED)
{
- expressionS exp;
- bfd_boolean is_string;
- int tag;
- unsigned int i = 0;
- char *s = NULL;
- char saved_char;
-
- expression (& exp);
- if (exp.X_op != O_constant)
- goto bad;
-
- tag = exp.X_add_number;
- if (tag == 4 || tag == 5 || tag == 32 || (tag > 32 && (tag & 1) != 0))
- is_string = 1;
- else
- is_string = 0;
-
- if (skip_past_comma (&input_line_pointer) == FAIL)
- goto bad;
- if (tag == 32 || !is_string)
- {
- expression (& exp);
- if (exp.X_op != O_constant)
- {
- as_bad (_("expected numeric constant"));
- ignore_rest_of_line ();
- return;
- }
- i = exp.X_add_number;
- }
- if (tag == Tag_compatibility
- && skip_past_comma (&input_line_pointer) == FAIL)
- {
- as_bad (_("expected comma"));
- ignore_rest_of_line ();
- return;
- }
- if (is_string)
- {
- skip_whitespace(input_line_pointer);
- if (*input_line_pointer != '"')
- goto bad_string;
- input_line_pointer++;
- s = input_line_pointer;
- while (*input_line_pointer && *input_line_pointer != '"')
- input_line_pointer++;
- if (*input_line_pointer != '"')
- goto bad_string;
- saved_char = *input_line_pointer;
- *input_line_pointer = 0;
- }
- else
- {
- s = NULL;
- saved_char = 0;
- }
-
- if (tag == Tag_compatibility)
- elf32_arm_add_eabi_attr_compat (stdoutput, i, s);
- else if (is_string)
- elf32_arm_add_eabi_attr_string (stdoutput, tag, s);
- else
- elf32_arm_add_eabi_attr_int (stdoutput, tag, i);
-
- if (s)
- {
- *input_line_pointer = saved_char;
- input_line_pointer++;
- }
- demand_empty_rest_of_line ();
- return;
-bad_string:
- as_bad (_("bad string constant"));
- ignore_rest_of_line ();
- return;
-bad:
- as_bad (_("expected <tag> , <value>"));
- ignore_rest_of_line ();
+ s_vendor_attribute (OBJ_ATTR_PROC);
}
#endif /* OBJ_ELF */
case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break;
case OP_oRND:
case OP_RND: po_reg_or_fail (REG_TYPE_VFD); break;
- case OP_RVC: po_reg_or_fail (REG_TYPE_VFC); break;
+ case OP_RVC:
+ po_reg_or_goto (REG_TYPE_VFC, coproc_reg);
+ break;
+ /* Also accept generic coprocessor regs for unknown registers. */
+ coproc_reg:
+ po_reg_or_fail (REG_TYPE_CN);
+ break;
case OP_RMF: po_reg_or_fail (REG_TYPE_MVF); break;
case OP_RMD: po_reg_or_fail (REG_TYPE_MVD); break;
case OP_RMFX: po_reg_or_fail (REG_TYPE_MVFX); break;
inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
}
+ else if (inst.operands[1].shifted && inst.operands[1].immisreg
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ /* Register shifts are encoded as separate shift instructions. */
+ bfd_boolean flags = (inst.instruction == T_MNEM_movs);
+
+ if (current_it_mask)
+ narrow = !flags;
+ else
+ narrow = flags;
+
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (!low_regs || inst.operands[1].imm > 7)
+ narrow = FALSE;
+
+ if (inst.operands[0].reg != inst.operands[1].reg)
+ narrow = FALSE;
+
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL:
+ opcode = narrow ? T_OPCODE_LSL_R : THUMB_OP32 (T_MNEM_lsl);
+ break;
+ case SHIFT_ASR:
+ opcode = narrow ? T_OPCODE_ASR_R : THUMB_OP32 (T_MNEM_asr);
+ break;
+ case SHIFT_LSR:
+ opcode = narrow ? T_OPCODE_LSR_R : THUMB_OP32 (T_MNEM_lsr);
+ break;
+ case SHIFT_ROR:
+ opcode = narrow ? T_OPCODE_ROR_R : THUMB_OP32 (T_MNEM_ror);
+ break;
+ default:
+ abort();
+ }
+
+ inst.instruction = opcode;
+ if (narrow)
+ {
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].imm << 3;
+ }
+ else
+ {
+ if (flags)
+ inst.instruction |= CONDS_BIT;
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ }
else if (!narrow)
{
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << r0off;
- encode_thumb32_shifted_operand (1);
+ /* Some mov with immediate shift have narrow variants.
+ Register shifts are handled above. */
+ if (low_regs && inst.operands[1].shifted
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ if (current_it_mask)
+ narrow = (inst.instruction == T_MNEM_mov);
+ else
+ narrow = (inst.instruction == T_MNEM_movs);
+ }
+
+ if (narrow)
+ {
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ default: narrow = FALSE; break;
+ }
+ }
+
+ if (narrow)
+ {
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
}
else
switch (inst.instruction)
/* VFP control registers. */
REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC),
REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC),
+ REGDEF(fpinst,9,VFC), REGDEF(fpinst2,10,VFC),
+ REGDEF(FPINST,9,VFC), REGDEF(FPINST2,10,VFC),
+ REGDEF(mvfr0,7,VFC), REGDEF(mvfr1,6,VFC),
+ REGDEF(MVFR0,7,VFC), REGDEF(MVFR1,6,VFC),
/* Maverick DSP coprocessor registers. */
REGSET(mvf,MVF), REGSET(mvd,MVD), REGSET(mvfx,MVFX), REGSET(mvdx,MVDX),
#undef ARM_VARIANT
#define ARM_VARIANT &arm_ext_v5e /* ARM Architecture 5TE. */
TUF(pld, 450f000, f810f000, 1, (ADDR), pld, t_pld),
- TC3(ldrd, 00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
- TC3(strd, 00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(ldrd, 00000d0, e8500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(strd, 00000f0, e8400000, 3, (RRnpc, oRRnpc, 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),
#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),
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),
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(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex),
TCE(umaal, 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal),
TCE(usad8, 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
TCE(usada8, 7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
if (stretch != 0
&& sym_frag->relax_marker != fragp->relax_marker)
- addr += stretch;
+ {
+ fragS *f;
+
+ /* Adjust stretch for any alignment frag. Note that if have
+ been expanding the earlier code, the symbol may be
+ defined in what appears to be an earlier frag. FIXME:
+ This doesn't handle the fr_subtype field, which specifies
+ a maximum number of bytes to skip when doing an
+ alignment. */
+ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
+ {
+ if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+ {
+ if (stretch < 0)
+ stretch = - ((- stretch)
+ & ~ ((1 << (int) f->fr_offset) - 1));
+ else
+ stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+ if (stretch == 0)
+ break;
+ }
+ }
+ if (f != NULL)
+ addr += stretch;
+ }
return addr;
}
for (i = 0; p[i]; i++)
p[i] = TOUPPER (p[i]);
}
- elf32_arm_add_eabi_attr_string (stdoutput, 5, p);
+ bfd_elf_add_proc_attr_string (stdoutput, 5, p);
}
/* Tag_CPU_arch. */
- elf32_arm_add_eabi_attr_int (stdoutput, 6, arch);
+ bfd_elf_add_proc_attr_int (stdoutput, 6, arch);
/* Tag_CPU_arch_profile. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'A');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'A');
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'R');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'R');
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'M');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'M');
/* Tag_ARM_ISA_use. */
if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_full))
- elf32_arm_add_eabi_attr_int (stdoutput, 8, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 8, 1);
/* Tag_THUMB_ISA_use. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_full))
- elf32_arm_add_eabi_attr_int (stdoutput, 9,
+ bfd_elf_add_proc_attr_int (stdoutput, 9,
ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_t2) ? 2 : 1);
/* Tag_VFP_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v3)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v3))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 3);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 3);
else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v2)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v2))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 2);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 2);
else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1)
|| ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1xd)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1xd))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 1);
/* Tag_WMMX_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_cext_iwmmxt)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, arm_cext_iwmmxt))
- elf32_arm_add_eabi_attr_int (stdoutput, 11, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 11, 1);
/* Tag_NEON_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_neon_ext_v1)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_neon_ext_v1))
- elf32_arm_add_eabi_attr_int (stdoutput, 12, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 12, 1);
}
-/* Add the .ARM.attributes section. */
+/* Add the default contents for the .ARM.attributes section. */
void
arm_md_end (void)
{
- segT s;
- char *p;
- addressT addr;
- offsetT size;
-
if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
return;
aeabi_set_public_attributes ();
- size = elf32_arm_eabi_attr_size (stdoutput);
- s = subseg_new (".ARM.attributes", 0);
- bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA);
- addr = frag_now_fix ();
- p = frag_more (size);
- elf32_arm_set_eabi_attr_contents (stdoutput, (bfd_byte *)p, size);
}
#endif /* OBJ_ELF */