/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
- Copyright 1998, 1999, 2000, 2001, 2002, 2003 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.
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
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
{".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. */
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 declarations: */
-static int ar_is_in_integer_unit PARAMS ((int regnum));
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));
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 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 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 void 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 (STREQ ("unwind"))
return SHT_IA_64_UNWIND;
- if (STREQ ("init_array"))
- return SHT_INIT_ARRAY;
-
- if (STREQ ("fini_array"))
- return SHT_FINI_ARRAY;
-
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;
return ptr;
}
+/* Dummy unwind record used for calculating the length of the last prologue or
+ body region. */
+
+static unw_rec_list *
+output_endp ()
+{
+ unw_rec_list *ptr = alloc_record (endp);
+ return ptr;
+}
+
static unw_rec_list *
output_prologue ()
{
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 (finalize_syms)
+ if (! before_relax)
{
- /* We can get the final addresses only after relaxation is
- done. */
+ /* 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
/* 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;
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;
/* Find either the next body/prologue start, or the end of
- the list, and determine the size of the region. */
- last_addr = list->next_slot_number;
- last_frag = list->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:
}
}
+/* Estimate the size of a frag before relaxing. We only have one type of frag
+ to handle here, which is the unwind info frag. */
+
+int
+ia64_estimate_size_before_relax (fragS *frag,
+ asection *segtype ATTRIBUTE_UNUSED)
+{
+ 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 + a pointer for the personality offset. */
+ size = len + 8 + 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);
+
+ return frag->fr_fix + 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
int len, size, pad;
valueT flag_value;
+ /* ??? This code is identical to ia64_estimate_size_before_relax. */
list = (unw_rec_list *) frag->fr_opcode;
- fixup_unw_records (list);
+ fixup_unw_records (list, 0);
len = calc_record_size (list);
/* pad to pointer-size boundary. */
/* Skip the header. */
vbyte_mem_ptr = frag->fr_literal + 8;
process_unw_records (list, output_vbyte_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);
+
frag->fr_fix += size;
frag->fr_type = rs_fill;
frag->fr_var = 0;
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)
add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
}
+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)
+{
+ /*
+ 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.
+ */
+
+ 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;
+
+ 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 = "";
+
+ /* 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;
+
+ 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';
+
+ /* 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 (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 (text_name)
- const char *text_name;
+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);
+ fixup_unw_records (list, 1);
size = calc_record_size (list);
if (size > 0 || unwind.force_unwind_entry)
/* If there are unwind records, switch sections, and output the info. */
if (size != 0)
{
- char *sec_name;
expressionS exp;
bfd_reloc_code_real_type reloc;
- 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);
+ 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. */
unwind.info = expr_build_dot ();
frag_var (rs_machine_dependent, size, size, 0, 0,
- (offsetT) unwind.personality_routine, (char *) list);
+ (offsetT) (long) unwind.personality_routine,
+ (char *) list);
/* Add the personality address to the image. */
if (unwind.personality_routine != 0)
unwind.personality_routine = 0;
}
}
+ else
+ start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO, 1);
free_saved_prologue_counts ();
unwind.list = unwind.tail = unwind.current_entry = NULL;
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 = "";
-
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 ();
}
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
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");
}
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)
{
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)
{
long where;
segT saved_seg;
subsegT saved_subseg;
- const char *sec_name, *text_name;
char *name, *p, c;
symbolS *sym;
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)
{
subseg_set (md.last_text_seg, 0);
unwind.proc_end = expr_build_dot ();
- make_unw_section_name (SPECIAL_SECTION_UNWIND, text_name, sec_name);
- set_section ((char *) sec_name);
- bfd_set_section_flags (stdoutput, now_seg,
- SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+ 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. */
bytes_per_address);
}
+ else
+ start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND, 1);
+
subseg_set (saved_seg, saved_subseg);
/* Parse names of main and alternate entry points and set symbol sizes. */
if (byteorder == -1)
{
if (seginfo->tc_segment_info_data.endian == 0)
- seginfo->tc_segment_info_data.endian
- = TARGET_BYTES_BIG_ENDIAN ? 1 : 2;
+ seginfo->tc_segment_info_data.endian = default_big_endian ? 1 : 2;
byteorder = seginfo->tc_segment_info_data.endian == 1;
}
else
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
{
{ "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 },
{ "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 },
{ "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' },
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)
{
continue; /* try next slot */
}
- {
- bfd_vma addr;
+ if (debug_type == DEBUG_DWARF2 || md.slot[curr].loc_directive_seen)
+ {
+ bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
- addr = frag_now->fr_address + frag_now_fix () - 16 + i;
- dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
- }
+ md.slot[curr].loc_directive_seen = 0;
+ dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
+ }
if (errata_nop_necessary_p (md.slot + curr, insn_unit))
as_warn (_("Additional NOP may be necessary to workaround Itanium processor A/B step errata"));
build_insn (md.slot + curr, insn + i);
- /* Set slot counts for non prologue/body unwind records. */
- for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
- if (ptr->r.type != prologue && ptr->r.type != prologue_gr
- && ptr->r.type != body)
- {
- ptr->slot_number = (unsigned long) f + i;
- ptr->slot_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)
{
if (manual_bundling)
{
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");
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
return 0;
/* Make sure function pointers get initialized. */
target_big_endian = -1;
- dot_byteorder (TARGET_BYTES_BIG_ENDIAN);
+ dot_byteorder (default_big_endian);
alias_hash = hash_new ();
alias_name_hash = hash_new ();
}
}
+#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 ()
{
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:
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
+ mnemonic = "mov.m";
+ }
else
- mnemonic = "mov.m";
+ abort ();
ia64_free_opcode (idesc);
idesc = ia64_find_opcode (mnemonic);
while (idesc != NULL
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));
+ }
+ }
qp_regno = 0;
if (md.qp.X_op == O_register)
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)
{
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)
}
/* Instruction bundles are always little-endian. */
- memcpy (p, le_nop, 16);
+ memcpy (p, nop, 16);
fragp->fr_var = 16;
}
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);
}