/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
- Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation.
+ Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of GAS, the GNU Assembler.
DYNREG_NUM_TYPES
};
+enum operand_match_result
+ {
+ OPERAND_MATCH,
+ OPERAND_OUT_OF_RANGE,
+ OPERAND_MISMATCH
+ };
+
/* On the ia64, we can't know the address of a text label until the
instructions are packed into a bundle. To handle this, we keep
track of the list of labels that appear in front of each
explicit_mode : 1, /* which mode we're in */
default_explicit_mode : 1, /* which mode is the default */
mode_explicitly_set : 1, /* was the current mode explicitly set? */
- auto_align : 1;
+ auto_align : 1,
+ keep_pending_output : 1;
/* Each bundle consists of up to three instructions. We keep
track of four most recent instructions so we can correctly set
/* TRUE if processing unwind directives in a prologue region. */
int prologue;
int prologue_mask;
+ unsigned int prologue_count; /* number of .prologues seen so far */
} unwind;
typedef void (*vbyte_func) PARAMS ((int, char *, char *));
static symbolS *declare_register PARAMS ((const char *name, int regnum));
static void declare_register_set PARAMS ((const char *, int, int));
static unsigned int operand_width PARAMS ((enum ia64_opnd));
-static int operand_match PARAMS ((const struct ia64_opcode *idesc,
- int index, expressionS *e));
+static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
+ int index,
+ expressionS *e));
static int parse_operand PARAMS ((expressionS *e));
static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
static void build_insn PARAMS ((struct slot *, bfd_vma *));
int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
- unsigned long ecount = 0;
+ unsigned long ecount; /* # of _additional_ regions to pop */
int sep;
sep = parse_operand (&e1);
if (sep == ',')
{
parse_operand (&e2);
- if (e1.X_op != O_constant)
+ if (e2.X_op != O_constant || e2.X_add_number < 0)
{
- as_bad ("Second operand to .restore must be constant");
+ as_bad ("Second operand to .restore must be a constant >= 0");
return;
}
- ecount = e1.X_op;
+ ecount = e2.X_add_number;
}
+ else
+ ecount = unwind.prologue_count - 1;
add_unwind_entry (output_epilogue (ecount));
+
+ if (ecount < unwind.prologue_count)
+ unwind.prologue_count -= ecount + 1;
+ else
+ unwind.prologue_count = 0;
}
static void
demand_empty_rest_of_line ();
ia64_do_align (16);
+ unwind.prologue_count = 0;
unwind.list = unwind.tail = unwind.current_entry = NULL;
unwind.personality_routine = 0;
}
unwind.prologue = 1;
unwind.prologue_mask = mask;
+ ++unwind.prologue_count;
}
static void
if (!name)
return;
+ md.keep_pending_output = 1;
set_section (name);
cons (size);
obj_elf_previous (0);
+ md.keep_pending_output = 0;
}
/* Why doesn't float_cons() call md_cons_align() the way cons() does? */
if (!name)
return;
+ md.keep_pending_output = 1;
set_section (name);
stmt_float_cons (kind);
obj_elf_previous (0);
+ md.keep_pending_output = 0;
}
static void
if (!name)
return;
+ md.keep_pending_output = 1;
set_section (name);
stringer (zero);
obj_elf_previous (0);
+ md.keep_pending_output = 0;
}
static void
if (!name)
return;
+ md.keep_pending_output = 1;
set_section (name);
md.auto_align = 0;
cons (size);
md.auto_align = saved_auto_align;
obj_elf_previous (0);
+ md.keep_pending_output = 0;
}
static void
if (!name)
return;
+ md.keep_pending_output = 1;
set_section (name);
md.auto_align = 0;
stmt_float_cons (kind);
md.auto_align = saved_auto_align;
obj_elf_previous (0);
+ md.keep_pending_output = 0;
}
/* .reg.val <regname>,value */
return bits;
}
-static int
+static enum operand_match_result
operand_match (idesc, index, e)
const struct ia64_opcode *idesc;
int index;
case IA64_OPND_AR_CCV:
if (e->X_op == O_register && e->X_add_number == REG_AR + 32)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_AR_PFS:
if (e->X_op == O_register && e->X_add_number == REG_AR + 64)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_GR0:
if (e->X_op == O_register && e->X_add_number == REG_GR + 0)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_IP:
if (e->X_op == O_register && e->X_add_number == REG_IP)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_PR:
if (e->X_op == O_register && e->X_add_number == REG_PR)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_PR_ROT:
if (e->X_op == O_register && e->X_add_number == REG_PR_ROT)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_PSR:
if (e->X_op == O_register && e->X_add_number == REG_PSR)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_PSR_L:
if (e->X_op == O_register && e->X_add_number == REG_PSR_L)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_PSR_UM:
if (e->X_op == O_register && e->X_add_number == REG_PSR_UM)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_C1:
- if (e->X_op == O_constant && e->X_add_number == 1)
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 1)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_C8:
- if (e->X_op == O_constant && e->X_add_number == 8)
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 8)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_C16:
- if (e->X_op == O_constant && e->X_add_number == 16)
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 16)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
/* register operands: */
case IA64_OPND_AR3:
if (e->X_op == O_register && e->X_add_number >= REG_AR
&& e->X_add_number < REG_AR + 128)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_B1:
case IA64_OPND_B2:
if (e->X_op == O_register && e->X_add_number >= REG_BR
&& e->X_add_number < REG_BR + 8)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_CR3:
if (e->X_op == O_register && e->X_add_number >= REG_CR
&& e->X_add_number < REG_CR + 128)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_F1:
case IA64_OPND_F4:
if (e->X_op == O_register && e->X_add_number >= REG_FR
&& e->X_add_number < REG_FR + 128)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_P1:
case IA64_OPND_P2:
if (e->X_op == O_register && e->X_add_number >= REG_P
&& e->X_add_number < REG_P + 64)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_R1:
case IA64_OPND_R3:
if (e->X_op == O_register && e->X_add_number >= REG_GR
&& e->X_add_number < REG_GR + 128)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_R3_2:
- if (e->X_op == O_register && e->X_add_number >= REG_GR
- && e->X_add_number < REG_GR + 4)
- return 1;
+ if (e->X_op == O_register && e->X_add_number >= REG_GR)
+ {
+ if (e->X_add_number < REG_GR + 4)
+ return OPERAND_MATCH;
+ else if (e->X_add_number < REG_GR + 128)
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
/* indirect operands: */
if (e->X_op == O_index && e->X_op_symbol
&& (S_GET_VALUE (e->X_op_symbol) - IND_CPUID
== opnd - IA64_OPND_CPUID_R3))
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_MR3:
if (e->X_op == O_index && !e->X_op_symbol)
- return 1;
+ return OPERAND_MATCH;
break;
/* immediate operands: */
case IA64_OPND_LEN4:
case IA64_OPND_LEN6:
bits = operand_width (idesc->operands[index]);
- if (e->X_op == O_constant
- && (bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits))
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_CNT2b:
- if (e->X_op == O_constant
- && (bfd_vma) (e->X_add_number - 1) < 3)
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) (e->X_add_number - 1) < 3)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_CNT2c:
val = e->X_add_number;
- if (e->X_op == O_constant
- && (val == 0 || val == 7 || val == 15 || val == 16))
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((val == 0 || val == 7 || val == 15 || val == 16))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_SOR:
/* SOR must be an integer multiple of 8 */
- if (e->X_add_number & 0x7)
- break;
+ if (e->X_op == O_constant && e->X_add_number & 0x7)
+ return OPERAND_OUT_OF_RANGE;
case IA64_OPND_SOF:
case IA64_OPND_SOL:
- if (e->X_op == O_constant &&
- (bfd_vma) e->X_add_number <= 96)
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number <= 96)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_IMMU62:
if (e->X_op == O_constant)
{
if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << 62))
- return 1;
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
}
else
{
fix->expr = *e;
fix->is_pcrel = 0;
++CURR_SLOT.num_fixups;
- return 1;
+ return OPERAND_MATCH;
}
else if (e->X_op == O_constant)
- return 1;
+ return OPERAND_MATCH;
break;
case IA64_OPND_CCNT5:
case IA64_OPND_MHTYPE8:
case IA64_OPND_POS6:
bits = operand_width (idesc->operands[index]);
- if (e->X_op == O_constant
- && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_IMMU9:
bits = operand_width (idesc->operands[index]);
- if (e->X_op == O_constant
- && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+ if (e->X_op == O_constant)
{
- int lobits = e->X_add_number & 0x3;
- if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0)
- e->X_add_number |= (bfd_vma) 0x3;
- return 1;
+ if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+ {
+ int lobits = e->X_add_number & 0x3;
+ if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0)
+ e->X_add_number |= (bfd_vma) 0x3;
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
}
break;
case IA64_OPND_IMM44:
/* least 16 bits must be zero */
if ((e->X_add_number & 0xffff) != 0)
+ /* XXX technically, this is wrong: we should not be issuing warning
+ messages until we're sure this instruction pattern is going to
+ be used! */
as_warn (_("lower 16 bits of mask ignored"));
- if (e->X_op == O_constant
- && ((e->X_add_number >= 0
- && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 44))
- || (e->X_add_number < 0
- && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 44))))
+ if (e->X_op == O_constant)
{
- /* sign-extend */
- if (e->X_add_number >= 0
- && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0)
+ if (((e->X_add_number >= 0
+ && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 44))
+ || (e->X_add_number < 0
+ && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 44))))
{
- e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1);
+ /* sign-extend */
+ if (e->X_add_number >= 0
+ && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0)
+ {
+ e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1);
+ }
+ return OPERAND_MATCH;
}
- return 1;
+ else
+ return OPERAND_OUT_OF_RANGE;
}
break;
case IA64_OPND_IMM17:
/* bit 0 is a don't care (pr0 is hardwired to 1) */
- if (e->X_op == O_constant
- && ((e->X_add_number >= 0
- && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 17))
- || (e->X_add_number < 0
- && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 17))))
- {
- /* sign-extend */
- if (e->X_add_number >= 0
- && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0)
+ if (e->X_op == O_constant)
+ {
+ if (((e->X_add_number >= 0
+ && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 17))
+ || (e->X_add_number < 0
+ && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 17))))
{
- e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1);
+ /* sign-extend */
+ if (e->X_add_number >= 0
+ && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0)
+ {
+ e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1);
+ }
+ return OPERAND_MATCH;
}
- return 1;
+ else
+ return OPERAND_OUT_OF_RANGE;
}
break;
fix->expr = *e;
fix->is_pcrel = 0;
++CURR_SLOT.num_fixups;
- return 1;
+ return OPERAND_MATCH;
}
else if (e->X_op != O_constant
&& ! (e->X_op == O_big && opnd == IA64_OPND_IMM8M1U8))
- return 0;
+ return OPERAND_MISMATCH;
if (opnd == IA64_OPND_IMM8M1U4)
{
/* Zero is not valid for unsigned compares that take an adjusted
constant immediate range. */
if (e->X_add_number == 0)
- return 0;
+ return OPERAND_OUT_OF_RANGE;
/* Sign-extend 32-bit unsigned numbers, so that the following range
checks will work. */
/* Check for 0x100000000. This is valid because
0x100000000-1 is the same as ((uint32_t) -1). */
if (val == ((bfd_signed_vma) 1 << 32))
- return 1;
+ return OPERAND_MATCH;
val = val - 1;
}
/* Zero is not valid for unsigned compares that take an adjusted
constant immediate range. */
if (e->X_add_number == 0)
- return 0;
+ return OPERAND_OUT_OF_RANGE;
/* Check for 0x10000000000000000. */
if (e->X_op == O_big)
&& generic_bignum[2] == 0
&& generic_bignum[3] == 0
&& generic_bignum[4] == 1)
- return 1;
+ return OPERAND_MATCH;
else
- return 0;
+ return OPERAND_OUT_OF_RANGE;
}
else
val = e->X_add_number - 1;
if ((val >= 0 && (bfd_vma) val < ((bfd_vma) 1 << (bits - 1)))
|| (val < 0 && (bfd_vma) -val <= ((bfd_vma) 1 << (bits - 1))))
- return 1;
- break;
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
case IA64_OPND_INC3:
/* +/- 1, 4, 8, 16 */
val = e->X_add_number;
if (val < 0)
val = -val;
- if (e->X_op == O_constant
- && (val == 1 || val == 4 || val == 8 || val == 16))
- return 1;
+ if (e->X_op == O_constant)
+ {
+ if ((val == 1 || val == 4 || val == 8 || val == 16))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
break;
case IA64_OPND_TGT25:
fix->expr = *e;
fix->is_pcrel = 1;
++CURR_SLOT.num_fixups;
- return 1;
+ return OPERAND_MATCH;
}
case IA64_OPND_TAG13:
case IA64_OPND_TAG13b:
switch (e->X_op)
{
case O_constant:
- return 1;
+ return OPERAND_MATCH;
case O_symbol:
fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
fix->expr = *e;
fix->is_pcrel = 1;
++CURR_SLOT.num_fixups;
- return 1;
+ return OPERAND_MATCH;
default:
break;
default:
break;
}
- return 0;
+ return OPERAND_MISMATCH;
}
static int
struct ia64_opcode *idesc;
{
int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0;
- int sep = 0;
+ int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0;
enum ia64_opnd expected_operand = IA64_OPND_NIL;
+ enum operand_match_result result;
char mnemonic[129];
char *first_arg = 0, *end, *saved_input_pointer;
unsigned int sof;
}
highest_unmatched_operand = 0;
+ curr_out_of_range_pos = -1;
+ error_pos = 0;
expected_operand = idesc->operands[0];
for (; idesc; idesc = get_next_opcode (idesc))
{
continue; /* mismatch in # of outputs */
CURR_SLOT.num_fixups = 0;
+
+ /* Try to match all operands. If we see an out-of-range operand,
+ then continue trying to match the rest of the operands, since if
+ the rest match, then this idesc will give the best error message. */
+
+ out_of_range_pos = -1;
for (i = 0; i < num_operands && idesc->operands[i]; ++i)
- if (!operand_match (idesc, i, CURR_SLOT.opnd + i))
- break;
+ {
+ result = operand_match (idesc, i, CURR_SLOT.opnd + i);
+ if (result != OPERAND_MATCH)
+ {
+ if (result != OPERAND_OUT_OF_RANGE)
+ break;
+ if (out_of_range_pos < 0)
+ /* remember position of the first out-of-range operand: */
+ out_of_range_pos = i;
+ }
+ }
- if (i != num_operands)
+ /* If we did not match all operands, or if at least one operand was
+ out-of-range, then this idesc does not match. Keep track of which
+ idesc matched the most operands before failing. If we have two
+ idescs that failed at the same position, and one had an out-of-range
+ operand, then prefer the out-of-range operand. Thus if we have
+ "add r0=0x1000000,r1" we get an error saying the constant is out
+ of range instead of an error saying that the constant should have been
+ a register. */
+
+ if (i != num_operands || out_of_range_pos >= 0)
{
- if (i > highest_unmatched_operand)
+ if (i > highest_unmatched_operand
+ || (i == highest_unmatched_operand
+ && out_of_range_pos > curr_out_of_range_pos))
{
highest_unmatched_operand = i;
- expected_operand = idesc->operands[i];
+ if (out_of_range_pos >= 0)
+ {
+ expected_operand = idesc->operands[out_of_range_pos];
+ error_pos = out_of_range_pos;
+ }
+ else
+ {
+ expected_operand = idesc->operands[i];
+ error_pos = i;
+ }
+ curr_out_of_range_pos = out_of_range_pos;
}
continue;
}
{
if (expected_operand)
as_bad ("Operand %u of `%s' should be %s",
- highest_unmatched_operand + 1, mnemonic,
+ error_pos + 1, mnemonic,
elf64_ia64_operands[expected_operand].desc);
else
as_bad ("Operand mismatch");
int c;
char *arg;
{
+
switch (c)
{
/* Switches from the Intel assembler. */
if (md.flags & EF_IA_64_BE)
{
if (md.flags & EF_IA_64_ABI64)
+#ifdef TE_AIX50
+ return "elf64-ia64-aix-big";
+#else
return "elf64-ia64-big";
+#endif
else
+#ifdef TE_AIX50
+ return "elf32-ia64-aix-big";
+#else
return "elf32-ia64-big";
+#endif
}
else
{
if (md.flags & EF_IA_64_ABI64)
+#ifdef TE_AIX50
+ return "elf64-ia64-aix-little";
+#else
return "elf64-ia64-little";
+#endif
else
+#ifdef TE_AIX50
+ return "elf32-ia64-aix-little";
+#else
return "elf32-ia64-little";
+#endif
}
}
else
char *s;
char c;
symbolS *tag;
+ int temp;
if (md.qp.X_op == O_register)
{
as_bad ("Tag must come before qualifying predicate.");
return 0;
}
- s = input_line_pointer;
- c = get_symbol_end ();
+
+ /* This implements just enough of read_a_source_file in read.c to
+ recognize labels. */
+ if (is_name_beginner (*input_line_pointer))
+ {
+ s = input_line_pointer;
+ c = get_symbol_end ();
+ }
+ else if (LOCAL_LABELS_FB
+ && isdigit ((unsigned char) *input_line_pointer))
+ {
+ temp = 0;
+ while (isdigit ((unsigned char) *input_line_pointer))
+ temp = (temp * 10) + *input_line_pointer++ - '0';
+ fb_label_instance_inc (temp);
+ s = fb_label_name (temp, 0);
+ c = *input_line_pointer;
+ }
+ else
+ {
+ s = NULL;
+ c = '\0';
+ }
if (c != ':')
{
/* Put ':' back for error messages' sake. */
as_bad ("Expected ':'");
return 0;
}
+
defining_tag = 1;
tag = colon (s);
defining_tag = 0;
void
ia64_flush_pending_output ()
{
- if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+ if (!md.keep_pending_output
+ && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
{
/* ??? This causes many unnecessary stop bits to be emitted.
Unfortunately, it isn't clear if it is safe to remove this. */