/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
- Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Free Software Foundation, Inc.
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of GAS, the GNU Assembler.
- optional operands
- directives:
- .alias
.eb
.estate
.lb
enum reloc_func
{
+ FUNC_DTP_MODULE,
+ FUNC_DTP_RELATIVE,
FUNC_FPTR_RELATIVE,
FUNC_GP_RELATIVE,
FUNC_LT_RELATIVE,
+ FUNC_LT_RELATIVE_X,
FUNC_PC_RELATIVE,
FUNC_PLT_RELATIVE,
FUNC_SEC_RELATIVE,
FUNC_SEG_RELATIVE,
+ FUNC_TP_RELATIVE,
FUNC_LTV_RELATIVE,
FUNC_LT_FPTR_RELATIVE,
+ FUNC_LT_DTP_MODULE,
+ FUNC_LT_DTP_RELATIVE,
+ FUNC_LT_TP_RELATIVE,
FUNC_IPLT_RELOC,
};
struct symbol *sym;
};
+/* This is the endianness of the current section. */
extern int target_big_endian;
+/* This is the default endianness. */
+static int default_big_endian = TARGET_BYTES_BIG_ENDIAN;
+
+void (*ia64_number_to_chars) PARAMS ((char *, valueT, int));
+
+static void ia64_float_to_chars_bigendian
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void ia64_float_to_chars_littleendian
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void (*ia64_float_to_chars)
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+
+static struct hash_control *alias_hash;
+static struct hash_control *alias_name_hash;
+static struct hash_control *secalias_hash;
+static struct hash_control *secalias_name_hash;
+
+/* List of chars besides those in app.c:symbol_chars that can start an
+ operand. Used to prevent the scrubber eating vital white-space. */
+const char ia64_symbol_chars[] = "@?";
+
/* Characters which always start a comment. */
const char comment_chars[] = "";
auto_align : 1,
keep_pending_output : 1;
+ /* What to do when something is wrong with unwind directives. */
+ enum
+ {
+ unwind_check_warning,
+ unwind_check_error
+ } unwind_check;
+
/* Each bundle consists of up to three instructions. We keep
track of four most recent instructions so we can correctly set
the end_of_insn_group for the last instruction in a bundle. */
unsigned int
end_of_insn_group : 1,
manual_bundling_on : 1,
- manual_bundling_off : 1;
+ manual_bundling_off : 1,
+ loc_directive_seen : 1;
signed char user_template; /* user-selected template, if any */
unsigned char qp_regno; /* qualifying predicate */
/* This duplicates a good fraction of "struct fix" but we
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;
int pointer_size; /* size in bytes of a pointer */
int pointer_size_shift; /* shift size of a pointer for alignment */
pseudo_func[] =
{
/* reloc pseudo functions (these must come first!): */
+ { "dtpmod", PSEUDO_FUNC_RELOC, { 0 } },
+ { "dtprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "fptr", PSEUDO_FUNC_RELOC, { 0 } },
{ "gprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltoffx", PSEUDO_FUNC_RELOC, { 0 } },
{ "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "pltoff", PSEUDO_FUNC_RELOC, { 0 } },
{ "secrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "segrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "tprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltv", PSEUDO_FUNC_RELOC, { 0 } },
- { "", 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_MODULE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_RELATIVE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_TP_RELATIVE */
{ "iplt", PSEUDO_FUNC_RELOC, { 0 } },
/* mbtype4 constants: */
{ "natval", PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */
+ /* hint constants: */
+ { "pause", PSEUDO_FUNC_CONST, { 0x0 } },
+
/* unwind-related constants: */
- { "svr4", PSEUDO_FUNC_CONST, { 0 } },
- { "hpux", PSEUDO_FUNC_CONST, { 1 } },
- { "nt", PSEUDO_FUNC_CONST, { 2 } },
+ { "svr4", PSEUDO_FUNC_CONST, { ELFOSABI_NONE } },
+ { "hpux", PSEUDO_FUNC_CONST, { ELFOSABI_HPUX } },
+ { "nt", PSEUDO_FUNC_CONST, { 2 } }, /* conflicts w/ELFOSABI_NETBSD */
+ { "linux", PSEUDO_FUNC_CONST, { ELFOSABI_LINUX } },
+ { "freebsd", PSEUDO_FUNC_CONST, { ELFOSABI_FREEBSD } },
+ { "openvms", PSEUDO_FUNC_CONST, { ELFOSABI_OPENVMS } },
+ { "nsk", PSEUDO_FUNC_CONST, { ELFOSABI_NSK } },
/* unwind-related registers: */
{ "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
{".init_array"}, {".fini_array"}
};
-static char *special_linkonce_name[] =
- {
- ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
- };
-
/* The best template for a particular sequence of up to three
instructions: */
#define N IA64_NUM_TYPES
valueT value;
} gr_values[128] = {{ 1, 0, 0 }};
+/* Remember the alignment frag. */
+static fragS *align_frag;
+
/* These are the routines required to output the various types of
unwind records. */
unwind_record r;
unsigned long slot_number;
fragS *slot_frag;
+ unsigned long next_slot_number;
+ fragS *next_slot_frag;
struct unw_rec_list *next;
} unw_rec_list;
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;
unw_rec_list *tail;
/* These are used to create the unwind table entry for this function. */
symbolS *proc_start;
- symbolS *proc_end;
symbolS *info; /* pointer to unwind info */
symbolS *personality_routine;
segT saved_text_seg;
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 : 1;
+ unsigned int prologue_mask : 4;
+ unsigned int body : 1;
+ unsigned int insn : 1;
unsigned int prologue_count; /* number of .prologues seen so far */
/* Prologue counts at previous .label_state directives. */
struct label_prologue_count * saved_prologue_counts;
} unwind;
+/* The input value is a negated offset from psp, and specifies an address
+ psp - offset. The encoded value is psp + 16 - (4 * offset). Thus we
+ must add 16 and divide by 4 to get the encoded value. */
+
+#define ENCODED_PSP_OFFSET(OFFSET) (((OFFSET) + 16) / 4)
+
typedef void (*vbyte_func) PARAMS ((int, char *, char *));
-/* Forward delarations: */
-static int ar_is_in_integer_unit PARAMS ((int regnum));
+/* Forward declarations: */
static void set_section PARAMS ((char *name));
static unsigned int set_regstack PARAMS ((unsigned int, unsigned int,
unsigned int, unsigned int));
+static void dot_align (int);
static void dot_radix PARAMS ((int));
static void dot_special_section PARAMS ((int));
static void dot_proc PARAMS ((int));
static void print_prmask PARAMS ((valueT mask));
static void dot_pred_rel PARAMS ((int));
static void dot_reg_val PARAMS ((int));
+static void dot_serialize PARAMS ((int));
static void dot_dv_mode PARAMS ((int));
static void dot_entry PARAMS ((int));
static void dot_mem_offset PARAMS ((int));
expressionS *e));
static int parse_operand PARAMS ((expressionS *e));
static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
-static int errata_nop_necessary_p PARAMS ((struct slot *, enum ia64_unit));
static void build_insn PARAMS ((struct slot *, bfd_vma *));
static void emit_one_bundle PARAMS ((void));
static void fix_insn PARAMS ((fixS *, const struct ia64_operand *, valueT));
static void output_X3_format PARAMS ((vbyte_func, unw_record_type, int, int, int, unsigned long,
unsigned long));
static void output_X4_format PARAMS ((vbyte_func, int, int, int, int, int, int, unsigned long));
-static void free_list_records PARAMS ((unw_rec_list *));
+static unw_rec_list *output_endp PARAMS ((void));
static unw_rec_list *output_prologue PARAMS ((void));
static unw_rec_list *output_prologue_gr PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_body PARAMS ((void));
static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
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, fragS *,
- unsigned long, fragS *));
+ unsigned long, fragS *,
+ int));
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 void fixup_unw_records PARAMS ((unw_rec_list *, int));
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 ((const char *));
static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
static void save_prologue_count PARAMS ((unsigned long, unsigned int));
static void free_saved_prologue_counts PARAMS ((void));
-/* 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) \
- { \
- const char *_prefix = special_section_name[special]; \
- const char *_suffix = text_name; \
- size_t _prefix_len, _suffix_len; \
- char *_result; \
- if (strncmp (text_name, ".gnu.linkonce.t.", \
- sizeof (".gnu.linkonce.t.") - 1) == 0) \
- { \
- _prefix = special_linkonce_name[special - SPECIAL_SECTION_UNWIND]; \
- _suffix += sizeof (".gnu.linkonce.t.") - 1; \
- } \
- _prefix_len = strlen (_prefix), _suffix_len = strlen (_suffix); \
- _result = alloca (_prefix_len + _suffix_len + 1); \
- memcpy (_result, _prefix, _prefix_len); \
- memcpy (_result + _prefix_len, _suffix, _suffix_len); \
- _result[_prefix_len + _suffix_len] = '\0'; \
- result = _result; \
- } \
-while (0)
-
-/* Determine if application register REGNUM resides in the integer
+/* Determine if application register REGNUM resides only in the integer
unit (as opposed to the memory unit). */
static int
-ar_is_in_integer_unit (reg)
- int reg;
+ar_is_only_in_integer_unit (int reg)
{
reg -= REG_AR;
+ return reg >= 64 && reg <= 111;
+}
- return (reg == 64 /* pfs */
- || reg == 65 /* lc */
- || reg == 66 /* ec */
- /* ??? ias accepts and puts these in the integer unit. */
- || (reg >= 112 && reg <= 127));
+/* Determine if application register REGNUM resides only in the memory
+ unit (as opposed to the integer unit). */
+static int
+ar_is_only_in_memory_unit (int reg)
+{
+ reg -= REG_AR;
+ return reg >= 0 && reg <= 47;
}
/* Switch to section NAME and create section if necessary. It's
{
if (letter == 's')
return SHF_IA_64_SHORT;
+ else if (letter == 'o')
+ return SHF_LINK_ORDER;
- *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S in string");
- return 0;
+ *ptr_msg = _("Bad .section directive: want a,o,s,w,x,M,S,G,T in string");
+ return -1;
}
/* Map SHF_IA_64_SHORT to SEC_SMALL_DATA. */
if (STREQ (ELF_STRING_ia64_unwind_once))
return SHT_IA_64_UNWIND;
- if (STREQ ("init_array"))
- return SHT_INIT_ARRAY;
-
- if (STREQ ("fini_array"))
- return SHT_FINI_ARRAY;
+ if (STREQ ("unwind"))
+ return SHT_IA_64_UNWIND;
return -1;
#undef STREQ
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. */
+ resolve those now. We only handle prologue, body, and endp 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)
+ switch (ptr->r.type)
{
+ case prologue:
+ case prologue_gr:
+ case body:
+ case endp:
ptr->slot_number = (unsigned long) frag_more (0);
ptr->slot_frag = frag_now;
+ break;
+
+ /* Allow any record which doesn't have a "t" field (i.e.,
+ doesn't relate to a particular instruction). */
+ case unwabi:
+ case br_gr:
+ case copy_state:
+ case fr_mem:
+ case frgr_mem:
+ case gr_gr:
+ case gr_mem:
+ case label_state:
+ case rp_br:
+ case spill_base:
+ case spill_mask:
+ /* nothing */
+ break;
+
+ default:
+ as_bad (_("Unwind directive not followed by an instruction."));
+ break;
}
- else
- as_bad (_("Unwind directive not followed by an instruction."));
}
unwind.current_entry = NULL;
as_bad ("qualifying predicate not followed by instruction");
}
-void
-ia64_do_align (nbytes)
- int nbytes;
+static void
+ia64_do_align (int nbytes)
{
char *saved_input_line_pointer = input_line_pointer;
ptr->next = NULL;
ptr->slot_number = SLOT_NUM_NOT_SET;
ptr->r.type = t;
+ ptr->next_slot_number = 0;
+ ptr->next_slot_frag = 0;
return ptr;
}
-/* This function frees an entire list of record structures. */
+/* Dummy unwind record used for calculating the length of the last prologue or
+ body region. */
-void
-free_list_records (unw_rec_list *first)
+static unw_rec_list *
+output_endp ()
{
- unw_rec_list *ptr;
- for (ptr = first; ptr != NULL;)
- {
- unw_rec_list *tmp = ptr;
-
- if ((tmp->r.type == prologue || tmp->r.type == prologue_gr)
- && tmp->r.record.r.mask.i)
- free (tmp->r.record.r.mask.i);
-
- ptr = ptr->next;
- free (tmp);
- }
+ unw_rec_list *ptr = alloc_record (endp);
+ return ptr;
}
static unw_rec_list *
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rp_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (pfs_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (preds_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (spill_base);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (unat_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (lc_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (fpsr_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (priunat_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bsp_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bspstore_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rnat_psprel);
- ptr->r.record.p.pspoff = offset / 4;
+ ptr->r.record.p.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unw_rec_list *ptr = alloc_record (spill_psprel);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
- ptr->r.record.x.pspoff = offset / 4;
+ ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
return ptr;
}
unw_rec_list *ptr = alloc_record (spill_psprel_p);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
- ptr->r.record.x.pspoff = offset / 4;
+ ptr->r.record.x.pspoff = ENCODED_PSP_OFFSET (offset);
ptr->r.record.x.qp = predicate;
return ptr;
}
switch (ptr->r.type)
{
+ /* This is a dummy record that takes up no space in the output. */
+ case endp:
+ break;
+
case gr_mem:
case fr_mem:
case br_mem:
}
}
-static int
-count_bits (unsigned long mask)
-{
- int n = 0;
-
- while (mask)
- {
- mask &= mask - 1;
- ++n;
- }
- 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. */
+ containing FIRST_ADDR. If BEFORE_RELAX, then we use worst-case estimates
+ for frag sizes. */
unsigned long
-slot_index (slot_addr, slot_frag, first_addr, first_frag)
+slot_index (slot_addr, slot_frag, first_addr, first_frag, before_relax)
unsigned long slot_addr;
fragS *slot_frag;
unsigned long first_addr;
fragS *first_frag;
+ int before_relax;
{
unsigned long index = 0;
{
unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
+ if (! before_relax)
+ {
+ /* We can get the final addresses only during and after
+ relaxation. */
+ if (first_frag->fr_next && first_frag->fr_next->fr_address)
+ index += 3 * ((first_frag->fr_next->fr_address
+ - first_frag->fr_address
+ - first_frag->fr_fix) >> 4);
+ }
+ else
+ /* We don't know what the final addresses will be. We try our
+ best to estimate. */
+ switch (first_frag->fr_type)
+ {
+ default:
+ break;
+
+ case rs_space:
+ as_fatal ("only constant space allocation is supported");
+ break;
+
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ /* Take alignment into account. Assume the worst case
+ before relaxation. */
+ index += 3 * ((1 << first_frag->fr_offset) >> 4);
+ break;
+
+ case rs_org:
+ if (first_frag->fr_symbol)
+ {
+ as_fatal ("only constant offsets are supported");
+ break;
+ }
+ case rs_fill:
+ index += 3 * (first_frag->fr_offset >> 4);
+ break;
+ }
+
/* 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. */
/* 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)))
+ && (list->next->r.type == endp
+ || (list->next->r.type == body && list->next->next->r.type == endp)))
return NULL;
return list;
/* Given a complete record list, process any records which have
unresolved fields, (ie length counts for a prologue). After
- this has been run, all neccessary information should be available
+ this has been run, all necessary information should be available
within each record to generate an image. */
static void
-fixup_unw_records (list)
+fixup_unw_records (list, before_relax)
unw_rec_list *list;
+ int before_relax;
{
unw_rec_list *ptr, *region = 0;
unsigned long first_addr = 0, rlen = 0, t;
if (ptr->slot_number == SLOT_NUM_NOT_SET)
as_bad (" Insn slot not set in unwind record.");
t = slot_index (ptr->slot_number, ptr->slot_frag,
- first_addr, first_frag);
+ first_addr, first_frag, before_relax);
switch (ptr->r.type)
{
case prologue:
case body:
{
unw_rec_list *last;
- int size, dir_len = 0;
- unsigned long last_addr;
- fragS *last_frag;
+ int size;
+ unsigned long last_addr = 0;
+ fragS *last_frag = NULL;
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;
+ the function, and determine the size of the region. */
for (last = ptr->next; last != NULL; last = last->next)
if (last->r.type == prologue || last->r.type == prologue_gr
- || last->r.type == body)
+ || last->r.type == body || last->r.type == endp)
{
last_addr = last->slot_number;
last_frag = last->slot_frag;
break;
}
- else if (!last->next)
- {
- /* In the absence of an explicit .body directive,
- the prologue ends after the last instruction
- covered by an unwind directive. */
- if (ptr->r.type != body)
- {
- last_addr = last->slot_number;
- last_frag = last->slot_frag;
- switch (last->r.type)
- {
- case frgr_mem:
- dir_len = (count_bits (last->r.record.p.frmask)
- + count_bits (last->r.record.p.grmask));
- break;
- case fr_mem:
- case gr_mem:
- dir_len += count_bits (last->r.record.p.rmask);
- break;
- case br_mem:
- case br_gr:
- dir_len += count_bits (last->r.record.p.brmask);
- break;
- case gr_gr:
- dir_len += count_bits (last->r.record.p.grmask);
- break;
- default:
- dir_len = 1;
- break;
- }
- }
- break;
- }
- size = (slot_index (last_addr, last_frag, first_addr, first_frag)
- + dir_len);
+ size = slot_index (last_addr, last_frag, first_addr, first_frag,
+ before_relax);
rlen = ptr->r.record.r.rlen = size;
if (ptr->r.type == body)
/* End of region. */
break;
}
case epilogue:
- ptr->r.record.b.t = rlen - 1 - t;
+ if (t < rlen)
+ ptr->r.record.b.t = rlen - 1 - t;
+ else
+ /* This happens when a memory-stack-less procedure uses a
+ ".restore sp" directive at the end of a region to pop
+ the frame state. */
+ ptr->r.record.b.t = 0;
break;
case mem_stack_f:
case frgr_mem:
if (!region)
{
- as_bad ("frgr_mem record before region record!\n");
+ as_bad ("frgr_mem record before region record!");
return;
}
region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
case fr_mem:
if (!region)
{
- as_bad ("fr_mem record before region record!\n");
+ as_bad ("fr_mem record before region record!");
return;
}
region->r.record.r.mask.fr_mem |= ptr->r.record.p.rmask;
case gr_mem:
if (!region)
{
- as_bad ("gr_mem record before region record!\n");
+ as_bad ("gr_mem record before region record!");
return;
}
region->r.record.r.mask.gr_mem |= ptr->r.record.p.rmask;
case br_mem:
if (!region)
{
- as_bad ("br_mem record before region record!\n");
+ as_bad ("br_mem record before region record!");
return;
}
region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask;
case gr_gr:
if (!region)
{
- as_bad ("gr_gr record before region record!\n");
+ as_bad ("gr_gr record before region record!");
return;
}
set_imask (region, ptr->r.record.p.grmask, t, 2);
case br_gr:
if (!region)
{
- as_bad ("br_gr record before region record!\n");
+ as_bad ("br_gr record before region record!");
return;
}
set_imask (region, ptr->r.record.p.brmask, t, 3);
}
}
-/* Helper routine for output_unw_records. Emits the header for the unwind
- info. */
+/* Estimate the size of a frag before relaxing. We only have one type of frag
+ to handle here, which is the unwind info frag. */
-static int
-setup_unwind_header (int size, unsigned char **mem)
+int
+ia64_estimate_size_before_relax (fragS *frag,
+ asection *segtype ATTRIBUTE_UNUSED)
{
- int x, extra = 0;
- valueT flag_value;
+ unw_rec_list *list;
+ int len, size, pad;
+
+ /* ??? This code is identical to the first part of ia64_convert_frag. */
+ list = (unw_rec_list *) frag->fr_opcode;
+ fixup_unw_records (list, 0);
+
+ len = calc_record_size (list);
+ /* pad to pointer-size boundary. */
+ pad = len % md.pointer_size;
+ if (pad != 0)
+ len += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size = len + 8;
+ /* Add a pointer for the personality offset. */
+ if (frag->fr_offset)
+ size += md.pointer_size;
- /* pad to pointer-size boundry. */
- x = size % md.pointer_size;
- if (x != 0)
- extra = md.pointer_size - x;
+ /* fr_var carries the max_chars that we created the fragment with.
+ We must, of course, have allocated enough memory earlier. */
+ assert (frag->fr_var >= size);
- /* Add 8 for the header + a pointer for the
- personality offset. */
- *mem = xmalloc (size + extra + 8 + md.pointer_size);
+ return frag->fr_fix + size;
+}
- /* Clear the padding area and personality. */
- memset (*mem + 8 + size, 0, extra + md.pointer_size);
+/* This function converts a rs_machine_dependent variant frag into a
+ normal fill frag with the unwind image from the the record list. */
+void
+ia64_convert_frag (fragS *frag)
+{
+ unw_rec_list *list;
+ int len, size, pad;
+ valueT flag_value;
- /* Initialize the header area. */
- if (unwind.personality_routine)
+ /* ??? This code is identical to ia64_estimate_size_before_relax. */
+ list = (unw_rec_list *) frag->fr_opcode;
+ fixup_unw_records (list, 0);
+
+ len = calc_record_size (list);
+ /* pad to pointer-size boundary. */
+ pad = len % md.pointer_size;
+ if (pad != 0)
+ len += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size = len + 8;
+ /* Add a pointer for the personality offset. */
+ if (frag->fr_offset)
+ size += md.pointer_size;
+
+ /* fr_var carries the max_chars that we created the fragment with.
+ We must, of course, have allocated enough memory earlier. */
+ assert (frag->fr_var >= size);
+
+ /* Initialize the header area. fr_offset is initialized with
+ unwind.personality_routine. */
+ if (frag->fr_offset)
{
if (md.flags & EF_IA_64_ABI64)
flag_value = (bfd_vma) 3 << 32;
else
flag_value = 0;
- md_number_to_chars (*mem, (((bfd_vma) 1 << 48) /* Version. */
- | flag_value /* U & E handler flags. */
- | ((size + extra) / md.pointer_size)), /* Length. */
- 8);
-
- return extra;
-}
-
-/* Generate an unwind image from a record list. Returns the number of
- bytes in the resulting image. The memory image itselof is returned
- in the 'ptr' parameter. */
-static int
-output_unw_records (list, ptr)
- unw_rec_list *list;
- void **ptr;
-{
- int size, extra;
- unsigned char *mem;
-
- *ptr = NULL;
-
- list = optimize_unw_records (list);
- fixup_unw_records (list);
- size = calc_record_size (list);
-
- if (size > 0 || unwind.force_unwind_entry)
- {
- unwind.force_unwind_entry = 0;
- extra = setup_unwind_header (size, &mem);
+ md_number_to_chars (frag->fr_literal,
+ (((bfd_vma) 1 << 48) /* Version. */
+ | flag_value /* U & E handler flags. */
+ | (len / md.pointer_size)), /* Length. */
+ 8);
- vbyte_mem_ptr = mem + 8;
- process_unw_records (list, output_vbyte_mem);
+ /* Skip the header. */
+ vbyte_mem_ptr = frag->fr_literal + 8;
+ process_unw_records (list, output_vbyte_mem);
- *ptr = mem;
+ /* Fill the padding bytes with zeros. */
+ if (pad != 0)
+ md_number_to_chars (frag->fr_literal + len + 8 - md.pointer_size + pad, 0,
+ md.pointer_size - pad);
- size += extra + 8 + md.pointer_size;
- }
- return size;
+ frag->fr_fix += size;
+ frag->fr_type = rs_fill;
+ frag->fr_var = 0;
+ frag->fr_offset = 0;
}
static int
return 1;
}
+static void
+dot_align (int arg)
+{
+ /* The current frag is an alignment frag. */
+ align_frag = frag_now;
+ s_align_bytes (arg);
+}
+
static void
dot_radix (dummy)
int dummy ATTRIBUTE_UNUSED;
}
}
+/* Helper function for .loc directives. If the assembler is not generating
+ line number info, then we need to remember which instructions have a .loc
+ directive, and only call dwarf2_gen_line_info for those instructions. */
+
+static void
+dot_loc (int x)
+{
+ CURR_SLOT.loc_directive_seen = 1;
+ dwarf2_directive_loc (x);
+}
+
/* .sbss, .bss etc. are macros that expand into ".section SECNAME". */
static void
dot_special_section (which)
set_section ((char *) special_section_name[which]);
}
+/* Return -1 for warning and 0 for error. */
+
+static int
+unwind_diagnostic (const char * region, const char *directive)
+{
+ if (md.unwind_check == unwind_check_warning)
+ {
+ as_warn (".%s outside of %s", directive, region);
+ return -1;
+ }
+ else
+ {
+ as_bad (".%s outside of %s", directive, region);
+ ignore_rest_of_line ();
+ return 0;
+ }
+}
+
+/* Return 1 if a directive is in a procedure, -1 if a directive isn't in
+ a procedure but the unwind directive check is set to warning, 0 if
+ a directive isn't in a procedure and the unwind directive check is set
+ to error. */
+
+static int
+in_procedure (const char *directive)
+{
+ if (unwind.proc_start
+ && (!unwind.saved_text_seg || strcmp (directive, "endp") == 0))
+ return 1;
+ return unwind_diagnostic ("procedure", directive);
+}
+
+/* Return 1 if a directive is in a prologue, -1 if a directive isn't in
+ a prologue but the unwind directive check is set to warning, 0 if
+ a directive isn't in a prologue and the unwind directive check is set
+ to error. */
+
+static int
+in_prologue (const char *directive)
+{
+ int in = in_procedure (directive);
+ if (in)
+ {
+ /* We are in a procedure. Check if we are in a prologue. */
+ if (unwind.prologue)
+ return 1;
+ /* We only want to issue one message. */
+ if (in == 1)
+ return unwind_diagnostic ("prologue", directive);
+ else
+ return -1;
+ }
+ return 0;
+}
+
+/* Return 1 if a directive is in a body, -1 if a directive isn't in
+ a body but the unwind directive check is set to warning, 0 if
+ a directive isn't in a body and the unwind directive check is set
+ to error. */
+
+static int
+in_body (const char *directive)
+{
+ int in = in_procedure (directive);
+ if (in)
+ {
+ /* We are in a procedure. Check if we are in a body. */
+ if (unwind.body)
+ return 1;
+ /* We only want to issue one message. */
+ if (in == 1)
+ return unwind_diagnostic ("body region", directive);
+ else
+ return -1;
+ }
+ return 0;
+}
+
static void
add_unwind_entry (ptr)
unw_rec_list *ptr;
{
expressionS e;
+ if (!in_prologue ("fframe"))
+ return;
+
parse_operand (&e);
if (e.X_op != O_constant)
expressionS e;
unsigned reg;
+ if (!in_prologue ("vframe"))
+ return;
+
parse_operand (&e);
reg = e.X_add_number - REG_GR;
if (e.X_op == O_register && reg < 128)
{
expressionS e;
+ if (!in_prologue ("vframesp"))
+ return;
+
parse_operand (&e);
if (e.X_op == O_constant)
{
add_unwind_entry (output_psp_sprel (e.X_add_number));
}
else
- as_bad ("First operand to .vframesp must be a general register");
+ as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
}
static void
{
expressionS e;
+ if (!in_prologue ("vframepsp"))
+ return;
+
parse_operand (&e);
if (e.X_op == O_constant)
{
add_unwind_entry (output_psp_sprel (e.X_add_number));
}
else
- as_bad ("First operand to .vframepsp must be a general register");
+ as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
}
static void
int sep;
int reg1, reg2;
+ if (!in_prologue ("save"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
as_bad ("No second operand to .save");
unsigned long ecount; /* # of _additional_ regions to pop */
int sep;
+ if (!in_body ("restore"))
+ return;
+
sep = parse_operand (&e1);
if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
{
unsigned int ab, reg;
expressionS e;
+ if (!in_procedure ("restorereg"))
+ return;
+
parse_operand (&e);
if (!convert_expr_to_ab_reg (&e, &ab, ®))
expressionS e1, e2;
int sep;
+ if (!in_procedure ("restorereg.p"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
}
-static int
-generate_unwind_image (text_name)
- const char *text_name;
+static char *special_linkonce_name[] =
+ {
+ ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
+ };
+
+static void
+start_unwind_section (const segT text_seg, int sec_index, int linkonce_empty)
{
- int size;
- unsigned char *unw_rec;
+ /*
+ Use a slightly ugly scheme to derive the unwind section names from
+ the text section name:
- /* Force out pending instructions, to make sure all unwind records have
- a valid slot_number field. */
- ia64_flush_insns ();
+ text sect. unwind table sect.
+ name: name: comments:
+ ---------- ----------------- --------------------------------
+ .text .IA_64.unwind
+ .text.foo .IA_64.unwind.text.foo
+ .foo .IA_64.unwind.foo
+ .gnu.linkonce.t.foo
+ .gnu.linkonce.ia64unw.foo
+ _info .IA_64.unwind_info gas issues error message (ditto)
+ _infoFOO .IA_64.unwind_infoFOO gas issues error message (ditto)
- /* Generate the unwind record. */
- size = output_unw_records (unwind.list, (void **) &unw_rec);
- if (size % md.pointer_size != 0)
- as_bad ("Unwind record is not a multiple of %d bytes.", md.pointer_size);
+ This mapping is done so that:
- /* If there are unwind records, switch sections, and output the info. */
- if (size != 0)
- {
- unsigned char *where;
- char *sec_name;
- expressionS exp;
- bfd_reloc_code_real_type reloc;
+ (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.
- 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);
+ (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).
- /* Make sure the section has 4 byte alignment for ILP32 and
- 8 byte alignment for LP64. */
- frag_align (md.pointer_size_shift, 0, 0);
- record_alignment (now_seg, md.pointer_size_shift);
+ (c) An error is issued if there would be a name clash.
+ */
- /* Set expression which points to start of unwind descriptor area. */
- unwind.info = expr_build_dot ();
+ const char *text_name, *sec_text_name;
+ char *sec_name;
+ const char *prefix = special_section_name [sec_index];
+ const char *suffix;
+ size_t prefix_len, suffix_len, sec_name_len;
- where = (unsigned char *) frag_more (size);
+ sec_text_name = segment_name (text_seg);
+ text_name = sec_text_name;
+ 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 = "";
- /* Issue a label for this address, and keep track of it to put it
- in the unwind section. */
+ /* Build the unwind section name by appending the (possibly stripped)
+ text section name to the unwind prefix. */
+ suffix = text_name;
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ sizeof (".gnu.linkonce.t.") - 1) == 0)
+ {
+ prefix = special_linkonce_name [sec_index - SPECIAL_SECTION_UNWIND];
+ suffix += sizeof (".gnu.linkonce.t.") - 1;
+ }
+ else if (linkonce_empty)
+ return;
- /* Copy the information from the unwind record into this section. The
- data is already in the correct byte order. */
- memcpy (where, unw_rec, size);
+ prefix_len = strlen (prefix);
+ suffix_len = strlen (suffix);
+ sec_name_len = prefix_len + suffix_len;
+ sec_name = alloca (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, suffix, suffix_len);
+ sec_name [sec_name_len] = '\0';
- /* Add the personality address to the image. */
- if (unwind.personality_routine != 0)
- {
- exp.X_op = O_symbol;
- exp.X_add_symbol = unwind.personality_routine;
- exp.X_add_number = 0;
+ /* Handle COMDAT group. */
+ if (suffix == text_name && (text_seg->flags & SEC_LINK_ONCE) != 0)
+ {
+ char *section;
+ size_t len, group_name_len;
+ const char *group_name = elf_group_name (text_seg);
- if (md.flags & EF_IA_64_BE)
- {
- if (md.flags & EF_IA_64_ABI64)
- reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB;
- else
- reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB;
- }
+ if (group_name == NULL)
+ {
+ as_bad ("Group section `%s' has no group signature",
+ sec_text_name);
+ ignore_rest_of_line ();
+ return;
+ }
+ /* We have to construct a fake section directive. */
+ group_name_len = strlen (group_name);
+ len = (sec_name_len
+ + 16 /* ,"aG",@progbits, */
+ + group_name_len /* ,group_name */
+ + 7); /* ,comdat */
+
+ section = alloca (len + 1);
+ memcpy (section, sec_name, sec_name_len);
+ memcpy (section + sec_name_len, ",\"aG\",@progbits,", 16);
+ memcpy (section + sec_name_len + 16, group_name, group_name_len);
+ memcpy (section + len - 7, ",comdat", 7);
+ section [len] = '\0';
+ set_section (section);
+ }
+ else
+ {
+ set_section (sec_name);
+ bfd_set_section_flags (stdoutput, now_seg,
+ SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+ }
+
+ elf_linked_to_section (now_seg) = text_seg;
+}
+
+static void
+generate_unwind_image (const segT text_seg)
+{
+ int size, pad;
+ unw_rec_list *list;
+
+ /* Mark the end of the unwind info, so that we can compute the size of the
+ last unwind region. */
+ add_unwind_entry (output_endp ());
+
+ /* Force out pending instructions, to make sure all unwind records have
+ a valid slot_number field. */
+ ia64_flush_insns ();
+
+ /* Generate the unwind record. */
+ list = optimize_unw_records (unwind.list);
+ fixup_unw_records (list, 1);
+ size = calc_record_size (list);
+
+ if (size > 0 || unwind.force_unwind_entry)
+ {
+ unwind.force_unwind_entry = 0;
+ /* pad to pointer-size boundary. */
+ pad = size % md.pointer_size;
+ if (pad != 0)
+ size += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size += 8;
+ /* Add a pointer for the personality offset. */
+ if (unwind.personality_routine)
+ size += md.pointer_size;
+ }
+
+ /* If there are unwind records, switch sections, and output the info. */
+ if (size != 0)
+ {
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO, 0);
+
+ /* Make sure the section has 4 byte alignment for ILP32 and
+ 8 byte alignment for LP64. */
+ frag_align (md.pointer_size_shift, 0, 0);
+ record_alignment (now_seg, md.pointer_size_shift);
+
+ /* Set expression which points to start of unwind descriptor area. */
+ unwind.info = expr_build_dot ();
+
+ frag_var (rs_machine_dependent, size, size, 0, 0,
+ (offsetT) (long) unwind.personality_routine,
+ (char *) list);
+
+ /* Add the personality address to the image. */
+ if (unwind.personality_routine != 0)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = unwind.personality_routine;
+ exp.X_add_number = 0;
+
+ if (md.flags & EF_IA_64_BE)
+ {
+ if (md.flags & EF_IA_64_ABI64)
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB;
+ else
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB;
+ }
else
{
if (md.flags & EF_IA_64_ABI64)
unwind.personality_routine = 0;
}
}
+ else
+ start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO, 1);
- free_list_records (unwind.list);
free_saved_prologue_counts ();
unwind.list = unwind.tail = unwind.current_entry = NULL;
-
- return size;
}
static void
dot_handlerdata (dummy)
int dummy ATTRIBUTE_UNUSED;
{
- 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 = "";
-
+ if (!in_procedure ("handlerdata"))
+ return;
unwind.force_unwind_entry = 1;
/* Remember which segment we're in so we can switch back after .endp */
/* 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);
+ generate_unwind_image (now_seg);
demand_empty_rest_of_line ();
}
dot_unwentry (dummy)
int dummy ATTRIBUTE_UNUSED;
{
+ if (!in_procedure ("unwentry"))
+ return;
unwind.force_unwind_entry = 1;
demand_empty_rest_of_line ();
}
expressionS e;
unsigned reg;
+ if (!in_prologue ("altrp"))
+ return;
+
parse_operand (&e);
reg = e.X_add_number - REG_BR;
if (e.X_op == O_register && reg < 8)
int sep;
int reg1, val;
+ if (!in_prologue (psprel ? "savepsp" : "savesp"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
as_bad ("No second operand to .save%ssp", psprel ? "p" : "");
{
expressionS e1, e2;
int sep;
+
+ if (!in_prologue ("save.g"))
+ return;
+
sep = parse_operand (&e1);
if (sep == ',')
parse_operand (&e2);
{
expressionS e1;
int sep;
+
+ if (!in_prologue ("save.f"))
+ return;
+
sep = parse_operand (&e1);
if (e1.X_op != O_constant)
unsigned char sep;
int brmask;
+ if (!in_prologue ("save.b"))
+ return;
+
sep = parse_operand (&e1);
if (e1.X_op != O_constant)
{
add_unwind_entry (output_br_mem (brmask));
if (!is_end_of_line[sep] && !is_it_end_of_statement ())
- ignore_rest_of_line ();
+ demand_empty_rest_of_line ();
}
static void
{
expressionS e1, e2;
int sep;
+
+ if (!in_prologue ("save.gf"))
+ return;
+
sep = parse_operand (&e1);
if (sep == ',')
parse_operand (&e2);
expressionS e;
unsigned char sep;
+ if (!in_prologue ("spill"))
+ return;
+
sep = parse_operand (&e);
if (!is_end_of_line[sep] && !is_it_end_of_statement ())
- ignore_rest_of_line ();
+ demand_empty_rest_of_line ();
if (e.X_op != O_constant)
as_bad ("Operand to .spill must be a constant");
int sep, ab, xy, reg, treg;
expressionS e1, e2;
+ if (!in_procedure ("spillreg"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
expressionS e1, e2;
int sep, ab, reg;
+ if (!in_procedure ("spillmem"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
expressionS e1, e2, e3;
unsigned int qp;
+ if (!in_procedure ("spillreg.p"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
int sep, ab, reg;
unsigned int qp;
+ if (!in_procedure ("spillmem.p"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
{
expressionS e;
+ if (!in_body ("label_state"))
+ return;
+
parse_operand (&e);
if (e.X_op != O_constant)
{
{
expressionS e;
+ if (!in_body ("copy_state"))
+ return;
+
parse_operand (&e);
if (e.X_op != O_constant)
{
expressionS e1, e2;
unsigned char sep;
+ if (!in_procedure ("unwabi"))
+ return;
+
sep = parse_operand (&e1);
if (sep != ',')
{
}
sep = parse_operand (&e2);
if (!is_end_of_line[sep] && !is_it_end_of_statement ())
- ignore_rest_of_line ();
+ demand_empty_rest_of_line ();
if (e1.X_op != O_constant)
{
int dummy ATTRIBUTE_UNUSED;
{
char *name, *p, c;
+ if (!in_procedure ("personality"))
+ return;
SKIP_WHITESPACE ();
name = input_line_pointer;
c = get_symbol_end ();
char *name, *p, c;
symbolS *sym;
- unwind.proc_start = expr_build_dot ();
+ unwind.proc_start = 0;
/* Parse names of main and alternate entry points and mark them as
function symbols: */
while (1)
name = input_line_pointer;
c = get_symbol_end ();
p = input_line_pointer;
- sym = symbol_find_or_make (name);
- if (unwind.proc_start == 0)
+ if (!*name)
+ as_bad ("Empty argument of .proc");
+ else
{
- unwind.proc_start = sym;
+ sym = symbol_find_or_make (name);
+ if (S_IS_DEFINED (sym))
+ as_bad ("`%s' was already defined", name);
+ else if (unwind.proc_start == 0)
+ {
+ unwind.proc_start = sym;
+ }
+ symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
}
- symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
*p = c;
SKIP_WHITESPACE ();
if (*input_line_pointer != ',')
break;
++input_line_pointer;
}
+ if (unwind.proc_start == 0)
+ unwind.proc_start = expr_build_dot ();
demand_empty_rest_of_line ();
ia64_do_align (16);
+ unwind.prologue = 0;
unwind.prologue_count = 0;
+ unwind.body = 0;
+ unwind.insn = 0;
unwind.list = unwind.tail = unwind.current_entry = NULL;
unwind.personality_routine = 0;
}
dot_body (dummy)
int dummy ATTRIBUTE_UNUSED;
{
+ if (!in_procedure ("body"))
+ return;
+ if (!unwind.prologue && !unwind.body && unwind.insn)
+ as_warn ("Initial .body should precede any instructions");
+
unwind.prologue = 0;
unwind.prologue_mask = 0;
+ unwind.body = 1;
add_unwind_entry (output_body ());
demand_empty_rest_of_line ();
unsigned char sep;
int mask = 0, grsave = 0;
+ if (!in_procedure ("prologue"))
+ return;
+ if (unwind.prologue)
+ {
+ as_bad (".prologue within prologue");
+ ignore_rest_of_line ();
+ return;
+ }
+ if (!unwind.body && unwind.insn)
+ as_warn ("Initial .prologue should precede any instructions");
+
if (!is_it_end_of_statement ())
{
expressionS e1, e2;
as_bad ("No second operand to .prologue");
sep = parse_operand (&e2);
if (!is_end_of_line[sep] && !is_it_end_of_statement ())
- ignore_rest_of_line ();
+ demand_empty_rest_of_line ();
if (e1.X_op == O_constant)
{
unwind.prologue = 1;
unwind.prologue_mask = mask;
+ unwind.body = 0;
++unwind.prologue_count;
}
long where;
segT saved_seg;
subsegT saved_subseg;
- const char *sec_name, *text_name;
- char *name, *p, c;
+ char *name, *default_name, *p, c;
symbolS *sym;
+ int unwind_check = md.unwind_check;
+
+ md.unwind_check = unwind_check_error;
+ if (!in_procedure ("endp"))
+ return;
+ md.unwind_check = unwind_check;
if (unwind.saved_text_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
- .gnu.linkonce.t.foo
- .gnu.linkonce.ia64unw.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 = "";
-
insn_group_break (1, 0, 0);
/* If there wasn't a .handlerdata, we haven't generated an image yet. */
if (!unwind.info)
- generate_unwind_image (text_name);
+ generate_unwind_image (saved_seg);
if (unwind.info || unwind.force_unwind_entry)
{
+ symbolS *proc_end;
+
subseg_set (md.last_text_seg, 0);
- unwind.proc_end = expr_build_dot ();
+ 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);
+ start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 0);
/* Make sure that section has 4 byte alignment for ILP32 and
8 byte alignment for LP64. */
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;
+ e.X_add_symbol = proc_end;
ia64_cons_fix_new (frag_now, where + bytes_per_address,
bytes_per_address, &e);
bytes_per_address);
}
+ else
+ start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 1);
+
subseg_set (saved_seg, saved_subseg);
+ if (unwind.proc_start)
+ default_name = (char *) S_GET_NAME (unwind.proc_start);
+ else
+ default_name = NULL;
+
/* Parse names of main and alternate entry points and set symbol sizes. */
while (1)
{
name = input_line_pointer;
c = get_symbol_end ();
p = input_line_pointer;
- sym = symbol_find (name);
- if (sym && unwind.proc_start
- && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
- && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
- {
- fragS *fr = symbol_get_frag (unwind.proc_start);
- fragS *frag = symbol_get_frag (sym);
-
- /* Check whether the function label is at or beyond last
- .proc directive. */
- while (fr && fr != frag)
- fr = fr->fr_next;
- if (fr)
- {
- if (frag == frag_now && SEG_NORMAL (now_seg))
- S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+ if (!*name)
+ {
+ if (md.unwind_check == unwind_check_warning)
+ {
+ if (default_name)
+ {
+ as_warn ("Empty argument of .endp. Use the default name `%s'",
+ default_name);
+ name = default_name;
+ }
else
+ as_warn ("Empty argument of .endp");
+ }
+ else
+ as_bad ("Empty argument of .endp");
+ }
+ if (*name)
+ {
+ sym = symbol_find (name);
+ if (!sym
+ && md.unwind_check == unwind_check_warning
+ && default_name
+ && default_name != name)
+ {
+ /* We have a bad name. Try the default one if needed. */
+ as_warn ("`%s' was not defined within procedure. Use the default name `%s'",
+ name, default_name);
+ name = default_name;
+ sym = symbol_find (name);
+ }
+ if (!sym || !S_IS_DEFINED (sym))
+ as_bad ("`%s' was not defined within procedure", name);
+ else if (unwind.proc_start
+ && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
+ && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
+ {
+ fragS *fr = symbol_get_frag (unwind.proc_start);
+ fragS *frag = symbol_get_frag (sym);
+
+ /* Check whether the function label is at or beyond last
+ .proc directive. */
+ while (fr && fr != frag)
+ fr = fr->fr_next;
+ if (fr)
{
- symbol_get_obj (sym)->size =
- (expressionS *) xmalloc (sizeof (expressionS));
- symbol_get_obj (sym)->size->X_op = O_subtract;
- symbol_get_obj (sym)->size->X_add_symbol
- = symbol_new (FAKE_LABEL_NAME, now_seg,
- frag_now_fix (), frag_now);
- symbol_get_obj (sym)->size->X_op_symbol = sym;
- symbol_get_obj (sym)->size->X_add_number = 0;
+ if (frag == frag_now && SEG_NORMAL (now_seg))
+ S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+ else
+ {
+ symbol_get_obj (sym)->size =
+ (expressionS *) xmalloc (sizeof (expressionS));
+ symbol_get_obj (sym)->size->X_op = O_subtract;
+ symbol_get_obj (sym)->size->X_add_symbol
+ = symbol_new (FAKE_LABEL_NAME, now_seg,
+ frag_now_fix (), frag_now);
+ symbol_get_obj (sym)->size->X_op_symbol = sym;
+ symbol_get_obj (sym)->size->X_add_number = 0;
+ }
}
}
}
++input_line_pointer;
}
demand_empty_rest_of_line ();
- unwind.proc_start = unwind.proc_end = unwind.info = 0;
+ unwind.proc_start = unwind.info = 0;
}
static void
for (dr = md.dynreg[type]; dr && dr->num_regs; dr = dr->next)
{
hash_delete (md.dynreg_hash, dr->name);
+ /* FIXME: Free dr->name. */
dr->num_regs = 0;
}
{
start = input_line_pointer;
ch = get_symbol_end ();
+ len = strlen (ia64_canonicalize_symbol_name (start));
*input_line_pointer = ch;
- len = (input_line_pointer - start);
SKIP_WHITESPACE ();
if (*input_line_pointer != '[')
break;
}
- name = obstack_alloc (¬es, len + 1);
- memcpy (name, start, len);
- name[len] = '\0';
-
if (!*drpp)
{
*drpp = obstack_alloc (¬es, sizeof (*dr));
memset (*drpp, 0, sizeof (*dr));
}
+ name = obstack_alloc (¬es, len + 1);
+ memcpy (name, start, len);
+ name[len] = '\0';
+
dr = *drpp;
dr->name = name;
dr->num_regs = num_regs;
if (hash_insert (md.dynreg_hash, name, dr))
{
as_bad ("Attempt to redefine register set `%s'", name);
+ obstack_free (¬es, name);
goto err;
}
dot_byteorder (byteorder)
int byteorder;
{
- target_big_endian = byteorder;
+ segment_info_type *seginfo = seg_info (now_seg);
+
+ if (byteorder == -1)
+ {
+ if (seginfo->tc_segment_info_data.endian == 0)
+ seginfo->tc_segment_info_data.endian = default_big_endian ? 1 : 2;
+ byteorder = seginfo->tc_segment_info_data.endian == 1;
+ }
+ else
+ seginfo->tc_segment_info_data.endian = byteorder ? 1 : 2;
+
+ if (target_big_endian != byteorder)
+ {
+ target_big_endian = byteorder;
+ if (target_big_endian)
+ {
+ ia64_number_to_chars = number_to_chars_bigendian;
+ ia64_float_to_chars = ia64_float_to_chars_bigendian;
+ }
+ else
+ {
+ ia64_number_to_chars = number_to_chars_littleendian;
+ ia64_float_to_chars = ia64_float_to_chars_littleendian;
+ }
+ }
}
static void
demand_empty_rest_of_line ();
}
-static void
-dot_alias (dummy)
- int dummy ATTRIBUTE_UNUSED;
-{
- as_bad (".alias not implemented yet");
-}
-
static void
dot_ln (dummy)
int dummy ATTRIBUTE_UNUSED;
int len;
SKIP_WHITESPACE ();
- if (*input_line_pointer != '"')
+ if (*input_line_pointer == '"')
+ name = demand_copy_C_string (&len);
+ else
{
- as_bad ("Missing section name");
- ignore_rest_of_line ();
- return 0;
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (input_line_pointer == start)
+ {
+ as_bad ("Missing section name");
+ ignore_rest_of_line ();
+ return 0;
+ }
+ name = obstack_copy (¬es, start, input_line_pointer - start + 1);
+ *input_line_pointer = c;
}
- name = demand_copy_C_string (&len);
if (!name)
{
ignore_rest_of_line ();
stmt_float_cons (kind)
int kind;
{
- size_t size;
+ size_t alignment;
switch (kind)
{
- case 'd': size = 8; break;
- case 'x': size = 10; break;
+ case 'd':
+ alignment = 8;
+ break;
+
+ case 'x':
+ case 'X':
+ alignment = 16;
+ break;
case 'f':
default:
- size = 4;
+ alignment = 4;
break;
}
- ia64_do_align (size);
+ ia64_do_align (alignment);
float_cons (kind);
}
demand_empty_rest_of_line ();
}
+/*
+ .serialize.data
+ .serialize.instruction
+ */
+static void
+dot_serialize (type)
+ int type;
+{
+ insn_group_break (0, 0, 0);
+ if (type)
+ instruction_serialization ();
+ else
+ data_serialization ();
+ insn_group_break (0, 0, 0);
+ demand_empty_rest_of_line ();
+}
+
/* select dv checking mode
.auto
.explicit
}
/*
- .pred.rel.clear [p1 [,p2 [,...]]] (also .pred.rel "clear")
- .pred.rel.imply p1, p2 (also .pred.rel "imply")
- .pred.rel.mutex p1, p2 [,...] (also .pred.rel "mutex")
+ .pred.rel.clear [p1 [,p2 [,...]]] (also .pred.rel "clear" or @clear)
+ .pred.rel.imply p1, p2 (also .pred.rel "imply" or @imply)
+ .pred.rel.mutex p1, p2 [,...] (also .pred.rel "mutex" or @mutex)
.pred.safe_across_calls p1 [, p2 [,...]]
*/
if (type == 0)
{
- if (*input_line_pointer != '"')
- {
- as_bad (_("Missing predicate relation type"));
- ignore_rest_of_line ();
- return;
- }
- else
+ if (*input_line_pointer == '"')
{
int len;
char *form = demand_copy_C_string (&len);
+
if (strcmp (form, "mutex") == 0)
type = 'm';
else if (strcmp (form, "clear") == 0)
type = 'c';
else if (strcmp (form, "imply") == 0)
type = 'i';
- else
- {
- as_bad (_("Unrecognized predicate relation type"));
- ignore_rest_of_line ();
- return;
- }
+ obstack_free (¬es, form);
}
- if (*input_line_pointer == ',')
- ++input_line_pointer;
- SKIP_WHITESPACE ();
- }
-
- SKIP_WHITESPACE ();
- while (1)
- {
- valueT bit = 1;
- int regno;
-
- if (TOUPPER (*input_line_pointer) != 'P'
- || (regno = atoi (++input_line_pointer)) < 0
- || regno > 63)
+ else if (*input_line_pointer == '@')
{
- as_bad (_("Predicate register expected"));
- ignore_rest_of_line ();
- return;
- }
- while (ISDIGIT (*input_line_pointer))
- ++input_line_pointer;
- if (p1 == -1)
- p1 = regno;
- else if (p2 == -1)
- p2 = regno;
- bit <<= regno;
- if (mask & bit)
- as_warn (_("Duplicate predicate register ignored"));
- mask |= bit;
- count++;
- /* See if it's a range. */
- if (*input_line_pointer == '-')
+ char *form = ++input_line_pointer;
+ char c = get_symbol_end();
+
+ if (strcmp (form, "mutex") == 0)
+ type = 'm';
+ else if (strcmp (form, "clear") == 0)
+ type = 'c';
+ else if (strcmp (form, "imply") == 0)
+ type = 'i';
+ *input_line_pointer = c;
+ }
+ else
{
- valueT stop = 1;
- ++input_line_pointer;
+ as_bad (_("Missing predicate relation type"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (type == 0)
+ {
+ as_bad (_("Unrecognized predicate relation type"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
- if (TOUPPER (*input_line_pointer) != 'P'
- || (regno = atoi (++input_line_pointer)) < 0
- || regno > 63)
- {
- as_bad (_("Predicate register expected"));
- ignore_rest_of_line ();
- return;
- }
- while (ISDIGIT (*input_line_pointer))
- ++input_line_pointer;
- stop <<= regno;
- if (bit >= stop)
+ SKIP_WHITESPACE ();
+ while (1)
+ {
+ valueT bits = 1;
+ int regno;
+ expressionS pr, *pr1, *pr2;
+
+ expression (&pr);
+ if (pr.X_op == O_register
+ && pr.X_add_number >= REG_P
+ && pr.X_add_number <= REG_P + 63)
+ {
+ regno = pr.X_add_number - REG_P;
+ bits <<= regno;
+ count++;
+ if (p1 == -1)
+ p1 = regno;
+ else if (p2 == -1)
+ p2 = regno;
+ }
+ else if (type != 'i'
+ && pr.X_op == O_subtract
+ && (pr1 = symbol_get_value_expression (pr.X_add_symbol))
+ && pr1->X_op == O_register
+ && pr1->X_add_number >= REG_P
+ && pr1->X_add_number <= REG_P + 63
+ && (pr2 = symbol_get_value_expression (pr.X_op_symbol))
+ && pr2->X_op == O_register
+ && pr2->X_add_number >= REG_P
+ && pr2->X_add_number <= REG_P + 63)
+ {
+ /* It's a range. */
+ int stop;
+
+ regno = pr1->X_add_number - REG_P;
+ stop = pr2->X_add_number - REG_P;
+ if (regno >= stop)
{
as_bad (_("Bad register range"));
ignore_rest_of_line ();
return;
}
- while (bit < stop)
- {
- bit <<= 1;
- mask |= bit;
- count++;
- }
- SKIP_WHITESPACE ();
+ bits = ((bits << stop) << 1) - (bits << regno);
+ count += stop - regno + 1;
+ }
+ else
+ {
+ as_bad (_("Predicate register expected"));
+ ignore_rest_of_line ();
+ return;
}
+ if (mask & bits)
+ as_warn (_("Duplicate predicate register ignored"));
+ mask |= bits;
if (*input_line_pointer != ',')
break;
++input_line_pointer;
{
{ "radix", dot_radix, 0 },
{ "lcomm", s_lcomm_bytes, 1 },
+ { "loc", dot_loc, 0 },
{ "bss", dot_special_section, SPECIAL_SECTION_BSS },
{ "sbss", dot_special_section, SPECIAL_SECTION_SBSS },
{ "sdata", dot_special_section, SPECIAL_SECTION_SDATA },
{ "body", dot_body, 0 },
{ "prologue", dot_prologue, 0 },
{ "endp", dot_endp, 0 },
- { "file", dwarf2_directive_file, 0 },
- { "loc", dwarf2_directive_loc, 0 },
{ "fframe", dot_fframe, 0 },
{ "vframe", dot_vframe, 0 },
{ "copy_state", dot_copy_state, 0 },
{ "unwabi", dot_unwabi, 0 },
{ "personality", dot_personality, 0 },
-#if 0
- { "estate", dot_estate, 0 },
-#endif
{ "mii", dot_template, 0x0 },
{ "mli", dot_template, 0x2 }, /* old format, for compatibility */
{ "mlx", dot_template, 0x2 },
{ "bbb", dot_template, 0xb },
{ "mmb", dot_template, 0xc },
{ "mfb", dot_template, 0xe },
-#if 0
- { "lb", dot_scope, 0 },
- { "le", dot_scope, 1 },
-#endif
- { "align", s_align_bytes, 0 },
+ { "align", dot_align, 0 },
{ "regstk", dot_regstk, 0 },
{ "rotr", dot_rot, DYNREG_GR },
{ "rotf", dot_rot, DYNREG_FR },
{ "msb", dot_byteorder, 1 },
{ "psr", dot_psr, 0 },
{ "alias", dot_alias, 0 },
+ { "secalias", dot_alias, 1 },
{ "ln", dot_ln, 0 }, /* source line info (for debugging) */
{ "xdata1", dot_xdata, 1 },
{ "xdata2", dot_xdata, 2 },
{ "xdata4", dot_xdata, 4 },
{ "xdata8", dot_xdata, 8 },
+ { "xdata16", dot_xdata, 16 },
{ "xreal4", dot_xfloat_cons, 'f' },
{ "xreal8", dot_xfloat_cons, 'd' },
{ "xreal10", dot_xfloat_cons, 'x' },
+ { "xreal16", dot_xfloat_cons, 'X' },
{ "xstring", dot_xstringer, 0 },
{ "xstringz", dot_xstringer, 1 },
{ "xdata2.ua", dot_xdata_ua, 2 },
{ "xdata4.ua", dot_xdata_ua, 4 },
{ "xdata8.ua", dot_xdata_ua, 8 },
+ { "xdata16.ua", dot_xdata_ua, 16 },
{ "xreal4.ua", dot_xfloat_cons_ua, 'f' },
{ "xreal8.ua", dot_xfloat_cons_ua, 'd' },
{ "xreal10.ua", dot_xfloat_cons_ua, 'x' },
+ { "xreal16.ua", dot_xfloat_cons_ua, 'X' },
/* annotations/DV checking support */
{ "entry", dot_entry, 0 },
{ "pred.rel.mutex", dot_pred_rel, 'm' },
{ "pred.safe_across_calls", dot_pred_rel, 's' },
{ "reg.val", dot_reg_val, 0 },
+ { "serialize.data", dot_serialize, 0 },
+ { "serialize.instruction", dot_serialize, 1 },
{ "auto", dot_dv_mode, 'a' },
{ "explicit", dot_dv_mode, 'e' },
{ "default", dot_dv_mode, 'd' },
{ "real4", stmt_float_cons, 'f' },
{ "real8", stmt_float_cons, 'd' },
{ "real10", stmt_float_cons, 'x' },
+ { "real16", stmt_float_cons, 'X' },
{ "string", stringer, 0 },
{ "stringz", stringer, 1 },
{ "real4.ua", float_cons, 'f' },
{ "real8.ua", float_cons, 'd' },
{ "real10.ua", float_cons, 'x' },
+ { "real16.ua", float_cons, 'X' },
};
/* Declare a register by creating a symbol for it and entering it in
return OPERAND_MATCH;
break;
+ case IA64_OPND_AR_CSD:
+ if (e->X_op == O_register && e->X_add_number == REG_AR + 25)
+ return OPERAND_MATCH;
+ break;
+
case IA64_OPND_AR_PFS:
if (e->X_op == O_register && e->X_add_number == REG_AR + 64)
return OPERAND_MATCH;
}
break;
+ case IA64_OPND_LDXMOV:
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+ fix->code = BFD_RELOC_IA64_LDXMOV;
+ fix->opnd = idesc->operands[index];
+ fix->expr = *e;
+ fix->is_pcrel = 0;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+
default:
break;
}
assert (strlen (idesc->name) <= 128);
strcpy (mnemonic, idesc->name);
- if (idesc->operands[2] == IA64_OPND_SOF)
+ if (idesc->operands[2] == IA64_OPND_SOF
+ || idesc->operands[1] == IA64_OPND_SOF)
{
/* To make the common idiom "alloc loc?=ar.pfs,0,1,0,0" work, we
can't parse the first operand until we have parsed the
++num_outputs;
}
- for (; i < NELEMS (CURR_SLOT.opnd); ++i)
+ for (; ; ++i)
{
- sep = parse_operand (CURR_SLOT.opnd + i);
- if (CURR_SLOT.opnd[i].X_op == O_absent)
- break;
+ if (i < NELEMS (CURR_SLOT.opnd))
+ {
+ sep = parse_operand (CURR_SLOT.opnd + i);
+ if (CURR_SLOT.opnd[i].X_op == O_absent)
+ break;
+ }
+ else
+ {
+ expressionS dummy;
+
+ sep = parse_operand (&dummy);
+ if (dummy.X_op == O_absent)
+ break;
+ }
++num_operands;
return 0;
}
- if (idesc->operands[2] == IA64_OPND_SOF)
+ if (idesc->operands[2] == IA64_OPND_SOF
+ || idesc->operands[1] == IA64_OPND_SOF)
{
/* map alloc r1=ar.pfs,i,l,o,r to alloc r1=ar.pfs,(i+l+o),(i+l),r */
know (strcmp (idesc->name, "alloc") == 0);
- if (num_operands == 5 /* first_arg not included in this count! */
- && CURR_SLOT.opnd[2].X_op == O_constant
- && CURR_SLOT.opnd[3].X_op == O_constant
- && CURR_SLOT.opnd[4].X_op == O_constant
- && CURR_SLOT.opnd[5].X_op == O_constant)
- {
- sof = set_regstack (CURR_SLOT.opnd[2].X_add_number,
- CURR_SLOT.opnd[3].X_add_number,
- CURR_SLOT.opnd[4].X_add_number,
- CURR_SLOT.opnd[5].X_add_number);
+ i = (CURR_SLOT.opnd[1].X_op == O_register
+ && CURR_SLOT.opnd[1].X_add_number == REG_AR + AR_PFS) ? 2 : 1;
+ if (num_operands == i + 3 /* first_arg not included in this count! */
+ && CURR_SLOT.opnd[i].X_op == O_constant
+ && CURR_SLOT.opnd[i + 1].X_op == O_constant
+ && CURR_SLOT.opnd[i + 2].X_op == O_constant
+ && CURR_SLOT.opnd[i + 3].X_op == O_constant)
+ {
+ sof = set_regstack (CURR_SLOT.opnd[i].X_add_number,
+ CURR_SLOT.opnd[i + 1].X_add_number,
+ CURR_SLOT.opnd[i + 2].X_add_number,
+ CURR_SLOT.opnd[i + 3].X_add_number);
/* now we can parse the first arg: */
saved_input_pointer = input_line_pointer;
--num_outputs; /* force error */
input_line_pointer = saved_input_pointer;
- CURR_SLOT.opnd[2].X_add_number = sof;
- CURR_SLOT.opnd[3].X_add_number
- = sof - CURR_SLOT.opnd[4].X_add_number;
- CURR_SLOT.opnd[4] = CURR_SLOT.opnd[5];
+ CURR_SLOT.opnd[i].X_add_number = sof;
+ CURR_SLOT.opnd[i + 1].X_add_number
+ = sof - CURR_SLOT.opnd[i + 2].X_add_number;
+ CURR_SLOT.opnd[i + 2] = CURR_SLOT.opnd[i + 3];
}
}
- highest_unmatched_operand = 0;
+ highest_unmatched_operand = -4;
curr_out_of_range_pos = -1;
error_pos = 0;
- expected_operand = idesc->operands[0];
for (; idesc; idesc = get_next_opcode (idesc))
{
if (num_outputs != idesc->num_outputs)
continue; /* mismatch in # of outputs */
+ if (highest_unmatched_operand < 0)
+ highest_unmatched_operand |= 1;
+ if (num_operands > NELEMS (idesc->operands)
+ || (num_operands < NELEMS (idesc->operands)
+ && idesc->operands[num_operands])
+ || (num_operands > 0 && !idesc->operands[num_operands - 1]))
+ continue; /* mismatch in number of arguments */
+ if (highest_unmatched_operand < 0)
+ highest_unmatched_operand |= 2;
CURR_SLOT.num_fixups = 0;
continue;
}
- if (num_operands < NELEMS (idesc->operands)
- && idesc->operands[num_operands])
- continue; /* mismatch in number of arguments */
-
break;
}
if (!idesc)
as_bad ("Operand %u of `%s' should be %s",
error_pos + 1, mnemonic,
elf64_ia64_operands[expected_operand].desc);
+ else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 1))
+ as_bad ("Wrong number of output operands");
+ else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 2))
+ as_bad ("Wrong number of input operands");
else
as_bad ("Operand mismatch");
return 0;
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;
static void
emit_one_bundle ()
{
- unsigned int manual_bundling_on = 0, manual_bundling_off = 0;
- unsigned int manual_bundling = 0;
+ int manual_bundling_off = 0, manual_bundling = 0;
enum ia64_unit required_unit, insn_unit = 0;
enum ia64_insn_type type[3], insn_type;
unsigned int template, orig_template;
struct ia64_opcode *idesc;
int end_of_insn_group = 0, user_template = -1;
int n, i, j, first, curr;
- unw_rec_list *ptr;
+ unw_rec_list *ptr, *last_ptr, *end_ptr;
bfd_vma t0 = 0, t1 = 0;
struct label_fix *lfix;
struct insn_fix *ifix;
char mnemonic[16];
fixS *fix;
char *f;
+ int addr_mod;
first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS;
know (first >= 0 & first < NUM_SLOTS);
f = frag_more (16);
+ /* Check to see if this bundle is at an offset that is a multiple of 16-bytes
+ from the start of the frag. */
+ addr_mod = frag_now_fix () & 15;
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 16"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
/* now fill in slots with as many insns as possible: */
curr = first;
idesc = md.slot[curr].idesc;
end_of_insn_group = 0;
for (i = 0; i < 3 && md.num_slots_in_use > 0; ++i)
{
- /* Set the slot number for prologue/body records now as those
- refer to the current point, not the point after the
- instruction has been issued: */
- /* Don't try to delete prologue/body records here, as that will cause
- them to also be deleted from the master list of 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_frag = frag_now;
- }
+ /* If we have unwind records, we may need to update some now. */
+ ptr = md.slot[curr].unwind_record;
+ if (ptr)
+ {
+ /* Find the last prologue/body record in the list for the current
+ insn, and set the slot number for all records up to that point.
+ This needs to be done now, because prologue/body records refer to
+ the current point, not the point after the instruction has been
+ issued. This matters because there may have been nops emitted
+ meanwhile. Any non-prologue non-body record followed by a
+ prologue/body record must also refer to the current point. */
+ last_ptr = NULL;
+ end_ptr = md.slot[(curr + 1) % NUM_SLOTS].unwind_record;
+ for (; ptr != end_ptr; ptr = ptr->next)
+ if (ptr->r.type == prologue || ptr->r.type == prologue_gr
+ || ptr->r.type == body)
+ last_ptr = ptr;
+ if (last_ptr)
+ {
+ /* Make last_ptr point one after the last prologue/body
+ record. */
+ last_ptr = last_ptr->next;
+ for (ptr = md.slot[curr].unwind_record; ptr != last_ptr;
+ ptr = ptr->next)
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
+ /* Remove the initialized records, so that we won't accidentally
+ update them again if we insert a nop and continue. */
+ md.slot[curr].unwind_record = last_ptr;
+ }
+ }
- if (idesc->flags & IA64_OPCODE_SLOT2)
+ manual_bundling_off = md.slot[curr].manual_bundling_off;
+ if (md.slot[curr].manual_bundling_on)
{
- if (manual_bundling && i != 2)
- as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
- "`%s' must be last in bundle", idesc->name);
+ if (curr == first)
+ manual_bundling = 1;
else
- i = 2;
+ break; /* Need to start a new bundle. */
+ }
+
+ if (idesc->flags & IA64_OPCODE_SLOT2)
+ {
+ if (manual_bundling && !manual_bundling_off)
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ "`%s' must be last in bundle", idesc->name);
+ if (i < 2)
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
+ i = 2;
}
if (idesc->flags & IA64_OPCODE_LAST)
{
required_slot = i;
break;
}
- if (manual_bundling && i != required_slot)
- as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
- "`%s' must be last in instruction group",
- idesc->name);
+ if (manual_bundling
+ && (i > required_slot
+ || (required_slot == 2 && !manual_bundling_off)
+ || (user_template >= 0
+ /* Changing from MMI to M;MI is OK. */
+ && (template ^ required_template) > 1)))
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ "`%s' must be last in instruction group",
+ idesc->name);
+ if (i < 2 && required_slot == 2 && !manual_bundling_off)
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
if (required_slot < i)
/* Can't fit this instruction. */
break;
}
if (curr != first && md.slot[curr].label_fixups)
{
- if (manual_bundling_on)
- as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ if (manual_bundling)
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
"Label must be first in a bundle");
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
/* This insn must go into the first slot of a bundle. */
break;
}
- manual_bundling_on = md.slot[curr].manual_bundling_on;
- manual_bundling_off = md.slot[curr].manual_bundling_off;
-
- if (manual_bundling_on)
- {
- if (curr == first)
- manual_bundling = 1;
- else
- break; /* need to start a new bundle */
- }
-
if (end_of_insn_group && md.num_slots_in_use >= 1)
{
/* We need an instruction group boundary in the middle of a
reason we have to check for this is that otherwise we
may end up generating "MI;;I M.." which has the deadly
effect that the second M instruction is no longer the
- first in the bundle! --davidm 99/12/16 */
+ first in the group! --davidm 99/12/16 */
&& (idesc->flags & IA64_OPCODE_FIRST) == 0)
{
template = 1;
end_of_insn_group = 0;
}
+ else if (i == 1
+ && user_template == 0
+ && !(idesc->flags & IA64_OPCODE_FIRST))
+ /* Use the next slot. */
+ continue;
else if (curr != first)
/* can't fit this insn */
break;
}
required_unit = ia64_templ_desc[template].exec_unit[i];
- /* resolve dynamic opcodes such as "break" and "nop": */
+ /* resolve dynamic opcodes such as "break", "hint", and "nop": */
if (idesc->type == IA64_TYPE_DYN)
{
+ enum ia64_opnd opnd1, opnd2;
+
if ((strcmp (idesc->name, "nop") == 0)
+ || (strcmp (idesc->name, "hint") == 0)
|| (strcmp (idesc->name, "break") == 0))
insn_unit = required_unit;
- else if (strcmp (idesc->name, "chk.s") == 0)
+ else if (strcmp (idesc->name, "chk.s") == 0
+ || strcmp (idesc->name, "mov") == 0)
{
insn_unit = IA64_UNIT_M;
- if (required_unit == IA64_UNIT_I)
+ if (required_unit == IA64_UNIT_I
+ || (required_unit == IA64_UNIT_F && template == 6))
insn_unit = IA64_UNIT_I;
}
else
as_fatal ("emit_one_bundle: unexpected dynamic op");
- sprintf (mnemonic, "%s.%c", idesc->name, "?imbf??"[insn_unit]);
+ sprintf (mnemonic, "%s.%c", idesc->name, "?imbfxx"[insn_unit]);
+ opnd1 = idesc->operands[0];
+ opnd2 = idesc->operands[1];
ia64_free_opcode (idesc);
- md.slot[curr].idesc = idesc = ia64_find_opcode (mnemonic);
-#if 0
- know (!idesc->next); /* no resolved dynamic ops have collisions */
-#endif
+ idesc = ia64_find_opcode (mnemonic);
+ /* moves to/from ARs have collisions */
+ if (opnd1 == IA64_OPND_AR3 || opnd2 == IA64_OPND_AR3)
+ {
+ while (idesc != NULL
+ && (idesc->operands[0] != opnd1
+ || idesc->operands[1] != opnd2))
+ idesc = get_next_opcode (idesc);
+ }
+ md.slot[curr].idesc = idesc;
}
else
{
continue; /* try next slot */
}
- {
- bfd_vma addr;
-
- addr = frag_now->fr_address + frag_now_fix () - 16 + i;
- dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
- }
+ if (debug_type == DEBUG_DWARF2 || md.slot[curr].loc_directive_seen)
+ {
+ bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
- 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"));
+ md.slot[curr].loc_directive_seen = 0;
+ dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
+ }
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_frag = frag_now;
- }
- md.slot[curr].unwind_record = NULL;
+ ptr = md.slot[curr].unwind_record;
+ if (ptr)
+ {
+ /* Set slot numbers for all remaining unwind records belonging to the
+ current insn. There can not be any prologue/body unwind records
+ here. */
+ end_ptr = md.slot[(curr + 1) % NUM_SLOTS].unwind_record;
+ for (; ptr != end_ptr; ptr = ptr->next)
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
+ md.slot[curr].unwind_record = NULL;
+ }
if (required_unit == IA64_UNIT_L)
{
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]));
curr = (curr + 1) % NUM_SLOTS;
idesc = md.slot[curr].idesc;
}
- if (manual_bundling)
+ if (manual_bundling > 0)
{
if (md.num_slots_in_use > 0)
- as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
- "`%s' does not fit into %s template",
- idesc->name, ia64_templ_desc[template].name);
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ "`%s' does not fit into %s template",
+ idesc->name, ia64_templ_desc[template].name);
+ --md.num_slots_in_use;
+ }
else
as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
"Missing '}' at end of file");
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;
+ if (unwind.list)
+ {
+ unwind.list->next_slot_number = (unsigned long) f + 16;
+ unwind.list->next_slot_frag = frag_now;
+ }
}
int
else if (strcmp (arg, "le") == 0)
{
md.flags &= ~EF_IA_64_BE;
+ default_big_endian = 0;
}
else if (strcmp (arg, "be") == 0)
{
md.flags |= EF_IA_64_BE;
+ default_big_endian = 1;
+ }
+ else if (strncmp (arg, "unwind-check=", 13) == 0)
+ {
+ arg += 13;
+ if (strcmp (arg, "warning") == 0)
+ md.unwind_check = unwind_check_warning;
+ else if (strcmp (arg, "error") == 0)
+ md.unwind_check = unwind_check_error;
+ else
+ return 0;
}
else
return 0;
{
md.default_explicit_mode = 0;
}
+ else if (strcmp (arg, "none") == 0)
+ {
+ md.detect_dv = 0;
+ }
else if (strcmp (arg, "debug") == 0)
{
md.debug_dv = 1;
md.default_explicit_mode = 1;
md.debug_dv = 1;
}
+ else if (strcmp (arg, "debugn") == 0)
+ {
+ md.debug_dv = 1;
+ md.detect_dv = 0;
+ }
else
{
as_bad (_("Unrecognized option '-x%s'"), arg);
EF_IA_64_NOFUNCDESC_CONS_GP)\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"),
+ -munwind-check=[warning|error]\n\
+ unwind directive check (default -munwind-check=warning)\n\
+ -x | -xexplicit turn on dependency violation checking\n\
+ -xauto automagically remove dependency violations (default)\n\
+ -xnone turn off dependency violation checking\n\
+ -xdebug debug dependency violation checker\n\
+ -xdebugn debug dependency violation checker but turn off\n\
+ dependency violation checking\n\
+ -xdebugx debug dependency violation checker and turn on\n\
+ dependency violation checking\n"),
stream);
}
bfd_set_section_alignment (stdoutput, text_section, 4);
- target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+ /* Make sure function pointers get initialized. */
+ target_big_endian = -1;
+ dot_byteorder (default_big_endian);
+
+ alias_hash = hash_new ();
+ alias_name_hash = hash_new ();
+ secalias_hash = hash_new ();
+ secalias_name_hash = hash_new ();
+
+ pseudo_func[FUNC_DTP_MODULE].u.sym =
+ symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_DTP_RELATIVE].u.sym =
+ symbol_new (".<dtprel>", undefined_section, FUNC_DTP_RELATIVE,
+ &zero_address_frag);
+
pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
&zero_address_frag);
symbol_new (".<ltoff>", undefined_section, FUNC_LT_RELATIVE,
&zero_address_frag);
+ pseudo_func[FUNC_LT_RELATIVE_X].u.sym =
+ symbol_new (".<ltoffx>", undefined_section, FUNC_LT_RELATIVE_X,
+ &zero_address_frag);
+
pseudo_func[FUNC_PC_RELATIVE].u.sym =
symbol_new (".<pcrel>", undefined_section, FUNC_PC_RELATIVE,
&zero_address_frag);
symbol_new (".<segrel>", undefined_section, FUNC_SEG_RELATIVE,
&zero_address_frag);
+ pseudo_func[FUNC_TP_RELATIVE].u.sym =
+ symbol_new (".<tprel>", undefined_section, FUNC_TP_RELATIVE,
+ &zero_address_frag);
+
pseudo_func[FUNC_LTV_RELATIVE].u.sym =
symbol_new (".<ltv>", undefined_section, FUNC_LTV_RELATIVE,
&zero_address_frag);
symbol_new (".<ltoff.fptr>", undefined_section, FUNC_LT_FPTR_RELATIVE,
&zero_address_frag);
+ pseudo_func[FUNC_LT_DTP_MODULE].u.sym =
+ symbol_new (".<ltoff.dtpmod>", undefined_section, FUNC_LT_DTP_MODULE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym =
+ symbol_new (".<ltoff.dptrel>", undefined_section, FUNC_LT_DTP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_TP_RELATIVE].u.sym =
+ symbol_new (".<ltoff.tprel>", undefined_section, FUNC_LT_TP_RELATIVE,
+ &zero_address_frag);
+
pseudo_func[FUNC_IPLT_RELOC].u.sym =
symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
&zero_address_frag);
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. */
+/* Set the default options in md. Cannot do this in md_begin because
+ that is called after md_parse_option which is where we set the
+ options in md based on command line options. */
void
ia64_init (argc, argv)
char **argv ATTRIBUTE_UNUSED;
{
md.flags = MD_FLAGS_DEFAULT;
+ md.detect_dv = 1;
+ /* FIXME: We should change it to unwind_check_error someday. */
+ md.unwind_check = unwind_check_warning;
}
/* Return a string for the target object file format. */
if (input_line_pointer[0] == ';' && input_line_pointer[-1] == ';')
{
if (md.detect_dv && !md.explicit_mode)
- as_warn (_("Explicit stops are ignored in auto mode"));
+ {
+ static int warned;
+
+ if (!warned)
+ {
+ warned = 1;
+ as_warn (_("Explicit stops are ignored in auto mode"));
+ }
+ }
else
insn_group_break (1, 0, 0);
}
}
}
+#ifdef TE_HPUX
+/* The HP-UX linker will give unresolved symbol errors for symbols
+ that are declared but unused. This routine removes declared,
+ unused symbols from an object. */
+int
+ia64_frob_symbol (sym)
+ struct symbol *sym;
+{
+ if ((S_GET_SEGMENT (sym) == &bfd_und_section && ! symbol_used_p (sym) &&
+ ELF_ST_VISIBILITY (S_GET_OTHER (sym)) == STV_DEFAULT)
+ || (S_GET_SEGMENT (sym) == &bfd_abs_section
+ && ! S_IS_EXTERNAL (sym)))
+ return 1;
+ return 0;
+}
+#endif
+
void
ia64_flush_pending_output ()
{
}
int
-ia64_parse_name (name, e)
+ia64_parse_name (name, e, nextcharP)
char *name;
expressionS *e;
+ char *nextcharP;
{
struct const_desc *cdesc;
struct dynreg *dr = 0;
unsigned int regnum;
+ unsigned int idx;
struct symbol *sym;
char *end;
+ if (*name == '@')
+ {
+ enum pseudo_type pseudo_type = PSEUDO_FUNC_NONE;
+
+ /* Find what relocation pseudo-function we're dealing with. */
+ for (idx = 0; idx < NELEMS (pseudo_func); ++idx)
+ if (pseudo_func[idx].name
+ && pseudo_func[idx].name[0] == name[1]
+ && strcmp (pseudo_func[idx].name + 1, name + 2) == 0)
+ {
+ pseudo_type = pseudo_func[idx].type;
+ break;
+ }
+ switch (pseudo_type)
+ {
+ case PSEUDO_FUNC_RELOC:
+ end = input_line_pointer;
+ if (*nextcharP != '(')
+ {
+ as_bad ("Expected '('");
+ break;
+ }
+ /* Skip '('. */
+ ++input_line_pointer;
+ expression (e);
+ if (*input_line_pointer != ')')
+ {
+ as_bad ("Missing ')'");
+ goto done;
+ }
+ /* Skip ')'. */
+ ++input_line_pointer;
+ if (e->X_op != O_symbol)
+ {
+ if (e->X_op != O_pseudo_fixup)
+ {
+ as_bad ("Not a symbolic expression");
+ goto done;
+ }
+ if (idx != FUNC_LT_RELATIVE)
+ {
+ as_bad ("Illegal combination of relocation functions");
+ goto done;
+ }
+ switch (S_GET_VALUE (e->X_op_symbol))
+ {
+ case FUNC_FPTR_RELATIVE:
+ idx = FUNC_LT_FPTR_RELATIVE; break;
+ case FUNC_DTP_MODULE:
+ idx = FUNC_LT_DTP_MODULE; break;
+ case FUNC_DTP_RELATIVE:
+ idx = FUNC_LT_DTP_RELATIVE; break;
+ case FUNC_TP_RELATIVE:
+ idx = FUNC_LT_TP_RELATIVE; break;
+ default:
+ as_bad ("Illegal combination of relocation functions");
+ goto done;
+ }
+ }
+ /* Make sure gas doesn't get rid of local symbols that are used
+ in relocs. */
+ e->X_op = O_pseudo_fixup;
+ e->X_op_symbol = pseudo_func[idx].u.sym;
+ done:
+ *nextcharP = *input_line_pointer;
+ break;
+
+ case PSEUDO_FUNC_CONST:
+ e->X_op = O_constant;
+ e->X_add_number = pseudo_func[idx].u.ival;
+ break;
+
+ case PSEUDO_FUNC_REG:
+ e->X_op = O_register;
+ e->X_add_number = pseudo_func[idx].u.ival;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+ }
+
/* first see if NAME is a known register name: */
sym = hash_find (md.reg_hash, name);
if (sym)
}
}
+ end = alloca (strlen (name) + 1);
+ strcpy (end, name);
+ name = ia64_canonicalize_symbol_name (end);
if ((dr = hash_find (md.dynreg_hash, name)))
{
/* We've got ourselves the name of a rotating register set.
ia64_canonicalize_symbol_name (name)
char *name;
{
- size_t len = strlen (name);
- if (len > 1 && name[len - 1] == '#')
- name[len - 1] = '\0';
+ size_t len = strlen (name), full = len;
+
+ while (len > 0 && name[len - 1] == '#')
+ --len;
+ if (len <= 0)
+ {
+ if (full > 0)
+ as_bad ("Standalone `#' is illegal");
+ else
+ as_bad ("Zero-length symbol is illegal");
+ }
+ else if (len < full - 1)
+ as_warn ("Redundant `#' suffix operators");
+ name[len] = '\0';
return name;
}
}
}
-/* Remove any mutexes which contain any of the PRs indicated in the mask.
-
- Any changes to a PR clears the mutex relations which include that PR. */
+/* MASK contains 2 and only 2 PRs which are mutually exclusive. Remove
+ any mutexes which contain one of the PRs and create new ones when
+ needed. */
-static void
-clear_qp_mutex (mask)
- valueT mask;
+static int
+update_qp_mutex (valueT mask)
{
int i;
+ int add = 0;
i = 0;
while (i < qp_mutexeslen)
{
if ((qp_mutexes[i].prmask & mask) != 0)
{
- if (md.debug_dv)
+ /* If it destroys and creates the same mutex, do nothing. */
+ if (qp_mutexes[i].prmask == mask
+ && qp_mutexes[i].path == md.path)
{
- fprintf (stderr, " Clearing mutex relation");
- print_prmask (qp_mutexes[i].prmask);
- fprintf (stderr, "\n");
+ i++;
+ add = -1;
}
- qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
- }
- else
- ++i;
- }
+ else
+ {
+ int keep = 0;
+
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Clearing mutex relation");
+ print_prmask (qp_mutexes[i].prmask);
+ fprintf (stderr, "\n");
+ }
+
+ /* Deal with the old mutex with more than 3+ PRs only if
+ the new mutex on the same execution path with it.
+
+ FIXME: The 3+ mutex support is incomplete.
+ dot_pred_rel () may be a better place to fix it. */
+ if (qp_mutexes[i].path == md.path)
+ {
+ /* If it is a proper subset of the mutex, create a
+ new mutex. */
+ if (add == 0
+ && (qp_mutexes[i].prmask & mask) == mask)
+ add = 1;
+
+ qp_mutexes[i].prmask &= ~mask;
+ if (qp_mutexes[i].prmask & (qp_mutexes[i].prmask - 1))
+ {
+ /* Modify the mutex if there are more than one
+ PR left. */
+ keep = 1;
+ i++;
+ }
+ }
+
+ if (keep == 0)
+ /* Remove the mutex. */
+ qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (add == 1)
+ add_qp_mutex (mask);
+
+ return add;
+}
+
+/* Remove any mutexes which contain any of the PRs indicated in the mask.
+
+ Any changes to a PR clears the mutex relations which include that PR. */
+
+static void
+clear_qp_mutex (mask)
+ valueT mask;
+{
+ int i;
+
+ i = 0;
+ while (i < qp_mutexeslen)
+ {
+ if ((qp_mutexes[i].prmask & mask) != 0)
+ {
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Clearing mutex relation");
+ print_prmask (qp_mutexes[i].prmask);
+ fprintf (stderr, "\n");
+ }
+ qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+ }
+ else
+ ++i;
+ }
}
/* Clear implies relations which contain PRs in the given masks.
else if (idesc->operands[i] == IA64_OPND_PR_ROT)
{
if (idesc->operands[1] & ((valueT) 1 << 43))
- qp_changemask = ~(valueT) 0xFFFFFFFFFFF | idesc->operands[1];
+ qp_changemask = -((valueT) 1 << 44) | idesc->operands[1];
else
qp_changemask = idesc->operands[1];
qp_changemask &= ~(valueT) 0xFFFF;
{
int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
- valueT p1mask = (valueT) 1 << p1;
- valueT p2mask = (valueT) 1 << p2;
+ valueT p1mask = (p1 != 0) ? (valueT) 1 << p1 : 0;
+ valueT p2mask = (p2 != 0) ? (valueT) 1 << p2 : 0;
- /* If one of the PRs is PR0, we can't really do anything. */
- if (p1 == 0 || p2 == 0)
+ /* If both PRs are PR0, we can't really do anything. */
+ if (p1 == 0 && p2 == 0)
{
if (md.debug_dv)
fprintf (stderr, " Ignoring PRs due to inclusion of p0\n");
else if (has_suffix_p (idesc->name, ".or.andcm")
|| has_suffix_p (idesc->name, ".and.orcm"))
{
- add_qp_mutex (p1mask | p2mask);
clear_qp_implies (p2mask, p1mask);
}
else if (has_suffix_p (idesc->name, ".andcm")
}
else
{
+ int added = 0;
+
clear_qp_implies (p1mask | p2mask, p1mask | p2mask);
- if (has_suffix_p (idesc->name, ".unc"))
+
+ /* If one of the PRs is PR0, we call clear_qp_mutex. */
+ if (p1 == 0 || p2 == 0)
+ clear_qp_mutex (p1mask | p2mask);
+ else
+ added = update_qp_mutex (p1mask | p2mask);
+
+ if (CURR_SLOT.qp_regno == 0
+ || has_suffix_p (idesc->name, ".unc"))
{
- add_qp_mutex (p1mask | p2mask);
+ if (added == 0 && p1 && p2)
+ add_qp_mutex (p1mask | p2mask);
if (CURR_SLOT.qp_regno != 0)
{
- add_qp_imply (CURR_SLOT.opnd[0].X_add_number - REG_P,
- CURR_SLOT.qp_regno);
- add_qp_imply (CURR_SLOT.opnd[1].X_add_number - REG_P,
- CURR_SLOT.qp_regno);
+ if (p1)
+ add_qp_imply (p1, CURR_SLOT.qp_regno);
+ if (p2)
+ add_qp_imply (p2, CURR_SLOT.qp_regno);
}
}
- else if (CURR_SLOT.qp_regno == 0)
- {
- add_qp_mutex (p1mask | p2mask);
- }
- else
- {
- clear_qp_mutex (p1mask | p2mask);
- }
}
}
/* Look for mov imm insns into GRs. */
else if (specs[count].index == rs->index)
return 1;
}
-#if 0
- if (md.debug_dv)
- fprintf (stderr, " No %s conflicts\n", rs->dependency->name);
-#endif
return 0;
}
insn_group_break (1, 0, 0);
if (rs->insn_srlz < STATE_SRLZ)
{
- int oldqp = CURR_SLOT.qp_regno;
- struct ia64_opcode *oldidesc = CURR_SLOT.idesc;
+ struct slot oldslot = CURR_SLOT;
/* Manually jam a srlz.i insn into the stream */
- CURR_SLOT.qp_regno = 0;
+ memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
CURR_SLOT.idesc = ia64_find_opcode ("srlz.i");
instruction_serialization ();
md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
if (++md.num_slots_in_use >= NUM_SLOTS)
emit_one_bundle ();
- CURR_SLOT.qp_regno = oldqp;
- CURR_SLOT.idesc = oldidesc;
+ CURR_SLOT = oldslot;
}
insn_group_break (1, 0, 0);
break;
if (rs->data_srlz < STATE_STOP)
insn_group_break (1, 0, 0);
{
- int oldqp = CURR_SLOT.qp_regno;
- struct ia64_opcode *oldidesc = CURR_SLOT.idesc;
+ struct slot oldslot = CURR_SLOT;
/* Manually jam a srlz.d insn into the stream */
- CURR_SLOT.qp_regno = 0;
+ memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
CURR_SLOT.idesc = ia64_find_opcode ("srlz.d");
data_serialization ();
md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
if (++md.num_slots_in_use >= NUM_SLOTS)
emit_one_bundle ();
- CURR_SLOT.qp_regno = oldqp;
- CURR_SLOT.idesc = oldidesc;
+ CURR_SLOT = oldslot;
}
break;
case IA64_DVS_IMPLIED:
count = specify_resource (dep, idesc, DV_REG, specs, note, md.path);
-#if 0
- if (md.debug_dv && !count)
- fprintf (stderr, " No %s %s usage found (path %d)\n",
- dv_mode[dep->mode], dep->name, md.path);
-#endif
-
while (count-- > 0)
{
mark_resource (idesc, dep, &specs[count],
mnemonic = "adds";
ia64_free_opcode (idesc);
idesc = ia64_find_opcode (mnemonic);
-#if 0
- know (!idesc->next);
-#endif
}
else if (strcmp (idesc->name, "mov") == 0)
{
rop = 1;
else
abort ();
- if (CURR_SLOT.opnd[rop].X_op == O_register
- && ar_is_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
- mnemonic = "mov.i";
+ if (CURR_SLOT.opnd[rop].X_op == O_register)
+ {
+ if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+ mnemonic = "mov.i";
+ else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
+ mnemonic = "mov.m";
+ else
+ rop = -1;
+ }
else
- mnemonic = "mov.m";
- ia64_free_opcode (idesc);
- idesc = ia64_find_opcode (mnemonic);
- while (idesc != NULL
- && (idesc->operands[0] != opnd1
- || idesc->operands[1] != opnd2))
- idesc = get_next_opcode (idesc);
+ abort ();
+ if (rop >= 0)
+ {
+ ia64_free_opcode (idesc);
+ idesc = ia64_find_opcode (mnemonic);
+ while (idesc != NULL
+ && (idesc->operands[0] != opnd1
+ || idesc->operands[1] != opnd2))
+ idesc = get_next_opcode (idesc);
+ }
+ }
+ }
+ else if (strcmp (idesc->name, "mov.i") == 0
+ || strcmp (idesc->name, "mov.m") == 0)
+ {
+ enum ia64_opnd opnd1, opnd2;
+ int rop;
+
+ opnd1 = idesc->operands[0];
+ opnd2 = idesc->operands[1];
+ if (opnd1 == IA64_OPND_AR3)
+ rop = 0;
+ else if (opnd2 == IA64_OPND_AR3)
+ rop = 1;
+ else
+ abort ();
+ if (CURR_SLOT.opnd[rop].X_op == O_register)
+ {
+ char unit = 'a';
+ if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+ unit = 'i';
+ else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
+ unit = 'm';
+ if (unit != 'a' && unit != idesc->name [4])
+ as_bad ("AR %d cannot be accessed by %c-unit",
+ (int) (CURR_SLOT.opnd[rop].X_add_number - REG_AR),
+ TOUPPER (unit));
}
}
flags = idesc->flags;
if ((flags & IA64_OPCODE_FIRST) != 0)
- insn_group_break (1, 0, 0);
+ {
+ /* The alignment frag has to end with a stop bit only if the
+ next instruction after the alignment directive has to be
+ the first instruction in an instruction group. */
+ if (align_frag)
+ {
+ while (align_frag->fr_type != rs_align_code)
+ {
+ align_frag = align_frag->fr_next;
+ if (!align_frag)
+ break;
+ }
+ /* align_frag can be NULL if there are directives in
+ between. */
+ if (align_frag && align_frag->fr_next == frag_now)
+ align_frag->tc_frag_data = 1;
+ }
+
+ insn_group_break (1, 0, 0);
+ }
+ align_frag = NULL;
if ((flags & IA64_OPCODE_NO_PRED) != 0 && qp_regno != 0)
{
CURR_SLOT.unwind_record = unwind.current_entry;
unwind.current_entry = NULL;
}
+ if (unwind.proc_start && S_IS_DEFINED (unwind.proc_start))
+ unwind.insn = 1;
/* Check for dependency violations. */
if (md.detect_dv)
md_operand (e)
expressionS *e;
{
- enum pseudo_type pseudo_type;
- const char *name;
- size_t len;
- int ch, i;
-
switch (*input_line_pointer)
{
- case '@':
- /* Find what relocation pseudo-function we're dealing with. */
- pseudo_type = 0;
- ch = *++input_line_pointer;
- for (i = 0; i < NELEMS (pseudo_func); ++i)
- if (pseudo_func[i].name && pseudo_func[i].name[0] == ch)
- {
- len = strlen (pseudo_func[i].name);
- if (strncmp (pseudo_func[i].name + 1,
- input_line_pointer + 1, len - 1) == 0
- && !is_part_of_name (input_line_pointer[len]))
- {
- input_line_pointer += len;
- pseudo_type = pseudo_func[i].type;
- break;
- }
- }
- switch (pseudo_type)
- {
- case PSEUDO_FUNC_RELOC:
- SKIP_WHITESPACE ();
- if (*input_line_pointer != '(')
- {
- as_bad ("Expected '('");
- goto err;
- }
- /* Skip '('. */
- ++input_line_pointer;
- expression (e);
- if (*input_line_pointer++ != ')')
- {
- as_bad ("Missing ')'");
- goto err;
- }
- if (e->X_op != O_symbol)
- {
- if (e->X_op != O_pseudo_fixup)
- {
- as_bad ("Not a symbolic expression");
- goto err;
- }
- if (S_GET_VALUE (e->X_op_symbol) == FUNC_FPTR_RELATIVE
- && i == FUNC_LT_RELATIVE)
- i = FUNC_LT_FPTR_RELATIVE;
- else
- {
- as_bad ("Illegal combination of relocation functions");
- goto err;
- }
- }
- /* Make sure gas doesn't get rid of local symbols that are used
- in relocs. */
- e->X_op = O_pseudo_fixup;
- e->X_op_symbol = pseudo_func[i].u.sym;
- break;
-
- case PSEUDO_FUNC_CONST:
- e->X_op = O_constant;
- e->X_add_number = pseudo_func[i].u.ival;
- break;
-
- case PSEUDO_FUNC_REG:
- e->X_op = O_register;
- e->X_add_number = pseudo_func[i].u.ival;
- break;
-
- default:
- name = input_line_pointer - 1;
- get_symbol_end ();
- as_bad ("Unknown pseudo function `%s'", name);
- goto err;
- }
- break;
-
case '[':
++input_line_pointer;
expression (e);
if (*input_line_pointer != ']')
{
- as_bad ("Closing bracket misssing");
+ as_bad ("Closing bracket missing");
goto err;
}
else
case BFD_RELOC_IA64_PLTOFF64I:
case BFD_RELOC_IA64_PLTOFF64MSB:
case BFD_RELOC_IA64_PLTOFF64LSB:
+
+ case BFD_RELOC_IA64_LTOFF22X:
+ case BFD_RELOC_IA64_LDXMOV:
return 1;
default:
- return 0;
+ break;
}
- return 0;
+
+ return generic_force_reloc (fix);
}
/* Decide from what point a pc-relative relocation is relative to,
return off;
}
+
+/* Used to emit section-relative relocs for the dwarf2 debug data. */
+void
+ia64_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+ expressionS expr;
+
+ expr.X_op = O_pseudo_fixup;
+ expr.X_op_symbol = pseudo_func[FUNC_SEC_RELATIVE].u.sym;
+ expr.X_add_number = 0;
+ expr.X_add_symbol = symbol;
+ emit_expr (&expr, size);
+}
+
/* This is called whenever some data item (not an instruction) needs a
fixup. We pick the right reloc code depending on the byteorder
currently in effect. */
code = BFD_RELOC_IA64_IPLTMSB;
else
code = BFD_RELOC_IA64_IPLTLSB;
-
exp->X_op = O_symbol;
break;
}
ignore_rest_of_line ();
return;
}
+
if (exp->X_op == O_pseudo_fixup)
{
- /* ??? */
exp->X_op = O_symbol;
code = ia64_gen_real_reloc_type (exp->X_op_symbol, code);
+ /* ??? If code unchanged, unsupported. */
}
fix = fix_new_exp (f, where, nbytes, exp, 0, code);
bfd_reloc_code_real_type r_type;
{
bfd_reloc_code_real_type new = 0;
+ const char *type = NULL, *suffix = "";
if (sym == NULL)
{
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_FPTR32LSB; break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_FPTR64MSB; break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_FPTR64LSB; break;
- default: break;
+ default: type = "FPTR"; break;
}
break;
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_GPREL32LSB; break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_GPREL64MSB; break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_GPREL64LSB; break;
- default: break;
+ default: type = "GPREL"; break;
}
break;
{
case BFD_RELOC_IA64_IMM22: new = BFD_RELOC_IA64_LTOFF22; break;
case BFD_RELOC_IA64_IMM64: new = BFD_RELOC_IA64_LTOFF64I; break;
- default: break;
+ default: type = "LTOFF"; break;
+ }
+ break;
+
+ case FUNC_LT_RELATIVE_X:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: new = BFD_RELOC_IA64_LTOFF22X; break;
+ default: type = "LTOFF"; suffix = "X"; break;
}
break;
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_PCREL32LSB; break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_PCREL64MSB; break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_PCREL64LSB; break;
- default: break;
+ default: type = "PCREL"; break;
}
break;
case BFD_RELOC_IA64_IMM64: new = BFD_RELOC_IA64_PLTOFF64I; break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_PLTOFF64MSB;break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_PLTOFF64LSB;break;
- default: break;
+ default: type = "PLTOFF"; break;
}
break;
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_SECREL32LSB;break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_SECREL64MSB;break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_SECREL64LSB;break;
- default: break;
+ default: type = "SECREL"; break;
}
break;
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_SEGREL32LSB;break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_SEGREL64MSB;break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_SEGREL64LSB;break;
- default: break;
+ default: type = "SEGREL"; break;
}
break;
case BFD_RELOC_IA64_DIR32LSB: new = BFD_RELOC_IA64_LTV32LSB; break;
case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_LTV64MSB; break;
case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_LTV64LSB; break;
- default: break;
+ default: type = "LTV"; break;
}
break;
new = BFD_RELOC_IA64_LTOFF_FPTR22; break;
case BFD_RELOC_IA64_IMM64:
new = BFD_RELOC_IA64_LTOFF_FPTR64I; break;
+ case BFD_RELOC_IA64_DIR32MSB:
+ new = BFD_RELOC_IA64_LTOFF_FPTR32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB:
+ new = BFD_RELOC_IA64_LTOFF_FPTR32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB:
+ new = BFD_RELOC_IA64_LTOFF_FPTR64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ new = BFD_RELOC_IA64_LTOFF_FPTR64LSB; break;
default:
- break;
+ type = "LTOFF_FPTR"; break;
+ }
+ break;
+
+ case FUNC_TP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM14: new = BFD_RELOC_IA64_TPREL14; break;
+ case BFD_RELOC_IA64_IMM22: new = BFD_RELOC_IA64_TPREL22; break;
+ case BFD_RELOC_IA64_IMM64: new = BFD_RELOC_IA64_TPREL64I; break;
+ case BFD_RELOC_IA64_DIR64MSB: new = BFD_RELOC_IA64_TPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: new = BFD_RELOC_IA64_TPREL64LSB; break;
+ default: type = "TPREL"; break;
+ }
+ break;
+
+ case FUNC_LT_TP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
+ default:
+ type = "LTOFF_TPREL"; break;
+ }
+ break;
+
+ case FUNC_DTP_MODULE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR64MSB:
+ new = BFD_RELOC_IA64_DTPMOD64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ new = BFD_RELOC_IA64_DTPMOD64LSB; break;
+ default:
+ type = "DTPMOD"; break;
+ }
+ break;
+
+ case FUNC_LT_DTP_MODULE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
+ default:
+ type = "LTOFF_DTPMOD"; break;
+ }
+ break;
+
+ case FUNC_DTP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB:
+ new = BFD_RELOC_IA64_DTPREL32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB:
+ new = BFD_RELOC_IA64_DTPREL32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB:
+ new = BFD_RELOC_IA64_DTPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ new = BFD_RELOC_IA64_DTPREL64LSB; break;
+ case BFD_RELOC_IA64_IMM14:
+ new = BFD_RELOC_IA64_DTPREL14; break;
+ case BFD_RELOC_IA64_IMM22:
+ new = BFD_RELOC_IA64_DTPREL22; break;
+ case BFD_RELOC_IA64_IMM64:
+ new = BFD_RELOC_IA64_DTPREL64I; break;
+ default:
+ type = "DTPREL"; break;
+ }
+ break;
+
+ case FUNC_LT_DTP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
+ default:
+ type = "LTOFF_DTPREL"; break;
}
break;
case FUNC_IPLT_RELOC:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IPLTMSB: return r_type;
+ case BFD_RELOC_IA64_IPLTLSB: return r_type;
+ default: type = "IPLT"; break;
+ }
break;
default:
abort ();
}
- /* Hmmmm. Should this ever occur? */
+
if (new)
return new;
else
- return r_type;
+ {
+ int width;
+
+ if (!type)
+ abort ();
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB: width = 32; suffix = "MSB"; break;
+ case BFD_RELOC_IA64_DIR32LSB: width = 32; suffix = "LSB"; break;
+ case BFD_RELOC_IA64_DIR64MSB: width = 64; suffix = "MSB"; break;
+ case BFD_RELOC_IA64_DIR64LSB: width = 64; suffix = "LSB"; break;
+ case BFD_RELOC_IA64_IMM14: width = 14; break;
+ case BFD_RELOC_IA64_IMM22: width = 22; break;
+ case BFD_RELOC_IA64_IMM64: width = 64; suffix = "I"; break;
+ default: abort ();
+ }
+
+ /* This should be an error, but since previously there wasn't any
+ diagnostic here, dont't make it fail because of this for now. */
+ as_warn ("Cannot express %s%d%s relocation", type, width, suffix);
+ return r_type;
+ }
}
/* Here is where generate the appropriate reloc for pseudo relocation
default:
break;
}
-
- return;
}
static void
{
char *fixpos;
valueT value = *valP;
- int adjust = 0;
fixpos = fix->fx_frag->fr_literal + fix->fx_where;
{
case BFD_RELOC_IA64_DIR32MSB:
fix->fx_r_type = BFD_RELOC_IA64_PCREL32MSB;
- adjust = 1;
break;
case BFD_RELOC_IA64_DIR32LSB:
fix->fx_r_type = BFD_RELOC_IA64_PCREL32LSB;
- adjust = 1;
break;
case BFD_RELOC_IA64_DIR64MSB:
fix->fx_r_type = BFD_RELOC_IA64_PCREL64MSB;
- adjust = 1;
break;
case BFD_RELOC_IA64_DIR64LSB:
fix->fx_r_type = BFD_RELOC_IA64_PCREL64LSB;
- adjust = 1;
break;
default:
}
if (fix->fx_addsy)
{
- if (fix->fx_r_type == (int) BFD_RELOC_UNUSED)
+ switch (fix->fx_r_type)
{
+ case BFD_RELOC_UNUSED:
/* 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,
elf64_ia64_operands[fix->tc_fix_data.opnd].desc);
fix->fx_done = 1;
return;
- }
- /* ??? This is a hack copied from tc-i386.c to make PCREL relocs
- work. There should be a better way to handle this. */
- if (adjust)
- fix->fx_offset += fix->fx_where + fix->fx_frag->fr_address;
+ case BFD_RELOC_IA64_TPREL14:
+ case BFD_RELOC_IA64_TPREL22:
+ case BFD_RELOC_IA64_TPREL64I:
+ case BFD_RELOC_IA64_LTOFF_TPREL22:
+ case BFD_RELOC_IA64_LTOFF_DTPMOD22:
+ case BFD_RELOC_IA64_DTPREL14:
+ case BFD_RELOC_IA64_DTPREL22:
+ case BFD_RELOC_IA64_DTPREL64I:
+ case BFD_RELOC_IA64_LTOFF_DTPREL22:
+ S_SET_THREAD_LOCAL (fix->fx_addsy);
+ break;
+
+ default:
+ break;
+ }
}
else if (fix->tc_fix_data.opnd == IA64_OPND_NIL)
{
int *size;
{
LITTLENUM_TYPE words[MAX_LITTLENUMS];
- LITTLENUM_TYPE *word;
char *t;
int prec;
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
- *size = prec * sizeof (LITTLENUM_TYPE);
- for (word = words + prec - 1; prec--;)
+ (*ia64_float_to_chars) (lit, words, prec);
+
+ if (type == 'X')
{
- md_number_to_chars (lit, (long) (*word--), sizeof (LITTLENUM_TYPE));
- lit += sizeof (LITTLENUM_TYPE);
+ /* It is 10 byte floating point with 6 byte padding. */
+ memset (&lit [10], 0, 6);
+ *size = 8 * sizeof (LITTLENUM_TYPE);
}
- return 0;
-}
-
-/* Round up a section's size to the appropriate boundary. */
-valueT
-md_section_align (seg, size)
- segT seg;
- valueT size;
-{
- int align = bfd_get_section_alignment (stdoutput, seg);
- valueT mask = ((valueT) 1 << align) - 1;
+ else
+ *size = prec * sizeof (LITTLENUM_TYPE);
- return (size + mask) & ~mask;
+ return 0;
}
/* Handle ia64 specific semantics of the align directive. */
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};
+ static const unsigned char le_nop_stop[]
+ = { 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
int bytes;
char *p;
+ const unsigned char *nop;
if (fragp->fr_type != rs_align_code)
return;
+ /* Check if this frag has to end with a stop bit. */
+ nop = fragp->tc_frag_data ? le_nop_stop : le_nop;
+
bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
p = fragp->fr_literal + fragp->fr_fix;
+ /* If no paddings are needed, we check if we need a stop bit. */
+ if (!bytes && fragp->tc_frag_data)
+ {
+ if (fragp->fr_fix < 16)
+#if 1
+ /* FIXME: It won't work with
+ .align 16
+ alloc r32=ar.pfs,1,2,4,0
+ */
+ ;
+#else
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("Can't add stop bit to mark end of instruction group"));
+#endif
+ else
+ /* Bundles are always in little-endian byte order. Make sure
+ the previous bundle has the stop bit. */
+ *(p - 16) |= 1;
+ }
+
/* Make sure we are on a 16-byte boundary, in case someone has been
putting data into a text section. */
if (bytes & 15)
fragp->fr_fix += fix;
}
- memcpy (p, (target_big_endian ? be_nop : le_nop), 16);
+ /* Instruction bundles are always little-endian. */
+ memcpy (p, nop, 16);
fragp->fr_var = 16;
}
+
+static void
+ia64_float_to_chars_bigendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_bigendian (lit, (long) (*words++),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+static void
+ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_littleendian (lit, (long) (words[prec]),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+void
+ia64_elf_section_change_hook (void)
+{
+ if (elf_section_type (now_seg) == SHT_IA_64_UNWIND
+ && elf_linked_to_section (now_seg) == NULL)
+ elf_linked_to_section (now_seg) = text_section;
+ dot_byteorder (-1);
+}
+
+/* Check if a label should be made global. */
+void
+ia64_check_label (symbolS *label)
+{
+ if (*input_line_pointer == ':')
+ {
+ S_SET_EXTERNAL (label);
+ input_line_pointer++;
+ }
+}
+
+/* Used to remember where .alias and .secalias directives are seen. We
+ will rename symbol and section names when we are about to output
+ the relocatable file. */
+struct alias
+{
+ char *file; /* The file where the directive is seen. */
+ unsigned int line; /* The line number the directive is at. */
+ const char *name; /* The orignale name of the symbol. */
+};
+
+/* Called for .alias and .secalias directives. If SECTION is 1, it is
+ .secalias. Otherwise, it is .alias. */
+static void
+dot_alias (int section)
+{
+ char *name, *alias;
+ char delim;
+ char *end_name;
+ int len;
+ const char *error_string;
+ struct alias *h;
+ const char *a;
+ struct hash_control *ahash, *nhash;
+ const char *kind;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = delim;
+
+ if (name == end_name)
+ {
+ as_bad (_("expected symbol name"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ *end_name = 0;
+ as_bad (_("expected comma after \"%s\""), name);
+ *end_name = delim;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++;
+ *end_name = 0;
+ ia64_canonicalize_symbol_name (name);
+
+ /* We call demand_copy_C_string to check if alias string is valid.
+ There should be a closing `"' and no `\0' in the string. */
+ alias = demand_copy_C_string (&len);
+ if (alias == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Make a copy of name string. */
+ len = strlen (name) + 1;
+ obstack_grow (¬es, name, len);
+ name = obstack_finish (¬es);
+
+ if (section)
+ {
+ kind = "section";
+ ahash = secalias_hash;
+ nhash = secalias_name_hash;
+ }
+ else
+ {
+ kind = "symbol";
+ ahash = alias_hash;
+ nhash = alias_name_hash;
+ }
+
+ /* Check if alias has been used before. */
+ h = (struct alias *) hash_find (ahash, alias);
+ if (h)
+ {
+ if (strcmp (h->name, name))
+ as_bad (_("`%s' is already the alias of %s `%s'"),
+ alias, kind, h->name);
+ goto out;
+ }
+
+ /* Check if name already has an alias. */
+ a = (const char *) hash_find (nhash, name);
+ if (a)
+ {
+ if (strcmp (a, alias))
+ as_bad (_("%s `%s' already has an alias `%s'"), kind, name, a);
+ goto out;
+ }
+
+ h = (struct alias *) xmalloc (sizeof (struct alias));
+ as_where (&h->file, &h->line);
+ h->name = name;
+
+ error_string = hash_jam (ahash, alias, (PTR) h);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s alias hash table failed: %s"),
+ alias, kind, error_string);
+ goto out;
+ }
+
+ error_string = hash_jam (nhash, name, (PTR) alias);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s name hash table failed: %s"),
+ alias, kind, error_string);
+out:
+ obstack_free (¬es, name);
+ obstack_free (¬es, alias);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* It renames the original symbol name to its alias. */
+static void
+do_alias (const char *alias, PTR value)
+{
+ struct alias *h = (struct alias *) value;
+ symbolS *sym = symbol_find (h->name);
+
+ if (sym == NULL)
+ as_warn_where (h->file, h->line,
+ _("symbol `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ else
+ S_SET_NAME (sym, (char *) alias);
+}
+
+/* Called from write_object_file. */
+void
+ia64_adjust_symtab (void)
+{
+ hash_traverse (alias_hash, do_alias);
+}
+
+/* It renames the original section name to its alias. */
+static void
+do_secalias (const char *alias, PTR value)
+{
+ struct alias *h = (struct alias *) value;
+ segT sec = bfd_get_section_by_name (stdoutput, h->name);
+
+ if (sec == NULL)
+ as_warn_where (h->file, h->line,
+ _("section `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ else
+ sec->name = alias;
+}
+
+/* Called from write_object_file. */
+void
+ia64_frob_file (void)
+{
+ hash_traverse (secalias_hash, do_secalias);
+}