| /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture. |
| Copyright (C) 1998-2021 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> |
| #include <limits.h> |
| |
| #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; |
| bool 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 htab_t alias_hash; |
| static htab_t alias_name_hash; |
| static htab_t secalias_hash; |
| static htab_t 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 |
| { |
| htab_t pseudo_hash; /* pseudo opcode hash table */ |
| htab_t reg_hash; /* register name hash table */ |
| htab_t dynreg_hash; /* dynamic register hash table */ |
| htab_t const_hash; /* constant hash table */ |
| htab_t entry_hash; /* code entry hint hash table */ |
| |
| /* If X_op is != O_absent, the register name 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; |
| subsegT last_text_subseg; |
| |
| 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; |
| bool mark; |
| |
| if (!md.last_text_seg) |
| return; |
| |
| saved_seg = now_seg; |
| saved_subseg = now_subseg; |
| |
| subseg_set (md.last_text_seg, md.last_text_subseg); |
| |
| 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 (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_set_section_size (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")); |
| break; |
| } |
| /* Fall through. */ |
| case rs_fill: |
| s_index += 3 * (first_frag->fr_offset >> 4); |
| break; |
| } |
| |
| /* Add in the full size of the frag converted to instruction slots. */ |
| s_index += 3 * (first_frag->fr_fix >> 4); |
| /* Subtract away the initial part before first_addr. */ |
| s_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; |
| |
| /* This can happen if there is section switching in the middle of a |
| function, causing the frag chain for the function to be broken. |
| It is too difficult to recover safely from this problem, so we just |
| exit with an error. */ |
| if (first_frag == NULL) |
| as_fatal (_("Section switching in code is not supported.")); |
| } |
| |
| /* Add in the used part of the last frag. */ |
| s_index += (3 * ((slot_addr >> 4) - (first_addr >> 4)) |
| + ((slot_addr & 0x3) - (first_addr & 0x3))); |
| return s_index; |
| } |
| |
| /* Optimize unwind record directives. */ |
| |
| static unw_rec_list * |
| optimize_unw_records (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->r.type == endp |
| || (list->next->r.type == body && list->next->next->r.type == endp))) |
| 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 necessary information should be available |
| within each record to generate an image. */ |
| |
| static void |
| fixup_unw_records (unw_rec_list *list, int before_relax) |
| { |
| 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, before_relax); |
| switch (ptr->r.type) |
| { |
| case prologue: |
| case prologue_gr: |
| case body: |
| { |
| unw_rec_list *last; |
| int size; |
| unsigned long last_addr = 0; |
| fragS *last_frag = NULL; |
| |
| first_addr = ptr->slot_number; |
| first_frag = ptr->slot_frag; |
| /* Find either the next body/prologue start, or the end of |
| the function, and determine the size of the region. */ |
| for (last = ptr->next; last != NULL; last = last->next) |
| if (last->r.type == prologue || last->r.type == prologue_gr |
| || last->r.type == body || last->r.type == endp) |
| { |
| last_addr = last->slot_number; |
| last_frag = last->slot_frag; |
| break; |
| } |
| size = slot_index (last_addr, last_frag, first_addr, first_frag, |
| before_relax); |
| rlen = ptr->r.record.r.rlen = size; |
| if (ptr->r.type == body) |
| /* End of region. */ |
| region = 0; |
| else |
| region = ptr; |
| break; |
| } |
| case epilogue: |
| if (t < rlen) |
| ptr->r.record.b.t = rlen - 1 - t; |
| else |
| /* This happens when a memory-stack-less procedure uses a |
| ".restore sp" directive at the end of a region to pop |
| the frame state. */ |
| ptr->r.record.b.t = 0; |
| break; |
| |
| case mem_stack_f: |
| 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: |
| if (!region) |
| { |
| as_bad (_("frgr_mem record before region record!")); |
| return; |
| } |
| region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask; |
| region->r.record.r.mask.gr_mem |= ptr->r.record.p.grmask; |
| set_imask (region, ptr->r.record.p.frmask, t, 1); |
| set_imask (region, ptr->r.record.p.grmask, t, 2); |
| break; |
| case fr_mem: |
| if (!region) |
| { |
| as_bad (_("fr_mem record before region record!")); |
| return; |
| } |
| region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask; |
| set_imask (region, ptr->r.record.p.frmask, t, 1); |
| break; |
| case gr_mem: |
| if (!region) |
| { |
| as_bad (_("gr_mem record before region record!")); |
| return; |
| } |
| region->r.record.r.mask.gr_mem |= ptr->r.record.p.grmask; |
| set_imask (region, ptr->r.record.p.grmask, t, 2); |
| break; |
| case br_mem: |
| if (!region) |
| { |
| as_bad (_("br_mem record before region record!")); |
| return; |
| } |
| region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask; |
| set_imask (region, ptr->r.record.p.brmask, t, 3); |
| break; |
| |
| case gr_gr: |
| if (!region) |
| { |
| as_bad (_("gr_gr record before region record!")); |
| return; |
| } |
| set_imask (region, ptr->r.record.p.grmask, t, 2); |
| break; |
| case br_gr: |
| if (!region) |
| { |
| as_bad (_("br_gr record before region record!")); |
| return; |
| } |
| set_imask (region, ptr->r.record.p.brmask, t, 3); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /* Estimate the size of a frag before relaxing. We only have one type of frag |
| to handle here, which is the unwind info frag. */ |
| |
| int |
| ia64_estimate_size_before_relax (fragS *frag, |
| asection *segtype ATTRIBUTE_UNUSED) |
| { |
| unw_rec_list *list; |
| int len, size, pad; |
| |
| /* ??? This code is identical to the first part of ia64_convert_frag. */ |
| list = (unw_rec_list *) frag->fr_opcode; |
| fixup_unw_records (list, 0); |
| |
| len = calc_record_size (list); |
| /* pad to pointer-size boundary. */ |
| pad = len % md.pointer_size; |
| if (pad != 0) |
| len += md.pointer_size - pad; |
| /* Add 8 for the header. */ |
| size = len + 8; |
| /* Add a pointer for the personality offset. */ |
| if (frag->fr_offset) |
| size += md.pointer_size; |
| |
| /* fr_var carries the max_chars that we created the fragment with. |
| We must, of course, have allocated enough memory earlier. */ |
| gas_assert (frag->fr_var >= size); |
| |
| return frag->fr_fix + size; |
| } |
| |
| /* This function converts a rs_machine_dependent variant frag into a |
| normal fill frag with the unwind image from the record list. */ |
| void |
| ia64_convert_frag (fragS *frag) |
| { |
| unw_rec_list *list; |
| int len, size, pad; |
| valueT flag_value; |
| |
| /* ??? This code is identical to ia64_estimate_size_before_relax. */ |
| list = (unw_rec_list *) frag->fr_opcode; |
| fixup_unw_records (list, 0); |
| |
| len = calc_record_size (list); |
| /* pad to pointer-size boundary. */ |
| pad = len % md.pointer_size; |
| if (pad != 0) |
| len += md.pointer_size - pad; |
| /* Add 8 for the header. */ |
| size = len + 8; |
| /* Add a pointer for the personality offset. */ |
| if (frag->fr_offset) |
| size += md.pointer_size; |
| |
| /* fr_var carries the max_chars that we created the fragment with. |
| We must, of course, have allocated enough memory earlier. */ |
| gas_assert (frag->fr_var >= size); |
| |
| /* Initialize the header area. fr_offset is initialized with |
| unwind.personality_routine. */ |
| if (frag->fr_offset) |
| { |
| if (md.flags & EF_IA_64_ABI64) |
| flag_value = (bfd_vma) 3 << 32; |
| else |
| /* 32-bit unwind info block. */ |
| flag_value = (bfd_vma) 0x1003 << 32; |
| } |
| else |
| flag_value = 0; |
| |
| md_number_to_chars (frag->fr_literal, |
| (((bfd_vma) 1 << 48) /* Version. */ |
| | flag_value /* U & E handler flags. */ |
| | (len / md.pointer_size)), /* Length. */ |
| 8); |
| |
| /* Skip the header. */ |
| vbyte_mem_ptr = frag->fr_literal + 8; |
| process_unw_records (list, output_vbyte_mem); |
| |
| /* Fill the padding bytes with zeros. */ |
| if (pad != 0) |
| md_number_to_chars (frag->fr_literal + len + 8 - md.pointer_size + pad, 0, |
| md.pointer_size - pad); |
| /* Fill the unwind personality with zeros. */ |
| if (frag->fr_offset) |
| md_number_to_chars (frag->fr_literal + size - md.pointer_size, 0, |
| md.pointer_size); |
| |
| frag->fr_fix += size; |
| frag->fr_type = rs_fill; |
| frag->fr_var = 0; |
| frag->fr_offset = 0; |
| } |
| |
| static int |
| parse_predicate_and_operand (expressionS *e, unsigned *qp, const char *po) |
| { |
| int sep = parse_operand_and_eval (e, ','); |
| |
| *qp = e->X_add_number - REG_P; |
| if (e->X_op != O_register || |