X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-ia64.c;h=d065449638a957061a8e96580dd27f40b3b15ec2;hb=97762d08db7baa887e466890451958465253d014;hp=647724bed6ca6d05e7f32b3dd17a4230ae7f5cbe;hpb=012a452b43b77eaf0f0f8a48192561702beea958;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c index 647724bed6..d065449638 100644 --- a/gas/config/tc-ia64.c +++ b/gas/config/tc-ia64.c @@ -1,5 +1,6 @@ /* 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 This file is part of GAS, the GNU Assembler. @@ -153,8 +154,12 @@ struct label_fix 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 @@ -236,7 +241,8 @@ static struct 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 @@ -571,11 +577,6 @@ static char special_section_name[][20] = {".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 @@ -636,6 +637,9 @@ static struct gr { 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. */ @@ -652,6 +656,8 @@ typedef struct unw_rec_list { 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; @@ -668,9 +674,6 @@ typedef struct label_prologue_count 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; @@ -696,13 +699,19 @@ static struct 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)); @@ -752,6 +761,7 @@ static void dot_xfloat_cons_ua 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)); @@ -823,7 +833,7 @@ static void output_X2_format PARAMS ((vbyte_func, int, int, int, int, int, unsig 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)); @@ -898,57 +908,33 @@ static void process_one_record PARAMS ((unw_rec_list *, vbyte_func)); 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 @@ -1017,12 +1003,6 @@ ia64_elf_section_type (str, len) 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 } @@ -1091,18 +1071,40 @@ ia64_flush_insns () 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; @@ -1112,9 +1114,8 @@ ia64_flush_insns () 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; @@ -1710,26 +1711,19 @@ alloc_record (unw_record_type t) 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 * @@ -1823,7 +1817,7 @@ output_rp_psprel (offset) 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; } @@ -1857,7 +1851,7 @@ output_pfs_psprel (offset) 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; } @@ -1891,7 +1885,7 @@ output_preds_psprel (offset) 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; } @@ -1968,7 +1962,7 @@ output_spill_base (offset) 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; } @@ -1993,7 +1987,7 @@ output_unat_psprel (offset) 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; } @@ -2027,7 +2021,7 @@ output_lc_psprel (offset) 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; } @@ -2061,7 +2055,7 @@ output_fpsr_psprel (offset) 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; } @@ -2102,7 +2096,7 @@ output_priunat_psprel (offset) 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; } @@ -2136,7 +2130,7 @@ output_bsp_psprel (offset) 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; } @@ -2170,7 +2164,7 @@ output_bspstore_psprel (offset) 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; } @@ -2204,7 +2198,7 @@ output_rnat_psprel (offset) 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; } @@ -2261,7 +2255,7 @@ output_spill_psprel (ab, reg, offset) 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; } @@ -2288,7 +2282,7 @@ output_spill_psprel_p (ab, reg, offset, predicate) 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; } @@ -2352,6 +2346,10 @@ process_one_record (ptr, f) 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: @@ -2594,29 +2592,18 @@ set_imask (region, regmask, t, type) } } -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; @@ -2631,6 +2618,46 @@ slot_index (slot_addr, slot_frag, first_addr, first_frag) { 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. */ @@ -2660,8 +2687,8 @@ optimize_unw_records (list) /* 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; @@ -2669,12 +2696,13 @@ optimize_unw_records (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; @@ -2685,7 +2713,7 @@ fixup_unw_records (list) 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: @@ -2693,60 +2721,24 @@ fixup_unw_records (list) 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. */ @@ -2756,7 +2748,13 @@ fixup_unw_records (list) 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: @@ -2846,29 +2844,63 @@ fixup_unw_records (list) } } -/* 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; + 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 +ia64_convert_frag (fragS *frag) +{ + unw_rec_list *list; + int len, size, pad; valueT flag_value; - /* pad to pointer-size boundry. */ - x = size % md.pointer_size; - if (x != 0) - extra = md.pointer_size - x; + /* ??? This code is identical to ia64_estimate_size_before_relax. */ + list = (unw_rec_list *) frag->fr_opcode; + fixup_unw_records (list, 0); - /* Add 8 for the header + a pointer for the - personality offset. */ - *mem = xmalloc (size + extra + 8 + md.pointer_size); + 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; - /* Clear the padding area and personality. */ - memset (*mem + 8 + size, 0, extra + 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. */ - if (unwind.personality_routine) + /* 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; @@ -2879,44 +2911,25 @@ setup_unwind_header (int size, unsigned char **mem) 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 @@ -3004,6 +3017,14 @@ convert_expr_to_xy_reg (e, xy, regp) 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; @@ -3021,6 +3042,17 @@ dot_radix (dummy) } } +/* 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) @@ -3278,34 +3310,162 @@ dot_restorereg_p (dummy) 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) +{ + /* + 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 (const segT text_seg) { - int size; - void *unw_rec; + 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. */ - size = output_unw_records (unwind.list, &unw_rec); - if (size % md.pointer_size != 0) - as_bad ("Unwind record is not a multiple of %d bytes.", md.pointer_size); + 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 + a pointer for the personality + offset. */ + size += 8 + md.pointer_size; + } /* 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; - 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. */ @@ -3314,15 +3474,10 @@ generate_unwind_image (text_name) /* Set expression which points to start of unwind descriptor area. */ unwind.info = expr_build_dot (); - - where = (unsigned char *) frag_more (size); - - /* Issue a label for this address, and keep track of it to put it - in the unwind section. */ - - /* Copy the information from the unwind record into this section. The - data is already in the correct byte order. */ - memcpy (where, unw_rec, size); + + 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) @@ -3351,25 +3506,17 @@ generate_unwind_image (text_name) 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 = ""; - unwind.force_unwind_entry = 1; /* Remember which segment we're in so we can switch back after .endp */ @@ -3379,7 +3526,7 @@ dot_handlerdata (dummy) /* 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 (); } @@ -3574,7 +3721,7 @@ dot_saveb (dummy) 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 @@ -3606,7 +3753,7 @@ dot_spill (dummy) 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"); @@ -3882,7 +4029,7 @@ dot_unwabi (dummy) } 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) { @@ -3977,7 +4124,7 @@ dot_prologue (dummy) 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) { @@ -4014,7 +4161,6 @@ dot_endp (dummy) long where; segT saved_seg; subsegT saved_subseg; - const char *sec_name, *text_name; char *name, *p, c; symbolS *sym; @@ -4030,64 +4176,18 @@ dot_endp (dummy) 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. */ @@ -4127,6 +4227,9 @@ dot_endp (dummy) 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. */ @@ -4335,8 +4438,7 @@ dot_byteorder (byteorder) 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 @@ -4584,6 +4686,23 @@ dot_reg_val (dummy) 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 @@ -4865,6 +4984,7 @@ const pseudo_typeS md_pseudo_table[] = { { "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 }, @@ -4925,7 +5045,7 @@ const pseudo_typeS md_pseudo_table[] = { "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 }, @@ -4966,6 +5086,8 @@ const pseudo_typeS md_pseudo_table[] = { "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' }, @@ -6032,13 +6154,14 @@ emit_one_bundle () 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); @@ -6070,24 +6193,53 @@ emit_one_bundle () 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) { @@ -6218,25 +6370,42 @@ emit_one_bundle () /* 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]); + opnd1 = idesc->operands[0]; + opnd2 = idesc->operands[1]; ia64_free_opcode (idesc); - md.slot[curr].idesc = idesc = ia64_find_opcode (mnemonic); + 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); + } #if 0 - know (!idesc->next); /* no resolved dynamic ops have collisions */ + else + /* no other resolved dynamic ops have collisions */ + know (!get_next_opcode (idesc)); #endif + md.slot[curr].idesc = idesc; } else { @@ -6280,27 +6449,33 @@ emit_one_bundle () 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) { @@ -6358,9 +6533,12 @@ emit_one_bundle () 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"); @@ -6373,8 +6551,11 @@ emit_one_bundle () 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 @@ -6400,10 +6581,12 @@ md_parse_option (c, arg) 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; @@ -6574,9 +6757,9 @@ md_begin () bfd_set_section_alignment (stdoutput, text_section, 4); - /* Make sure fucntion pointers get initialized. */ + /* 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 (); @@ -7115,6 +7298,23 @@ ia64_frob_label (sym) } } +#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 () { @@ -9424,17 +9624,15 @@ remove_marked_resource (rs) 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; @@ -9447,17 +9645,15 @@ remove_marked_resource (rs) 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: @@ -9888,17 +10084,53 @@ md_assemble (str) 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)); } } @@ -9912,7 +10144,27 @@ md_assemble (str) 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) { @@ -10486,8 +10738,6 @@ ia64_validate_fix (fix) default: break; } - - return; } static void @@ -10747,16 +10997,43 @@ ia64_handle_align (fragp) 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) @@ -10769,7 +11046,7 @@ ia64_handle_align (fragp) } /* Instruction bundles are always little-endian. */ - memcpy (p, le_nop, 16); + memcpy (p, nop, 16); fragp->fr_var = 16; } @@ -10800,6 +11077,9 @@ ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words, 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); }