| /* 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: |
|