/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
- Copyright (C) 1998, 1999, 2000 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
/* ia64-specific option processing: */
-const char *md_shortopts = "M:N:x::";
+const char *md_shortopts = "m:N:x::";
struct option md_longopts[] =
{
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
fixup[2]; /* at most two fixups per insn */
struct ia64_opcode *idesc;
struct label_fix *label_fixups;
+ struct label_fix *tag_fixups;
struct unw_rec_list *unwind_record; /* Unwind directive. */
expressionS opnd[6];
char *src_file;
the current DV-checking block. */
int maxpaths; /* size currently allocated for
entry_labels */
+ /* Support for hardware errata workarounds. */
+
+ /* Record data about the last three insn groups. */
+ struct group
+ {
+ /* B-step workaround.
+ For each predicate register, this is set if the corresponding insn
+ group conditionally sets this register with one of the affected
+ instructions. */
+ int p_reg_set[64];
+ /* B-step workaround.
+ For each general register, this is set if the corresponding insn
+ a) is conditional one one of the predicate registers for which
+ P_REG_SET is 1 in the corresponding entry of the previous group,
+ b) sets this general register with one of the affected
+ instructions. */
+ int g_reg_set_conditionally[128];
+ } last_groups[3];
+ int group_idx;
}
md;
pseudo_func[] =
{
/* reloc pseudo functions (these must come first!): */
- { "fptr", PSEUDO_FUNC_RELOC },
- { "gprel", PSEUDO_FUNC_RELOC },
- { "ltoff", PSEUDO_FUNC_RELOC },
- { "pcrel", PSEUDO_FUNC_RELOC },
- { "pltoff", PSEUDO_FUNC_RELOC },
- { "secrel", PSEUDO_FUNC_RELOC },
- { "segrel", PSEUDO_FUNC_RELOC },
- { "ltv", PSEUDO_FUNC_RELOC },
- { 0, }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
+ { "fptr", PSEUDO_FUNC_RELOC, { 0 } },
+ { "gprel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
+ { "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "pltoff", PSEUDO_FUNC_RELOC, { 0 } },
+ { "secrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "segrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltv", PSEUDO_FUNC_RELOC, { 0 } },
+ { "", 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
/* mbtype4 constants: */
{ "alt", PSEUDO_FUNC_CONST, { 0xa } },
int data_srlz; /* current data serialization state */
int qp_regno; /* qualifying predicate for this usage */
char *file; /* what file marked this dependency */
- int line; /* what line marked this dependency */
+ unsigned int line; /* what line marked this dependency */
struct mem_offset mem_offset; /* optional memory offset hint */
+ enum { CMP_NONE, CMP_OR, CMP_AND } cmp_type; /* OR or AND compare? */
int path; /* corresponding code entry index */
} *regdeps = NULL;
static int regdepslen = 0;
static int regdepstotlen = 0;
static const char *dv_mode[] = { "RAW", "WAW", "WAR" };
static const char *dv_sem[] = { "none", "implied", "impliedf",
- "data", "instr", "specific", "other" };
+ "data", "instr", "specific", "stop", "other" };
+static const char *dv_cmp_type[] = { "none", "OR", "AND" };
/* Current state of PR mutexation */
static struct qpmutex {
unsigned known:1;
int path;
valueT value;
-} gr_values[128] = {{ 1, 0 }};
+} gr_values[128] = {{ 1, 0, 0 }};
/* These are the routines required to output the various types of
unwind records. */
+/* A slot_number is a frag address plus the slot index (0-2). We use the
+ frag address here so that if there is a section switch in the middle of
+ a function, then instructions emitted to a different section are not
+ counted. Since there may be more than one frag for a function, this
+ means we also need to keep track of which frag this address belongs to
+ so we can compute inter-frag distances. This also nicely solves the
+ problem with nops emitted for align directives, which can't easily be
+ counted, but can easily be derived from frag sizes. */
+
typedef struct unw_rec_list {
unwind_record r;
unsigned long slot_number;
+ fragS *slot_frag;
struct unw_rec_list *next;
} unw_rec_list;
-#define SLOT_NUM_NOT_SET -1
+#define SLOT_NUM_NOT_SET (unsigned)-1
static struct
{
unsigned long next_slot_number;
+ fragS *next_slot_frag;
/* Maintain a list of unwind entries for the current function. */
unw_rec_list *list;
symbolS *proc_end;
symbolS *info; /* pointer to unwind info */
symbolS *personality_routine;
+ segT saved_text_seg;
+ subsegT saved_text_subseg;
+ unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
/* 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 *));
static int calc_record_size PARAMS ((unw_rec_list *));
static void set_imask PARAMS ((unw_rec_list *, unsigned long, unsigned long, unsigned int));
static int count_bits PARAMS ((unsigned long));
-static unsigned long slot_index PARAMS ((unsigned long, unsigned long));
+static unsigned long slot_index PARAMS ((unsigned long, fragS *,
+ unsigned long, fragS *));
+static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
static void fixup_unw_records PARAMS ((unw_rec_list *));
static int output_unw_records PARAMS ((unw_rec_list *, void **));
static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int generate_unwind_image PARAMS ((void));
+static int generate_unwind_image PARAMS ((const char *));
+
+/* Build the unwind section name by appending the (possibly stripped)
+ text section NAME to the unwind PREFIX. The resulting string
+ pointer is assigned to RESULT. The string is allocated on the
+ stack, so this must be a macro... */
+#define make_unw_section_name(special, text_name, result) \
+ { \
+ char *_prefix = special_section_name[special]; \
+ size_t _prefix_len = strlen (_prefix), _text_len = strlen (text_name); \
+ char *_result = alloca (_prefix_len + _text_len + 1); \
+ memcpy(_result, _prefix, _prefix_len); \
+ memcpy(_result + _prefix_len, text_name, _text_len); \
+ _result[_prefix_len + _text_len] = '\0'; \
+ result = _result; \
+ } \
+while (0)
/* Determine if application register REGNUM resides in the integer
unit (as opposed to the memory unit). */
flagword
ia64_elf_section_flags (flags, attr, type)
flagword flags;
- int attr, type;
+ int attr, type ATTRIBUTE_UNUSED;
{
if (attr & SHF_IA_64_SHORT)
flags |= SEC_SMALL_DATA;
return flags;
}
+int
+ia64_elf_section_type (str, len)
+ const char *str;
+ size_t len;
+{
+ len = sizeof (ELF_STRING_ia64_unwind_info) - 1;
+ if (strncmp (str, ELF_STRING_ia64_unwind_info, len) == 0)
+ return SHT_PROGBITS;
+
+ len = sizeof (ELF_STRING_ia64_unwind) - 1;
+ if (strncmp (str, ELF_STRING_ia64_unwind, len) == 0)
+ return SHT_IA_64_UNWIND;
+
+ return -1;
+}
+
static unsigned int
set_regstack (ins, locs, outs, rots)
unsigned int ins, locs, outs, rots;
struct label_fix *lfix;
segT saved_seg;
subsegT saved_subseg;
+ unw_rec_list *ptr;
if (!md.last_text_seg)
return;
symbol_set_frag (lfix->sym, frag_now);
}
CURR_SLOT.label_fixups = 0;
+ for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
+ {
+ S_SET_VALUE (lfix->sym, frag_now_fix ());
+ symbol_set_frag (lfix->sym, frag_now);
+ }
+ CURR_SLOT.tag_fixups = 0;
+
+ /* In case there are unwind directives following the last instruction,
+ resolve those now. We only handle body and prologue directives here.
+ Give an error for others. */
+ for (ptr = unwind.current_entry; ptr; ptr = ptr->next)
+ {
+ if (ptr->r.type == prologue || ptr->r.type == prologue_gr
+ || ptr->r.type == body)
+ {
+ ptr->slot_number = (unsigned long) frag_more (0);
+ ptr->slot_frag = frag_now;
+ }
+ else
+ as_bad (_("Unwind directive not followed by an instruction."));
+ }
+ unwind.current_entry = NULL;
subseg_set (saved_seg, saved_subseg);
+
+ if (md.qp.X_op == O_register)
+ as_bad ("qualifying predicate not followed by instruction");
}
void
output_vbyte_mem (count, ptr, comment)
int count;
char *ptr;
- char *comment;
+ char *comment ATTRIBUTE_UNUSED;
{
int x;
if (vbyte_mem_ptr == NULL)
void
count_output (count, ptr, comment)
int count;
- char *ptr;
- char *comment;
+ char *ptr ATTRIBUTE_UNUSED;
+ char *comment ATTRIBUTE_UNUSED;
{
vbyte_count += count;
}
return n;
}
+/* Return the number of instruction slots from FIRST_ADDR to SLOT_ADDR.
+ SLOT_FRAG is the frag containing SLOT_ADDR, and FIRST_FRAG is the frag
+ containing FIRST_ADDR. */
+
unsigned long
-slot_index (unsigned long slot_addr, unsigned long first_addr)
+slot_index (slot_addr, slot_frag, first_addr, first_frag)
+ unsigned long slot_addr;
+ fragS *slot_frag;
+ unsigned long first_addr;
+ fragS *first_frag;
{
- return (3 * ((slot_addr >> 4) - (first_addr >> 4))
- + ((slot_addr & 0x3) - (first_addr & 0x3)));
+ unsigned long index = 0;
+
+ /* First time we are called, the initial address and frag are invalid. */
+ if (first_addr == 0)
+ return 0;
+
+ /* If the two addresses are in different frags, then we need to add in
+ the remaining size of this frag, and then the entire size of intermediate
+ frags. */
+ while (slot_frag != first_frag)
+ {
+ unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
+
+ /* Add in the full size of the frag converted to instruction slots. */
+ index += 3 * (first_frag->fr_fix >> 4);
+ /* Subtract away the initial part before first_addr. */
+ index -= (3 * ((first_addr >> 4) - (start_addr >> 4))
+ + ((first_addr & 0x3) - (start_addr & 0x3)));
+
+ /* Move to the beginning of the next frag. */
+ first_frag = first_frag->fr_next;
+ first_addr = (unsigned long) &first_frag->fr_literal;
+ }
+
+ /* Add in the used part of the last frag. */
+ index += (3 * ((slot_addr >> 4) - (first_addr >> 4))
+ + ((slot_addr & 0x3) - (first_addr & 0x3)));
+ return index;
+}
+
+/* Optimize unwind record directives. */
+
+static unw_rec_list *
+optimize_unw_records (list)
+ unw_rec_list *list;
+{
+ if (!list)
+ return NULL;
+
+ /* If the only unwind record is ".prologue" or ".prologue" followed
+ by ".body", then we can optimize the unwind directives away. */
+ if (list->r.type == prologue
+ && (list->next == NULL
+ || (list->next->r.type == body && list->next->next == NULL)))
+ return NULL;
+
+ return list;
}
/* Given a complete record list, process any records which have
{
unw_rec_list *ptr, *region = 0;
unsigned long first_addr = 0, rlen = 0, t;
+ fragS *first_frag = 0;
for (ptr = list; ptr; ptr = ptr->next)
{
if (ptr->slot_number == SLOT_NUM_NOT_SET)
as_bad (" Insn slot not set in unwind record.");
- t = slot_index (ptr->slot_number, first_addr);
+ t = slot_index (ptr->slot_number, ptr->slot_frag,
+ first_addr, first_frag);
switch (ptr->r.type)
{
case prologue:
unw_rec_list *last;
int size, dir_len = 0;
unsigned long last_addr;
+ fragS *last_frag;
first_addr = ptr->slot_number;
+ first_frag = ptr->slot_frag;
ptr->slot_number = 0;
/* Find either the next body/prologue start, or the end of
the list, and determine the size of the region. */
last_addr = unwind.next_slot_number;
+ last_frag = unwind.next_slot_frag;
for (last = ptr->next; last != NULL; last = last->next)
if (last->r.type == prologue || last->r.type == prologue_gr
|| last->r.type == body)
{
last_addr = last->slot_number;
+ last_frag = last->slot_frag;
break;
}
else if (!last->next)
if (ptr->r.type != body)
{
last_addr = last->slot_number;
+ last_frag = last->slot_frag;
switch (last->r.type)
{
case frgr_mem:
}
break;
}
- size = slot_index (last_addr, first_addr) + dir_len;
+ size = (slot_index (last_addr, last_frag, first_addr, first_frag)
+ + dir_len);
rlen = ptr->r.record.r.rlen = size;
region = ptr;
break;
int size, x, extra = 0;
unsigned char *mem;
+ *ptr = NULL;
+
+ list = optimize_unw_records (list);
fixup_unw_records (list);
size = calc_record_size (list);
x = size % 8;
if (x != 0)
extra = 8 - x;
- /* Add 8 for the header + 8 more bytes for the personality offset. */
- mem = xmalloc (size + extra + 16);
- vbyte_mem_ptr = mem + 8;
- /* Clear the padding area and personality. */
- memset (mem + 8 + size, 0 , extra + 8);
- /* Initialize the header area. */
- md_number_to_chars (mem, (((bfd_vma) 1 << 48) /* version */
- | (unwind.personality_routine
- ? ((bfd_vma) 3 << 32) /* U & E handler flags */
- : 0)
- | ((size + extra) / 8)), /* length (dwords) */
- 8);
+ if (size > 0 || unwind.force_unwind_entry)
+ {
+ unwind.force_unwind_entry = 0;
+
+ /* Add 8 for the header + 8 more bytes for the personality offset. */
+ mem = xmalloc (size + extra + 16);
+
+ vbyte_mem_ptr = mem + 8;
+ /* Clear the padding area and personality. */
+ memset (mem + 8 + size, 0 , extra + 8);
+ /* Initialize the header area. */
+ md_number_to_chars (mem,
+ (((bfd_vma) 1 << 48) /* version */
+ | (unwind.personality_routine
+ ? ((bfd_vma) 3 << 32) /* U & E handler flags */
+ : 0)
+ | ((size + extra) / 8)), /* length (dwords) */
+ 8);
+
+ process_unw_records (list, output_vbyte_mem);
- process_unw_records (list, output_vbyte_mem);
+ *ptr = mem;
- *ptr = mem;
- return size + extra + 16;
+ size += extra + 16;
+ }
+ return size;
}
static int
return 0;
reg = e->X_add_number;
- if (reg >= REG_GR + 4 && reg <= REG_GR + 7)
+ if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
{
*ab = 0;
*regp = reg - REG_GR;
}
- else if ((reg >= REG_FR + 2 && reg <= REG_FR + 5)
- || (reg >= REG_FR + 16 && reg <= REG_FR + 31))
+ else if ((reg >= (REG_FR + 2) && reg <= (REG_FR + 5))
+ || (reg >= (REG_FR + 16) && reg <= (REG_FR + 31)))
{
*ab = 1;
*regp = reg - REG_FR;
}
- else if (reg >= REG_BR + 1 && reg <= REG_BR + 5)
+ else if (reg >= (REG_BR + 1) && reg <= (REG_BR + 5))
{
*ab = 2;
*regp = reg - REG_BR;
reg = e->X_add_number;
- if (reg >= REG_GR && reg <= REG_GR + 127)
+ if (/* reg >= REG_GR && */ reg <= (REG_GR + 127))
{
*xy = 0;
*regp = reg - REG_GR;
}
- else if (reg >= REG_FR && reg <= REG_FR + 127)
+ else if (reg >= REG_FR && reg <= (REG_FR + 127))
{
*xy = 1;
*regp = reg - REG_FR;
}
- else if (reg >= REG_BR && reg <= REG_BR + 7)
+ else if (reg >= REG_BR && reg <= (REG_BR + 7))
{
*xy = 2;
*regp = reg - REG_BR;
static void
dot_radix (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
int radix;
static void
dot_fframe (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
static void
dot_vframe (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
unsigned reg;
static void
dot_vframesp (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
static void
dot_vframepsp (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
static void
dot_save (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
int sep;
static void
dot_restore (dummy)
- int dummy;
+ 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
dot_restorereg (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
unsigned int ab, reg;
expressionS e;
static void
dot_restorereg_p (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
unsigned int qp, ab, reg;
expressionS e1, e2;
}
static int
-generate_unwind_image ()
+generate_unwind_image (text_name)
+ const char *text_name;
{
int size;
unsigned char *unw_rec;
if (size != 0)
{
unsigned char *where;
+ char *sec_name;
expressionS exp;
- set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND_INFO]);
+
+ make_unw_section_name (SPECIAL_SECTION_UNWIND_INFO, text_name, sec_name);
+ set_section (sec_name);
+ bfd_set_section_flags (stdoutput, now_seg,
+ SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+
+ /* Make sure the section has 8 byte alignment. */
+ record_alignment (now_seg, 3);
/* Set expression which points to start of unwind descriptor area. */
unwind.info = expr_build_dot ();
/* Copy the information from the unwind record into this section. The
data is already in the correct byte order. */
memcpy (where, unw_rec, size);
+
/* Add the personality address to the image. */
if (unwind.personality_routine != 0)
{
&exp, 0, BFD_RELOC_IA64_LTOFF_FPTR64LSB);
unwind.personality_routine = 0;
}
- obj_elf_previous (0);
}
free_list_records (unwind.list);
static void
dot_handlerdata (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
- generate_unwind_image ();
+ const char *text_name = segment_name (now_seg);
+
+ /* If text section name starts with ".text" (which it should),
+ strip this prefix off. */
+ if (strcmp (text_name, ".text") == 0)
+ text_name = "";
+
+ unwind.force_unwind_entry = 1;
+
+ /* Remember which segment we're in so we can switch back after .endp */
+ unwind.saved_text_seg = now_seg;
+ unwind.saved_text_subseg = now_subseg;
+
+ /* Generate unwind info into unwind-info section and then leave that
+ section as the currently active one so dataXX directives go into
+ the language specific data area of the unwind info block. */
+ generate_unwind_image (text_name);
demand_empty_rest_of_line ();
}
static void
dot_unwentry (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
+ unwind.force_unwind_entry = 1;
demand_empty_rest_of_line ();
}
static void
dot_altrp (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
unsigned reg;
static void
dot_saveg (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
int sep;
static void
dot_savef (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1;
int sep;
static void
dot_saveb (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
unsigned int reg;
static void
dot_savegf (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
int sep;
static void
dot_spill (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
unsigned char sep;
static void
dot_spillreg (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
int sep, ab, xy, reg, treg;
expressionS e1, e2;
static void
dot_spillreg_p (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
int sep, ab, xy, reg, treg;
expressionS e1, e2, e3;
static void
dot_label_state (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
static void
dot_copy_state (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
static void
dot_unwabi (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e1, e2;
unsigned char sep;
static void
dot_personality (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
char *name, *p, c;
SKIP_WHITESPACE ();
c = get_symbol_end ();
p = input_line_pointer;
unwind.personality_routine = symbol_find_or_make (name);
+ unwind.force_unwind_entry = 1;
*p = c;
SKIP_WHITESPACE ();
demand_empty_rest_of_line ();
static void
dot_proc (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
char *name, *p, c;
symbolS *sym;
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;
}
static void
dot_body (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
unwind.prologue = 0;
unwind.prologue_mask = 0;
static void
dot_prologue (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
unsigned char sep;
- int mask = 0, grsave;
+ int mask = 0, grsave = 0;
if (!is_it_end_of_statement ())
{
unwind.prologue = 1;
unwind.prologue_mask = mask;
+ ++unwind.prologue_count;
}
static void
dot_endp (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS e;
unsigned char *ptr;
+ int bytes_per_address;
long where;
segT saved_seg;
subsegT saved_subseg;
+ const char *sec_name, *text_name;
- saved_seg = now_seg;
- saved_subseg = now_subseg;
+ if (unwind.saved_text_seg)
+ {
+ saved_seg = unwind.saved_text_seg;
+ saved_subseg = unwind.saved_text_subseg;
+ unwind.saved_text_seg = NULL;
+ }
+ else
+ {
+ saved_seg = now_seg;
+ saved_subseg = now_subseg;
+ }
+
+ /*
+ Use a slightly ugly scheme to derive the unwind section names from
+ the text section name:
+
+ text sect. unwind table sect.
+ name: name: comments:
+ ---------- ----------------- --------------------------------
+ .text .IA_64.unwind
+ .text.foo .IA_64.unwind.text.foo
+ .foo .IA_64.unwind.foo
+ _info .IA_64.unwind_info gas issues error message (ditto)
+ _infoFOO .IA_64.unwind_infoFOO gas issues error message (ditto)
+
+ This mapping is done so that:
+
+ (a) An object file with unwind info only in .text will use
+ unwind section names .IA_64.unwind and .IA_64.unwind_info.
+ This follows the letter of the ABI and also ensures backwards
+ compatibility with older toolchains.
+
+ (b) An object file with unwind info in multiple text sections
+ will use separate unwind sections for each text section.
+ This allows us to properly set the "sh_info" and "sh_link"
+ fields in SHT_IA_64_UNWIND as required by the ABI and also
+ lets GNU ld support programs with multiple segments
+ containing unwind info (as might be the case for certain
+ embedded applications).
+
+ (c) An error is issued if there would be a name clash.
+ */
+ text_name = segment_name (saved_seg);
+ if (strncmp (text_name, "_info", 5) == 0)
+ {
+ as_bad ("Illegal section name `%s' (causes unwind section name clash)",
+ text_name);
+ ignore_rest_of_line ();
+ return;
+ }
+ if (strcmp (text_name, ".text") == 0)
+ text_name = "";
expression (&e);
demand_empty_rest_of_line ();
insn_group_break (1, 0, 0);
- /* If there was a .handlerdata, we haven't generated an image yet. */
- if (unwind.info == 0)
+ /* If there wasn't a .handlerdata, we haven't generated an image yet. */
+ if (!unwind.info)
+ generate_unwind_image (text_name);
+
+ if (unwind.info || unwind.force_unwind_entry)
{
- generate_unwind_image ();
- }
+ subseg_set (md.last_text_seg, 0);
+ unwind.proc_end = expr_build_dot ();
- subseg_set (md.last_text_seg, 0);
- unwind.proc_end = expr_build_dot ();
+ make_unw_section_name (SPECIAL_SECTION_UNWIND, text_name, sec_name);
+ set_section ((char *) sec_name);
+ bfd_set_section_flags (stdoutput, now_seg,
+ SEC_LOAD | SEC_ALLOC | SEC_READONLY);
- set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND]);
- ptr = frag_more (24);
- where = frag_now_fix () - 24;
+ /* Make sure the section has 8 byte alignment. */
+ record_alignment (now_seg, 3);
- /* Issue the values of a) Proc Begin, b) Proc End, c) Unwind Record. */
- e.X_op = O_pseudo_fixup;
- e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
- e.X_add_number = 0;
- e.X_add_symbol = unwind.proc_start;
- ia64_cons_fix_new (frag_now, where, 8, &e);
+ ptr = frag_more (24);
+ where = frag_now_fix () - 24;
+ bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
- e.X_op = O_pseudo_fixup;
- e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
- e.X_add_number = 0;
- e.X_add_symbol = unwind.proc_end;
- ia64_cons_fix_new (frag_now, where + 8, 8, &e);
+ /* Issue the values of a) Proc Begin, b) Proc End, c) Unwind Record. */
+ e.X_op = O_pseudo_fixup;
+ e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+ e.X_add_number = 0;
+ e.X_add_symbol = unwind.proc_start;
+ ia64_cons_fix_new (frag_now, where, bytes_per_address, &e);
- if (unwind.info != 0)
- {
e.X_op = O_pseudo_fixup;
e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
e.X_add_number = 0;
- e.X_add_symbol = unwind.info;
- ia64_cons_fix_new (frag_now, where + 16, 8, &e);
- }
- else
- md_number_to_chars (ptr + 16, 0, 8);
+ e.X_add_symbol = unwind.proc_end;
+ ia64_cons_fix_new (frag_now, where + bytes_per_address,
+ bytes_per_address, &e);
+
+ if (unwind.info)
+ {
+ e.X_op = O_pseudo_fixup;
+ e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+ e.X_add_number = 0;
+ e.X_add_symbol = unwind.info;
+ ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2),
+ bytes_per_address, &e);
+ }
+ else
+ md_number_to_chars (ptr + (bytes_per_address * 2), 0,
+ bytes_per_address);
+ }
subseg_set (saved_seg, saved_subseg);
unwind.proc_start = unwind.proc_end = unwind.info = 0;
}
static void
dot_regstk (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
int ins, locs, outs, rots;
static void
dot_psr (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
char *option;
int ch;
static void
dot_alias (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
as_bad (".alias not implemented yet");
}
static void
dot_ln (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
new_logical_line (0, get_absolute_expression ());
demand_empty_rest_of_line ();
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 */
static void
dot_reg_val (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
expressionS reg;
static void
dot_entry (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
const char *err;
char *name;
static void
dot_mem_offset (dummy)
- int dummy;
+ int dummy ATTRIBUTE_UNUSED;
{
md.mem_offset.hint = 1;
md.mem_offset.offset = get_absolute_expression ();
{ "proc", dot_proc, 0 },
{ "body", dot_body, 0 },
{ "prologue", dot_prologue, 0 },
- { "endp", dot_endp },
- { "file", dwarf2_directive_file },
- { "loc", dwarf2_directive_loc },
-
- { "fframe", dot_fframe },
- { "vframe", dot_vframe },
- { "vframesp", dot_vframesp },
- { "vframepsp", dot_vframepsp },
- { "save", dot_save },
- { "restore", dot_restore },
- { "restorereg", dot_restorereg },
- { "restorereg.p", dot_restorereg_p },
- { "handlerdata", dot_handlerdata },
- { "unwentry", dot_unwentry },
- { "altrp", dot_altrp },
+ { "endp", dot_endp, 0 },
+ { "file", dwarf2_directive_file, 0 },
+ { "loc", dwarf2_directive_loc, 0 },
+
+ { "fframe", dot_fframe, 0 },
+ { "vframe", dot_vframe, 0 },
+ { "vframesp", dot_vframesp, 0 },
+ { "vframepsp", dot_vframepsp, 0 },
+ { "save", dot_save, 0 },
+ { "restore", dot_restore, 0 },
+ { "restorereg", dot_restorereg, 0 },
+ { "restorereg.p", dot_restorereg_p, 0 },
+ { "handlerdata", dot_handlerdata, 0 },
+ { "unwentry", dot_unwentry, 0 },
+ { "altrp", dot_altrp, 0 },
{ "savesp", dot_savemem, 0 },
{ "savepsp", dot_savemem, 1 },
- { "save.g", dot_saveg },
- { "save.f", dot_savef },
- { "save.b", dot_saveb },
- { "save.gf", dot_savegf },
- { "spill", dot_spill },
- { "spillreg", dot_spillreg },
+ { "save.g", dot_saveg, 0 },
+ { "save.f", dot_savef, 0 },
+ { "save.b", dot_saveb, 0 },
+ { "save.gf", dot_savegf, 0 },
+ { "spill", dot_spill, 0 },
+ { "spillreg", dot_spillreg, 0 },
{ "spillsp", dot_spillmem, 0 },
{ "spillpsp", dot_spillmem, 1 },
- { "spillreg.p", dot_spillreg_p },
+ { "spillreg.p", dot_spillreg_p, 0 },
{ "spillsp.p", dot_spillmem_p, 0 },
{ "spillpsp.p", dot_spillmem_p, 1 },
- { "label_state", dot_label_state },
- { "copy_state", dot_copy_state },
- { "unwabi", dot_unwabi },
- { "personality", dot_personality },
+ { "label_state", dot_label_state, 0 },
+ { "copy_state", dot_copy_state, 0 },
+ { "unwabi", dot_unwabi, 0 },
+ { "personality", dot_personality, 0 },
#if 0
- { "estate", dot_estate },
+ { "estate", dot_estate, 0 },
#endif
{ "mii", dot_template, 0x0 },
{ "mli", dot_template, 0x2 }, /* old format, for compatibility */
/* annotations/DV checking support */
{ "entry", dot_entry, 0 },
- { "mem.offset", dot_mem_offset },
+ { "mem.offset", dot_mem_offset, 0 },
{ "pred.rel", dot_pred_rel, 0 },
{ "pred.rel.clear", dot_pred_rel, 'c' },
{ "pred.rel.imply", dot_pred_rel, 'i' },
{ "pred.rel.mutex", dot_pred_rel, 'm' },
{ "pred.safe_across_calls", dot_pred_rel, 's' },
- { "reg.val", dot_reg_val },
+ { "reg.val", dot_reg_val, 0 },
{ "auto", dot_dv_mode, 'a' },
{ "explicit", dot_dv_mode, 'e' },
{ "default", dot_dv_mode, 'd' },
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
- && e->X_add_number < ((bfd_vma) 1 << 44))
- || (e->X_add_number < 0
- && -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
- && e->X_add_number < ((bfd_vma) 1 << 17))
- || (e->X_add_number < 0
- && -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;
else
val = e->X_add_number;
- if ((val >= 0 && val < ((bfd_vma) 1 << (bits - 1)))
- || (val < 0 && -val <= ((bfd_vma) 1 << (bits - 1))))
- return 1;
- break;
+ if ((val >= 0 && (bfd_vma) val < ((bfd_vma) 1 << (bits - 1)))
+ || (val < 0 && (bfd_vma) -val <= ((bfd_vma) 1 << (bits - 1))))
+ 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->code = ia64_gen_real_reloc_type (e->X_op_symbol, 0);
+ /* There are no external relocs for TAG13/TAG13b fields, so we
+ create a dummy reloc. This will not live past md_apply_fix3. */
+ fix->code = BFD_RELOC_UNUSED;
+ fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
fix->opnd = idesc->operands[index];
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 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)
+ 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");
return idesc;
}
+/* Keep track of state necessary to determine whether a NOP is necessary
+ to avoid an erratum in A and B step Itanium chips, and return 1 if we
+ detect a case where additional NOPs may be necessary. */
+static int
+errata_nop_necessary_p (slot, insn_unit)
+ struct slot *slot;
+ enum ia64_unit insn_unit;
+{
+ int i;
+ struct group *this_group = md.last_groups + md.group_idx;
+ struct group *prev_group = md.last_groups + (md.group_idx + 2) % 3;
+ struct ia64_opcode *idesc = slot->idesc;
+
+ /* Test whether this could be the first insn in a problematic sequence. */
+ if (insn_unit == IA64_UNIT_F)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int regno = slot->opnd[i].X_add_number - REG_P;
+ /* Ignore invalid operands; they generate errors elsewhere. */
+ if (regno >= 64)
+ return 0;
+ this_group->p_reg_set[regno] = 1;
+ }
+ }
+
+ /* Test whether this could be the second insn in a problematic sequence. */
+ if (insn_unit == IA64_UNIT_M && slot->qp_regno > 0
+ && prev_group->p_reg_set[slot->qp_regno])
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ if (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3)
+ {
+ int regno = slot->opnd[i].X_add_number - REG_GR;
+ /* Ignore invalid operands; they generate errors elsewhere. */
+ if (regno >= 128)
+ return 0;
+ if (strncmp (idesc->name, "add", 3) != 0
+ && strncmp (idesc->name, "sub", 3) != 0
+ && strncmp (idesc->name, "shladd", 6) != 0
+ && (idesc->flags & IA64_OPCODE_POSTINC) == 0)
+ this_group->g_reg_set_conditionally[regno] = 1;
+ }
+ }
+
+ /* Test whether this could be the third insn in a problematic sequence. */
+ for (i = 0; i < NELEMS (idesc->operands) && idesc->operands[i]; i++)
+ {
+ if (/* For fc, ptc, ptr, tak, thash, tpa, ttag, probe, ptr, ptc. */
+ idesc->operands[i] == IA64_OPND_R3
+ /* For mov indirect. */
+ || idesc->operands[i] == IA64_OPND_RR_R3
+ || idesc->operands[i] == IA64_OPND_DBR_R3
+ || idesc->operands[i] == IA64_OPND_IBR_R3
+ || idesc->operands[i] == IA64_OPND_PKR_R3
+ || idesc->operands[i] == IA64_OPND_PMC_R3
+ || idesc->operands[i] == IA64_OPND_PMD_R3
+ || idesc->operands[i] == IA64_OPND_MSR_R3
+ || idesc->operands[i] == IA64_OPND_CPUID_R3
+ /* For itr. */
+ || idesc->operands[i] == IA64_OPND_ITR_R3
+ || idesc->operands[i] == IA64_OPND_DTR_R3
+ /* Normal memory addresses (load, store, xchg, cmpxchg, etc.). */
+ || idesc->operands[i] == IA64_OPND_MR3)
+ {
+ int regno = slot->opnd[i].X_add_number - REG_GR;
+ /* Ignore invalid operands; they generate errors elsewhere. */
+ if (regno >= 128)
+ return 0;
+ if (idesc->operands[i] == IA64_OPND_R3)
+ {
+ if (strcmp (idesc->name, "fc") != 0
+ && strcmp (idesc->name, "tak") != 0
+ && strcmp (idesc->name, "thash") != 0
+ && strcmp (idesc->name, "tpa") != 0
+ && strcmp (idesc->name, "ttag") != 0
+ && strncmp (idesc->name, "ptr", 3) != 0
+ && strncmp (idesc->name, "ptc", 3) != 0
+ && strncmp (idesc->name, "probe", 5) != 0)
+ return 0;
+ }
+ if (prev_group->g_reg_set_conditionally[regno])
+ return 1;
+ }
+ }
+ return 0;
+}
+
static void
build_insn (slot, insnp)
struct slot *slot;
struct ia64_opcode *idesc;
int end_of_insn_group = 0, user_template = -1;
int n, i, j, first, curr;
- unw_rec_list *ptr, *prev;
+ unw_rec_list *ptr;
bfd_vma t0 = 0, t1 = 0;
struct label_fix *lfix;
struct insn_fix *ifix;
for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
if (ptr->r.type == prologue || ptr->r.type == prologue_gr
|| ptr->r.type == body)
- ptr->slot_number = (unsigned long) f + i;
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
if (idesc->flags & IA64_OPCODE_SLOT2)
{
}
if (idesc->flags & IA64_OPCODE_LAST)
{
- int required_slot, required_template;
+ int required_slot;
+ unsigned int required_template;
/* If we need a stop bit after an M slot, our only choice is
template 5 (M;;MI). If we need a stop bit after a B
continue; /* try next slot */
}
- if (debug_type == DEBUG_DWARF2)
{
bfd_vma addr;
- addr = frag_now->fr_address + frag_now_fix () - 16 + 1 * i;
+ addr = frag_now->fr_address + frag_now_fix () - 16 + i;
dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
}
+ if (errata_nop_necessary_p (md.slot + curr, insn_unit))
+ as_warn (_("Additional NOP may be necessary to workaround Itanium processor A/B step errata"));
+
build_insn (md.slot + curr, insn + i);
/* Set slot counts for non prologue/body unwind records. */
for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
if (ptr->r.type != prologue && ptr->r.type != prologue_gr
&& ptr->r.type != body)
- ptr->slot_number = (unsigned long) f + i;
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
md.slot[curr].unwind_record = NULL;
- unwind.next_slot_number = (unsigned long) f + i + ((i == 2)?(0x10-2):1);
if (required_unit == IA64_UNIT_L)
{
S_SET_VALUE (lfix->sym, frag_now_fix () - 16);
symbol_set_frag (lfix->sym, frag_now);
}
+ /* and fix up the tags also. */
+ for (lfix = md.slot[curr].tag_fixups; lfix; lfix = lfix->next)
+ {
+ S_SET_VALUE (lfix->sym, frag_now_fix () - 16 + i);
+ symbol_set_frag (lfix->sym, frag_now);
+ }
for (j = 0; j < md.slot[curr].num_fixups; ++j)
{
ifix = md.slot[curr].fixup + j;
- fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 4,
+ fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8,
&ifix->expr, ifix->is_pcrel, ifix->code);
fix->tc_fix_data.opnd = ifix->opnd;
fix->fx_plt = (fix->fx_r_type == BFD_RELOC_IA64_PLTOFF22);
end_of_insn_group = md.slot[curr].end_of_insn_group;
+ if (end_of_insn_group)
+ {
+ md.group_idx = (md.group_idx + 1) % 3;
+ memset (md.last_groups + md.group_idx, 0, sizeof md.last_groups[0]);
+ }
+
/* clear slot: */
ia64_free_opcode (md.slot[curr].idesc);
memset (md.slot + curr, 0, sizeof (md.slot[curr]));
t0 = end_of_insn_group | (template << 1) | (insn[0] << 5) | (insn[1] << 46);
t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
- md_number_to_chars (f + 0, t0, 8);
- md_number_to_chars (f + 8, t1, 8);
+ number_to_chars_littleendian (f + 0, t0, 8);
+ number_to_chars_littleendian (f + 8, t1, 8);
+
+ unwind.next_slot_number = (unsigned long) f + 16;
+ unwind.next_slot_frag = frag_now;
}
int
int c;
char *arg;
{
+
switch (c)
{
/* Switches from the Intel assembler. */
- case 'M':
+ case 'm':
if (strcmp (arg, "ilp64") == 0
|| strcmp (arg, "lp64") == 0
|| strcmp (arg, "p64") == 0)
break;
case 'a':
- /* ??? Conflicts with gas' listing option. */
/* indirect=<tgt> Assume unannotated indirect branches behavior
according to <tgt> --
exit: branch out from the current context (default)
labels: all labels in context may be branch targets
*/
+ if (strncmp (arg, "indirect=", 9) != 0)
+ return 0;
break;
case 'x':
{
fputs (_("\
IA-64 options:\n\
- -Milp32|-Milp64|-Mlp64|-Mp64 select data model (default -Mlp64)\n\
- -Mle | -Mbe select little- or big-endian byte order (default -Mle)\n\
+ -milp32|-milp64|-mlp64|-mp64 select data model (default -mlp64)\n\
+ -mle | -mbe select little- or big-endian byte order (default -mle)\n\
-x | -xexplicit turn on dependency violation checking (default)\n\
-xauto automagically remove dependency violations\n\
-xdebug debug dependency violation checker\n"),
static inline int
extra_goodness (int templ, int slot)
{
- if (match (templ, IA64_TYPE_F, slot))
+ if (slot == 1 && match (templ, IA64_TYPE_F, slot))
return 2;
- if (match (templ, IA64_TYPE_B, slot))
+ if (slot == 2 && match (templ, IA64_TYPE_B, slot))
return 1;
return 0;
}
void
md_begin ()
{
- int i, j, k, t, total, ar_base, cr_base, goodness, best, regnum;
+ int i, j, k, t, total, ar_base, cr_base, goodness, best, regnum, ok;
const char *err;
char name[8];
bfd_set_section_alignment (stdoutput, text_section, 4);
- target_big_endian = 0;
+ target_big_endian = TARGET_BYTES_BIG_ENDIAN;
pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
&zero_address_frag);
name, err);
}
- /* Default to 64-bit mode. */
- /* ??? This overrides the -M options, but they aren't working anyways. */
- md.flags |= EF_IA_64_ABI64;
+ /* Set the architecture and machine depending on defaults and command line
+ options. */
+ if (md.flags & EF_IA_64_ABI64)
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf64);
+ else
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf32);
+
+ if (! ok)
+ as_warn (_("Could not set architecture and machine"));
md.mem_offset.hint = 0;
md.path = 0;
md.entry_labels = NULL;
}
+/* Set the elf type to 64 bit ABI by default. Cannot do this in md_begin
+ because that is called after md_parse_option which is where we do the
+ dynamic changing of md.flags based on -mlp64 or -milp32. Also, set the
+ default endianness. */
+
+void
+ia64_init (argc, argv)
+ int argc ATTRIBUTE_UNUSED;
+ char **argv ATTRIBUTE_UNUSED;
+{
+ md.flags = EF_IA_64_ABI64;
+ if (TARGET_BYTES_BIG_ENDIAN)
+ md.flags |= EF_IA_64_BE;
+}
+
+/* Return a string for the target object file format. */
+
+const char *
+ia64_target_format ()
+{
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ 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
+ return "unknown-format";
+}
+
void
ia64_end_of_source ()
{
bfd_set_private_flags (stdoutput, md.flags);
- if (debug_type == DEBUG_DWARF2)
- dwarf2_finish ();
-
md.mem_offset.hint = 0;
}
void
ia64_start_line ()
{
+ if (md.qp.X_op == O_register)
+ as_bad ("qualifying predicate not followed by instruction");
md.qp.X_op = O_absent;
if (ignore_input ())
}
}
+/* This is a hook for ia64_frob_label, so that it can distinguish tags from
+ labels. */
+static int defining_tag = 0;
+
int
ia64_unrecognized_line (ch)
int ch;
demand_empty_rest_of_line ();
return 1;
+ case '[':
+ {
+ 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;
+ }
+
+ /* 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. */
+ *input_line_pointer++ = ':';
+ as_bad ("Expected ':'");
+ return 0;
+ }
+
+ defining_tag = 1;
+ tag = colon (s);
+ defining_tag = 0;
+ /* Put ':' back for error messages' sake. */
+ *input_line_pointer++ = ':';
+ if (*input_line_pointer++ != ']')
+ {
+ as_bad ("Expected ']'");
+ return 0;
+ }
+ if (! tag)
+ {
+ as_bad ("Tag name expected");
+ return 0;
+ }
+ return 1;
+ }
+
default:
break;
}
{
struct label_fix *fix;
+ /* Tags need special handling since they are not bundle breaks like
+ labels. */
+ if (defining_tag)
+ {
+ fix = obstack_alloc (¬es, sizeof (*fix));
+ fix->sym = sym;
+ fix->next = CURR_SLOT.tag_fixups;
+ CURR_SLOT.tag_fixups = fix;
+
+ return;
+ }
+
if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
{
md.last_text_seg = now_seg;
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. */
tmpl.mem_offset.hint = 0;
tmpl.specific = 1;
tmpl.index = 0;
+ tmpl.cmp_type = CMP_NONE;
#define UNHANDLED \
as_warn (_("Unhandled dependency %s for %s (%s), note %d"), \
for (i = 0; i < NELEMS (gr_values); i++)
{
/* Uses all registers *except* the one in R3. */
- if (i != (gr_values[regno].value & 0xFF))
+ if ((unsigned)i != (gr_values[regno].value & 0xFF))
{
specs[count] = tmpl;
specs[count++].index = i;
}
break;
+ /* This is the same as IA64_RS_PRr, except that the register range is
+ from 1 - 15, and there are no rotating register reads/writes here. */
case IA64_RS_PR:
if (note == 0)
{
- if (idesc->operands[0] == IA64_OPND_PR_ROT)
+ for (i = 1; i < 16; i++)
{
- for (i = 16; i < 63; i++)
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ /* Mark only those registers indicated by the mask. */
+ if (rsrc_write)
+ {
+ mask = CURR_SLOT.opnd[2].X_add_number;
+ for (i = 1; i < 16; i++)
+ if (mask & ((valueT) 1 << i))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 11) /* note 11 implies note 1 as well */
+ {
+ if (rsrc_write)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_P;
+ if (regno >= 1 && regno < 16)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 12)
+ {
+ if (CURR_SLOT.qp_regno >= 1 && CURR_SLOT.qp_regno < 16)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.qp_regno;
+ }
+ }
+ else if (note == 1)
+ {
+ if (rsrc_write)
+ {
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+
+ if ((idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2)
+ && p1 >= 1 && p1 < 16)
{
specs[count] = tmpl;
- specs[count++].index = i;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
+ specs[count++].index = p1;
+ }
+ if ((idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2)
+ && p2 >= 1 && p2 < 16)
+ {
+ specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
+ specs[count++].index = p2;
}
}
else
{
- for (i = 1; i < 63; i++)
+ if (CURR_SLOT.qp_regno >= 1 && CURR_SLOT.qp_regno < 16)
{
specs[count] = tmpl;
- specs[count++].index = i;
+ specs[count++].index = CURR_SLOT.qp_regno;
}
+ if (idesc->operands[1] == IA64_OPND_PR)
+ {
+ for (i = 1; i < 16; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ /* This is the general case for PRs. IA64_RS_PR and IA64_RS_PR63 are
+ simplified cases of this. */
+ case IA64_RS_PRr:
+ if (note == 0)
+ {
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
}
}
else if (note == 7)
&& idesc->operands[0] == IA64_OPND_PR)
{
mask = CURR_SLOT.opnd[2].X_add_number;
- if (mask & ((valueT) 1 << 16))
- mask |= ~(valueT) 0xffff;
- for (i = 1; i < 63; i++)
- {
- if (mask & ((valueT) 1 << i))
- {
- specs[count] = tmpl;
- specs[count++].index = i;
- }
- }
+ if (mask & ((valueT) 1<<16))
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
}
else if (rsrc_write
&& idesc->operands[0] == IA64_OPND_PR_ROT)
|| idesc->operands[i] == IA64_OPND_P2)
{
int regno = CURR_SLOT.opnd[i].X_add_number - REG_P;
- if (regno != 0)
+ if (regno >= 16 && regno < 63)
{
specs[count] = tmpl;
specs[count++].index = regno;
}
else if (note == 12)
{
- if (CURR_SLOT.qp_regno != 0)
+ if (CURR_SLOT.qp_regno >= 16 && CURR_SLOT.qp_regno < 63)
{
specs[count] = tmpl;
specs[count++].index = CURR_SLOT.qp_regno;
{
int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+
if ((idesc->operands[0] == IA64_OPND_P1
|| idesc->operands[0] == IA64_OPND_P2)
- && p1 != 0 && p1 != 63)
+ && p1 >= 16 && p1 < 63)
{
specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
specs[count++].index = p1;
}
if ((idesc->operands[1] == IA64_OPND_P1
|| idesc->operands[1] == IA64_OPND_P2)
- && p2 != 0 && p2 != 63)
+ && p2 >= 16 && p2 < 63)
{
specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
specs[count++].index = p2;
}
}
else
{
- if (CURR_SLOT.qp_regno != 0)
+ if (CURR_SLOT.qp_regno >= 16 && CURR_SLOT.qp_regno < 63)
{
specs[count] = tmpl;
specs[count++].index = CURR_SLOT.qp_regno;
}
if (idesc->operands[1] == IA64_OPND_PR)
{
- for (i = 1; i < 63; i++)
+ for (i = 16; i < 63; i++)
{
specs[count] = tmpl;
specs[count++].index = i;
}
break;
+ /* This is the same as IA64_RS_PRr, except simplified to account for
+ the fact that there is only one register. */
case IA64_RS_PR63:
if (note == 0)
{
specs[count++] = tmpl;
}
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ if (idesc->operands[2] == IA64_OPND_IMM17)
+ mask = CURR_SLOT.opnd[2].X_add_number;
+ if (mask & ((valueT) 1 << 63))
+ specs[count++] = tmpl;
+ }
else if (note == 11)
{
if ((idesc->operands[0] == IA64_OPND_P1
specs[count++] = tmpl;
}
}
- else if (note == 7)
- {
- valueT mask = 0;
- if (idesc->operands[2] == IA64_OPND_IMM17)
- mask = CURR_SLOT.opnd[2].X_add_number;
- if (mask & ((valueT) 1 << 63))
- {
- specs[count++] = tmpl;
- }
- }
else if (note == 1)
{
if (rsrc_write)
{
- for (i = 0; i < idesc->num_outputs; i++)
- if ((idesc->operands[i] == IA64_OPND_P1
- || idesc->operands[i] == IA64_OPND_P2)
- && CURR_SLOT.opnd[i].X_add_number - REG_P == 63)
- {
- specs[count++] = tmpl;
- }
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+
+ if (p1 == 63
+ && (idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2))
+ {
+ specs[count] = tmpl;
+ specs[count++].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
+ }
+ if (p2 == 63
+ && (idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2))
+ {
+ specs[count] = tmpl;
+ specs[count++].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
+ }
}
else
{
else if (strncmp (idesc->name, "br.call", 6) == 0
|| strncmp (idesc->name, "brl.call", 7) == 0)
{
- // FIXME keep GR values which are marked as "safe_across_calls"
+ /* FIXME keep GR values which are marked as "safe_across_calls" */
clear_register_values ();
clear_qp_mutex (~qp_safe_across_calls);
clear_qp_implies (~qp_safe_across_calls, ~qp_safe_across_calls);
gr_values[regno].value = CURR_SLOT.opnd[1].X_add_number;
gr_values[regno].path = md.path;
if (md.debug_dv)
- fprintf (stderr, " Know gr%d = 0x%llx\n",
- regno, gr_values[regno].value);
+ {
+ fprintf (stderr, " Know gr%d = ", regno);
+ fprintf_vma (stderr, gr_values[regno].value);
+ fputs ("\n", stderr);
+ }
}
}
else
}
}
+ /* Skip apparent PR write conflicts where both writes are an AND or both
+ writes are an OR. */
+ if (rs->dependency->specifier == IA64_RS_PR
+ || rs->dependency->specifier == IA64_RS_PRr
+ || rs->dependency->specifier == IA64_RS_PR63)
+ {
+ if (specs[count].cmp_type != CMP_NONE
+ && specs[count].cmp_type == rs->cmp_type)
+ {
+ if (md.debug_dv)
+ fprintf (stderr, " %s on parallel compare allowed (PR%d)\n",
+ dv_mode[rs->dependency->mode],
+ rs->dependency->specifier != IA64_RS_PR63 ?
+ specs[count].index : 63);
+ continue;
+ }
+ if (md.debug_dv)
+ fprintf (stderr,
+ " %s on parallel compare conflict %s vs %s on PR%d\n",
+ dv_mode[rs->dependency->mode],
+ dv_cmp_type[rs->cmp_type],
+ dv_cmp_type[specs[count].cmp_type],
+ rs->dependency->specifier != IA64_RS_PR63 ?
+ specs[count].index : 63);
+
+ }
+
/* If either resource is not specific, conservatively assume a conflict
*/
if (!specs[count].specific || !rs->specific)
static void
mark_resource (idesc, dep, spec, depind, path)
- struct ia64_opcode *idesc;
- const struct ia64_dependency *dep;
+ struct ia64_opcode *idesc ATTRIBUTE_UNUSED;
+ const struct ia64_dependency *dep ATTRIBUTE_UNUSED;
struct rsrc *spec;
int depind;
int path;
regdepstotlen += 20;
regdeps = (struct rsrc *)
xrealloc ((void *) regdeps,
- regdepstotlen * sizeof(struct rsrc));
+ regdepstotlen * sizeof (struct rsrc));
}
regdeps[regdepslen] = *spec;
if (regdeps[depind].specific && regdeps[depind].index != 0)
fprintf (stderr, " (%d)", regdeps[depind].index);
if (regdeps[depind].mem_offset.hint)
- fprintf (stderr, " 0x%llx+0x%llx",
- regdeps[depind].mem_offset.base,
- regdeps[depind].mem_offset.offset);
+ {
+ fputs (" ", stderr);
+ fprintf_vma (stderr, regdeps[depind].mem_offset.base);
+ fputs ("+", stderr);
+ fprintf_vma (stderr, regdeps[depind].mem_offset.offset);
+ }
fprintf (stderr, "\n");
}
}
if (add_only_qp_reads
&& !(dep->mode == IA64_DV_WAR
&& (dep->specifier == IA64_RS_PR
+ || dep->specifier == IA64_RS_PRr
|| dep->specifier == IA64_RS_PR63)))
continue;
qp_regno = 0;
if (md.qp.X_op == O_register)
- qp_regno = md.qp.X_add_number - REG_P;
+ {
+ qp_regno = md.qp.X_add_number - REG_P;
+ md.qp.X_op = O_absent;
+ }
flags = idesc->flags;
CURR_SLOT.qp_regno = qp_regno;
CURR_SLOT.idesc = idesc;
as_where (&CURR_SLOT.src_file, &CURR_SLOT.src_line);
- if (debug_type == DEBUG_DWARF2)
- dwarf2_where (&CURR_SLOT.debug_line);
+ dwarf2_where (&CURR_SLOT.debug_line);
/* Add unwind entry, if there is one. */
if (unwind.current_entry)
symbolS *
md_undefined_symbol (name)
- char *name;
+ char *name ATTRIBUTE_UNUSED;
{
return 0;
}
t0 = control_bits | (insn[0] << 5) | (insn[1] << 46);
t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
- md_number_to_chars (fixpos + 0, t0, 8);
- md_number_to_chars (fixpos + 8, t1, 8);
+ number_to_chars_littleendian (fixpos + 0, t0, 8);
+ number_to_chars_littleendian (fixpos + 8, t1, 8);
}
/* Attempt to simplify or even eliminate a fixup. The return value is
md_apply_fix3 (fix, valuep, seg)
fixS *fix;
valueT *valuep;
- segT seg;
+ segT seg ATTRIBUTE_UNUSED;
{
char *fixpos;
valueT value = *valuep;
}
if (fix->fx_addsy)
{
- switch (fix->fx_r_type)
+ if (fix->fx_r_type == (int) BFD_RELOC_UNUSED)
{
- case 0:
+ /* This must be a TAG13 or TAG13b operand. There are no external
+ relocs defined for them, so we must give an error. */
as_bad_where (fix->fx_file, fix->fx_line,
"%s must have a constant value",
elf64_ia64_operands[fix->tc_fix_data.opnd].desc);
- break;
-
- default:
- break;
+ fix->fx_done = 1;
+ return 1;
}
/* ??? This is a hack copied from tc-i386.c to make PCREL relocs
arelent *
tc_gen_reloc (sec, fixp)
- asection *sec;
+ asection *sec ATTRIBUTE_UNUSED;
fixS *fixp;
{
arelent *reloc;
/* Handle ia64 specific semantics of the align directive. */
-int
+void
ia64_md_do_align (n, fill, len, max)
- int n;
- const char *fill;
- int len;
- int max;
+ int n ATTRIBUTE_UNUSED;
+ const char *fill ATTRIBUTE_UNUSED;
+ int len ATTRIBUTE_UNUSED;
+ int max ATTRIBUTE_UNUSED;
{
- /* Fill any pending bundle with nops. */
- if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+ if (subseg_text_p (now_seg))
ia64_flush_insns ();
+}
- /* When we align code in a text section, emit a bundle of 3 nops instead of
- zero bytes. We can only do this if a multiple of 16 bytes was requested.
- N is log base 2 of the requested alignment. */
- if (fill == NULL
- && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE
- && n > 4)
- {
- /* Use mfi bundle of nops with no stop bits. */
- static const unsigned char be_nop[]
- = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c};
- static const unsigned char le_nop[]
- = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
-
- /* Make sure we are on a 16-byte boundary, in case someone has been
- putting data into a text section. */
- frag_align (4, 0, 0);
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
- if (target_big_endian)
- frag_align_pattern (n, be_nop, 16, max);
- else
- frag_align_pattern (n, le_nop, 16, max);
- return 1;
+void
+ia64_handle_align (fragp)
+ fragS *fragp;
+{
+ /* Use mfi bundle of nops with no stop bits. */
+ static const unsigned char be_nop[]
+ = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c};
+ static const unsigned char le_nop[]
+ = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
+
+ int bytes;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ /* Make sure we are on a 16-byte boundary, in case someone has been
+ putting data into a text section. */
+ if (bytes & 15)
+ {
+ int fix = bytes & 15;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ fragp->fr_fix += fix;
}
- return 0;
+ memcpy (p, (target_big_endian ? be_nop : le_nop), 16);
+ fragp->fr_var = 16;
}