blob: 83ea813792aac768077782d58ed85478e910e890 [file] [log] [blame]
/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
Copyright (C) 1998-2016 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 3, 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, 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1301, USA. */
/*
TODO:
- optional operands
- directives:
.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 "safe-ctype.h"
#include "dwarf2dbg.h"
#include "subsegs.h"
#include "opcode/ia64.h"
#include "elf/ia64.h"
#include "bfdver.h"
#include <time.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#define NELEMS(a) ((int) (sizeof (a)/sizeof ((a)[0])))
/* Some systems define MIN in, e.g., param.h. */
#undef MIN
#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
{
/* IA-64 ABI section pseudo-ops. */
SPECIAL_SECTION_BSS = 0,
SPECIAL_SECTION_SBSS,
SPECIAL_SECTION_SDATA,
SPECIAL_SECTION_RODATA,
SPECIAL_SECTION_COMMENT,
SPECIAL_SECTION_UNWIND,
SPECIAL_SECTION_UNWIND_INFO,
/* HPUX specific section pseudo-ops. */
SPECIAL_SECTION_INIT_ARRAY,
SPECIAL_SECTION_FINI_ARRAY,
};
enum reloc_func
{
FUNC_DTP_MODULE,
FUNC_DTP_RELATIVE,
FUNC_FPTR_RELATIVE,
FUNC_GP_RELATIVE,
FUNC_LT_RELATIVE,
FUNC_LT_RELATIVE_X,
FUNC_PC_RELATIVE,
FUNC_PLT_RELATIVE,
FUNC_SEC_RELATIVE,
FUNC_SEG_RELATIVE,
FUNC_TP_RELATIVE,
FUNC_LTV_RELATIVE,
FUNC_LT_FPTR_RELATIVE,
FUNC_LT_DTP_MODULE,
FUNC_LT_DTP_RELATIVE,
FUNC_LT_TP_RELATIVE,
FUNC_IPLT_RELOC,
#ifdef TE_VMS
FUNC_SLOTCOUNT_RELOC,
#endif
};
enum reg_symbol
{
REG_GR = 0,
REG_FR = (REG_GR + 128),
REG_AR = (REG_FR + 128),
REG_CR = (REG_AR + 128),
REG_DAHR = (REG_CR + 128),
REG_P = (REG_DAHR + 8),
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_MSR,
IND_PKR,
IND_PMC,
IND_PMD,
IND_DAHR,
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;
bfd_boolean dw2_mark_labels;
};
#ifdef TE_VMS
/* An internally used relocation. */
#define DUMMY_RELOC_IA64_SLOTCOUNT (BFD_RELOC_UNUSED + 1)
#endif
/* 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) (char *, valueT, int);
static void ia64_float_to_chars_bigendian (char *, LITTLENUM_TYPE *, int);
static void ia64_float_to_chars_littleendian (char *, LITTLENUM_TYPE *, int);
static void (*ia64_float_to_chars) (char *, LITTLENUM_TYPE *, int);
static struct hash_control *alias_hash;
static struct hash_control *alias_name_hash;
static struct hash_control *secalias_hash;
static struct hash_control *secalias_name_hash;
/* List of chars besides those in app.c:symbol_chars that can start an
operand. Used to prevent the scrubber eating vital white-space. */
const char ia64_symbol_chars[] = "@?";
/* Characters which always start a comment. */
const char comment_chars[] = "";
/* 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 */
/* If X_op is != O_absent, the registername for the instruction's
qualifying predicate. If NULL, p0 is assumed for instructions
that are predictable. */
expressionS qp;
/* Optimize for which CPU. */
enum
{
itanium1,
itanium2
} tune;
/* What to do when hint.b is used. */
enum
{
hint_b_error,
hint_b_warning,
hint_b_ok
} hint_b;
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;
/* What to do when something is wrong with unwind directives. */
enum
{
unwind_check_warning,
unwind_check_error
} unwind_check;
/* Each bundle consists of up to three instructions. We keep
track of four most recent instructions so we can correctly set
the end_of_insn_group for the last instruction in a bundle. */
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,
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
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];
const 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 */
int pointer_size; /* size in bytes of a pointer */
int pointer_size_shift; /* shift size of a pointer for alignment */
symbolS *indregsym[IND_RR - IND_CPUID + 1];
}
md;
/* These are not const, because they are modified to MMI for non-itanium1
targets below. */
/* MFI bundle of nops. */
static unsigned char le_nop[16] =
{
0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
};
/* MFI bundle of nops with stop-bit. */
static unsigned char le_nop_stop[16] =
{
0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
};
/* 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_FCR 21
#define AR_EFLAG 24
#define AR_CSD 25
#define AR_SSD 26
#define AR_CFLG 27
#define AR_FSR 28
#define AR_FIR 29
#define AR_FDR 30
#define AR_CCV 32
#define AR_UNAT 36
#define AR_FPSR 40
#define AR_ITC 44
#define AR_RUC 45
#define AR_PFS 64
#define AR_LC 65
#define AR_EC 66
static const struct
{
const char *name;
unsigned int regnum;
}
ar[] =
{
{"ar.k0", AR_K0}, {"ar.k1", AR_K0 + 1},
{"ar.k2", AR_K0 + 2}, {"ar.k3", AR_K0 + 3},
{"ar.k4", AR_K0 + 4}, {"ar.k5", AR_K0 + 5},
{"ar.k6", AR_K0 + 6}, {"ar.k7", AR_K7},
{"ar.rsc", AR_RSC}, {"ar.bsp", AR_BSP},
{"ar.bspstore", AR_BSPSTORE}, {"ar.rnat", AR_RNAT},
{"ar.fcr", AR_FCR}, {"ar.eflag", AR_EFLAG},
{"ar.csd", AR_CSD}, {"ar.ssd", AR_SSD},
{"ar.cflg", AR_CFLG}, {"ar.fsr", AR_FSR},
{"ar.fir", AR_FIR}, {"ar.fdr", AR_FDR},
{"ar.ccv", AR_CCV}, {"ar.unat", AR_UNAT},
{"ar.fpsr", AR_FPSR}, {"ar.itc", AR_ITC},
{"ar.ruc", AR_RUC}, {"ar.pfs", AR_PFS},
{"ar.lc", AR_LC}, {"ar.ec", AR_EC},
};
/* control registers: */
#define CR_DCR 0
#define CR_ITM 1
#define CR_IVA 2
#define CR_PTA 8
#define CR_GPTA 9
#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_IIB0 26
#define CR_IIB1 27
#define CR_LID 64
#define CR_IVR 65
#define CR_TPR 66
#define CR_EOI 67
#define CR_IRR0 68
#define CR_IRR3 71
#define CR_ITV 72
#define CR_PMV 73
#define CR_CMCV 74
#define CR_LRR0 80
#define CR_LRR1 81
static const struct
{
const char *name;
unsigned int regnum;
}
cr[] =
{
{"cr.dcr", CR_DCR},
{"cr.itm", CR_ITM},
{"cr.iva", CR_IVA},
{"cr.pta", CR_PTA},
{"cr.gpta", CR_GPTA},
{"cr.ipsr", CR_IPSR},
{"cr.isr", CR_ISR},
{"cr.iip", CR_IIP},
{"cr.ifa", CR_IFA},
{"cr.itir", CR_ITIR},
{"cr.iipa", CR_IIPA},
{"cr.ifs", CR_IFS},
{"cr.iim", CR_IIM},
{"cr.iha", CR_IHA},
{"cr.iib0", CR_IIB0},
{"cr.iib1", CR_IIB1},
{"cr.lid", CR_LID},
{"cr.ivr", CR_IVR},
{"cr.tpr", CR_TPR},
{"cr.eoi", CR_EOI},
{"cr.irr0", CR_IRR0},
{"cr.irr1", CR_IRR0 + 1},
{"cr.irr2", CR_IRR0 + 2},
{"cr.irr3", CR_IRR3},
{"cr.itv", CR_ITV},
{"cr.pmv", CR_PMV},
{"cr.cmcv", CR_CMCV},
{"cr.lrr0", CR_LRR0},
{"cr.lrr1", CR_LRR1}
};
#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;
unsigned 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 },
{ "dahr", IND_DAHR },
{ "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!): */
{ "dtpmod", PSEUDO_FUNC_RELOC, { 0 } },
{ "dtprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "fptr", PSEUDO_FUNC_RELOC, { 0 } },
{ "gprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltoffx", PSEUDO_FUNC_RELOC, { 0 } },
{ "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "pltoff", PSEUDO_FUNC_RELOC, { 0 } },
{ "secrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "segrel", PSEUDO_FUNC_RELOC, { 0 } },
{ "tprel", PSEUDO_FUNC_RELOC, { 0 } },
{ "ltv", PSEUDO_FUNC_RELOC, { 0 } },
{ NULL, 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
{ NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_MODULE */
{ NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_RELATIVE */
{ NULL, 0, { 0 } }, /* placeholder for FUNC_LT_TP_RELATIVE */
{ "iplt", PSEUDO_FUNC_RELOC, { 0 } },
#ifdef TE_VMS
{ "slotcount", PSEUDO_FUNC_RELOC, { 0 } },
#endif
/* 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 */
/* hint constants: */
{ "pause", PSEUDO_FUNC_CONST, { 0x0 } },
{ "priority", PSEUDO_FUNC_CONST, { 0x1 } },
/* tf constants: */
{ "clz", PSEUDO_FUNC_CONST, { 32 } },
{ "mpy", PSEUDO_FUNC_CONST, { 33 } },
{ "datahints", PSEUDO_FUNC_CONST, { 34 } },
/* unwind-related constants: */
{ "svr4", PSEUDO_FUNC_CONST, { ELFOSABI_NONE } },
{ "hpux", PSEUDO_FUNC_CONST, { ELFOSABI_HPUX } },
{ "nt", PSEUDO_FUNC_CONST, { 2 } }, /* conflicts w/ELFOSABI_NETBSD */
{ "linux", PSEUDO_FUNC_CONST, { ELFOSABI_GNU } },
{ "freebsd", PSEUDO_FUNC_CONST, { ELFOSABI_FREEBSD } },
{ "openvms", PSEUDO_FUNC_CONST, { ELFOSABI_OPENVMS } },
{ "nsk", PSEUDO_FUNC_CONST, { ELFOSABI_NSK } },
/* unwind-related registers: */
{ "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
};
/* 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 */
0x0000000000LL, /* L-"unit" nop immediate */
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"},
{".init_array"}, {".fini_array"}
};
/* 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 */
const 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,
#ifdef INT_MAX
INT_MAX,
#else
(((1 << (8 * sizeof(gr_values->path) - 2)) - 1) << 1) + 1,
#endif
0
}
};
/* Remember the alignment frag. */
static fragS *align_frag;
/* 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
/* Linked list of saved prologue counts. A very poor
implementation of a map from label numbers to prologue counts. */
typedef struct label_prologue_count
{
struct label_prologue_count *next;
unsigned long label_number;
unsigned int prologue_count;
} label_prologue_count;
typedef struct proc_pending
{
symbolS *sym;
struct proc_pending *next;
} proc_pending;
static struct
{
/* Maintain a list of unwind entries for the current function. */
unw_rec_list *list;
unw_rec_list *tail;
/* Any unwind entries 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. */
proc_pending proc_pending;
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. */
unsigned int prologue : 1;
unsigned int prologue_mask : 4;
unsigned int prologue_gr : 7;
unsigned int body : 1;
unsigned int insn : 1;
unsigned int prologue_count; /* number of .prologues seen so far */
/* Prologue counts at previous .label_state directives. */
struct label_prologue_count * saved_prologue_counts;
/* List of split up .save-s. */
unw_p_record *pending_saves;
} 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) (int, char *, char *);
/* Forward declarations: */
static void dot_alias (int);
static int parse_operand_and_eval (expressionS *, int);
static void emit_one_bundle (void);
static bfd_reloc_code_real_type ia64_gen_real_reloc_type (struct symbol *,
bfd_reloc_code_real_type);
static void insn_group_break (int, int, int);
static void add_qp_mutex (valueT);
static void add_qp_imply (int, int);
static void clear_qp_mutex (valueT);
static void clear_qp_implies (valueT, valueT);
static void print_dependency (const char *, int);
static void instruction_serialization (void);
static void data_serialization (void);
static void output_R3_format (vbyte_func, unw_record_type, unsigned long);
static void output_B3_format (vbyte_func, unsigned long, unsigned long);
static void output_B4_format (vbyte_func, unw_record_type, unsigned long);
static void free_saved_prologue_counts (void);
/* Determine if application register REGNUM resides only in the integer
unit (as opposed to the memory unit). */
static int
ar_is_only_in_integer_unit (int reg)
{
reg -= REG_AR;
return reg >= 64 && reg <= 111;
}
/* 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
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 (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 's' to SHF_IA_64_SHORT. */
bfd_vma
ia64_elf_section_letter (int letter, const char **ptr_msg)
{
if (letter == 's')
return SHF_IA_64_SHORT;
else if (letter == 'o')
return SHF_LINK_ORDER;
#ifdef TE_VMS
else if (letter == 'O')
return SHF_IA_64_VMS_OVERLAID;
else if (letter == 'g')
return SHF_IA_64_VMS_GLOBAL;
#endif
*ptr_msg = _("bad .section directive: want a,o,s,w,x,M,S,G,T in string");
return -1;
}
/* Map SHF_IA_64_SHORT to SEC_SMALL_DATA. */
flagword
ia64_elf_section_flags (flagword flags,
bfd_vma attr,
int type ATTRIBUTE_UNUSED)
{
if (attr & SHF_IA_64_SHORT)
flags |= SEC_SMALL_DATA;
return flags;
}
int
ia64_elf_section_type (const char *str, size_t len)
{
#define STREQ(s) ((len == sizeof (s) - 1) && (strncmp (str, s, sizeof (s) - 1) == 0))
if (STREQ (ELF_STRING_ia64_unwind_info))
return SHT_PROGBITS;
if (STREQ (ELF_STRING_ia64_unwind_info_once))
return SHT_PROGBITS;
if (STREQ (ELF_STRING_ia64_unwind))
return SHT_IA_64_UNWIND;
if (STREQ (ELF_STRING_ia64_unwind_once))
return SHT_IA_64_UNWIND;
if (STREQ ("unwind"))
return SHT_IA_64_UNWIND;
return -1;
#undef STREQ
}
static unsigned int
set_regstack (unsigned int ins,
unsigned int locs,
unsigned int outs,
unsigned int 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 (void)
{
struct label_fix *lfix;
segT saved_seg;
subsegT saved_subseg;
unw_rec_list *ptr;
bfd_boolean mark;
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. */
mark = FALSE;
for (lfix = CURR_SLOT.label_fixups; lfix; lfix = lfix->next)
{
symbol_set_value_now (lfix->sym);
mark |= lfix->dw2_mark_labels;
}
if (mark)
{
dwarf2_where (&CURR_SLOT.debug_line);
CURR_SLOT.debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
dwarf2_gen_line_info (frag_now_fix (), &CURR_SLOT.debug_line);
dwarf2_consume_line_info ();
}
CURR_SLOT.label_fixups = 0;
for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
symbol_set_value_now (lfix->sym);
CURR_SLOT.tag_fixups = 0;
/* In case there are unwind directives following the last instruction,
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)
{
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;
}
}
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_cons_align (int nbytes)
{
if (md.auto_align)
{
int log;
for (log = 0; (nbytes & 1) != 1; nbytes >>= 1)
log++;
do_align (log, NULL, 0, 0);
}
}
#ifdef TE_VMS
/* .vms_common section, symbol, size, alignment */
static void
obj_elf_vms_common (int ignore ATTRIBUTE_UNUSED)
{
const char *sec_name;
char *sym_name;
char c;
offsetT size;
offsetT cur_size;
offsetT temp;
symbolS *symbolP;
segT current_seg = now_seg;
subsegT current_subseg = now_subseg;
offsetT log_align;
/* Section name. */
sec_name = obj_elf_section_name ();
if (sec_name == NULL)
return;
/* Symbol name. */
SKIP_WHITESPACE ();
if (*input_line_pointer == ',')
{
input_line_pointer++;
SKIP_WHITESPACE ();
}
else
{
as_bad (_("expected ',' after section name"));
ignore_rest_of_line ();
return;
}
c = get_symbol_name (&sym_name);
if (input_line_pointer == sym_name)
{
(void) restore_line_pointer (c);
as_bad (_("expected symbol name"));
ignore_rest_of_line ();
return;
}
symbolP = symbol_find_or_make (sym_name);
(void) restore_line_pointer (c);
if ((S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
&& !S_IS_COMMON (symbolP))
{
as_bad (_("Ignoring attempt to re-define symbol"));
ignore_rest_of_line ();
return;
}
/* Symbol size. */
SKIP_WHITESPACE ();
if (*input_line_pointer == ',')
{
input_line_pointer++;
SKIP_WHITESPACE ();
}
else
{
as_bad (_("expected ',' after symbol name"));
ignore_rest_of_line ();
return;
}
temp = get_absolute_expression ();
size = temp;
size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1;
if (temp != size)
{
as_warn (_("size (%ld) out of range, ignored"), (long) temp);
ignore_rest_of_line ();
return;
}
/* Alignment. */
SKIP_WHITESPACE ();
if (*input_line_pointer == ',')
{
input_line_pointer++;
SKIP_WHITESPACE ();
}
else
{
as_bad (_("expected ',' after symbol size"));
ignore_rest_of_line ();
return;
}
log_align = get_absolute_expression ();
demand_empty_rest_of_line ();
obj_elf_change_section
(sec_name, SHT_NOBITS,
SHF_ALLOC | SHF_WRITE | SHF_IA_64_VMS_OVERLAID | SHF_IA_64_VMS_GLOBAL,
0, NULL, 1, 0);
S_SET_VALUE (symbolP, 0);
S_SET_SIZE (symbolP, size);
S_SET_EXTERNAL (symbolP);
S_SET_SEGMENT (symbolP, now_seg);
symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
record_alignment (now_seg, log_align);
cur_size = bfd_section_size (stdoutput, now_seg);
if ((int) size > cur_size)
{
char *pfrag
= frag_var (rs_fill, 1, 1, (relax_substateT)0, NULL,
(valueT)size - (valueT)cur_size, NULL);
*pfrag = 0;
bfd_section_size (stdoutput, now_seg) = size;
}
/* Switch back to current segment. */
subseg_set (current_seg, current_subseg);
#ifdef md_elf_section_change_hook
md_elf_section_change_hook ();
#endif
}
#endif /* TE_VMS */
/* Output COUNT bytes to a memory location. */
static char *vbyte_mem_ptr = NULL;
static void
output_vbyte_mem (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;
static void
count_output (int count,
char *ptr ATTRIBUTE_UNUSED,
char *comment ATTRIBUTE_UNUSED)
{
vbyte_count += count;
}
static void
output_R1_format (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 (vbyte_func f, int mask, int 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 (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 (vbyte_func f, int brmask)
{
char byte;
byte = UNW_P1 | (brmask & 0x1f);
(*f) (1, &byte, NULL);
}
static void
output_P2_format (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 (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 (vbyte_func f, unsigned char *imask, unsigned long imask_size)
{
imask[0] = UNW_P4;
(*f) (imask_size, (char *) imask, NULL);
}
static void
output_P5_format (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (int ab, int reg)
{
int ret;
ab = (ab & 3);
reg = (reg & 0x1f);
ret = (ab << 5) | reg;
return ret;
}
static void
output_X1_format (vbyte_func f,
unw_record_type rtype,
int ab,
int 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 (vbyte_func f,
int ab,
int reg,
int x,
int y,
int 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 (vbyte_func f,
unw_record_type rtype,
int qp,
int ab,
int 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 (vbyte_func f,
int qp,
int ab,
int reg,
int x,
int y,
int 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 checks whether there are any outstanding .save-s and
discards them if so. */
static void
check_pending_save (void)
{
if (unwind.pending_saves)
{
unw_rec_list *cur, *prev;
as_warn (_("Previous .save incomplete"));
for (cur = unwind.list, prev = NULL; cur; )
if (&cur->r.record.p == unwind.pending_saves)
{
if (prev)
prev->next = cur->next;
else
unwind.list = cur->next;
if (cur == unwind.tail)
unwind.tail = prev;
if (cur == unwind.current_entry)
unwind.current_entry = cur->next;
/* Don't free the first discarded record, it's being used as
terminator for (currently) br_gr and gr_gr processing, and
also prevents leaving a dangling pointer to it in its
predecessor. */
cur->r.record.p.grmask = 0;
cur->r.record.p.brmask = 0;
cur->r.record.p.frmask = 0;
prev = cur->r.record.p.next;
cur->r.record.p.next = NULL;
cur = prev;
break;
}
else
{
prev = cur;
cur = cur->next;
}
while (cur)
{
prev = cur;
cur = cur->r.record.p.next;
free (prev);
}
unwind.pending_saves = 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 = XNEW (unw_rec_list);
memset (ptr, 0, sizeof (*ptr));
ptr->slot_number = SLOT_NUM_NOT_SET;
ptr->r.type = t;
return ptr;
}
/* Dummy unwind record used for calculating the length of the last prologue or
body region. */
static unw_rec_list *
output_endp (void)
{
unw_rec_list *ptr = alloc_record (endp);
return ptr;
}
static unw_rec_list *
output_prologue (void)
{
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 (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 (void)
{
unw_rec_list *ptr = alloc_record (body);
return ptr;
}
static unw_rec_list *
output_mem_stack_f (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 (void)
{
unw_rec_list *ptr = alloc_record (mem_stack_v);
return ptr;
}
static unw_rec_list *
output_psp_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (psp_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_psp_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (psp_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_rp_when (void)
{
unw_rec_list *ptr = alloc_record (rp_when);
return ptr;
}
static unw_rec_list *
output_rp_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (rp_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_rp_br (unsigned int br)
{
unw_rec_list *ptr = alloc_record (rp_br);
ptr->r.record.p.r.br = br;
return ptr;
}
static unw_rec_list *
output_rp_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (rp_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_rp_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (rp_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_pfs_when (void)
{
unw_rec_list *ptr = alloc_record (pfs_when);
return ptr;
}
static unw_rec_list *
output_pfs_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (pfs_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_pfs_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (pfs_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_pfs_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (pfs_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_preds_when (void)
{
unw_rec_list *ptr = alloc_record (preds_when);
return ptr;
}
static unw_rec_list *
output_preds_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (preds_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_preds_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (preds_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_preds_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (preds_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_fr_mem (unsigned int mask)
{
unw_rec_list *ptr = alloc_record (fr_mem);
unw_rec_list *cur = ptr;
ptr->r.record.p.frmask = mask;
unwind.pending_saves = &ptr->r.record.p;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
mask &= ~(mask & (~mask + 1));
if (!mask)
return ptr;
cur = alloc_record (fr_mem);
cur->r.record.p.frmask = mask;
/* Retain only least significant bit. */
prev->r.record.p.frmask ^= mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_frgr_mem (unsigned int gr_mask, unsigned int fr_mask)
{
unw_rec_list *ptr = alloc_record (frgr_mem);
unw_rec_list *cur = ptr;
unwind.pending_saves = &cur->r.record.p;
cur->r.record.p.frmask = fr_mask;
while (fr_mask)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
fr_mask &= ~(fr_mask & (~fr_mask + 1));
if (!gr_mask && !fr_mask)
return ptr;
cur = alloc_record (frgr_mem);
cur->r.record.p.frmask = fr_mask;
/* Retain only least significant bit. */
prev->r.record.p.frmask ^= fr_mask;
prev->r.record.p.next = cur;
}
cur->r.record.p.grmask = gr_mask;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
gr_mask &= ~(gr_mask & (~gr_mask + 1));
if (!gr_mask)
return ptr;
cur = alloc_record (frgr_mem);
cur->r.record.p.grmask = gr_mask;
/* Retain only least significant bit. */
prev->r.record.p.grmask ^= gr_mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_gr_gr (unsigned int mask, unsigned int reg)
{
unw_rec_list *ptr = alloc_record (gr_gr);
unw_rec_list *cur = ptr;
ptr->r.record.p.grmask = mask;
ptr->r.record.p.r.gr = reg;
unwind.pending_saves = &ptr->r.record.p;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
mask &= ~(mask & (~mask + 1));
if (!mask)
return ptr;
cur = alloc_record (gr_gr);
cur->r.record.p.grmask = mask;
/* Indicate this record shouldn't be output. */
cur->r.record.p.r.gr = REG_NUM;
/* Retain only least significant bit. */
prev->r.record.p.grmask ^= mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_gr_mem (unsigned int mask)
{
unw_rec_list *ptr = alloc_record (gr_mem);
unw_rec_list *cur = ptr;
ptr->r.record.p.grmask = mask;
unwind.pending_saves = &ptr->r.record.p;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
mask &= ~(mask & (~mask + 1));
if (!mask)
return ptr;
cur = alloc_record (gr_mem);
cur->r.record.p.grmask = mask;
/* Retain only least significant bit. */
prev->r.record.p.grmask ^= mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_br_mem (unsigned int mask)
{
unw_rec_list *ptr = alloc_record (br_mem);
unw_rec_list *cur = ptr;
ptr->r.record.p.brmask = mask;
unwind.pending_saves = &ptr->r.record.p;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
mask &= ~(mask & (~mask + 1));
if (!mask)
return ptr;
cur = alloc_record (br_mem);
cur->r.record.p.brmask = mask;
/* Retain only least significant bit. */
prev->r.record.p.brmask ^= mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_br_gr (unsigned int mask, unsigned int reg)
{
unw_rec_list *ptr = alloc_record (br_gr);
unw_rec_list *cur = ptr;
ptr->r.record.p.brmask = mask;
ptr->r.record.p.r.gr = reg;
unwind.pending_saves = &ptr->r.record.p;
for (;;)
{
unw_rec_list *prev = cur;
/* Clear least significant set bit. */
mask &= ~(mask & (~mask + 1));
if (!mask)
return ptr;
cur = alloc_record (br_gr);
cur->r.record.p.brmask = mask;
/* Indicate this record shouldn't be output. */
cur->r.record.p.r.gr = REG_NUM;
/* Retain only least significant bit. */
prev->r.record.p.brmask ^= mask;
prev->r.record.p.next = cur;
}
}
static unw_rec_list *
output_spill_base (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (spill_base);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_unat_when (void)
{
unw_rec_list *ptr = alloc_record (unat_when);
return ptr;
}
static unw_rec_list *
output_unat_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (unat_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_unat_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (unat_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_unat_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (unat_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_lc_when (void)
{
unw_rec_list *ptr = alloc_record (lc_when);
return ptr;
}
static unw_rec_list *
output_lc_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (lc_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_lc_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (lc_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_lc_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (lc_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_fpsr_when (void)
{
unw_rec_list *ptr = alloc_record (fpsr_when);
return ptr;
}
static unw_rec_list *
output_fpsr_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (fpsr_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_fpsr_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (fpsr_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_fpsr_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (fpsr_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_priunat_when_gr (void)
{
unw_rec_list *ptr = alloc_record (priunat_when_gr);
return ptr;
}
static unw_rec_list *
output_priunat_when_mem (void)
{
unw_rec_list *ptr = alloc_record (priunat_when_mem);
return ptr;
}
static unw_rec_list *
output_priunat_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (priunat_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_priunat_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (priunat_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_priunat_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (priunat_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_bsp_when (void)
{
unw_rec_list *ptr = alloc_record (bsp_when);
return ptr;
}
static unw_rec_list *
output_bsp_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (bsp_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_bsp_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (bsp_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_bsp_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (bsp_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_bspstore_when (void)
{
unw_rec_list *ptr = alloc_record (bspstore_when);
return ptr;
}
static unw_rec_list *
output_bspstore_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (bspstore_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_bspstore_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (bspstore_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_bspstore_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (bspstore_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_rnat_when (void)
{
unw_rec_list *ptr = alloc_record (rnat_when);
return ptr;
}
static unw_rec_list *
output_rnat_gr (unsigned int gr)
{
unw_rec_list *ptr = alloc_record (rnat_gr);
ptr->r.record.p.r.gr = gr;
return ptr;
}
static unw_rec_list *
output_rnat_psprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (rnat_psprel);
ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
return ptr;
}
static unw_rec_list *
output_rnat_sprel (unsigned int offset)
{
unw_rec_list *ptr = alloc_record (rnat_sprel);
ptr->r.record.p.off.sp = offset / 4;
return ptr;
}
static unw_rec_list *
output_unwabi (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 (unsigned int ab,
unsigned int reg,
unsigned int offset,
unsigned int predicate)
{
unw_rec_list *ptr = alloc_record (predicate ? spill_psprel_p : spill_psprel);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.where.pspoff = ENCODED_PSP_OFFSET (offset);
ptr->r.record.x.qp = predicate;
return ptr;
}
static unw_rec_list *
output_spill_sprel (unsigned int ab,
unsigned int reg,
unsigned int offset,
unsigned int predicate)
{
unw_rec_list *ptr = alloc_record (predicate ? spill_sprel_p : spill_sprel);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.where.spoff = offset / 4;
ptr->r.record.x.qp = predicate;
return ptr;
}
static unw_rec_list *
output_spill_reg (unsigned int ab,
unsigned int reg,
unsigned int targ_reg,
unsigned int xy,
unsigned int predicate)
{
unw_rec_list *ptr = alloc_record (predicate ? spill_reg_p : spill_reg);
ptr->r.record.x.ab = ab;
ptr->r.record.x.reg = reg;
ptr->r.record.x.where.reg = 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 (unw_rec_list *ptr, vbyte_func f)
{
unsigned int fr_mask, gr_mask;
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:
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.r.gr);
break;
case rp_br:
output_P3_format (f, rp_br, ptr->r.record.p.r.br);
break;
case psp_sprel:
output_P7_format (f, psp_sprel, ptr->r.record.p.off.sp, 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.off.psp, 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.off.sp);
break;
case gr_gr:
if (ptr->r.record.p.r.gr < REG_NUM)
{
const unw_rec_list *cur = ptr;
gr_mask = cur->r.record.p.grmask;
while ((cur = cur->r.record.p.next) != NULL)
gr_mask |= cur->r.record.p.grmask;
output_P9_format (f, gr_mask, ptr->r.record.p.r.gr);
}
break;
case br_gr:
if (ptr->r.record.p.r.gr < REG_NUM)
{
const unw_rec_list *cur = ptr;
gr_mask = cur->r.record.p.brmask;
while ((cur = cur->r.record.p.next) != NULL)
gr_mask |= cur->r.record.p.brmask;
output_P2_format (f, gr_mask, ptr->r.record.p.r.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.off.psp);
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.where.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.where.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.where.reg, 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.where.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.where.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.where.reg,
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 (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 (unw_rec_list *list)
{
vbyte_count = 0;
process_unw_records (list, count_output);
return vbyte_count;
}
/* Return the number of bits set in the input value.
Perhaps this has a better place... */
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define popcount __builtin_popcount
#else
static int
popcount (unsigned x)
{
static const unsigned char popcnt[16] =
{
0, 1, 1, 2,
1, 2, 2, 3,
1, 2, 2, 3,
2, 3, 3, 4
};
if (x < NELEMS (popcnt))
return popcnt[x];
return popcnt[x % NELEMS (popcnt)] + popcount (x / NELEMS (popcnt));
}
#endif
/* 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 (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 = XCNEWVEC (unsigned char, 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;
}
}
}
/* 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. If BEFORE_RELAX, then we use worst-case estimates
for frag sizes. */
static unsigned long
slot_index (unsigned long slot_addr,
fragS *slot_frag,
unsigned long first_addr,
fragS *first_frag,
int before_relax)
{
unsigned long s_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;
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)
s_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. */
s_index += 3 * ((1 << first_frag->fr_offset) >> 4);
break;
case rs_org:
if (first_frag->fr_symbol)
{
as_fatal (_("Only constant offsets are supported"));