blob: 0e1c578d468486695f38ea5edb4aacd83afc4028 [file] [log] [blame]
/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/*
TODO:
- optional operands
- directives:
.alias
.eb
.estate
.lb
.popsection
.previous
.psr
.pushsection
- labels are wrong if automatic alignment is introduced
(e.g., checkout the second real10 definition in test-data.s)
- DV-related stuff:
<reg>.safe_across_calls and any other DV-related directives I don't
have documentation for.
verify mod-sched-brs reads/writes are checked/marked (and other
notes)
*/
#include "as.h"
#include "dwarf2dbg.h"
#include "subsegs.h"
#include "opcode/ia64.h"
#include "elf/ia64.h"
#define NELEMS(a) ((int) (sizeof (a)/sizeof ((a)[0])))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define NUM_SLOTS 4
#define PREV_SLOT md.slot[(md.curr_slot + NUM_SLOTS - 1) % NUM_SLOTS]
#define CURR_SLOT md.slot[md.curr_slot]
#define O_pseudo_fixup (O_max + 1)
enum special_section
{
SPECIAL_SECTION_BSS = 0,
SPECIAL_SECTION_SBSS,
SPECIAL_SECTION_SDATA,
SPECIAL_SECTION_RODATA,
SPECIAL_SECTION_COMMENT,
SPECIAL_SECTION_UNWIND,
SPECIAL_SECTION_UNWIND_INFO
};
enum reloc_func
{
FUNC_FPTR_RELATIVE,
FUNC_GP_RELATIVE,
FUNC_LT_RELATIVE,
FUNC_PC_RELATIVE,
FUNC_PLT_RELATIVE,
FUNC_SEC_RELATIVE,
FUNC_SEG_RELATIVE,
FUNC_LTV_RELATIVE,
FUNC_LT_FPTR_RELATIVE,
};
enum reg_symbol
{
REG_GR = 0,
REG_FR = (REG_GR + 128),
REG_AR = (REG_FR + 128),
REG_CR = (REG_AR + 128),
REG_P = (REG_CR + 128),
REG_BR = (REG_P + 64),
REG_IP = (REG_BR + 8),
REG_CFM,
REG_PR,
REG_PR_ROT,
REG_PSR,
REG_PSR_L,
REG_PSR_UM,
/* The following are pseudo-registers for use by gas only. */
IND_CPUID,
IND_DBR,
IND_DTR,
IND_ITR,
IND_IBR,
IND_MEM,
IND_MSR,
IND_PKR,
IND_PMC,
IND_PMD,
IND_RR,
/* The following pseudo-registers are used for unwind directives only: */
REG_PSP,
REG_PRIUNAT,
REG_NUM
};
enum dynreg_type
{
DYNREG_GR = 0, /* dynamic general purpose register */
DYNREG_FR, /* dynamic floating point register */
DYNREG_PR, /* dynamic predicate register */
DYNREG_NUM_TYPES
};
enum operand_match_result
{
OPERAND_MATCH,
OPERAND_OUT_OF_RANGE,
OPERAND_MISMATCH
};
/* On the ia64, we can't know the address of a text label until the
instructions are packed into a bundle. To handle this, we keep
track of the list of labels that appear in front of each
instruction. */
struct label_fix
{
struct label_fix *next;
struct symbol *sym;
};
extern int target_big_endian;
/* Characters which always start a comment. */
const char comment_chars[] = "";
/* Characters which start a comment at the beginning of a line. */
const char line_comment_chars[] = "#";
/* Characters which may be used to separate multiple commands on a
single line. */
const char line_separator_chars[] = ";";
/* Characters which are used to indicate an exponent in a floating
point number. */
const char EXP_CHARS[] = "eE";
/* Characters which mean that a number is a floating point constant,
as in 0d1.0. */
const char FLT_CHARS[] = "rRsSfFdDxXpP";
/* ia64-specific option processing: */
const char *md_shortopts = "m:N:x::";
struct option md_longopts[] =
{
#define OPTION_MCONSTANT_GP (OPTION_MD_BASE + 1)
{"mconstant-gp", no_argument, NULL, OPTION_MCONSTANT_GP},
#define OPTION_MAUTO_PIC (OPTION_MD_BASE + 2)
{"mauto-pic", no_argument, NULL, OPTION_MAUTO_PIC}
};
size_t md_longopts_size = sizeof (md_longopts);
static struct
{
struct hash_control *pseudo_hash; /* pseudo opcode hash table */
struct hash_control *reg_hash; /* register name hash table */
struct hash_control *dynreg_hash; /* dynamic register hash table */
struct hash_control *const_hash; /* constant hash table */
struct hash_control *entry_hash; /* code entry hint hash table */
symbolS *regsym[REG_NUM];
/* If X_op is != O_absent, the registername for the instruction's
qualifying predicate. If NULL, p0 is assumed for instructions
that are predicatable. */
expressionS qp;
unsigned int
manual_bundling : 1,
debug_dv: 1,
detect_dv: 1,
explicit_mode : 1, /* which mode we're in */
default_explicit_mode : 1, /* which mode is the default */
mode_explicitly_set : 1, /* was the current mode explicitly set? */
auto_align : 1,
keep_pending_output : 1;
/* Each bundle consists of up to three instructions. We keep
track of four most recent instructions so we can correctly set
the end_of_insn_group for the last instruction in a bundle. */
int curr_slot;
int num_slots_in_use;
struct slot
{
unsigned int
end_of_insn_group : 1,
manual_bundling_on : 1,
manual_bundling_off : 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
can't use a "struct fix" instead since we can't call
fix_new_exp() until we know the address of the instruction. */
int num_fixups;
struct insn_fix
{
bfd_reloc_code_real_type code;
enum ia64_opnd opnd; /* type of operand in need of fix */
unsigned int is_pcrel : 1; /* is operand pc-relative? */
expressionS expr; /* the value to be inserted */
}
fixup[2]; /* at most two fixups per insn */
struct ia64_opcode *idesc;
struct label_fix *label_fixups;
struct label_fix *tag_fixups;
struct unw_rec_list *unwind_record; /* Unwind directive. */
expressionS opnd[6];
char *src_file;
unsigned int src_line;
struct dwarf2_line_info debug_line;
}
slot[NUM_SLOTS];
segT last_text_seg;
struct dynreg
{
struct dynreg *next; /* next dynamic register */
const char *name;
unsigned short base; /* the base register number */
unsigned short num_regs; /* # of registers in this set */
}
*dynreg[DYNREG_NUM_TYPES], in, loc, out, rot;
flagword flags; /* ELF-header flags */
struct mem_offset {
unsigned hint:1; /* is this hint currently valid? */
bfd_vma offset; /* mem.offset offset */
bfd_vma base; /* mem.offset base */
} mem_offset;
int path; /* number of alt. entry points seen */
const char **entry_labels; /* labels of all alternate paths in
the current DV-checking block. */
int maxpaths; /* size currently allocated for
entry_labels */
/* Support for hardware errata workarounds. */
/* Record data about the last three insn groups. */
struct group
{
/* B-step workaround.
For each predicate register, this is set if the corresponding insn
group conditionally sets this register with one of the affected
instructions. */
int p_reg_set[64];
/* B-step workaround.
For each general register, this is set if the corresponding insn
a) is conditional one one of the predicate registers for which
P_REG_SET is 1 in the corresponding entry of the previous group,
b) sets this general register with one of the affected
instructions. */
int g_reg_set_conditionally[128];
} last_groups[3];
int group_idx;
}
md;
/* application registers: */
#define AR_K0 0
#define AR_K7 7
#define AR_RSC 16
#define AR_BSP 17
#define AR_BSPSTORE 18
#define AR_RNAT 19
#define AR_UNAT 36
#define AR_FPSR 40
#define AR_ITC 44
#define AR_PFS 64
#define AR_LC 65
static const struct
{
const char *name;
int regnum;
}
ar[] =
{
{"ar.k0", 0}, {"ar.k1", 1}, {"ar.k2", 2}, {"ar.k3", 3},
{"ar.k4", 4}, {"ar.k5", 5}, {"ar.k6", 6}, {"ar.k7", 7},
{"ar.rsc", 16}, {"ar.bsp", 17},
{"ar.bspstore", 18}, {"ar.rnat", 19},
{"ar.fcr", 21}, {"ar.eflag", 24},
{"ar.csd", 25}, {"ar.ssd", 26},
{"ar.cflg", 27}, {"ar.fsr", 28},
{"ar.fir", 29}, {"ar.fdr", 30},
{"ar.ccv", 32}, {"ar.unat", 36},
{"ar.fpsr", 40}, {"ar.itc", 44},
{"ar.pfs", 64}, {"ar.lc", 65},
{"ar.ec", 66},
};
#define CR_IPSR 16
#define CR_ISR 17
#define CR_IIP 19
#define CR_IFA 20
#define CR_ITIR 21
#define CR_IIPA 22
#define CR_IFS 23
#define CR_IIM 24
#define CR_IHA 25
#define CR_IVR 65
#define CR_TPR 66
#define CR_EOI 67
#define CR_IRR0 68
#define CR_IRR3 71
#define CR_LRR0 80
#define CR_LRR1 81
/* control registers: */
static const struct
{
const char *name;
int regnum;
}
cr[] =
{
{"cr.dcr", 0},
{"cr.itm", 1},
{"cr.iva", 2},
{"cr.pta", 8},
{"cr.gpta", 9},
{"cr.ipsr", 16},
{"cr.isr", 17},
{"cr.iip", 19},
{"cr.ifa", 20},
{"cr.itir", 21},
{"cr.iipa", 22},
{"cr.ifs", 23},
{"cr.iim", 24},
{"cr.iha", 25},
{"cr.lid", 64},
{"cr.ivr", 65},
{"cr.tpr", 66},
{"cr.eoi", 67},
{"cr.irr0", 68},
{"cr.irr1", 69},
{"cr.irr2", 70},
{"cr.irr3", 71},
{"cr.itv", 72},
{"cr.pmv", 73},
{"cr.cmcv", 74},
{"cr.lrr0", 80},
{"cr.lrr1", 81}
};
#define PSR_MFL 4
#define PSR_IC 13
#define PSR_DFL 18
#define PSR_CPL 32
static const struct const_desc
{
const char *name;
valueT value;
}
const_bits[] =
{
/* PSR constant masks: */
/* 0: reserved */
{"psr.be", ((valueT) 1) << 1},
{"psr.up", ((valueT) 1) << 2},
{"psr.ac", ((valueT) 1) << 3},
{"psr.mfl", ((valueT) 1) << 4},
{"psr.mfh", ((valueT) 1) << 5},
/* 6-12: reserved */
{"psr.ic", ((valueT) 1) << 13},
{"psr.i", ((valueT) 1) << 14},
{"psr.pk", ((valueT) 1) << 15},
/* 16: reserved */
{"psr.dt", ((valueT) 1) << 17},
{"psr.dfl", ((valueT) 1) << 18},
{"psr.dfh", ((valueT) 1) << 19},
{"psr.sp", ((valueT) 1) << 20},
{"psr.pp", ((valueT) 1) << 21},
{"psr.di", ((valueT) 1) << 22},
{"psr.si", ((valueT) 1) << 23},
{"psr.db", ((valueT) 1) << 24},
{"psr.lp", ((valueT) 1) << 25},
{"psr.tb", ((valueT) 1) << 26},
{"psr.rt", ((valueT) 1) << 27},
/* 28-31: reserved */
/* 32-33: cpl (current privilege level) */
{"psr.is", ((valueT) 1) << 34},
{"psr.mc", ((valueT) 1) << 35},
{"psr.it", ((valueT) 1) << 36},
{"psr.id", ((valueT) 1) << 37},
{"psr.da", ((valueT) 1) << 38},
{"psr.dd", ((valueT) 1) << 39},
{"psr.ss", ((valueT) 1) << 40},
/* 41-42: ri (restart instruction) */
{"psr.ed", ((valueT) 1) << 43},
{"psr.bn", ((valueT) 1) << 44},
};
/* indirect register-sets/memory: */
static const struct
{
const char *name;
int regnum;
}
indirect_reg[] =
{
{ "CPUID", IND_CPUID },
{ "cpuid", IND_CPUID },
{ "dbr", IND_DBR },
{ "dtr", IND_DTR },
{ "itr", IND_ITR },
{ "ibr", IND_IBR },
{ "msr", IND_MSR },
{ "pkr", IND_PKR },
{ "pmc", IND_PMC },
{ "pmd", IND_PMD },
{ "rr", IND_RR },
};
/* Pseudo functions used to indicate relocation types (these functions
start with an at sign (@). */
static struct
{
const char *name;
enum pseudo_type
{
PSEUDO_FUNC_NONE,
PSEUDO_FUNC_RELOC,
PSEUDO_FUNC_CONST,
PSEUDO_FUNC_REG,
PSEUDO_FUNC_FLOAT
}
type;
union
{
unsigned long ival;
symbolS *sym;
}
u;
}
pseudo_func[] =
{
/* reloc pseudo functions (these must come first!): */
{ "fptr", PSEUDO_FUNC_RELOC, { 0 } },
{ "gprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
{ "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "pltoff", PSEUDO_FUNC_RELOC, { 0 } },
{ "secrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "segrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltv", PSEUDO_FUNC_RELOC, { 0 } },
{ "", 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
/* mbtype4 constants: */
{ "alt", PSEUDO_FUNC_CONST, { 0xa } },
{ "brcst", PSEUDO_FUNC_CONST, { 0x0 } },
{ "mix", PSEUDO_FUNC_CONST, { 0x8 } },
{ "rev", PSEUDO_FUNC_CONST, { 0xb } },
{ "shuf", PSEUDO_FUNC_CONST, { 0x9 } },
/* fclass constants: */
{ "nat", PSEUDO_FUNC_CONST, { 0x100 } },
{ "qnan", PSEUDO_FUNC_CONST, { 0x080 } },
{ "snan", PSEUDO_FUNC_CONST, { 0x040 } },
{ "pos", PSEUDO_FUNC_CONST, { 0x001 } },
{ "neg", PSEUDO_FUNC_CONST, { 0x002 } },
{ "zero", PSEUDO_FUNC_CONST, { 0x004 } },
{ "unorm", PSEUDO_FUNC_CONST, { 0x008 } },
{ "norm", PSEUDO_FUNC_CONST, { 0x010 } },
{ "inf", PSEUDO_FUNC_CONST, { 0x020 } },
{ "natval", PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */
/* unwind-related constants: */
{ "svr4", PSEUDO_FUNC_CONST, { 0 } },
{ "hpux", PSEUDO_FUNC_CONST, { 1 } },
{ "nt", PSEUDO_FUNC_CONST, { 2 } },
/* unwind-related registers: */
{ "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
};
/* 41-bit nop opcodes (one per unit): */
static const bfd_vma nop[IA64_NUM_UNITS] =
{
0x0000000000LL, /* NIL => break 0 */
0x0008000000LL, /* I-unit nop */
0x0008000000LL, /* M-unit nop */
0x4000000000LL, /* B-unit nop */
0x0008000000LL, /* F-unit nop */
0x0008000000LL, /* L-"unit" nop */
0x0008000000LL, /* X-unit nop */
};
/* Can't be `const' as it's passed to input routines (which have the
habit of setting temporary sentinels. */
static char special_section_name[][20] =
{
{".bss"}, {".sbss"}, {".sdata"}, {".rodata"}, {".comment"},
{".IA_64.unwind"}, {".IA_64.unwind_info"}
};
/* The best template for a particular sequence of up to three
instructions: */
#define N IA64_NUM_TYPES
static unsigned char best_template[N][N][N];
#undef N
/* Resource dependencies currently in effect */
static struct rsrc {
int depind; /* dependency index */
const struct ia64_dependency *dependency; /* actual dependency */
unsigned specific:1, /* is this a specific bit/regno? */
link_to_qp_branch:1; /* will a branch on the same QP clear it?*/
int index; /* specific regno/bit within dependency */
int note; /* optional qualifying note (0 if none) */
#define STATE_NONE 0
#define STATE_STOP 1
#define STATE_SRLZ 2
int insn_srlz; /* current insn serialization state */
int data_srlz; /* current data serialization state */
int qp_regno; /* qualifying predicate for this usage */
char *file; /* what file marked this dependency */
unsigned int line; /* what line marked this dependency */
struct mem_offset mem_offset; /* optional memory offset hint */
enum { CMP_NONE, CMP_OR, CMP_AND } cmp_type; /* OR or AND compare? */
int path; /* corresponding code entry index */
} *regdeps = NULL;
static int regdepslen = 0;
static int regdepstotlen = 0;
static const char *dv_mode[] = { "RAW", "WAW", "WAR" };
static const char *dv_sem[] = { "none", "implied", "impliedf",
"data", "instr", "specific", "stop", "other" };
static const char *dv_cmp_type[] = { "none", "OR", "AND" };
/* Current state of PR mutexation */
static struct qpmutex {
valueT prmask;
int path;
} *qp_mutexes = NULL; /* QP mutex bitmasks */
static int qp_mutexeslen = 0;
static int qp_mutexestotlen = 0;
static valueT qp_safe_across_calls = 0;
/* Current state of PR implications */
static struct qp_imply {
unsigned p1:6;
unsigned p2:6;
unsigned p2_branched:1;
int path;
} *qp_implies = NULL;
static int qp_implieslen = 0;
static int qp_impliestotlen = 0;
/* Keep track of static GR values so that indirect register usage can
sometimes be tracked. */
static struct gr {
unsigned known:1;
int path;
valueT value;
} gr_values[128] = {{ 1, 0, 0 }};
/* These are the routines required to output the various types of
unwind records. */
/* A slot_number is a frag address plus the slot index (0-2). We use the
frag address here so that if there is a section switch in the middle of
a function, then instructions emitted to a different section are not
counted. Since there may be more than one frag for a function, this
means we also need to keep track of which frag this address belongs to
so we can compute inter-frag distances. This also nicely solves the
problem with nops emitted for align directives, which can't easily be
counted, but can easily be derived from frag sizes. */
typedef struct unw_rec_list {
unwind_record r;
unsigned long slot_number;
fragS *slot_frag;
struct unw_rec_list *next;
} unw_rec_list;
#define SLOT_NUM_NOT_SET (unsigned)-1
static struct
{
unsigned long next_slot_number;
fragS *next_slot_frag;
/* Maintain a list of unwind entries for the current function. */
unw_rec_list *list;
unw_rec_list *tail;
/* Any unwind entires that should be attached to the current slot
that an insn is being constructed for. */
unw_rec_list *current_entry;
/* 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;
subsegT saved_text_subseg;
unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
/* TRUE if processing unwind directives in a prologue region. */
int prologue;
int prologue_mask;
unsigned int prologue_count; /* number of .prologues seen so far */
} unwind;
typedef void (*vbyte_func) PARAMS ((int, char *, char *));
/* Forward delarations: */
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_radix PARAMS ((int));
static void dot_special_section PARAMS ((int));
static void dot_proc PARAMS ((int));
static void dot_fframe PARAMS ((int));
static void dot_vframe PARAMS ((int));
static void dot_vframesp PARAMS ((int));
static void dot_vframepsp PARAMS ((int));
static void dot_save PARAMS ((int));
static void dot_restore PARAMS ((int));
static void dot_restorereg PARAMS ((int));
static void dot_restorereg_p PARAMS ((int));
static void dot_handlerdata PARAMS ((int));
static void dot_unwentry PARAMS ((int));
static void dot_altrp PARAMS ((int));
static void dot_savemem PARAMS ((int));
static void dot_saveg PARAMS ((int));
static void dot_savef PARAMS ((int));
static void dot_saveb PARAMS ((int));
static void dot_savegf PARAMS ((int));
static void dot_spill PARAMS ((int));
static void dot_spillreg PARAMS ((int));
static void dot_spillmem PARAMS ((int));
static void dot_spillreg_p PARAMS ((int));
static void dot_spillmem_p PARAMS ((int));
static void dot_label_state PARAMS ((int));
static void dot_copy_state PARAMS ((int));
static void dot_unwabi PARAMS ((int));
static void dot_personality PARAMS ((int));
static void dot_body PARAMS ((int));
static void dot_prologue PARAMS ((int));
static void dot_endp PARAMS ((int));
static void dot_template PARAMS ((int));
static void dot_regstk PARAMS ((int));
static void dot_rot PARAMS ((int));
static void dot_byteorder PARAMS ((int));
static void dot_psr PARAMS ((int));
static void dot_alias PARAMS ((int));
static void dot_ln PARAMS ((int));
static char *parse_section_name PARAMS ((void));
static void dot_xdata PARAMS ((int));
static void stmt_float_cons PARAMS ((int));
static void stmt_cons_ua PARAMS ((int));
static void dot_xfloat_cons PARAMS ((int));
static void dot_xstringer PARAMS ((int));
static void dot_xdata_ua PARAMS ((int));
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_dv_mode PARAMS ((int));
static void dot_entry PARAMS ((int));
static void dot_mem_offset PARAMS ((int));
static void add_unwind_entry PARAMS((unw_rec_list *ptr));
static symbolS *declare_register PARAMS ((const char *name, int regnum));
static void declare_register_set PARAMS ((const char *, int, int));
static unsigned int operand_width PARAMS ((enum ia64_opnd));
static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
int index,
expressionS *e));
static int parse_operand PARAMS ((expressionS *e));
static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
static void build_insn PARAMS ((struct slot *, bfd_vma *));
static void emit_one_bundle PARAMS ((void));
static void fix_insn PARAMS ((fixS *, const struct ia64_operand *, valueT));
static bfd_reloc_code_real_type ia64_gen_real_reloc_type PARAMS ((struct symbol *sym,
bfd_reloc_code_real_type r_type));
static void insn_group_break PARAMS ((int, int, int));
static void mark_resource PARAMS ((struct ia64_opcode *, const struct ia64_dependency *,
struct rsrc *, int depind, int path));
static void add_qp_mutex PARAMS((valueT mask));
static void add_qp_imply PARAMS((int p1, int p2));
static void clear_qp_branch_flag PARAMS((valueT mask));
static void clear_qp_mutex PARAMS((valueT mask));
static void clear_qp_implies PARAMS((valueT p1_mask, valueT p2_mask));
static void clear_register_values PARAMS ((void));
static void print_dependency PARAMS ((const char *action, int depind));
static void instruction_serialization PARAMS ((void));
static void data_serialization PARAMS ((void));
static void remove_marked_resource PARAMS ((struct rsrc *));
static int is_conditional_branch PARAMS ((struct ia64_opcode *));
static int is_taken_branch PARAMS ((struct ia64_opcode *));
static int is_interruption_or_rfi PARAMS ((struct ia64_opcode *));
static int depends_on PARAMS ((int, struct ia64_opcode *));
static int specify_resource PARAMS ((const struct ia64_dependency *,
struct ia64_opcode *, int, struct rsrc [], int, int));
static int check_dv PARAMS((struct ia64_opcode *idesc));
static void check_dependencies PARAMS((struct ia64_opcode *));
static void mark_resources PARAMS((struct ia64_opcode *));
static void update_dependencies PARAMS((struct ia64_opcode *));
static void note_register_values PARAMS((struct ia64_opcode *));
static int qp_mutex PARAMS ((int, int, int));
static int resources_match PARAMS ((struct rsrc *, struct ia64_opcode *, int, int, int));
static void output_vbyte_mem PARAMS ((int, char *, char *));
static void count_output PARAMS ((int, char *, char *));
static void output_R1_format PARAMS ((vbyte_func, unw_record_type, int));
static void output_R2_format PARAMS ((vbyte_func, int, int, unsigned long));
static void output_R3_format PARAMS ((vbyte_func, unw_record_type, unsigned long));
static void output_P1_format PARAMS ((vbyte_func, int));
static void output_P2_format PARAMS ((vbyte_func, int, int));
static void output_P3_format PARAMS ((vbyte_func, unw_record_type, int));
static void output_P4_format PARAMS ((vbyte_func, unsigned char *, unsigned long));
static void output_P5_format PARAMS ((vbyte_func, int, unsigned long));
static void output_P6_format PARAMS ((vbyte_func, unw_record_type, int));
static void output_P7_format PARAMS ((vbyte_func, unw_record_type, unsigned long, unsigned long));
static void output_P8_format PARAMS ((vbyte_func, unw_record_type, unsigned long));
static void output_P9_format PARAMS ((vbyte_func, int, int));
static void output_P10_format PARAMS ((vbyte_func, int, int));
static void output_B1_format PARAMS ((vbyte_func, unw_record_type, unsigned long));
static void output_B2_format PARAMS ((vbyte_func, unsigned long, unsigned long));
static void output_B3_format PARAMS ((vbyte_func, unsigned long, unsigned long));
static void output_B4_format PARAMS ((vbyte_func, unw_record_type, unsigned long));
static char format_ab_reg PARAMS ((int, int));
static void output_X1_format PARAMS ((vbyte_func, unw_record_type, int, int, unsigned long,
unsigned long));
static void output_X2_format PARAMS ((vbyte_func, int, int, int, int, int, unsigned long));
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_prologue PARAMS ((void));
static unw_rec_list *output_prologue_gr PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_body PARAMS ((void));
static unw_rec_list *output_mem_stack_f PARAMS ((unsigned int));
static unw_rec_list *output_mem_stack_v PARAMS ((void));
static unw_rec_list *output_psp_gr PARAMS ((unsigned int));
static unw_rec_list *output_psp_sprel PARAMS ((unsigned int));
static unw_rec_list *output_rp_when PARAMS ((void));
static unw_rec_list *output_rp_gr PARAMS ((unsigned int));
static unw_rec_list *output_rp_br PARAMS ((unsigned int));
static unw_rec_list *output_rp_psprel PARAMS ((unsigned int));
static unw_rec_list *output_rp_sprel PARAMS ((unsigned int));
static unw_rec_list *output_pfs_when PARAMS ((void));
static unw_rec_list *output_pfs_gr PARAMS ((unsigned int));
static unw_rec_list *output_pfs_psprel PARAMS ((unsigned int));
static unw_rec_list *output_pfs_sprel PARAMS ((unsigned int));
static unw_rec_list *output_preds_when PARAMS ((void));
static unw_rec_list *output_preds_gr PARAMS ((unsigned int));
static unw_rec_list *output_preds_psprel PARAMS ((unsigned int));
static unw_rec_list *output_preds_sprel PARAMS ((unsigned int));
static unw_rec_list *output_fr_mem PARAMS ((unsigned int));
static unw_rec_list *output_frgr_mem PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_gr_gr PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_gr_mem PARAMS ((unsigned int));
static unw_rec_list *output_br_mem PARAMS ((unsigned int));
static unw_rec_list *output_br_gr PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_spill_base PARAMS ((unsigned int));
static unw_rec_list *output_unat_when PARAMS ((void));
static unw_rec_list *output_unat_gr PARAMS ((unsigned int));
static unw_rec_list *output_unat_psprel PARAMS ((unsigned int));
static unw_rec_list *output_unat_sprel PARAMS ((unsigned int));
static unw_rec_list *output_lc_when PARAMS ((void));
static unw_rec_list *output_lc_gr PARAMS ((unsigned int));
static unw_rec_list *output_lc_psprel PARAMS ((unsigned int));
static unw_rec_list *output_lc_sprel PARAMS ((unsigned int));
static unw_rec_list *output_fpsr_when PARAMS ((void));
static unw_rec_list *output_fpsr_gr PARAMS ((unsigned int));
static unw_rec_list *output_fpsr_psprel PARAMS ((unsigned int));
static unw_rec_list *output_fpsr_sprel PARAMS ((unsigned int));
static unw_rec_list *output_priunat_when_gr PARAMS ((void));
static unw_rec_list *output_priunat_when_mem PARAMS ((void));
static unw_rec_list *output_priunat_gr PARAMS ((unsigned int));
static unw_rec_list *output_priunat_psprel PARAMS ((unsigned int));
static unw_rec_list *output_priunat_sprel PARAMS ((unsigned int));
static unw_rec_list *output_bsp_when PARAMS ((void));
static unw_rec_list *output_bsp_gr PARAMS ((unsigned int));
static unw_rec_list *output_bsp_psprel PARAMS ((unsigned int));
static unw_rec_list *output_bsp_sprel PARAMS ((unsigned int));
static unw_rec_list *output_bspstore_when PARAMS ((void));
static unw_rec_list *output_bspstore_gr PARAMS ((unsigned int));
static unw_rec_list *output_bspstore_psprel PARAMS ((unsigned int));
static unw_rec_list *output_bspstore_sprel PARAMS ((unsigned int));
static unw_rec_list *output_rnat_when PARAMS ((void));
static unw_rec_list *output_rnat_gr PARAMS ((unsigned int));
static unw_rec_list *output_rnat_psprel PARAMS ((unsigned int));
static unw_rec_list *output_rnat_sprel PARAMS ((unsigned int));
static unw_rec_list *output_unwabi PARAMS ((unsigned long, unsigned long));
static unw_rec_list *output_epilogue PARAMS ((unsigned long));
static unw_rec_list *output_label_state PARAMS ((unsigned long));
static unw_rec_list *output_copy_state PARAMS ((unsigned long));
static unw_rec_list *output_spill_psprel PARAMS ((unsigned int, unsigned int, unsigned int));
static unw_rec_list *output_spill_sprel PARAMS ((unsigned int, unsigned int, unsigned int));
static unw_rec_list *output_spill_psprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
unsigned int));
static unw_rec_list *output_spill_sprel_p PARAMS ((unsigned int, unsigned int, unsigned int,
unsigned int));
static unw_rec_list *output_spill_reg PARAMS ((unsigned int, unsigned int, unsigned int,
unsigned int));
static unw_rec_list *output_spill_reg_p PARAMS ((unsigned int, unsigned int, unsigned int,
unsigned int, unsigned int));
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 *));
static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
static void fixup_unw_records PARAMS ((unw_rec_list *));
static int output_unw_records PARAMS ((unw_rec_list *, void **));
static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
static int generate_unwind_image PARAMS ((const char *));
/* Build the unwind section name by appending the (possibly stripped)
text section NAME to the unwind PREFIX. The resulting string
pointer is assigned to RESULT. The string is allocated on the
stack, so this must be a macro... */
#define make_unw_section_name(special, text_name, result) \
{ \
char *_prefix = special_section_name[special]; \
size_t _prefix_len = strlen (_prefix), _text_len = strlen (text_name); \
char *_result = alloca (_prefix_len + _text_len + 1); \
memcpy(_result, _prefix, _prefix_len); \
memcpy(_result + _prefix_len, text_name, _text_len); \
_result[_prefix_len + _text_len] = '\0'; \
result = _result; \
} \
while (0)
/* Determine if application register REGNUM resides in the integer
unit (as opposed to the memory unit). */
static int
ar_is_in_integer_unit (reg)
int reg;
{
reg -= REG_AR;
return (reg == 64 /* pfs */
|| reg == 65 /* lc */
|| reg == 66 /* ec */
/* ??? ias accepts and puts these in the integer unit. */
|| (reg >= 112 && reg <= 127));
}
/* Switch to section NAME and create section if necessary. It's
rather ugly that we have to manipulate input_line_pointer but I
don't see any other way to accomplish the same thing without
changing obj-elf.c (which may be the Right Thing, in the end). */
static void
set_section (name)
char *name;
{
char *saved_input_line_pointer;
saved_input_line_pointer = input_line_pointer;
input_line_pointer = name;
obj_elf_section (0);
input_line_pointer = saved_input_line_pointer;
}
/* Map SHF_IA_64_SHORT to SEC_SMALL_DATA. */
flagword
ia64_elf_section_flags (flags, attr, type)
flagword flags;
int attr, type ATTRIBUTE_UNUSED;
{
if (attr & SHF_IA_64_SHORT)
flags |= SEC_SMALL_DATA;
return flags;
}
int
ia64_elf_section_type (str, len)
const char *str;
size_t len;
{
len = sizeof (ELF_STRING_ia64_unwind_info) - 1;
if (strncmp (str, ELF_STRING_ia64_unwind_info, len) == 0)
return SHT_PROGBITS;
len = sizeof (ELF_STRING_ia64_unwind) - 1;
if (strncmp (str, ELF_STRING_ia64_unwind, len) == 0)
return SHT_IA_64_UNWIND;
return -1;
}
static unsigned int
set_regstack (ins, locs, outs, rots)
unsigned int ins, locs, outs, rots;
{
/* Size of frame. */
unsigned int sof;
sof = ins + locs + outs;
if (sof > 96)
{
as_bad ("Size of frame exceeds maximum of 96 registers");
return 0;
}
if (rots > sof)
{
as_warn ("Size of rotating registers exceeds frame size");
return 0;
}
md.in.base = REG_GR + 32;
md.loc.base = md.in.base + ins;
md.out.base = md.loc.base + locs;
md.in.num_regs = ins;
md.loc.num_regs = locs;
md.out.num_regs = outs;
md.rot.num_regs = rots;
return sof;
}
void
ia64_flush_insns ()
{
struct label_fix *lfix;
segT saved_seg;
subsegT saved_subseg;
unw_rec_list *ptr;
if (!md.last_text_seg)
return;
saved_seg = now_seg;
saved_subseg = now_subseg;
subseg_set (md.last_text_seg, 0);
while (md.num_slots_in_use > 0)
emit_one_bundle (); /* force out queued instructions */
/* In case there are labels following the last instruction, resolve
those now: */
for (lfix = CURR_SLOT.label_fixups; lfix; lfix = lfix->next)
{
S_SET_VALUE (lfix->sym, frag_now_fix ());
symbol_set_frag (lfix->sym, frag_now);
}
CURR_SLOT.label_fixups = 0;
for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
{
S_SET_VALUE (lfix->sym, frag_now_fix ());
symbol_set_frag (lfix->sym, frag_now);
}
CURR_SLOT.tag_fixups = 0;
/* In case there are unwind directives following the last instruction,
resolve those now. We only handle body and prologue directives here.
Give an error for others. */
for (ptr = unwind.current_entry; ptr; ptr = ptr->next)
{
if (ptr->r.type == prologue || ptr->r.type == prologue_gr
|| ptr->r.type == body)
{
ptr->slot_number = (unsigned long) frag_more (0);
ptr->slot_frag = frag_now;
}
else
as_bad (_("Unwind directive not followed by an instruction."));
}
unwind.current_entry = NULL;
subseg_set (saved_seg, saved_subseg);
if (md.qp.X_op == O_register)
as_bad ("qualifying predicate not followed by instruction");
}
void
ia64_do_align (nbytes)
int nbytes;
{
char *saved_input_line_pointer = input_line_pointer;
input_line_pointer = "";
s_align_bytes (nbytes);
input_line_pointer = saved_input_line_pointer;
}
void
ia64_cons_align (nbytes)
int nbytes;
{
if (md.auto_align)
{
char *saved_input_line_pointer = input_line_pointer;
input_line_pointer = "";
s_align_bytes (nbytes);
input_line_pointer = saved_input_line_pointer;
}
}
/* Output COUNT bytes to a memory location. */
static unsigned char *vbyte_mem_ptr = NULL;
void
output_vbyte_mem (count, ptr, comment)
int count;
char *ptr;
char *comment ATTRIBUTE_UNUSED;
{
int x;
if (vbyte_mem_ptr == NULL)
abort ();
if (count == 0)
return;
for (x = 0; x < count; x++)
*(vbyte_mem_ptr++) = ptr[x];
}
/* Count the number of bytes required for records. */
static int vbyte_count = 0;
void
count_output (count, ptr, comment)
int count;
char *ptr ATTRIBUTE_UNUSED;
char *comment ATTRIBUTE_UNUSED;
{
vbyte_count += count;
}
static void
output_R1_format (f, rtype, rlen)
vbyte_func f;
unw_record_type rtype;
int rlen;
{
int r = 0;
char byte;
if (rlen > 0x1f)
{
output_R3_format (f, rtype, rlen);
return;
}
if (rtype == body)
r = 1;
else if (rtype != prologue)
as_bad ("record type is not valid");
byte = UNW_R1 | (r << 5) | (rlen & 0x1f);
(*f) (1, &byte, NULL);
}
static void
output_R2_format (f, mask, grsave, rlen)
vbyte_func f;
int mask, grsave;
unsigned long rlen;
{
char bytes[20];
int count = 2;
mask = (mask & 0x0f);
grsave = (grsave & 0x7f);
bytes[0] = (UNW_R2 | (mask >> 1));
bytes[1] = (((mask & 0x01) << 7) | grsave);
count += output_leb128 (bytes + 2, rlen, 0);
(*f) (count, bytes, NULL);
}
static void
output_R3_format (f, rtype, rlen)
vbyte_func f;
unw_record_type rtype;
unsigned long rlen;
{
int r = 0, count;
char bytes[20];
if (rlen <= 0x1f)
{
output_R1_format (f, rtype, rlen);
return;
}
if (rtype == body)
r = 1;
else if (rtype != prologue)
as_bad ("record type is not valid");
bytes[0] = (UNW_R3 | r);
count = output_leb128 (bytes + 1, rlen, 0);
(*f) (count + 1, bytes, NULL);
}
static void
output_P1_format (f, brmask)
vbyte_func f;
int brmask;
{
char byte;
byte = UNW_P1 | (brmask & 0x1f);
(*f) (1, &byte, NULL);
}
static void
output_P2_format (f, brmask, gr)
vbyte_func f;
int brmask;
int gr;
{
char bytes[2];
brmask = (brmask & 0x1f);
bytes[0] = UNW_P2 | (brmask >> 1);
bytes[1] = (((brmask & 1) << 7) | gr);
(*f) (2, bytes, NULL);
}
static void
output_P3_format (f, rtype, reg)
vbyte_func f;
unw_record_type rtype;
int reg;
{
char bytes[2];
int r = 0;
reg = (reg & 0x7f);
switch (rtype)
{
case psp_gr:
r = 0;
break;
case rp_gr:
r = 1;
break;
case pfs_gr:
r = 2;
break;
case preds_gr:
r = 3;
break;
case unat_gr:
r = 4;
break;
case lc_gr:
r = 5;
break;
case rp_br:
r = 6;
break;
case rnat_gr:
r = 7;
break;
case bsp_gr:
r = 8;
break;
case bspstore_gr:
r = 9;
break;
case fpsr_gr:
r = 10;
break;
case priunat_gr:
r = 11;
break;
default:
as_bad ("Invalid record type for P3 format.");
}
bytes[0] = (UNW_P3 | (r >> 1));
bytes[1] = (((r & 1) << 7) | reg);
(*f) (2, bytes, NULL);
}
static void
output_P4_format (f, imask, imask_size)
vbyte_func f;
unsigned char *imask;
unsigned long imask_size;
{
imask[0] = UNW_P4;
(*f) (imask_size, imask, NULL);
}
static void
output_P5_format (f, grmask, frmask)
vbyte_func f;
int grmask;
unsigned long frmask;
{
char bytes[4];
grmask = (grmask & 0x0f);
bytes[0] = UNW_P5;
bytes[1] = ((grmask << 4) | ((frmask & 0x000f0000) >> 16));
bytes[2] = ((frmask & 0x0000ff00) >> 8);
bytes[3] = (frmask & 0x000000ff);
(*f) (4, bytes, NULL);
}
static void
output_P6_format (f, rtype, rmask)
vbyte_func f;
unw_record_type rtype;
int rmask;
{
char byte;
int r = 0;
if (rtype == gr_mem)
r = 1;
else if (rtype != fr_mem)
as_bad ("Invalid record type for format P6");
byte = (UNW_P6 | (r << 4) | (rmask & 0x0f));
(*f) (1, &byte, NULL);
}
static void
output_P7_format (f, rtype, w1, w2)
vbyte_func f;
unw_record_type rtype;
unsigned long w1;
unsigned long w2;
{
char bytes[20];
int count = 1;
int r = 0;
count += output_leb128 (bytes + 1, w1, 0);
switch (rtype)
{
case mem_stack_f:
r = 0;
count += output_leb128 (bytes + count, w2 >> 4, 0);
break;
case mem_stack_v:
r = 1;
break;
case spill_base:
r = 2;
break;
case psp_sprel:
r = 3;
break;
case rp_when:
r = 4;
break;
case rp_psprel:
r = 5;
break;
case pfs_when:
r = 6;
break;
case pfs_psprel:
r = 7;
break;
case preds_when:
r = 8;
break;
case preds_psprel:
r = 9;
break;
case lc_when:
r = 10;
break;
case lc_psprel:
r = 11;
break;
case unat_when:
r = 12;
break;
case unat_psprel:
r = 13;
break;
case fpsr_when:
r = 14;
break;
case fpsr_psprel:
r = 15;
break;
default:
break;
}
bytes[0] = (UNW_P7 | r);
(*f) (count, bytes, NULL);
}
static void
output_P8_format (f, rtype, t)
vbyte_func f;
unw_record_type rtype;
unsigned long t;
{
char bytes[20];
int r = 0;
int count = 2;
bytes[0] = UNW_P8;
switch (rtype)
{
case rp_sprel:
r = 1;
break;
case pfs_sprel:
r = 2;
break;
case preds_sprel:
r = 3;
break;
case lc_sprel:
r = 4;
break;
case unat_sprel:
r = 5;
break;
case fpsr_sprel:
r = 6;
break;
case bsp_when:
r = 7;
break;
case bsp_psprel:
r = 8;
break;
case bsp_sprel:
r = 9;
break;
case bspstore_when:
r = 10;
break;
case bspstore_psprel:
r = 11;
break;
case bspstore_sprel:
r = 12;
break;
case rnat_when:
r = 13;
break;
case rnat_psprel:
r = 14;
break;
case rnat_sprel:
r = 15;
break;
case priunat_when_gr:
r = 16;
break;
case priunat_psprel:
r = 17;
break;
case priunat_sprel:
r = 18;
break;
case priunat_when_mem:
r = 19;
break;
default:
break;
}
bytes[1] = r;
count += output_leb128 (bytes + 2, t, 0);
(*f) (count, bytes, NULL);
}
static void
output_P9_format (f, grmask, gr)
vbyte_func f;
int grmask;
int gr;
{
char bytes[3];
bytes[0] = UNW_P9;
bytes[1] = (grmask & 0x0f);
bytes[2] = (gr & 0x7f);
(*f) (3, bytes, NULL);
}
static void
output_P10_format (f, abi, context)
vbyte_func f;
int abi;
int context;
{
char bytes[3];
bytes[0] = UNW_P10;
bytes[1] = (abi & 0xff);
bytes[2] = (context & 0xff);
(*f) (3, bytes, NULL);
}
static void
output_B1_format (f, rtype, label)
vbyte_func f;
unw_record_type rtype;
unsigned long label;
{
char byte;
int r = 0;
if (label > 0x1f)
{
output_B4_format (f, rtype, label);
return;
}
if (rtype == copy_state)
r = 1;
else if (rtype != label_state)
as_bad ("Invalid record type for format B1");
byte = (UNW_B1 | (r << 5) | (label & 0x1f));
(*f) (1, &byte, NULL);
}
static void
output_B2_format (f, ecount, t)
vbyte_func f;
unsigned long ecount;
unsigned long t;
{
char bytes[20];
int count = 1;
if (ecount > 0x1f)
{
output_B3_format (f, ecount, t);
return;
}
bytes[0] = (UNW_B2 | (ecount & 0x1f));
count += output_leb128 (bytes + 1, t, 0);
(*f) (count, bytes, NULL);
}
static void
output_B3_format (f, ecount, t)
vbyte_func f;
unsigned long ecount;
unsigned long t;
{
char bytes[20];
int count = 1;
if (ecount <= 0x1f)
{
output_B2_format (f, ecount, t);
return;
}
bytes[0] = UNW_B3;
count += output_leb128 (bytes + 1, t, 0);
count += output_leb128 (bytes + count, ecount, 0);
(*f) (count, bytes, NULL);
}
static void
output_B4_format (f, rtype, label)
vbyte_func f;
unw_record_type rtype;
unsigned long label;
{
char bytes[20];
int r = 0;
int count = 1;
if (label <= 0x1f)
{
output_B1_format (f, rtype, label);
return;
}
if (rtype == copy_state)
r = 1;
else if (rtype != label_state)
as_bad ("Invalid record type for format B1");
bytes[0] = (UNW_B4 | (r << 3));
count += output_leb128 (bytes + 1, label, 0);
(*f) (count, bytes, NULL);
}
static char
format_ab_reg (ab, reg)
int ab;
int reg;
{
int ret;
ab = (ab & 3);
reg = (reg & 0x1f);
ret = (ab << 5) | reg;
return ret;
}
static void
output_X1_format (f, rtype, ab, reg, t, w1)
vbyte_func f;
unw_record_type rtype;
int ab, reg;
unsigned long t;
unsigned long w1;
{
char bytes[20];
int r = 0;
int count = 2;
bytes[0] = UNW_X1;
if (rtype == spill_sprel)
r = 1;
else if (rtype != spill_psprel)
as_bad ("Invalid record type for format X1");
bytes[1] = ((r << 7) | format_ab_reg (ab, reg));
count += output_leb128 (bytes + 2, t, 0);
count += output_leb128 (bytes + count, w1, 0);
(*f) (count, bytes, NULL);
}
static void
output_X2_format (f, ab, reg, x, y, treg, t)
vbyte_func f;
int ab, reg;
int x, y, treg;
unsigned long t;
{
char bytes[20];
int count = 3;
bytes[0] = UNW_X2;
bytes[1] = (((x & 1) << 7) | format_ab_reg (ab, reg));
bytes[2] = (((y & 1) << 7) | (treg & 0x7f));
count += output_leb128 (bytes + 3, t, 0);
(*f) (count, bytes, NULL);
}
static void
output_X3_format (f, rtype, qp, ab, reg, t, w1)
vbyte_func f;
unw_record_type rtype;
int qp;
int ab, reg;
unsigned long t;
unsigned long w1;
{
char bytes[20];
int r = 0;
int count = 3;
bytes[0] = UNW_X3;
if (rtype == spill_sprel_p)
r = 1;
else if (rtype != spill_psprel_p)
as_bad ("Invalid record type for format X3");
bytes[1] = ((r << 7) | (qp & 0x3f));
bytes[2] = format_ab_reg (ab, reg);
count += output_leb128 (bytes + 3, t, 0);
count += output_leb128 (bytes + count, w1, 0);
(*f) (count, bytes, NULL);
}
static void
output_X4_format (f, qp, ab, reg, x, y, treg, t)
vbyte_func f;
int qp;
int ab, reg;
int x, y, treg;
unsigned long t;
{
char bytes[20];
int count = 4;
bytes[0] = UNW_X4;
bytes[1] = (qp & 0x3f);
bytes[2] = (((x & 1) << 7) | format_ab_reg (ab, reg));
bytes[3] = (((y & 1) << 7) | (treg & 0x7f));
count += output_leb128 (bytes + 4, t, 0);
(*f) (count, bytes, NULL);
}
/* This function allocates a record list structure, and initializes fields. */
static unw_rec_list *
alloc_record (unw_record_type t)
{
unw_rec_list *ptr;
ptr = xmalloc (sizeof (*ptr));
ptr->next = NULL;
ptr->slot_number = SLOT_NUM_NOT_SET;
ptr->r.type = t;
return ptr;
}
/* This function frees an entire list of record structures. */
void
free_list_records (unw_rec_list *first)
{
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);
}
}
static unw_rec_list *
output_prologue ()
{
unw_rec_list *ptr = alloc_record (prologue);
memset (&ptr->r.record.r.mask, 0, sizeof (ptr->r.record.r.mask));
return ptr;
}
static unw_rec_list *
output_prologue_gr (saved_mask, reg)
unsigned int saved_mask;
unsigned int reg;
{
unw_rec_list *ptr = alloc_record (prologue_gr);
memset (&ptr->r.record.r.mask, 0, sizeof (ptr->r.record.r.mask));
ptr->r.record.r.grmask = saved_mask;
ptr->r.record.r.grsave = reg;
return ptr;
}
static unw_rec_list *
output_body ()
{
unw_rec_list *ptr = alloc_record (body);
return ptr;
}
static unw_rec_list *
output_mem_stack_f (size)
unsigned int size;
{
unw_rec_list *ptr = alloc_record (mem_stack_f);
ptr->r.record.p.size = size;
return ptr;
}
static unw_rec_list *
output_mem_stack_v ()
{
unw_rec_list *ptr = alloc_record (mem_stack_v);
return ptr;
}
static unw_rec_list *
output_psp_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (psp_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_psp_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (psp_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_rp_when ()
{
unw_rec_list *ptr = alloc_record (rp_when);
return ptr;
}
static unw_rec_list *
output_rp_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (rp_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_rp_br (br)
unsigned int br;
{
unw_rec_list *ptr = alloc_record (rp_br);
ptr->r.record.p.br = br;
return ptr;
}
static unw_rec_list *
output_rp_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rp_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_rp_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rp_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_pfs_when ()
{
unw_rec_list *ptr = alloc_record (pfs_when);
return ptr;
}
static unw_rec_list *
output_pfs_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (pfs_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_pfs_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (pfs_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_pfs_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (pfs_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_preds_when ()
{
unw_rec_list *ptr = alloc_record (preds_when);
return ptr;
}
static unw_rec_list *
output_preds_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (preds_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_preds_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (preds_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_preds_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (preds_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_fr_mem (mask)
unsigned int mask;
{
unw_rec_list *ptr = alloc_record (fr_mem);
ptr->r.record.p.rmask = mask;
return ptr;
}
static unw_rec_list *
output_frgr_mem (gr_mask, fr_mask)
unsigned int gr_mask;
unsigned int fr_mask;
{
unw_rec_list *ptr = alloc_record (frgr_mem);
ptr->r.record.p.grmask = gr_mask;
ptr->r.record.p.frmask = fr_mask;
return ptr;
}
static unw_rec_list *
output_gr_gr (mask, reg)
unsigned int mask;
unsigned int reg;
{
unw_rec_list *ptr = alloc_record (gr_gr);
ptr->r.record.p.grmask = mask;
ptr->r.record.p.gr = reg;
return ptr;
}
static unw_rec_list *
output_gr_mem (mask)
unsigned int mask;
{
unw_rec_list *ptr = alloc_record (gr_mem);
ptr->r.record.p.rmask = mask;
return ptr;
}
static unw_rec_list *
output_br_mem (unsigned int mask)
{
unw_rec_list *ptr = alloc_record (br_mem);
ptr->r.record.p.brmask = mask;
return ptr;
}
static unw_rec_list *
output_br_gr (save_mask, reg)
unsigned int save_mask;
unsigned int reg;
{
unw_rec_list *ptr = alloc_record (br_gr);
ptr->r.record.p.brmask = save_mask;
ptr->r.record.p.gr = reg;
return ptr;
}
static unw_rec_list *
output_spill_base (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (spill_base);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_unat_when ()
{
unw_rec_list *ptr = alloc_record (unat_when);
return ptr;
}
static unw_rec_list *
output_unat_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (unat_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_unat_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (unat_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_unat_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (unat_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_lc_when ()
{
unw_rec_list *ptr = alloc_record (lc_when);
return ptr;
}
static unw_rec_list *
output_lc_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (lc_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_lc_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (lc_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_lc_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (lc_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_fpsr_when ()
{
unw_rec_list *ptr = alloc_record (fpsr_when);
return ptr;
}
static unw_rec_list *
output_fpsr_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (fpsr_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_fpsr_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (fpsr_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_fpsr_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (fpsr_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_priunat_when_gr ()
{
unw_rec_list *ptr = alloc_record (priunat_when_gr);
return ptr;
}
static unw_rec_list *
output_priunat_when_mem ()
{
unw_rec_list *ptr = alloc_record (priunat_when_mem);
return ptr;
}
static unw_rec_list *
output_priunat_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (priunat_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_priunat_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (priunat_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_priunat_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (priunat_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_bsp_when ()
{
unw_rec_list *ptr = alloc_record (bsp_when);
return ptr;
}
static unw_rec_list *
output_bsp_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (bsp_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_bsp_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bsp_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_bsp_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bsp_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_bspstore_when ()
{
unw_rec_list *ptr = alloc_record (bspstore_when);
return ptr;
}
static unw_rec_list *
output_bspstore_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (bspstore_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_bspstore_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bspstore_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_bspstore_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (bspstore_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_rnat_when ()
{
unw_rec_list *ptr = alloc_record (rnat_when);
return ptr;
}
static unw_rec_list *
output_rnat_gr (gr)
unsigned int gr;
{
unw_rec_list *ptr = alloc_record (rnat_gr);
ptr->r.record.p.gr = gr;
return ptr;
}
static unw_rec_list *
output_rnat_psprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rnat_psprel);
ptr->r.record.p.pspoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_rnat_sprel (offset)
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (rnat_sprel);
ptr->r.record.p.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_unwabi (abi, context)
unsigned long abi;
unsigned long context;
{
unw_rec_list *ptr = alloc_record (unwabi);
ptr->r.record.p.abi = abi;
ptr->r.record.p.context = context;
return ptr;
}
static unw_rec_list *
output_epilogue (unsigned long ecount)
{
unw_rec_list *ptr = alloc_record (epilogue);
ptr->r.record.b.ecount = ecount;
return ptr;
}
static unw_rec_list *
output_label_state (unsigned long label)
{
unw_rec_list *ptr = alloc_record (label_state);
ptr->r.record.b.label = label;
return ptr;
}
static unw_rec_list *
output_copy_state (unsigned long label)
{
unw_rec_list *ptr = alloc_record (copy_state);
ptr->r.record.b.label = label;
return ptr;
}
static unw_rec_list *
output_spill_psprel (ab, reg, offset)
unsigned int ab;
unsigned int reg;
unsigned int 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;
return ptr;
}
static unw_rec_list *
output_spill_sprel (ab, reg, offset)
unsigned int ab;
unsigned int reg;
unsigned int offset;
{
unw_rec_list *ptr = alloc_record (spill_sprel);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.spoff = offset / 4;
return ptr;
}
static unw_rec_list *
output_spill_psprel_p (ab, reg, offset, predicate)
unsigned int ab;
unsigned int reg;
unsigned int offset;
unsigned int 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.qp = predicate;
return ptr;
}
static unw_rec_list *
output_spill_sprel_p (ab, reg, offset, predicate)
unsigned int ab;
unsigned int reg;
unsigned int offset;
unsigned int predicate;
{
unw_rec_list *ptr = alloc_record (spill_sprel_p);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.spoff = offset / 4;
ptr->r.record.x.qp = predicate;
return ptr;
}
static unw_rec_list *
output_spill_reg (ab, reg, targ_reg, xy)
unsigned int ab;
unsigned int reg;
unsigned int targ_reg;
unsigned int xy;
{
unw_rec_list *ptr = alloc_record (spill_reg);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.treg = targ_reg;
ptr->r.record.x.xy = xy;
return ptr;
}
static unw_rec_list *
output_spill_reg_p (ab, reg, targ_reg, xy, predicate)
unsigned int ab;
unsigned int reg;
unsigned int targ_reg;
unsigned int xy;
unsigned int predicate;
{
unw_rec_list *ptr = alloc_record (spill_reg_p);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.treg = targ_reg;
ptr->r.record.x.xy = xy;
ptr->r.record.x.qp = predicate;
return ptr;
}
/* Given a unw_rec_list process the correct format with the
specified function. */
static void
process_one_record (ptr, f)
unw_rec_list *ptr;
vbyte_func f;
{
unsigned long fr_mask, gr_mask;
switch (ptr->r.type)
{
case gr_mem:
case fr_mem:
case br_mem:
case frgr_mem:
/* These are taken care of by prologue/prologue_gr. */
break;
case prologue_gr:
case prologue:
if (ptr->r.type == prologue_gr)
output_R2_format (f, ptr->r.record.r.grmask,
ptr->r.record.r.grsave, ptr->r.record.r.rlen);
else
output_R1_format (f, ptr->r.type, ptr->r.record.r.rlen);
/* Output descriptor(s) for union of register spills (if any). */
gr_mask = ptr->r.record.r.mask.gr_mem;
fr_mask = ptr->r.record.r.mask.fr_mem;
if (fr_mask)
{
if ((fr_mask & ~0xfUL) == 0)
output_P6_format (f, fr_mem, fr_mask);
else
{
output_P5_format (f, gr_mask, fr_mask);
gr_mask = 0;
}
}
if (gr_mask)
output_P6_format (f, gr_mem, gr_mask);
if (ptr->r.record.r.mask.br_mem)
output_P1_format (f, ptr->r.record.r.mask.br_mem);
/* output imask descriptor if necessary: */
if (ptr->r.record.r.mask.i)
output_P4_format (f, ptr->r.record.r.mask.i,
ptr->r.record.r.imask_size);
break;
case body:
output_R1_format (f, ptr->r.type, ptr->r.record.r.rlen);
break;
case mem_stack_f:
case mem_stack_v:
output_P7_format (f, ptr->r.type, ptr->r.record.p.t,
ptr->r.record.p.size);
break;
case psp_gr:
case rp_gr:
case pfs_gr:
case preds_gr:
case unat_gr:
case lc_gr:
case fpsr_gr:
case priunat_gr:
case bsp_gr:
case bspstore_gr:
case rnat_gr:
output_P3_format (f, ptr->r.type, ptr->r.record.p.gr);
break;
case rp_br:
output_P3_format (f, rp_br, ptr->r.record.p.br);
break;
case psp_sprel:
output_P7_format (f, psp_sprel, ptr->r.record.p.spoff, 0);
break;
case rp_when:
case pfs_when:
case preds_when:
case unat_when:
case lc_when:
case fpsr_when:
output_P7_format (f, ptr->r.type, ptr->r.record.p.t, 0);
break;
case rp_psprel:
case pfs_psprel:
case preds_psprel:
case unat_psprel:
case lc_psprel:
case fpsr_psprel:
case spill_base:
output_P7_format (f, ptr->r.type, ptr->r.record.p.pspoff, 0);
break;
case rp_sprel:
case pfs_sprel:
case preds_sprel:
case unat_sprel:
case lc_sprel:
case fpsr_sprel:
case priunat_sprel:
case bsp_sprel:
case bspstore_sprel:
case rnat_sprel:
output_P8_format (f, ptr->r.type, ptr->r.record.p.spoff);
break;
case gr_gr:
output_P9_format (f, ptr->r.record.p.grmask, ptr->r.record.p.gr);
break;
case br_gr:
output_P2_format (f, ptr->r.record.p.brmask, ptr->r.record.p.gr);
break;
case spill_mask:
as_bad ("spill_mask record unimplemented.");
break;
case priunat_when_gr:
case priunat_when_mem:
case bsp_when:
case bspstore_when:
case rnat_when:
output_P8_format (f, ptr->r.type, ptr->r.record.p.t);
break;
case priunat_psprel:
case bsp_psprel:
case bspstore_psprel:
case rnat_psprel:
output_P8_format (f, ptr->r.type, ptr->r.record.p.pspoff);
break;
case unwabi:
output_P10_format (f, ptr->r.record.p.abi, ptr->r.record.p.context);
break;
case epilogue:
output_B3_format (f, ptr->r.record.b.ecount, ptr->r.record.b.t);
break;
case label_state:
case copy_state:
output_B4_format (f, ptr->r.type, ptr->r.record.b.label);
break;
case spill_psprel:
output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
ptr->r.record.x.reg, ptr->r.record.x.t,
ptr->r.record.x.pspoff);
break;
case spill_sprel:
output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
ptr->r.record.x.reg, ptr->r.record.x.t,
ptr->r.record.x.spoff);
break;
case spill_reg:
output_X2_format (f, ptr->r.record.x.ab, ptr->r.record.x.reg,
ptr->r.record.x.xy >> 1, ptr->r.record.x.xy,
ptr->r.record.x.treg, ptr->r.record.x.t);
break;
case spill_psprel_p:
output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
ptr->r.record.x.ab, ptr->r.record.x.reg,
ptr->r.record.x.t, ptr->r.record.x.pspoff);
break;
case spill_sprel_p:
output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
ptr->r.record.x.ab, ptr->r.record.x.reg,
ptr->r.record.x.t, ptr->r.record.x.spoff);
break;
case spill_reg_p:
output_X4_format (f, ptr->r.record.x.qp, ptr->r.record.x.ab,
ptr->r.record.x.reg, ptr->r.record.x.xy >> 1,
ptr->r.record.x.xy, ptr->r.record.x.treg,
ptr->r.record.x.t);
break;
default:
as_bad ("record_type_not_valid");
break;
}
}
/* Given a unw_rec_list list, process all the records with
the specified function. */
static void
process_unw_records (list, f)
unw_rec_list *list;
vbyte_func f;
{
unw_rec_list *ptr;
for (ptr = list; ptr; ptr = ptr->next)
process_one_record (ptr, f);
}
/* Determine the size of a record list in bytes. */
static int
calc_record_size (list)
unw_rec_list *list;
{
vbyte_count = 0;
process_unw_records (list, count_output);
return vbyte_count;
}
/* Update IMASK bitmask to reflect the fact that one or more registers
of type TYPE are saved starting at instruction with index T. If N
bits are set in REGMASK, it is assumed that instructions T through
T+N-1 save these registers.
TYPE values:
0: no save
1: instruction saves next fp reg
2: instruction saves next general reg
3: instruction saves next branch reg */
static void
set_imask (region, regmask, t, type)
unw_rec_list *region;
unsigned long regmask;
unsigned long t;
unsigned int type;
{
unsigned char *imask;
unsigned long imask_size;
unsigned int i;
int pos;
imask = region->r.record.r.mask.i;
imask_size = region->r.record.r.imask_size;
if (!imask)
{
imask_size = (region->r.record.r.rlen * 2 + 7) / 8 + 1;
imask = xmalloc (imask_size);
memset (imask, 0, imask_size);
region->r.record.r.imask_size = imask_size;
region->r.record.r.mask.i = imask;
}
i = (t / 4) + 1;
pos = 2 * (3 - t % 4);
while (regmask)
{
if (i >= imask_size)
{
as_bad ("Ignoring attempt to spill beyond end of region");
return;
}
imask[i] |= (type & 0x3) << pos;
regmask &= (regmask - 1);
pos -= 2;
if (pos < 0)
{
pos = 0;
++i;
}
}
}
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. */
unsigned long
slot_index (slot_addr, slot_frag, first_addr, first_frag)
unsigned long slot_addr;
fragS *slot_frag;
unsigned long first_addr;
fragS *first_frag;
{
unsigned long index = 0;
/* First time we are called, the initial address and frag are invalid. */
if (first_addr == 0)
return 0;
/* If the two addresses are in different frags, then we need to add in
the remaining size of this frag, and then the entire size of intermediate
frags. */
while (slot_frag != first_frag)
{
unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
/* Add in the full size of the frag converted to instruction slots. */
index += 3 * (first_frag->fr_fix >> 4);
/* Subtract away the initial part before first_addr. */
index -= (3 * ((first_addr >> 4) - (start_addr >> 4))
+ ((first_addr & 0x3) - (start_addr & 0x3)));
/* Move to the beginning of the next frag. */
first_frag = first_frag->fr_next;
first_addr = (unsigned long) &first_frag->fr_literal;
}
/* Add in the used part of the last frag. */
index += (3 * ((slot_addr >> 4) - (first_addr >> 4))
+ ((slot_addr & 0x3) - (first_addr & 0x3)));
return index;
}
/* Optimize unwind record directives. */
static unw_rec_list *
optimize_unw_records (list)
unw_rec_list *list;
{
if (!list)
return NULL;
/* If the only unwind record is ".prologue" or ".prologue" followed
by ".body", then we can optimize the unwind directives away. */
if (list->r.type == prologue
&& (list->next == NULL
|| (list->next->r.type == body && list->next->next == NULL)))
return NULL;
return list;
}
/* Given a complete record list, process any records which have
unresolved fields, (ie length counts for a prologue). After
this has been run, all neccessary information should be available
within each record to generate an image. */
static void
fixup_unw_records (list)
unw_rec_list *list;
{
unw_rec_list *ptr, *region = 0;
unsigned long first_addr = 0, rlen = 0, t;
fragS *first_frag = 0;
for (ptr = list; ptr; ptr = ptr->next)
{
if (ptr->slot_number == SLOT_NUM_NOT_SET)
as_bad (" Insn slot not set in unwind record.");
t = slot_index (ptr->slot_number, ptr->slot_frag,
first_addr, first_frag);
switch (ptr->r.type)
{
case prologue:
case prologue_gr:
case body:
{
unw_rec_list *last;
int size, dir_len = 0;
unsigned long last_addr;
fragS *last_frag;
first_addr = ptr->slot_number;
first_frag = ptr->slot_frag;
ptr->slot_number = 0;
/* Find either the next body/prologue start, or the end of
the list, and determine the size of the region. */
last_addr = unwind.next_slot_number;
last_frag = unwind.next_slot_frag;
for (last = ptr->next; last != NULL; last = last->next)
if (last->r.type == prologue || last->r.type == prologue_gr
|| last->r.type == body)
{
last_addr = last->slot_number;
last_frag = last->slot_frag;
break;
}
else if (!last->next)
{
/* 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);
rlen = ptr->r.record.r.rlen = size;
region = ptr;
break;
}
case epilogue:
ptr->r.record.b.t = rlen - 1 - t;
break;
case mem_stack_f:
case mem_stack_v:
case rp_when:
case pfs_when:
case preds_when:
case unat_when:
case lc_when:
case fpsr_when:
case priunat_when_gr:
case priunat_when_mem:
case bsp_when:
case bspstore_when:
case rnat_when:
ptr->r.record.p.t = t;
break;
case spill_reg:
case spill_sprel:
case spill_psprel:
case spill_reg_p:
case spill_sprel_p:
case spill_psprel_p:
ptr->r.record.x.t = t;
break;
case frgr_mem: