| /* tc-hppa.c -- Assemble for the PA |
| Copyright (C) 1989-2021 Free Software Foundation, Inc. |
| |
| 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. */ |
| |
| /* HP PA-RISC support was contributed by the Center for Software Science |
| at the University of Utah. */ |
| |
| #include "as.h" |
| #include "safe-ctype.h" |
| #include "subsegs.h" |
| #include "dw2gencfi.h" |
| |
| #include "bfd/libhppa.h" |
| |
| /* Be careful, this file includes data *declarations*. */ |
| #include "opcode/hppa.h" |
| |
| #if defined (OBJ_ELF) && defined (OBJ_SOM) |
| error only one of OBJ_ELF and OBJ_SOM can be defined |
| #endif |
| |
| /* If we are using ELF, then we probably can support dwarf2 debug |
| records. Furthermore, if we are supporting dwarf2 debug records, |
| then we want to use the assembler support for compact line numbers. */ |
| #ifdef OBJ_ELF |
| #include "dwarf2dbg.h" |
| |
| /* A "convenient" place to put object file dependencies which do |
| not need to be seen outside of tc-hppa.c. */ |
| |
| /* Object file formats specify relocation types. */ |
| typedef enum elf_hppa_reloc_type reloc_type; |
| |
| /* Object file formats specify BFD symbol types. */ |
| typedef elf_symbol_type obj_symbol_type; |
| #define symbol_arg_reloc_info(sym)\ |
| (((obj_symbol_type *) symbol_get_bfdsym (sym))->tc_data.hppa_arg_reloc) |
| |
| #if TARGET_ARCH_SIZE == 64 |
| /* How to generate a relocation. */ |
| #define hppa_gen_reloc_type _bfd_elf64_hppa_gen_reloc_type |
| #define elf_hppa_reloc_final_type elf64_hppa_reloc_final_type |
| #else |
| #define hppa_gen_reloc_type _bfd_elf32_hppa_gen_reloc_type |
| #define elf_hppa_reloc_final_type elf32_hppa_reloc_final_type |
| #endif |
| |
| /* ELF objects can have versions, but apparently do not have anywhere |
| to store a copyright string. */ |
| #define obj_version obj_elf_version |
| #define obj_copyright obj_elf_version |
| |
| #define UNWIND_SECTION_NAME ".PARISC.unwind" |
| #endif /* OBJ_ELF */ |
| |
| #ifdef OBJ_SOM |
| /* Names of various debugging spaces/subspaces. */ |
| #define GDB_DEBUG_SPACE_NAME "$GDB_DEBUG$" |
| #define GDB_STRINGS_SUBSPACE_NAME "$GDB_STRINGS$" |
| #define GDB_SYMBOLS_SUBSPACE_NAME "$GDB_SYMBOLS$" |
| #define UNWIND_SECTION_NAME "$UNWIND$" |
| |
| /* Object file formats specify relocation types. */ |
| typedef int reloc_type; |
| |
| /* SOM objects can have both a version string and a copyright string. */ |
| #define obj_version obj_som_version |
| #define obj_copyright obj_som_copyright |
| |
| /* How to generate a relocation. */ |
| #define hppa_gen_reloc_type hppa_som_gen_reloc_type |
| |
| /* Object file formats specify BFD symbol types. */ |
| typedef som_symbol_type obj_symbol_type; |
| #define symbol_arg_reloc_info(sym)\ |
| (((obj_symbol_type *) symbol_get_bfdsym (sym))->tc_data.ap.hppa_arg_reloc) |
| |
| /* This apparently isn't in older versions of hpux reloc.h. */ |
| #ifndef R_DLT_REL |
| #define R_DLT_REL 0x78 |
| #endif |
| |
| #ifndef R_N0SEL |
| #define R_N0SEL 0xd8 |
| #endif |
| |
| #ifndef R_N1SEL |
| #define R_N1SEL 0xd9 |
| #endif |
| #endif /* OBJ_SOM */ |
| |
| #if TARGET_ARCH_SIZE == 64 |
| #define DEFAULT_LEVEL 25 |
| #else |
| #define DEFAULT_LEVEL 10 |
| #endif |
| |
| /* Various structures and types used internally in tc-hppa.c. */ |
| |
| /* Unwind table and descriptor. FIXME: Sync this with GDB version. */ |
| |
| struct unwind_desc |
| { |
| unsigned int cannot_unwind:1; |
| unsigned int millicode:1; |
| unsigned int millicode_save_rest:1; |
| unsigned int region_desc:2; |
| unsigned int save_sr:2; |
| unsigned int entry_fr:4; |
| unsigned int entry_gr:5; |
| unsigned int args_stored:1; |
| unsigned int call_fr:5; |
| unsigned int call_gr:5; |
| unsigned int save_sp:1; |
| unsigned int save_rp:1; |
| unsigned int save_rp_in_frame:1; |
| unsigned int extn_ptr_defined:1; |
| unsigned int cleanup_defined:1; |
| |
| unsigned int hpe_interrupt_marker:1; |
| unsigned int hpux_interrupt_marker:1; |
| unsigned int reserved:3; |
| unsigned int frame_size:27; |
| }; |
| |
| /* We can't rely on compilers placing bitfields in any particular |
| place, so use these macros when dumping unwind descriptors to |
| object files. */ |
| #define UNWIND_LOW32(U) \ |
| (((U)->cannot_unwind << 31) \ |
| | ((U)->millicode << 30) \ |
| | ((U)->millicode_save_rest << 29) \ |
| | ((U)->region_desc << 27) \ |
| | ((U)->save_sr << 25) \ |
| | ((U)->entry_fr << 21) \ |
| | ((U)->entry_gr << 16) \ |
| | ((U)->args_stored << 15) \ |
| | ((U)->call_fr << 10) \ |
| | ((U)->call_gr << 5) \ |
| | ((U)->save_sp << 4) \ |
| | ((U)->save_rp << 3) \ |
| | ((U)->save_rp_in_frame << 2) \ |
| | ((U)->extn_ptr_defined << 1) \ |
| | ((U)->cleanup_defined << 0)) |
| |
| #define UNWIND_HIGH32(U) \ |
| (((U)->hpe_interrupt_marker << 31) \ |
| | ((U)->hpux_interrupt_marker << 30) \ |
| | ((U)->frame_size << 0)) |
| |
| struct unwind_table |
| { |
| /* Starting and ending offsets of the region described by |
| descriptor. */ |
| unsigned int start_offset; |
| unsigned int end_offset; |
| struct unwind_desc descriptor; |
| }; |
| |
| /* This structure is used by the .callinfo, .enter, .leave pseudo-ops to |
| control the entry and exit code they generate. It is also used in |
| creation of the correct stack unwind descriptors. |
| |
| NOTE: GAS does not support .enter and .leave for the generation of |
| prologues and epilogues. FIXME. |
| |
| The fields in structure roughly correspond to the arguments available on the |
| .callinfo pseudo-op. */ |
| |
| struct call_info |
| { |
| /* The unwind descriptor being built. */ |
| struct unwind_table ci_unwind; |
| |
| /* Name of this function. */ |
| symbolS *start_symbol; |
| |
| /* (temporary) symbol used to mark the end of this function. */ |
| symbolS *end_symbol; |
| |
| /* Next entry in the chain. */ |
| struct call_info *ci_next; |
| }; |
| |
| /* Operand formats for FP instructions. Note not all FP instructions |
| allow all four formats to be used (for example fmpysub only allows |
| SGL and DBL). */ |
| typedef enum |
| { |
| SGL, DBL, ILLEGAL_FMT, QUAD, W, UW, DW, UDW, QW, UQW |
| } |
| fp_operand_format; |
| |
| /* This fully describes the symbol types which may be attached to |
| an EXPORT or IMPORT directive. Only SOM uses this formation |
| (ELF has no need for it). */ |
| typedef enum |
| { |
| SYMBOL_TYPE_UNKNOWN, |
| SYMBOL_TYPE_ABSOLUTE, |
| SYMBOL_TYPE_CODE, |
| SYMBOL_TYPE_DATA, |
| SYMBOL_TYPE_ENTRY, |
| SYMBOL_TYPE_MILLICODE, |
| SYMBOL_TYPE_PLABEL, |
| SYMBOL_TYPE_PRI_PROG, |
| SYMBOL_TYPE_SEC_PROG, |
| } |
| pa_symbol_type; |
| |
| /* This structure contains information needed to assemble |
| individual instructions. */ |
| struct pa_it |
| { |
| /* Holds the opcode after parsing by pa_ip. */ |
| unsigned long opcode; |
| |
| /* Holds an expression associated with the current instruction. */ |
| expressionS exp; |
| |
| /* Does this instruction use PC-relative addressing. */ |
| int pcrel; |
| |
| /* Floating point formats for operand1 and operand2. */ |
| fp_operand_format fpof1; |
| fp_operand_format fpof2; |
| |
| /* Whether or not we saw a truncation request on an fcnv insn. */ |
| int trunc; |
| |
| /* Holds the field selector for this instruction |
| (for example L%, LR%, etc). */ |
| long field_selector; |
| |
| /* Holds any argument relocation bits associated with this |
| instruction. (instruction should be some sort of call). */ |
| unsigned int arg_reloc; |
| |
| /* The format specification for this instruction. */ |
| int format; |
| |
| /* The relocation (if any) associated with this instruction. */ |
| reloc_type reloc; |
| }; |
| |
| /* PA-89 floating point registers are arranged like this: |
| |
| +--------------+--------------+ |
| | 0 or 16L | 16 or 16R | |
| +--------------+--------------+ |
| | 1 or 17L | 17 or 17R | |
| +--------------+--------------+ |
| | | | |
| |
| . . . |
| . . . |
| . . . |
| |
| | | | |
| +--------------+--------------+ |
| | 14 or 30L | 30 or 30R | |
| +--------------+--------------+ |
| | 15 or 31L | 31 or 31R | |
| +--------------+--------------+ */ |
| |
| /* Additional information needed to build argument relocation stubs. */ |
| struct call_desc |
| { |
| /* The argument relocation specification. */ |
| unsigned int arg_reloc; |
| |
| /* Number of arguments. */ |
| unsigned int arg_count; |
| }; |
| |
| #ifdef OBJ_SOM |
| /* This structure defines an entry in the subspace dictionary |
| chain. */ |
| |
| struct subspace_dictionary_chain |
| { |
| /* Nonzero if this space has been defined by the user code. */ |
| unsigned int ssd_defined; |
| |
| /* Name of this subspace. */ |
| char *ssd_name; |
| |
| /* GAS segment and subsegment associated with this subspace. */ |
| asection *ssd_seg; |
| int ssd_subseg; |
| |
| /* Next space in the subspace dictionary chain. */ |
| struct subspace_dictionary_chain *ssd_next; |
| }; |
| |
| typedef struct subspace_dictionary_chain ssd_chain_struct; |
| |
| /* This structure defines an entry in the subspace dictionary |
| chain. */ |
| |
| struct space_dictionary_chain |
| { |
| /* Nonzero if this space has been defined by the user code or |
| as a default space. */ |
| unsigned int sd_defined; |
| |
| /* Nonzero if this spaces has been defined by the user code. */ |
| unsigned int sd_user_defined; |
| |
| /* The space number (or index). */ |
| unsigned int sd_spnum; |
| |
| /* The name of this subspace. */ |
| char *sd_name; |
| |
| /* GAS segment to which this subspace corresponds. */ |
| asection *sd_seg; |
| |
| /* Current subsegment number being used. */ |
| int sd_last_subseg; |
| |
| /* The chain of subspaces contained within this space. */ |
| ssd_chain_struct *sd_subspaces; |
| |
| /* The next entry in the space dictionary chain. */ |
| struct space_dictionary_chain *sd_next; |
| }; |
| |
| typedef struct space_dictionary_chain sd_chain_struct; |
| |
| /* This structure defines attributes of the default subspace |
| dictionary entries. */ |
| |
| struct default_subspace_dict |
| { |
| /* Name of the subspace. */ |
| const char *name; |
| |
| /* FIXME. Is this still needed? */ |
| char defined; |
| |
| /* Nonzero if this subspace is loadable. */ |
| char loadable; |
| |
| /* Nonzero if this subspace contains only code. */ |
| char code_only; |
| |
| /* Nonzero if this is a comdat subspace. */ |
| char comdat; |
| |
| /* Nonzero if this is a common subspace. */ |
| char common; |
| |
| /* Nonzero if this is a common subspace which allows symbols |
| to be multiply defined. */ |
| char dup_common; |
| |
| /* Nonzero if this subspace should be zero filled. */ |
| char zero; |
| |
| /* Sort key for this subspace. */ |
| unsigned char sort; |
| |
| /* Access control bits for this subspace. Can represent RWX access |
| as well as privilege level changes for gateways. */ |
| int access; |
| |
| /* Index of containing space. */ |
| int space_index; |
| |
| /* Alignment (in bytes) of this subspace. */ |
| int alignment; |
| |
| /* Quadrant within space where this subspace should be loaded. */ |
| int quadrant; |
| |
| /* An index into the default spaces array. */ |
| int def_space_index; |
| |
| /* Subsegment associated with this subspace. */ |
| subsegT subsegment; |
| }; |
| |
| /* This structure defines attributes of the default space |
| dictionary entries. */ |
| |
| struct default_space_dict |
| { |
| /* Name of the space. */ |
| const char *name; |
| |
| /* Space number. It is possible to identify spaces within |
| assembly code numerically! */ |
| int spnum; |
| |
| /* Nonzero if this space is loadable. */ |
| char loadable; |
| |
| /* Nonzero if this space is "defined". FIXME is still needed */ |
| char defined; |
| |
| /* Nonzero if this space can not be shared. */ |
| char private; |
| |
| /* Sort key for this space. */ |
| unsigned char sort; |
| |
| /* Segment associated with this space. */ |
| asection *segment; |
| }; |
| #endif |
| |
| /* Structure for previous label tracking. Needed so that alignments, |
| callinfo declarations, etc can be easily attached to a particular |
| label. */ |
| typedef struct label_symbol_struct |
| { |
| struct symbol *lss_label; |
| #ifdef OBJ_SOM |
| sd_chain_struct *lss_space; |
| #endif |
| #ifdef OBJ_ELF |
| segT lss_segment; |
| #endif |
| struct label_symbol_struct *lss_next; |
| } |
| label_symbol_struct; |
| |
| /* Extra information needed to perform fixups (relocations) on the PA. */ |
| struct hppa_fix_struct |
| { |
| /* The field selector. */ |
| enum hppa_reloc_field_selector_type_alt fx_r_field; |
| |
| /* Type of fixup. */ |
| int fx_r_type; |
| |
| /* Format of fixup. */ |
| int fx_r_format; |
| |
| /* Argument relocation bits. */ |
| unsigned int fx_arg_reloc; |
| |
| /* The segment this fixup appears in. */ |
| segT segment; |
| }; |
| |
| /* Structure to hold information about predefined registers. */ |
| |
| struct pd_reg |
| { |
| const char *name; |
| int value; |
| }; |
| |
| /* This structure defines the mapping from a FP condition string |
| to a condition number which can be recorded in an instruction. */ |
| struct fp_cond_map |
| { |
| const char *string; |
| int cond; |
| }; |
| |
| /* This structure defines a mapping from a field selector |
| string to a field selector type. */ |
| struct selector_entry |
| { |
| const char *prefix; |
| int field_selector; |
| }; |
| |
| /* Prototypes for functions local to tc-hppa.c. */ |
| |
| #ifdef OBJ_SOM |
| static void pa_check_current_space_and_subspace (void); |
| #endif |
| |
| #if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD))) |
| static void pa_text (int); |
| static void pa_data (int); |
| static void pa_comm (int); |
| #endif |
| #ifdef OBJ_SOM |
| static int exact_log2 (int); |
| static void pa_compiler (int); |
| static void pa_align (int); |
| static void pa_space (int); |
| static void pa_spnum (int); |
| static void pa_subspace (int); |
| static sd_chain_struct *create_new_space (const char *, int, int, |
| int, int, int, |
| asection *, int); |
| static ssd_chain_struct *create_new_subspace (sd_chain_struct *, |
| const char *, int, int, |
| int, int, int, int, |
| int, int, int, int, |
| int, asection *); |
| static ssd_chain_struct *update_subspace (sd_chain_struct *, |
| char *, int, int, int, |
| int, int, int, int, |
| int, int, int, int, |
| asection *); |
| static sd_chain_struct *is_defined_space (const char *); |
| static ssd_chain_struct *is_defined_subspace (const char *); |
| static sd_chain_struct *pa_segment_to_space (asection *); |
| static ssd_chain_struct *pa_subsegment_to_subspace (asection *, |
| subsegT); |
| static sd_chain_struct *pa_find_space_by_number (int); |
| static unsigned int pa_subspace_start (sd_chain_struct *, int); |
| static sd_chain_struct *pa_parse_space_stmt (const char *, int); |
| #endif |
| |
| /* File and globally scoped variable declarations. */ |
| |
| #ifdef OBJ_SOM |
| /* Root and final entry in the space chain. */ |
| static sd_chain_struct *space_dict_root; |
| static sd_chain_struct *space_dict_last; |
| |
| /* The current space and subspace. */ |
| static sd_chain_struct *current_space; |
| static ssd_chain_struct *current_subspace; |
| #endif |
| |
| /* Root of the call_info chain. */ |
| static struct call_info *call_info_root; |
| |
| /* The last call_info (for functions) structure |
| seen so it can be associated with fixups and |
| function labels. */ |
| static struct call_info *last_call_info; |
| |
| /* The last call description (for actual calls). */ |
| static struct call_desc last_call_desc; |
| |
| /* handle of the OPCODE hash table */ |
| static htab_t op_hash = NULL; |
| |
| /* These characters can be suffixes of opcode names and they may be |
| followed by meaningful whitespace. We don't include `,' and `!' |
| as they never appear followed by meaningful whitespace. */ |
| const char hppa_symbol_chars[] = "*?=<>"; |
| |
| /* This array holds the chars that only start a comment at the beginning of |
| a line. If the line seems to have the form '# 123 filename' |
| .line and .file directives will appear in the pre-processed output. |
| |
| Note that input_file.c hand checks for '#' at the beginning of the |
| first line of the input file. This is because the compiler outputs |
| #NO_APP at the beginning of its output. |
| |
| Also note that C style comments will always work. */ |
| const char line_comment_chars[] = "#"; |
| |
| /* This array holds the chars that always start a comment. If the |
| pre-processor is disabled, these aren't very useful. */ |
| const char comment_chars[] = ";"; |
| |
| /* This array holds the characters which act as line separators. */ |
| const char line_separator_chars[] = "!"; |
| |
| /* Chars that can be used to separate mant from exp in floating point nums. */ |
| const char EXP_CHARS[] = "eE"; |
| |
| /* Chars that mean this number is a floating point constant. |
| As in 0f12.456 or 0d1.2345e12. |
| |
| Be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be |
| changed in read.c. Ideally it shouldn't have to know about it |
| at all, but nothing is ideal around here. */ |
| const char FLT_CHARS[] = "rRsSfFdDxXpP"; |
| |
| static struct pa_it the_insn; |
| |
| /* Points to the end of an expression just parsed by get_expression |
| and friends. FIXME. This shouldn't be handled with a file-global |
| variable. */ |
| static char *expr_end; |
| |
| /* Nonzero if a .callinfo appeared within the current procedure. */ |
| static int callinfo_found; |
| |
| /* Nonzero if the assembler is currently within a .entry/.exit pair. */ |
| static int within_entry_exit; |
| |
| /* Nonzero if the assembler is currently within a procedure definition. */ |
| static int within_procedure; |
| |
| /* Handle on structure which keep track of the last symbol |
| seen in each subspace. */ |
| static label_symbol_struct *label_symbols_rootp = NULL; |
| |
| /* Last label symbol */ |
| static label_symbol_struct last_label_symbol; |
| |
| /* Nonzero when strict matching is enabled. Zero otherwise. |
| |
| Each opcode in the table has a flag which indicates whether or |
| not strict matching should be enabled for that instruction. |
| |
| Mainly, strict causes errors to be ignored when a match failure |
| occurs. However, it also affects the parsing of register fields |
| by pa_parse_number. */ |
| static int strict; |
| |
| /* pa_parse_number returns values in `pa_number'. Mostly |
| pa_parse_number is used to return a register number, with floating |
| point registers being numbered from FP_REG_BASE upwards. |
| The bit specified with FP_REG_RSEL is set if the floating point |
| register has a `r' suffix. */ |
| #define FP_REG_BASE 64 |
| #define FP_REG_RSEL 128 |
| static int pa_number; |
| |
| #ifdef OBJ_SOM |
| /* A dummy bfd symbol so that all relocations have symbols of some kind. */ |
| static symbolS *dummy_symbol; |
| #endif |
| |
| /* Nonzero if errors are to be printed. */ |
| static int print_errors = 1; |
| |
| /* List of registers that are pre-defined: |
| |
| Each general register has one predefined name of the form |
| %r<REGNUM> which has the value <REGNUM>. |
| |
| Space and control registers are handled in a similar manner, |
| but use %sr<REGNUM> and %cr<REGNUM> as their predefined names. |
| |
| Likewise for the floating point registers, but of the form |
| %fr<REGNUM>. Floating point registers have additional predefined |
| names with 'L' and 'R' suffixes (e.g. %fr19L, %fr19R) which |
| again have the value <REGNUM>. |
| |
| Many registers also have synonyms: |
| |
| %r26 - %r23 have %arg0 - %arg3 as synonyms |
| %r28 - %r29 have %ret0 - %ret1 as synonyms |
| %fr4 - %fr7 have %farg0 - %farg3 as synonyms |
| %r30 has %sp as a synonym |
| %r27 has %dp as a synonym |
| %r2 has %rp as a synonym |
| |
| Almost every control register has a synonym; they are not listed |
| here for brevity. |
| |
| The table is sorted. Suitable for searching by a binary search. */ |
| |
| static const struct pd_reg pre_defined_registers[] = |
| { |
| {"%arg0", 26}, |
| {"%arg1", 25}, |
| {"%arg2", 24}, |
| {"%arg3", 23}, |
| {"%cr0", 0}, |
| {"%cr10", 10}, |
| {"%cr11", 11}, |
| {"%cr12", 12}, |
| {"%cr13", 13}, |
| {"%cr14", 14}, |
| {"%cr15", 15}, |
| {"%cr16", 16}, |
| {"%cr17", 17}, |
| {"%cr18", 18}, |
| {"%cr19", 19}, |
| {"%cr20", 20}, |
| {"%cr21", 21}, |
| {"%cr22", 22}, |
| {"%cr23", 23}, |
| {"%cr24", 24}, |
| {"%cr25", 25}, |
| {"%cr26", 26}, |
| {"%cr27", 27}, |
| {"%cr28", 28}, |
| {"%cr29", 29}, |
| {"%cr30", 30}, |
| {"%cr31", 31}, |
| {"%cr8", 8}, |
| {"%cr9", 9}, |
| {"%dp", 27}, |
| {"%eiem", 15}, |
| {"%eirr", 23}, |
| {"%farg0", 4 + FP_REG_BASE}, |
| {"%farg1", 5 + FP_REG_BASE}, |
| {"%farg2", 6 + FP_REG_BASE}, |
| {"%farg3", 7 + FP_REG_BASE}, |
| {"%fr0", 0 + FP_REG_BASE}, |
| {"%fr0l", 0 + FP_REG_BASE}, |
| {"%fr0r", 0 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr1", 1 + FP_REG_BASE}, |
| {"%fr10", 10 + FP_REG_BASE}, |
| {"%fr10l", 10 + FP_REG_BASE}, |
| {"%fr10r", 10 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr11", 11 + FP_REG_BASE}, |
| {"%fr11l", 11 + FP_REG_BASE}, |
| {"%fr11r", 11 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr12", 12 + FP_REG_BASE}, |
| {"%fr12l", 12 + FP_REG_BASE}, |
| {"%fr12r", 12 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr13", 13 + FP_REG_BASE}, |
| {"%fr13l", 13 + FP_REG_BASE}, |
| {"%fr13r", 13 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr14", 14 + FP_REG_BASE}, |
| {"%fr14l", 14 + FP_REG_BASE}, |
| {"%fr14r", 14 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr15", 15 + FP_REG_BASE}, |
| {"%fr15l", 15 + FP_REG_BASE}, |
| {"%fr15r", 15 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr16", 16 + FP_REG_BASE}, |
| {"%fr16l", 16 + FP_REG_BASE}, |
| {"%fr16r", 16 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr17", 17 + FP_REG_BASE}, |
| {"%fr17l", 17 + FP_REG_BASE}, |
| {"%fr17r", 17 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr18", 18 + FP_REG_BASE}, |
| {"%fr18l", 18 + FP_REG_BASE}, |
| {"%fr18r", 18 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr19", 19 + FP_REG_BASE}, |
| {"%fr19l", 19 + FP_REG_BASE}, |
| {"%fr19r", 19 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr1l", 1 + FP_REG_BASE}, |
| {"%fr1r", 1 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr2", 2 + FP_REG_BASE}, |
| {"%fr20", 20 + FP_REG_BASE}, |
| {"%fr20l", 20 + FP_REG_BASE}, |
| {"%fr20r", 20 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr21", 21 + FP_REG_BASE}, |
| {"%fr21l", 21 + FP_REG_BASE}, |
| {"%fr21r", 21 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr22", 22 + FP_REG_BASE}, |
| {"%fr22l", 22 + FP_REG_BASE}, |
| {"%fr22r", 22 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr23", 23 + FP_REG_BASE}, |
| {"%fr23l", 23 + FP_REG_BASE}, |
| {"%fr23r", 23 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr24", 24 + FP_REG_BASE}, |
| {"%fr24l", 24 + FP_REG_BASE}, |
| {"%fr24r", 24 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr25", 25 + FP_REG_BASE}, |
| {"%fr25l", 25 + FP_REG_BASE}, |
| {"%fr25r", 25 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr26", 26 + FP_REG_BASE}, |
| {"%fr26l", 26 + FP_REG_BASE}, |
| {"%fr26r", 26 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr27", 27 + FP_REG_BASE}, |
| {"%fr27l", 27 + FP_REG_BASE}, |
| {"%fr27r", 27 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr28", 28 + FP_REG_BASE}, |
| {"%fr28l", 28 + FP_REG_BASE}, |
| {"%fr28r", 28 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr29", 29 + FP_REG_BASE}, |
| {"%fr29l", 29 + FP_REG_BASE}, |
| {"%fr29r", 29 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr2l", 2 + FP_REG_BASE}, |
| {"%fr2r", 2 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr3", 3 + FP_REG_BASE}, |
| {"%fr30", 30 + FP_REG_BASE}, |
| {"%fr30l", 30 + FP_REG_BASE}, |
| {"%fr30r", 30 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr31", 31 + FP_REG_BASE}, |
| {"%fr31l", 31 + FP_REG_BASE}, |
| {"%fr31r", 31 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr3l", 3 + FP_REG_BASE}, |
| {"%fr3r", 3 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr4", 4 + FP_REG_BASE}, |
| {"%fr4l", 4 + FP_REG_BASE}, |
| {"%fr4r", 4 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr5", 5 + FP_REG_BASE}, |
| {"%fr5l", 5 + FP_REG_BASE}, |
| {"%fr5r", 5 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr6", 6 + FP_REG_BASE}, |
| {"%fr6l", 6 + FP_REG_BASE}, |
| {"%fr6r", 6 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr7", 7 + FP_REG_BASE}, |
| {"%fr7l", 7 + FP_REG_BASE}, |
| {"%fr7r", 7 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr8", 8 + FP_REG_BASE}, |
| {"%fr8l", 8 + FP_REG_BASE}, |
| {"%fr8r", 8 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fr9", 9 + FP_REG_BASE}, |
| {"%fr9l", 9 + FP_REG_BASE}, |
| {"%fr9r", 9 + FP_REG_BASE + FP_REG_RSEL}, |
| {"%fret", 4}, |
| {"%hta", 25}, |
| {"%iir", 19}, |
| {"%ior", 21}, |
| {"%ipsw", 22}, |
| {"%isr", 20}, |
| {"%itmr", 16}, |
| {"%iva", 14}, |
| #if TARGET_ARCH_SIZE == 64 |
| {"%mrp", 2}, |
| #else |
| {"%mrp", 31}, |
| #endif |
| {"%pcoq", 18}, |
| {"%pcsq", 17}, |
| {"%pidr1", 8}, |
| {"%pidr2", 9}, |
| {"%pidr3", 12}, |
| {"%pidr4", 13}, |
| {"%ppda", 24}, |
| {"%r0", 0}, |
| {"%r1", 1}, |
| {"%r10", 10}, |
| {"%r11", 11}, |
| {"%r12", 12}, |
| {"%r13", 13}, |
| {"%r14", 14}, |
| {"%r15", 15}, |
| {"%r16", 16}, |
| {"%r17", 17}, |
| {"%r18", 18}, |
| {"%r19", 19}, |
| {"%r2", 2}, |
| {"%r20", 20}, |
| {"%r21", 21}, |
| {"%r22", 22}, |
| {"%r23", 23}, |
| {"%r24", 24}, |
| {"%r25", 25}, |
| {"%r26", 26}, |
| {"%r27", 27}, |
| {"%r28", 28}, |
| {"%r29", 29}, |
| {"%r3", 3}, |
| {"%r30", 30}, |
| {"%r31", 31}, |
| {"%r4", 4}, |
| {"%r5", 5}, |
| {"%r6", 6}, |
| {"%r7", 7}, |
| {"%r8", 8}, |
| {"%r9", 9}, |
| {"%rctr", 0}, |
| {"%ret0", 28}, |
| {"%ret1", 29}, |
| {"%rp", 2}, |
| {"%sar", 11}, |
| {"%sp", 30}, |
| {"%sr0", 0}, |
| {"%sr1", 1}, |
| {"%sr2", 2}, |
| {"%sr3", 3}, |
| {"%sr4", 4}, |
| {"%sr5", 5}, |
| {"%sr6", 6}, |
| {"%sr7", 7}, |
| {"%t1", 22}, |
| {"%t2", 21}, |
| {"%t3", 20}, |
| {"%t4", 19}, |
| {"%tf1", 11}, |
| {"%tf2", 10}, |
| {"%tf3", 9}, |
| {"%tf4", 8}, |
| {"%tr0", 24}, |
| {"%tr1", 25}, |
| {"%tr2", 26}, |
| {"%tr3", 27}, |
| {"%tr4", 28}, |
| {"%tr5", 29}, |
| {"%tr6", 30}, |
| {"%tr7", 31} |
| }; |
| |
| /* This table is sorted by order of the length of the string. This is |
| so we check for <> before we check for <. If we had a <> and checked |
| for < first, we would get a false match. */ |
| static const struct fp_cond_map fp_cond_map[] = |
| { |
| {"false?", 0}, |
| {"false", 1}, |
| {"true?", 30}, |
| {"true", 31}, |
| {"!<=>", 3}, |
| {"!?>=", 8}, |
| {"!?<=", 16}, |
| {"!<>", 7}, |
| {"!>=", 11}, |
| {"!?>", 12}, |
| {"?<=", 14}, |
| {"!<=", 19}, |
| {"!?<", 20}, |
| {"?>=", 22}, |
| {"!?=", 24}, |
| {"!=t", 27}, |
| {"<=>", 29}, |
| {"=t", 5}, |
| {"?=", 6}, |
| {"?<", 10}, |
| {"<=", 13}, |
| {"!>", 15}, |
| {"?>", 18}, |
| {">=", 21}, |
| {"!<", 23}, |
| {"<>", 25}, |
| {"!=", 26}, |
| {"!?", 28}, |
| {"?", 2}, |
| {"=", 4}, |
| {"<", 9}, |
| {">", 17} |
| }; |
| |
| static const struct selector_entry selector_table[] = |
| { |
| {"f", e_fsel}, |
| {"l", e_lsel}, |
| {"ld", e_ldsel}, |
| {"lp", e_lpsel}, |
| {"lr", e_lrsel}, |
| {"ls", e_lssel}, |
| {"lt", e_ltsel}, |
| {"ltp", e_ltpsel}, |
| {"n", e_nsel}, |
| {"nl", e_nlsel}, |
| {"nlr", e_nlrsel}, |
| {"p", e_psel}, |
| {"r", e_rsel}, |
| {"rd", e_rdsel}, |
| {"rp", e_rpsel}, |
| {"rr", e_rrsel}, |
| {"rs", e_rssel}, |
| {"rt", e_rtsel}, |
| {"rtp", e_rtpsel}, |
| {"t", e_tsel}, |
| }; |
| |
| #ifdef OBJ_SOM |
| /* default space and subspace dictionaries */ |
| |
| #define GDB_SYMBOLS GDB_SYMBOLS_SUBSPACE_NAME |
| #define GDB_STRINGS GDB_STRINGS_SUBSPACE_NAME |
| |
| /* pre-defined subsegments (subspaces) for the HPPA. */ |
| #define SUBSEG_CODE 0 |
| #define SUBSEG_LIT 1 |
| #define SUBSEG_MILLI 2 |
| #define SUBSEG_DATA 0 |
| #define SUBSEG_BSS 2 |
| #define SUBSEG_UNWIND 3 |
| #define SUBSEG_GDB_STRINGS 0 |
| #define SUBSEG_GDB_SYMBOLS 1 |
| |
| static struct default_subspace_dict pa_def_subspaces[] = |
| { |
| {"$CODE$", 1, 1, 1, 0, 0, 0, 0, 24, 0x2c, 0, 8, 0, 0, SUBSEG_CODE}, |
| {"$DATA$", 1, 1, 0, 0, 0, 0, 0, 24, 0x1f, 1, 8, 1, 1, SUBSEG_DATA}, |
| {"$LIT$", 1, 1, 0, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, 0, SUBSEG_LIT}, |
| {"$MILLICODE$", 1, 1, 0, 0, 0, 0, 0, 8, 0x2c, 0, 8, 0, 0, SUBSEG_MILLI}, |
| {"$BSS$", 1, 1, 0, 0, 0, 0, 1, 80, 0x1f, 1, 8, 1, 1, SUBSEG_BSS}, |
| {NULL, 0, 1, 0, 0, 0, 0, 0, 255, 0x1f, 0, 4, 0, 0, 0} |
| }; |
| |
| static struct default_space_dict pa_def_spaces[] = |
| { |
| {"$TEXT$", 0, 1, 1, 0, 8, ASEC_NULL}, |
| {"$PRIVATE$", 1, 1, 1, 1, 16, ASEC_NULL}, |
| {NULL, 0, 0, 0, 0, 0, ASEC_NULL} |
| }; |
| |
| /* Misc local definitions used by the assembler. */ |
| |
| /* These macros are used to maintain spaces/subspaces. */ |
| #define SPACE_DEFINED(space_chain) (space_chain)->sd_defined |
| #define SPACE_USER_DEFINED(space_chain) (space_chain)->sd_user_defined |
| #define SPACE_SPNUM(space_chain) (space_chain)->sd_spnum |
| #define SPACE_NAME(space_chain) (space_chain)->sd_name |
| |
| #define SUBSPACE_DEFINED(ss_chain) (ss_chain)->ssd_defined |
| #define SUBSPACE_NAME(ss_chain) (ss_chain)->ssd_name |
| #endif |
| |
| /* Return nonzero if the string pointed to by S potentially represents |
| a right or left half of a FP register */ |
| #define IS_R_SELECT(S) (*(S) == 'R' || *(S) == 'r') |
| #define IS_L_SELECT(S) (*(S) == 'L' || *(S) == 'l') |
| |
| /* Store immediate values of shift/deposit/extract functions. */ |
| |
| #define SAVE_IMMEDIATE(VALUE) \ |
| { \ |
| if (immediate_check) \ |
| { \ |
| if (pos == -1) \ |
| pos = (VALUE); \ |
| else if (len == -1) \ |
| len = (VALUE); \ |
| } \ |
| } |
| |
| /* Insert FIELD into OPCODE starting at bit START. Continue pa_ip |
| main loop after insertion. */ |
| |
| #define INSERT_FIELD_AND_CONTINUE(OPCODE, FIELD, START) \ |
| { \ |
| ((OPCODE) |= (FIELD) << (START)); \ |
| continue; \ |
| } |
| |
| /* Simple range checking for FIELD against HIGH and LOW bounds. |
| IGNORE is used to suppress the error message. */ |
| |
| #define CHECK_FIELD(FIELD, HIGH, LOW, IGNORE) \ |
| { \ |
| if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \ |
| { \ |
| if (! IGNORE) \ |
| as_bad (_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \ |
| (int) (FIELD));\ |
| break; \ |
| } \ |
| } |
| |
| /* Variant of CHECK_FIELD for use in md_apply_fix and other places where |
| the current file and line number are not valid. */ |
| |
| #define CHECK_FIELD_WHERE(FIELD, HIGH, LOW, FILENAME, LINE) \ |
| { \ |
| if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \ |
| { \ |
| as_bad_where ((FILENAME), (LINE), \ |
| _("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \ |
| (int) (FIELD));\ |
| break; \ |
| } \ |
| } |
| |
| /* Simple alignment checking for FIELD against ALIGN (a power of two). |
| IGNORE is used to suppress the error message. */ |
| |
| #define CHECK_ALIGN(FIELD, ALIGN, IGNORE) \ |
| { \ |
| if ((FIELD) & ((ALIGN) - 1)) \ |
| { \ |
| if (! IGNORE) \ |
| as_bad (_("Field not properly aligned [%d] (%d)."), (ALIGN), \ |
| (int) (FIELD));\ |
| break; \ |
| } \ |
| } |
| |
| #define is_DP_relative(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$global$") == 0) |
| |
| #define is_SB_relative(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$segrel$") == 0) |
| |
| #define is_PC_relative(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$PIC_pcrel$0") == 0) |
| |
| #define is_tls_gdidx(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_gdidx$") == 0) |
| |
| #define is_tls_ldidx(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_ldidx$") == 0) |
| |
| #define is_tls_dtpoff(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_dtpoff$") == 0) |
| |
| #define is_tls_ieoff(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_ieoff$") == 0) |
| |
| #define is_tls_leoff(exp) \ |
| ((exp).X_op == O_subtract \ |
| && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_leoff$") == 0) |
| |
| /* We need some complex handling for stabs (sym1 - sym2). Luckily, we'll |
| always be able to reduce the expression to a constant, so we don't |
| need real complex handling yet. */ |
| #define is_complex(exp) \ |
| ((exp).X_op != O_constant && (exp).X_op != O_symbol) |
| |
| /* Actual functions to implement the PA specific code for the assembler. */ |
| |
| /* Called before writing the object file. Make sure entry/exit and |
| proc/procend pairs match. */ |
| |
| void |
| pa_check_eof (void) |
| { |
| if (within_entry_exit) |
| as_fatal (_("Missing .exit\n")); |
| |
| if (within_procedure) |
| as_fatal (_("Missing .procend\n")); |
| } |
| |
| /* Returns a pointer to the label_symbol_struct for the current space. |
| or NULL if no label_symbol_struct exists for the current space. */ |
| |
| static label_symbol_struct * |
| pa_get_label (void) |
| { |
| label_symbol_struct *label_chain = label_symbols_rootp; |
| |
| if (label_chain) |
| { |
| #ifdef OBJ_SOM |
| if (current_space == label_chain->lss_space && label_chain->lss_label) |
| return label_chain; |
| #endif |
| #ifdef OBJ_ELF |
| if (now_seg == label_chain->lss_segment && label_chain->lss_label) |
| return label_chain; |
| #endif |
| } |
| |
| return NULL; |
| } |
| |
| /* Defines a label for the current space. If one is already defined, |
| this function will replace it with the new label. */ |
| |
| void |
| pa_define_label (symbolS *symbol) |
| { |
| label_symbol_struct *label_chain = label_symbols_rootp; |
| |
| if (!label_chain) |
| label_chain = &last_label_symbol; |
| |
| label_chain->lss_label = symbol; |
| #ifdef OBJ_SOM |
| label_chain->lss_space = current_space; |
| #endif |
| #ifdef OBJ_ELF |
| label_chain->lss_segment = now_seg; |
| #endif |
| |
| /* Not used. */ |
| label_chain->lss_next = NULL; |
| |
| label_symbols_rootp = label_chain; |
| |
| #ifdef OBJ_ELF |
| dwarf2_emit_label (symbol); |
| #endif |
| } |
| |
| /* Removes a label definition for the current space. |
| If there is no label_symbol_struct entry, then no action is taken. */ |
| |
| static void |
| pa_undefine_label (void) |
| { |
| label_symbols_rootp = NULL; |
| } |
| |
| /* An HPPA-specific version of fix_new. This is required because the HPPA |
| code needs to keep track of some extra stuff. Each call to fix_new_hppa |
| results in the creation of an instance of an hppa_fix_struct. An |
| hppa_fix_struct stores the extra information along with a pointer to the |
| original fixS. This is attached to the original fixup via the |
| tc_fix_data field. */ |
| |
| static void |
| fix_new_hppa (fragS *frag, |
| int where, |
| int size, |
| symbolS *add_symbol, |
| offsetT offset, |
| expressionS *exp, |
| int pcrel, |
| bfd_reloc_code_real_type r_type, |
| enum hppa_reloc_field_selector_type_alt r_field, |
| int r_format, |
| unsigned int arg_reloc, |
| int unwind_bits ATTRIBUTE_UNUSED) |
| { |
| fixS *new_fix; |
| struct hppa_fix_struct *hppa_fix = XOBNEW (¬es, struct hppa_fix_struct); |
| |
| if (exp != NULL) |
| new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type); |
| else |
| new_fix = fix_new (frag, where, size, add_symbol, offset, pcrel, r_type); |
| new_fix->tc_fix_data = (void *) hppa_fix; |
| hppa_fix->fx_r_type = r_type; |
| hppa_fix->fx_r_field = r_field; |
| hppa_fix->fx_r_format = r_format; |
| hppa_fix->fx_arg_reloc = arg_reloc; |
| hppa_fix->segment = now_seg; |
| #ifdef OBJ_SOM |
| if (r_type == R_ENTRY || r_type == R_EXIT) |
| new_fix->fx_offset = unwind_bits; |
| #endif |
| |
| /* foo-$global$ is used to access non-automatic storage. $global$ |
| is really just a marker and has served its purpose, so eliminate |
| it now so as not to confuse write.c. Ditto for $PIC_pcrel$0. */ |
| if (new_fix->fx_subsy |
| && (strcmp (S_GET_NAME (new_fix->fx_subsy), "$global$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$segrel$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$PIC_pcrel$0") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_gdidx$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_ldidx$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_dtpoff$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_ieoff$") == 0 |
| || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_leoff$") == 0)) |
| new_fix->fx_subsy = NULL; |
| } |
| |
| /* This fix_new is called by cons via TC_CONS_FIX_NEW. |
| hppa_field_selector is set by the parse_cons_expression_hppa. */ |
| |
| void |
| cons_fix_new_hppa (fragS *frag, int where, int size, expressionS *exp, |
| int hppa_field_selector) |
| { |
| unsigned int rel_type; |
| |
| /* Get a base relocation type. */ |
| if (is_DP_relative (*exp)) |
| rel_type = R_HPPA_GOTOFF; |
| else if (is_PC_relative (*exp)) |
| rel_type = R_HPPA_PCREL_CALL; |
| #ifdef OBJ_ELF |
| else if (is_SB_relative (*exp)) |
| rel_type = R_PARISC_SEGREL32; |
| else if (is_tls_gdidx (*exp)) |
| rel_type = R_PARISC_TLS_GD21L; |
| else if (is_tls_ldidx (*exp)) |
| rel_type = R_PARISC_TLS_LDM21L; |
| else if (is_tls_dtpoff (*exp)) |
| rel_type = R_PARISC_TLS_LDO21L; |
| else if (is_tls_ieoff (*exp)) |
| rel_type = R_PARISC_TLS_IE21L; |
| else if (is_tls_leoff (*exp)) |
| rel_type = R_PARISC_TLS_LE21L; |
| #endif |
| else if (is_complex (*exp)) |
| rel_type = R_HPPA_COMPLEX; |
| else |
| rel_type = R_HPPA; |
| |
| if (hppa_field_selector != e_psel && hppa_field_selector != e_fsel) |
| { |
| as_warn (_("Invalid field selector. Assuming F%%.")); |
| hppa_field_selector = e_fsel; |
| } |
| |
| fix_new_hppa (frag, where, size, |
| (symbolS *) NULL, (offsetT) 0, exp, 0, rel_type, |
| hppa_field_selector, size * 8, 0, 0); |
| } |
| |
| /* Mark (via expr_end) the end of an expression (I think). FIXME. */ |
| |
| static void |
| get_expression (char *str) |
| { |
| char *save_in; |
| asection *seg; |
| |
| save_in = input_line_pointer; |
| input_line_pointer = str; |
| seg = expression (&the_insn.exp); |
| if (!(seg == absolute_section |
| || seg == undefined_section |
| || SEG_NORMAL (seg))) |
| { |
| as_warn (_("Bad segment in expression.")); |
| expr_end = input_line_pointer; |
| input_line_pointer = save_in; |
| return; |
| } |
| expr_end = input_line_pointer; |
| input_line_pointer = save_in; |
| } |
| |
| /* Parse a PA nullification completer (,n). Return nonzero if the |
| completer was found; return zero if no completer was found. */ |
| |
| static int |
| pa_parse_nullif (char **s) |
| { |
| int nullif; |
| |
| nullif = 0; |
| if (**s == ',') |
| { |
| *s = *s + 1; |
| if (strncasecmp (*s, "n", 1) == 0) |
| nullif = 1; |
| else |
| { |
| as_bad (_("Invalid Nullification: (%c)"), **s); |
| nullif = 0; |
| } |
| *s = *s + 1; |
| } |
| |
| return nullif; |
| } |
| |
| const char * |
| md_atof (int type, char *litP, int *sizeP) |
| { |
| return ieee_md_atof (type, litP, sizeP, true); |
| } |
| |
| /* Write out big-endian. */ |
| |
| void |
| md_number_to_chars (char *buf, valueT val, int n) |
| { |
| number_to_chars_bigendian (buf, val, n); |
| } |
| |
| /* Translate internal representation of relocation info to BFD target |
| format. */ |
| |
| arelent ** |
| tc_gen_reloc (asection *section, fixS *fixp) |
| { |
| arelent *reloc; |
| struct hppa_fix_struct *hppa_fixp; |
| static arelent *no_relocs = NULL; |
| arelent **relocs; |
| reloc_type **codes; |
| reloc_type code; |
| int n_relocs; |
| int i; |
| |
| hppa_fixp = (struct hppa_fix_struct *) fixp->tc_fix_data; |
| if (fixp->fx_addsy == 0) |
| return &no_relocs; |
| |
| gas_assert (hppa_fixp != 0); |
| gas_assert (section != 0); |
| |
| reloc = XNEW (arelent); |
| |
| reloc->sym_ptr_ptr = XNEW (asymbol *); |
| *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
| |
| /* Allow fixup_segment to recognize hand-written pc-relative relocations. |
| When we went through cons_fix_new_hppa, we classified them as complex. */ |
| /* ??? It might be better to hide this +8 stuff in tc_cfi_emit_pcrel_expr, |
| undefine DIFF_EXPR_OK, and let these sorts of complex expressions fail |
| when R_HPPA_COMPLEX == R_PARISC_UNIMPLEMENTED. */ |
| if (fixp->fx_r_type == (int) R_HPPA_COMPLEX |
| && fixp->fx_pcrel) |
| { |
| fixp->fx_r_type = (int) R_HPPA_PCREL_CALL; |
| fixp->fx_offset += 8; |
| } |
| |
| codes = hppa_gen_reloc_type (stdoutput, |
| (int) fixp->fx_r_type, |
| hppa_fixp->fx_r_format, |
| hppa_fixp->fx_r_field, |
| fixp->fx_subsy != NULL, |
| symbol_get_bfdsym (fixp->fx_addsy)); |
| |
| if (codes == NULL) |
| { |
| as_bad_where (fixp->fx_file, fixp->fx_line, _("Cannot handle fixup")); |
| abort (); |
| } |
| |
| for (n_relocs = 0; codes[n_relocs]; n_relocs++) |
| ; |
| |
| relocs = XNEWVEC (arelent *, n_relocs + 1); |
| reloc = XNEWVEC (arelent, n_relocs); |
| for (i = 0; i < n_relocs; i++) |
| relocs[i] = &reloc[i]; |
| |
| relocs[n_relocs] = NULL; |
| |
| #ifdef OBJ_ELF |
| switch (fixp->fx_r_type) |
| { |
| default: |
| gas_assert (n_relocs == 1); |
| |
| code = *codes[0]; |
| |
| /* Now, do any processing that is dependent on the relocation type. */ |
| switch (code) |
| { |
| case R_PARISC_DLTREL21L: |
| case R_PARISC_DLTREL14R: |
| case R_PARISC_DLTREL14F: |
| case R_PARISC_PLABEL32: |
| case R_PARISC_PLABEL21L: |
| case R_PARISC_PLABEL14R: |
| /* For plabel relocations, the addend of the |
| relocation should be either 0 (no static link) or 2 |
| (static link required). This adjustment is done in |
| bfd/elf32-hppa.c:elf32_hppa_relocate_section. |
| |
| We also slam a zero addend into the DLT relative relocs; |
| it doesn't make a lot of sense to use any addend since |
| it gets you a different (eg unknown) DLT entry. */ |
| reloc->addend = 0; |
| break; |
| |
| #ifdef ELF_ARG_RELOC |
| case R_PARISC_PCREL17R: |
| case R_PARISC_PCREL17F: |
| case R_PARISC_PCREL17C: |
| case R_PARISC_DIR17R: |
| case R_PARISC_DIR17F: |
| case R_PARISC_PCREL21L: |
| case R_PARISC_DIR21L: |
| reloc->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc, |
| fixp->fx_offset); |
| break; |
| #endif |
| |
| case R_PARISC_DIR32: |
| /* Facilitate hand-crafted unwind info. */ |
| if (strcmp (section->name, UNWIND_SECTION_NAME) == 0) |
| code = R_PARISC_SEGREL32; |
| /* Fallthru */ |
| |
| default: |
| reloc->addend = fixp->fx_offset; |
| break; |
| } |
| |
| reloc->sym_ptr_ptr = XNEW (asymbol *); |
| *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
| reloc->howto = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) code); |
| reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| |
| gas_assert (reloc->howto && (unsigned int) code == reloc->howto->type); |
| break; |
| } |
| #else /* OBJ_SOM */ |
| |
| /* Walk over reach relocation returned by the BFD backend. */ |
| for (i = 0; i < n_relocs; i++) |
| { |
| code = *codes[i]; |
| |
| relocs[i]->sym_ptr_ptr = XNEW (asymbol *); |
| *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
| relocs[i]->howto = |
| bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) code); |
| relocs[i]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| |
| switch (code) |
| { |
| case R_COMP2: |
| /* The only time we ever use a R_COMP2 fixup is for the difference |
| of two symbols. With that in mind we fill in all four |
| relocs now and break out of the loop. */ |
| gas_assert (i == 1); |
| relocs[0]->sym_ptr_ptr |
| = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr; |
| relocs[0]->howto |
| = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) *codes[0]); |
| relocs[0]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| relocs[0]->addend = 0; |
| relocs[1]->sym_ptr_ptr = XNEW (asymbol *); |
| *relocs[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
| relocs[1]->howto |
| = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) *codes[1]); |
| relocs[1]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| relocs[1]->addend = 0; |
| relocs[2]->sym_ptr_ptr = XNEW (asymbol *); |
| *relocs[2]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy); |
| relocs[2]->howto |
| = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) *codes[2]); |
| relocs[2]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| relocs[2]->addend = 0; |
| relocs[3]->sym_ptr_ptr |
| = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr; |
| relocs[3]->howto |
| = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) *codes[3]); |
| relocs[3]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| relocs[3]->addend = 0; |
| relocs[4]->sym_ptr_ptr |
| = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr; |
| relocs[4]->howto |
| = bfd_reloc_type_lookup (stdoutput, |
| (bfd_reloc_code_real_type) *codes[4]); |
| relocs[4]->address = fixp->fx_frag->fr_address + fixp->fx_where; |
| relocs[4]->addend = 0; |
| goto done; |
| case R_PCREL_CALL: |
| case R_ABS_CALL: |
| relocs[i]->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc, 0); |
| break; |
| |
| case R_DLT_REL: |
| case R_DATA_PLABEL: |
| case R_CODE_PLABEL: |
| /* For plabel relocations, the addend of the |
| relocation should be either 0 (no static link) or 2 |
| (static link required). |
| |
| FIXME: We always assume no static link! |
| |
| We also slam a zero addend into the DLT relative relocs; |
| it doesn't make a lot of sense to use any addend since |
| it gets you a different (eg unknown) DLT entry. */ |
| relocs[i]->addend = 0; |
| break; |
| |
| case R_N_MODE: |
| case R_S_MODE: |
| case R_D_MODE: |
| case R_R_MODE: |
| case R_FSEL: |
| case R_LSEL: |
| case R_RSEL: |
| case R_BEGIN_BRTAB: |
| case R_END_BRTAB: |
| case R_BEGIN_TRY: |
| case R_N0SEL: |
| case R_N1SEL: |
| /* There is no symbol or addend associated with these fixups. */ |
| relocs[i]->sym_ptr_ptr = XNEW (asymbol *); |
| *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (dummy_symbol); |
| relocs[i]->addend = 0; |
| break; |
| |
| case R_END_TRY: |
| case R_ENTRY: |
| case R_EXIT: |
| /* There is no symbol associated with these fixups. */ |
| relocs[i]->sym_ptr_ptr = XNEW (asymbol *); |
| *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (dummy_symbol); |
| relocs[i]->addend = fixp->fx_offset; |
| break; |
| |
| default: |
| relocs[i]->addend = fixp->fx_offset; |
| } |
| } |
| |
| done: |
| #endif |
| |
| return relocs; |
| } |
| |
| /* Process any machine dependent frag types. */ |
| |
| void |
| md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, |
| asection *sec ATTRIBUTE_UNUSED, |
| fragS *fragP) |
| { |
| unsigned int address; |
| |
| if (fragP->fr_type == rs_machine_dependent) |
| { |
| switch ((int) fragP->fr_subtype) |
| { |
| case 0: |
| fragP->fr_type = rs_fill; |
| know (fragP->fr_var == 1); |
| know (fragP->fr_next); |
| address = fragP->fr_address + fragP->fr_fix; |
| if (address % fragP->fr_offset) |
| { |
| fragP->fr_offset = |
| fragP->fr_next->fr_address |
| - fragP->fr_address |
| - fragP->fr_fix; |
| } |
| else |
| fragP->fr_offset = 0; |
| break; |
| } |
| } |
| } |
| |
| /* Round up a section size to the appropriate boundary. */ |
| |
| valueT |
| md_section_align (asection *segment, valueT size) |
| { |
| int align = bfd_section_alignment (segment); |
| int align2 = (1 << align) - 1; |
| |
| return (size + align2) & ~align2; |
| } |
| |
| /* Return the approximate size of a frag before relaxation has occurred. */ |
| |
| int |
| md_estimate_size_before_relax (fragS *fragP, asection *segment ATTRIBUTE_UNUSED) |
| { |
| int size; |
| |
| size = 0; |
| |
| while ((fragP->fr_fix + size) % fragP->fr_offset) |
| size++; |
| |
| return size; |
| } |
| |
| #ifdef OBJ_ELF |
| # ifdef WARN_COMMENTS |
| const char *md_shortopts = "Vc"; |
| # else |
| const char *md_shortopts = "V"; |
| # endif |
| #else |
| # ifdef WARN_COMMENTS |
| const char *md_shortopts = "c"; |
| # else |
| const char *md_shortopts = ""; |
| # endif |
| #endif |
| |
| struct option md_longopts[] = |
| { |
| #ifdef WARN_COMMENTS |
| {"warn-comment", no_argument, NULL, 'c'}, |
| #endif |
| {NULL, no_argument, NULL, 0} |
| }; |
| size_t md_longopts_size = sizeof (md_longopts); |
| |
| int |
| md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED) |
| { |
| switch (c) |
| { |
| default: |
| return 0; |
| |
| #ifdef OBJ_ELF |
| case 'V': |
| print_version_id (); |
| break; |
| #endif |
| #ifdef WARN_COMMENTS |
| case 'c': |
| warn_comment = 1; |
| break; |
| #endif |
| } |
| |
| return 1; |
| } |
| |
| void |
| md_show_usage (FILE *stream ATTRIBUTE_UNUSED) |
| { |
| #ifdef OBJ_ELF |
| fprintf (stream, _("\ |
| -Q ignored\n")); |
| #endif |
| #ifdef WARN_COMMENTS |
| fprintf (stream, _("\ |
| -c print a warning if a comment is found\n")); |
| #endif |
| } |
| |
| /* We have no need to default values of symbols. */ |
| |
| symbolS * |
| md_undefined_symbol (char *name ATTRIBUTE_UNUSED) |
| { |
| return NULL; |
| } |
| |
| #if defined (OBJ_SOM) || defined (ELF_ARG_RELOC) |
| #define nonzero_dibits(x) \ |
| ((x) | (((x) & 0x55555555) << 1) | (((x) & 0xAAAAAAAA) >> 1)) |
| #define arg_reloc_stub_needed(CALLER, CALLEE) \ |
| (((CALLER) ^ (CALLEE)) & nonzero_dibits (CALLER) & nonzero_dibits (CALLEE)) |
| #else |
| #define arg_reloc_stub_needed(CALLER, CALLEE) 0 |
| #endif |
| |
| /* Apply a fixup to an instruction. */ |
| |
| void |
| md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) |
| { |
| char *fixpos; |
| struct hppa_fix_struct *hppa_fixP; |
| offsetT new_val; |
| int insn, val, fmt; |
| |
| /* SOM uses R_HPPA_ENTRY and R_HPPA_EXIT relocations which can |
| never be "applied" (they are just markers). Likewise for |
| R_HPPA_BEGIN_BRTAB and R_HPPA_END_BRTAB. */ |
| #ifdef OBJ_SOM |
| if (fixP->fx_r_type == R_HPPA_ENTRY |
| || fixP->fx_r_type == R_HPPA_EXIT |
| || fixP->fx_r_type == R_HPPA_BEGIN_BRTAB |
| || fixP->fx_r_type == R_HPPA_END_BRTAB |
| || fixP->fx_r_type == R_HPPA_BEGIN_TRY) |
| return; |
| |
| /* Disgusting. We must set fx_offset ourselves -- R_HPPA_END_TRY |
| fixups are considered not adjustable, which in turn causes |
| adjust_reloc_syms to not set fx_offset. Ugh. */ |
| if (fixP->fx_r_type == R_HPPA_END_TRY) |
| { |
| fixP->fx_offset = * valP; |
| return; |
| } |
| #endif |
| #ifdef OBJ_ELF |
| if (fixP->fx_r_type == (int) R_PARISC_GNU_VTENTRY |
| || fixP->fx_r_type == (int) R_PARISC_GNU_VTINHERIT) |
| return; |
| #endif |
| |
| if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) |
| fixP->fx_done = 1; |
| |
| /* There should be a HPPA specific fixup associated with the GAS fixup. */ |
| hppa_fixP = (struct hppa_fix_struct *) fixP->tc_fix_data; |
| if (hppa_fixP == NULL) |
| { |
| as_bad_where (fixP->fx_file, fixP->fx_line, |
| _("no hppa_fixup entry for fixup type 0x%x"), |
| fixP->fx_r_type); |
| return; |
| } |
| |
| fixpos = fixP->fx_frag->fr_literal + fixP->fx_where; |
| |
| if (fixP->fx_size != 4 || hppa_fixP->fx_r_format == 32) |
| { |
| /* Handle constant output. */ |
| number_to_chars_bigendian (fixpos, *valP, fixP->fx_size); |
| return; |
| } |
| |
| insn = bfd_get_32 (stdoutput, fixpos); |
| fmt = bfd_hppa_insn2fmt (stdoutput, insn); |
| |
| /* If there is a symbol associated with this fixup, then it's something |
| which will need a SOM relocation (except for some PC-relative relocs). |
| In such cases we should treat the "val" or "addend" as zero since it |
| will be added in as needed from fx_offset in tc_gen_reloc. */ |
| if ((fixP->fx_addsy != NULL |
| || fixP->fx_r_type == (int) R_HPPA_NONE) |
| #ifdef OBJ_SOM |
| && fmt != 32 |
| #endif |
| ) |
| new_val = ((fmt == 12 || fmt == 17 || fmt == 22) ? 8 : 0); |
| #ifdef OBJ_SOM |
| /* These field selectors imply that we do not want an addend. */ |
| else if (hppa_fixP->fx_r_field == e_psel |
| || hppa_fixP->fx_r_field == e_rpsel |
| || hppa_fixP->fx_r_field == e_lpsel |
| || hppa_fixP->fx_r_field == e_tsel |
| || hppa_fixP->fx_r_field == e_rtsel |
| || hppa_fixP->fx_r_field == e_ltsel) |
| new_val = ((fmt == 12 || fmt == 17 || fmt == 22) ? 8 : 0); |
| #endif |
| else |
| new_val = hppa_field_adjust (* valP, 0, hppa_fixP->fx_r_field); |
| |
| /* Handle pc-relative exceptions from above. */ |
| if ((fmt == 12 || fmt == 17 || fmt == 22) |
| && fixP->fx_addsy |
| && fixP->fx_pcrel |
| && !arg_reloc_stub_needed (symbol_arg_reloc_info (fixP->fx_addsy), |
| hppa_fixP->fx_arg_reloc) |
| #ifdef OBJ_ELF |
| && (* valP - 8 + 8192 < 16384 |
| || (fmt == 17 && * valP - 8 + 262144 < 524288) |
| || (fmt == 22 && * valP - 8 + 8388608 < 16777216)) |
| #endif |
| #ifdef OBJ_SOM |
| && (* valP - 8 + 262144 < 524288 |
| || (fmt == 22 && * valP - 8 + 8388608 < 16777216)) |
| #endif |
| && !S_IS_EXTERNAL (fixP->fx_addsy) |
| && !S_IS_WEAK (fixP->fx_addsy) |
| && S_GET_SEGMENT (fixP->fx_addsy) == hppa_fixP->segment |
| && !(fixP->fx_subsy |
| && S_GET_SEGMENT (fixP->fx_subsy) != hppa_fixP->segment)) |
| { |
| new_val = hppa_field_adjust (* valP, 0, hppa_fixP->fx_r_field); |
| } |
| |
| switch (fmt) |
| { |
| case 10: |
| CHECK_FIELD_WHERE (new_val, 8191, -8192, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val; |
| |
| insn = (insn & ~ 0x3ff1) | (((val & 0x1ff8) << 1) |
| | ((val & 0x2000) >> 13)); |
| break; |
| case -11: |
| CHECK_FIELD_WHERE (new_val, 8191, -8192, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val; |
| |
| insn = (insn & ~ 0x3ff9) | (((val & 0x1ffc) << 1) |
| | ((val & 0x2000) >> 13)); |
| break; |
| /* Handle all opcodes with the 'j' operand type. */ |
| case 14: |
| CHECK_FIELD_WHERE (new_val, 8191, -8192, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val; |
| |
| insn = ((insn & ~ 0x3fff) | low_sign_unext (val, 14)); |
| break; |
| |
| /* Handle all opcodes with the 'k' operand type. */ |
| case 21: |
| CHECK_FIELD_WHERE (new_val, 1048575, -1048576, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val; |
| |
| insn = (insn & ~ 0x1fffff) | re_assemble_21 (val); |
| break; |
| |
| /* Handle all the opcodes with the 'i' operand type. */ |
| case 11: |
| CHECK_FIELD_WHERE (new_val, 1023, -1024, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val; |
| |
| insn = (insn & ~ 0x7ff) | low_sign_unext (val, 11); |
| break; |
| |
| /* Handle all the opcodes with the 'w' operand type. */ |
| case 12: |
| CHECK_FIELD_WHERE (new_val - 8, 8191, -8192, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val - 8; |
| |
| insn = (insn & ~ 0x1ffd) | re_assemble_12 (val >> 2); |
| break; |
| |
| /* Handle some of the opcodes with the 'W' operand type. */ |
| case 17: |
| { |
| offsetT distance = * valP; |
| |
| /* If this is an absolute branch (ie no link) with an out of |
| range target, then we want to complain. */ |
| if (fixP->fx_r_type == (int) R_HPPA_PCREL_CALL |
| && (insn & 0xffe00000) == 0xe8000000) |
| CHECK_FIELD_WHERE (distance - 8, 262143, -262144, |
| fixP->fx_file, fixP->fx_line); |
| |
| CHECK_FIELD_WHERE (new_val - 8, 262143, -262144, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val - 8; |
| |
| insn = (insn & ~ 0x1f1ffd) | re_assemble_17 (val >> 2); |
| break; |
| } |
| |
| case 22: |
| { |
| offsetT distance = * valP; |
| |
| /* If this is an absolute branch (ie no link) with an out of |
| range target, then we want to complain. */ |
| if (fixP->fx_r_type == (int) R_HPPA_PCREL_CALL |
| && (insn & 0xffe00000) == 0xe8000000) |
| CHECK_FIELD_WHERE (distance - 8, 8388607, -8388608, |
| fixP->fx_file, fixP->fx_line); |
| |
| CHECK_FIELD_WHERE (new_val - 8, 8388607, -8388608, |
| fixP->fx_file, fixP->fx_line); |
| val = new_val - 8; |
| |
| insn = (insn & ~ 0x3ff1ffd) | re_assemble_22 (val >> 2); |
| break; |
| } |
| |
| case -10: |
| val = new_val; |
| insn = (insn & ~ 0xfff1) | re_assemble_16 (val & -8); |
| break; |
| |
| case -16: |
| val = new_val; |
| insn = (insn & ~ 0xfff9) | re_assemble_16 (val & -4); |
| break; |
| |
| case 16: |
| val = new_val; |
| insn = (insn & ~ 0xffff) | re_assemble_16 (val); |
| break; |
| |
| case 32: |
| insn = new_val; |
| break; |
| |
| default: |
| as_bad_where (fixP->fx_file, fixP->fx_line, |
| _("Unknown relocation encountered in md_apply_fix.")); |
| return; |
| } |
| |
| #ifdef OBJ_ELF |
| switch (fixP->fx_r_type) |
| { |
| case R_PARISC_TLS_GD21L: |
| case R_PARISC_TLS_GD14R: |
| case R_PARISC_TLS_LDM21L: |
| case R_PARISC_TLS_LDM14R: |
| case R_PARISC_TLS_LE21L: |
| case R_PARISC_TLS_LE14R: |
| case R_PARISC_TLS_IE21L: |
| case R_PARISC_TLS_IE14R: |
| if (fixP->fx_addsy) |
| S_SET_THREAD_LOCAL (fixP->fx_addsy); |
| break; |
| default: |
| break; |
| } |
| #endif |
| |
| /* Insert the relocation. */ |
| bfd_put_32 (stdoutput, insn, fixpos); |
| } |
| |
| /* Exactly what point is a PC-relative offset relative TO? |
| On the PA, they're relative to the address of the offset. */ |
| |
| long |
| md_pcrel_from (fixS *fixP) |
| { |
| return fixP->fx_where + fixP->fx_frag->fr_address; |
| } |
| |
| /* Return nonzero if the input line pointer is at the end of |
| a statement. */ |
| |
| static int |
| is_end_of_statement (void) |
| { |
| return ((*input_line_pointer == '\n') |
| || (*input_line_pointer == ';') |
| || (*input_line_pointer == '!')); |
| } |
| |
| #define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg)) |
| |
| /* Given NAME, find the register number associated with that name, return |
| the integer value associated with the given name or -1 on failure. */ |
| |
| static int |
| reg_name_search (char *name) |
| { |
| int middle, low, high; |
| int cmp; |
| |
| low = 0; |
| high = REG_NAME_CNT - 1; |
| |
| do |
| { |
| middle = (low + high) / 2; |
| cmp = strcasecmp (name, pre_defined_registers[middle].name); |
| if (cmp < 0) |
| high = middle - 1; |
| else if (cmp > 0) |
| low = middle + 1; |
| else |
| return pre_defined_registers[middle].value; |
| } |
| while (low <= high); |
| |
| return -1; |
| } |
| |
| /* Read a number from S. The number might come in one of many forms, |
| the most common will be a hex or decimal constant, but it could be |
| a pre-defined register (Yuk!), or an absolute symbol. |
| |
| Return 1 on success or 0 on failure. If STRICT, then a missing |
| register prefix will cause a failure. The number itself is |
| returned in `pa_number'. |
| |
| IS_FLOAT indicates that a PA-89 FP register number should be |
| parsed; A `l' or `r' suffix is checked for if but 2 of IS_FLOAT is |
| not set. |
| |
| pa_parse_number can not handle negative constants and will fail |
| horribly if it is passed such a constant. */ |
| |
| static int |
| pa_parse_number (char **s, int is_float) |
| { |
| int num; |
| char *name; |
| char c; |
| symbolS *sym; |
| int status; |
| char *p = *s; |
| bool have_prefix; |
| |
| /* Skip whitespace before the number. */ |
| while (*p == ' ' || *p == '\t') |
| p = p + 1; |
| |
| pa_number = -1; |
| have_prefix = 0; |
| num = 0; |
| if (!strict && ISDIGIT (*p)) |
| { |
| /* Looks like a number. */ |
| |
| if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) |
| { |
| /* The number is specified in hex. */ |
| p += 2; |
| while (ISDIGIT (*p) || ((*p >= 'a') && (*p <= 'f')) |
| || ((*p >= 'A') && (*p <= 'F'))) |
| { |
| if (ISDIGIT (*p)) |
| num = num * 16 + *p - '0'; |
| else if (*p >= 'a' && *p <= 'f') |
| num = num * 16 + *p - 'a' + 10; |
| else |
| num = num * 16 + *p - 'A' + 10; |
| ++p; |
| } |
| } |
| else |
| { |
| /* The number is specified in decimal. */ |
| while (ISDIGIT (*p)) |
| { |
| num = num * 10 + *p - '0'; |
| ++p; |
| } |
| } |
| |
| pa_number = num; |
| |
| /* Check for a `l' or `r' suffix. */ |
| if (is_float) |
| { |
| pa_number += FP_REG_BASE; |
| if (! (is_float & 2)) |
| { |
| if (IS_R_SELECT (p)) |
| { |
| pa_number += FP_REG_RSEL; |
| ++p; |
| } |
| else if (IS_L_SELECT (p)) |
| { |
| ++p; |
| } |
| } |
| } |
| } |
| else if (*p == '%') |
| { |
| /* The number might be a predefined register. */ |
| have_prefix = 1; |
| name = p; |
| p++; |
| c = *p; |
| /* Tege hack: Special case for general registers as the general |
| code makes a binary search with case translation, and is VERY |
| slow. */ |
| if (c == 'r') |
| { |
| p++; |
| if (*p == 'e' && *(p + 1) == 't' |
| && (*(p + 2) == '0' || *(p + 2) == '1')) |
| { |
| p += 2; |
| num = *p - '0' + 28; |
| p++; |
| } |
| else if (*p == 'p') |
| { |
| num = 2; |
| p++; |
| } |
| else if (!ISDIGIT (*p)) |
| { |
| if (print_errors) |
| as_bad (_("Undefined register: '%s'."), name); |
| num = -1; |
| } |
| else |
| { |
| do |
| num = num * 10 + *p++ - '0'; |
| while (ISDIGIT (*p)); |
| } |
| } |
| else |
| { |
| /* Do a normal register search. */ |
| while (is_part_of_name (c)) |
| { |
| p = p + 1; |
| c = *p; |
| } |
| *p = 0; |
| status = reg_name_search (name); |
| if (status >= 0) |
| num = status; |
| else |
| { |
| if (print_errors) |
| as_bad (_("Undefined register: '%s'."), name); |
| num = -1; |
| } |
| *p = c; |
| } |
| |
| pa_number = num; |
| } |
| else |
| { |
| /* And finally, it could be a symbol in the absolute section which |
| is effectively a constant, or a register alias symbol. */ |
| name = p; |
| c = *p; |
| while (is_part_of_name (c)) |
| { |
| p = p + 1; |
| c = *p; |
| } |
| *p = 0; |
| if ((sym = symbol_find (name)) != NULL) |
| { |
| if (S_GET_SEGMENT (sym) == reg_section) |
| { |
| num = S_GET_VALUE (sym); |
| /* Well, we don't really have one, but we do have a |
| register, so... */ |
| have_prefix = true; |
| } |
| else if (S_GET_SEGMENT (sym) == bfd_abs_section_ptr) |
| num = S_GET_VALUE (sym); |
| else if (!strict) |
| { |
| if (print_errors) |
| as_bad (_("Non-absolute symbol: '%s'."), name); |
| num = -1; |
| } |
| } |
| else if (!strict) |
| { |
| /* There is where we'd come for an undefined symbol |
| or for an empty string. For an empty string we |
| will return zero. That's a concession made for |
| compatibility with the braindamaged HP assemblers. */ |
| if (*name == 0) |
| num = 0; |
| else |
| { |
| if (print_errors) |
| as_bad (_("Undefined absolute constant: '%s'."), name); |
| num = -1; |
| } |
| } |
| *p = c; |
| |
| pa_number = num; |
| } |
| |
| if (!strict || have_prefix) |
| { |
| *s = p; |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Return nonzero if the given INSN and L/R information will require |
| a new PA-1.1 opcode. */ |
| |
| static int |
| need_pa11_opcode (void) |
| { |
| if ((pa_number & FP_REG_RSEL) != 0 |
| && !(the_insn.fpof1 == DBL && the_insn.fpof2 == DBL)) |
| { |
| /* If this instruction is specific to a particular architecture, |
| then set a new architecture. */ |
| if (bfd_get_mach (stdoutput) < pa11) |
| { |
| if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, pa11)) |
| as_warn (_("could not update architecture and machine")); |
| } |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| /* Parse a condition for a fcmp instruction. Return the numerical |
| code associated with the condition. */ |
| |
| static int |
| pa_parse_fp_cmp_cond (char **s) |
| { |
| int cond, i; |
| |
| cond = 0; |
| |
| for (i = 0; i < 32; i++) |
| { |
| if (strncasecmp (*s, fp_cond_map[i].string, |
| strlen (fp_cond_map[i].string)) == 0) |
| { |
| cond = fp_cond_map[i].cond; |
| *s += strlen (fp_cond_map[i].string); |
| /* If not a complete match, back up the input string and |
| report an error. */ |
| if (**s != ' ' && **s != '\t') |
| { |
| *s -= strlen (fp_cond_map[i].string); |
| break; |
| } |
| while (**s == ' ' || **s == '\t') |
| *s = *s + 1; |
| return cond; |
| } |
| } |
| |
| as_bad (_("Invalid FP Compare Condition: %s"), *s); |
| |
| /* Advance over the bogus completer. */ |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| |
| return 0; |
| } |
| |
| /* Parse a graphics test complete for ftest. */ |
| |
| static int |
| pa_parse_ftest_gfx_completer (char **s) |
| { |
| int value; |
| |
| value = 0; |
| if (strncasecmp (*s, "acc8", 4) == 0) |
| { |
| value = 5; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "acc6", 4) == 0) |
| { |
| value = 9; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "acc4", 4) == 0) |
| { |
| value = 13; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "acc2", 4) == 0) |
| { |
| value = 17; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "acc", 3) == 0) |
| { |
| value = 1; |
| *s += 3; |
| } |
| else if (strncasecmp (*s, "rej8", 4) == 0) |
| { |
| value = 6; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "rej", 3) == 0) |
| { |
| value = 2; |
| *s += 3; |
| } |
| else |
| { |
| value = 0; |
| as_bad (_("Invalid FTEST completer: %s"), *s); |
| } |
| |
| return value; |
| } |
| |
| /* Parse an FP operand format completer returning the completer |
| type. */ |
| |
| static fp_operand_format |
| pa_parse_fp_cnv_format (char **s) |
| { |
| int format; |
| |
| format = SGL; |
| if (**s == ',') |
| { |
| *s += 1; |
| if (strncasecmp (*s, "sgl", 3) == 0) |
| { |
| format = SGL; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "dbl", 3) == 0) |
| { |
| format = DBL; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "quad", 4) == 0) |
| { |
| format = QUAD; |
| *s += 5; |
| } |
| else if (strncasecmp (*s, "w", 1) == 0) |
| { |
| format = W; |
| *s += 2; |
| } |
| else if (strncasecmp (*s, "uw", 2) == 0) |
| { |
| format = UW; |
| *s += 3; |
| } |
| else if (strncasecmp (*s, "dw", 2) == 0) |
| { |
| format = DW; |
| *s += 3; |
| } |
| else if (strncasecmp (*s, "udw", 3) == 0) |
| { |
| format = UDW; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "qw", 2) == 0) |
| { |
| format = QW; |
| *s += 3; |
| } |
| else if (strncasecmp (*s, "uqw", 3) == 0) |
| { |
| format = UQW; |
| *s += 4; |
| } |
| else |
| { |
| format = ILLEGAL_FMT; |
| as_bad (_("Invalid FP Operand Format: %3s"), *s); |
| } |
| } |
| |
| return format; |
| } |
| |
| /* Parse an FP operand format completer returning the completer |
| type. */ |
| |
| static fp_operand_format |
| pa_parse_fp_format (char **s) |
| { |
| int format; |
| |
| format = SGL; |
| if (**s == ',') |
| { |
| *s += 1; |
| if (strncasecmp (*s, "sgl", 3) == 0) |
| { |
| format = SGL; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "dbl", 3) == 0) |
| { |
| format = DBL; |
| *s += 4; |
| } |
| else if (strncasecmp (*s, "quad", 4) == 0) |
| { |
| format = QUAD; |
| *s += 5; |
| } |
| else |
| { |
| format = ILLEGAL_FMT; |
| as_bad (_("Invalid FP Operand Format: %3s"), *s); |
| } |
| } |
| |
| return format; |
| } |
| |
| /* Convert from a selector string into a selector type. */ |
| |
| static int |
| pa_chk_field_selector (char **str) |
| { |
| int middle, low, high; |
| int cmp; |
| char name[4]; |
| |
| /* Read past any whitespace. */ |
| /* FIXME: should we read past newlines and formfeeds??? */ |
| while (**str == ' ' || **str == '\t' || **str == '\n' || **str == '\f') |
| *str = *str + 1; |
| |
| if ((*str)[1] == '\'' || (*str)[1] == '%') |
| name[0] = TOLOWER ((*str)[0]), |
| name[1] = 0; |
| else if ((*str)[2] == '\'' || (*str)[2] == '%') |
| name[0] = TOLOWER ((*str)[0]), |
| name[1] = TOLOWER ((*str)[1]), |
| name[2] = 0; |
| else if ((*str)[3] == '\'' || (*str)[3] == '%') |
| name[0] = TOLOWER ((*str)[0]), |
| name[1] = TOLOWER ((*str)[1]), |
| name[2] = TOLOWER ((*str)[2]), |
| name[3] = 0; |
| else |
| return e_fsel; |
| |
| low = 0; |
| high = sizeof (selector_table) / sizeof (struct selector_entry) - 1; |
| |
| do |
| { |
| middle = (low + high) / 2; |
| cmp = strcmp (name, selector_table[middle].prefix); |
| if (cmp < 0) |
| high = middle - 1; |
| else if (cmp > 0) |
| low = middle + 1; |
| else |
| { |
| *str += strlen (name) + 1; |
| #ifndef OBJ_SOM |
| if (selector_table[middle].field_selector == e_nsel) |
| return e_fsel; |
| #endif |
| return selector_table[middle].field_selector; |
| } |
| } |
| while (low <= high); |
| |
| return e_fsel; |
| } |
| |
| /* Parse a .byte, .word, .long expression for the HPPA. Called by |
| cons via the TC_PARSE_CONS_EXPRESSION macro. */ |
| |
| int |
| parse_cons_expression_hppa (expressionS *exp) |
| { |
| int hppa_field_selector = pa_chk_field_selector (&input_line_pointer); |
| expression (exp); |
| return hppa_field_selector; |
| } |
| |
| /* Evaluate an absolute expression EXP which may be modified by |
| the selector FIELD_SELECTOR. Return the value of the expression. */ |
| static int |
| evaluate_absolute (struct pa_it *insn) |
| { |
| offsetT value; |
| expressionS exp; |
| int field_selector = insn->field_selector; |
| |
| exp = insn->exp; |
| value = exp.X_add_number; |
| |
| return hppa_field_adjust (0, value, field_selector); |
| } |
| |
| /* Mark (via expr_end) the end of an absolute expression. FIXME. */ |
| |
| static int |
| pa_get_absolute_expression (struct pa_it *insn, char **strp) |
| { |
| char *save_in; |
| |
| insn->field_selector = pa_chk_field_selector (strp); |
| save_in = input_line_pointer; |
| input_line_pointer = *strp; |
| expression (&insn->exp); |
| expr_end = input_line_pointer; |
| input_line_pointer = save_in; |
| if (insn->exp.X_op != O_constant) |
| { |
| /* We have a non-match in strict mode. */ |
| if (!strict) |
| as_bad (_("Bad segment (should be absolute).")); |
| return 0; |
| } |
| return evaluate_absolute (insn); |
| } |
| |
| /* Get an absolute number. The input string is terminated at the |
| first whitespace character. */ |
| |
| static int |
| pa_get_number (struct pa_it *insn, char **strp) |
| { |
| char *save_in; |
| char *s, c; |
| int result; |
| |
| save_in = input_line_pointer; |
| input_line_pointer = *strp; |
| |
| /* The PA assembly syntax is ambiguous in a variety of ways. Consider |
| this string "4 %r5" Is that the number 4 followed by the register |
| r5, or is that 4 MOD r5? This situation occurs for example in the |
| coprocessor load and store instructions. Previously, calling |
| pa_get_absolute_expression directly results in r5 being entered |
| in the symbol table. |
| |
| So, when looking for an absolute number, we cut off the input string |
| at the first whitespace character. Thus, expressions should generally |
| contain no whitespace. */ |
| |
| s = *strp; |
| while (*s != ',' && *s != ' ' && *s != '\t') |
| s++; |
| |
| c = *s; |
| *s = 0; |
| |
| result = pa_get_absolute_expression (insn, strp); |
| |
| input_line_pointer = save_in; |
| *s = c; |
| return result; |
| } |
| |
| /* Given an argument location specification return the associated |
| argument location number. */ |
| |
| static unsigned int |
| pa_build_arg_reloc (char *type_name) |
| { |
| |
| if (strncasecmp (type_name, "no", 2) == 0) |
| return 0; |
| if (strncasecmp (type_name, "gr", 2) == 0) |
| return 1; |
| else if (strncasecmp (type_name, "fr", 2) == 0) |
| return 2; |
| else if (strncasecmp (type_name, "fu", 2) == 0) |
| return 3; |
| else |
| as_bad (_("Invalid argument location: %s\n"), type_name); |
| |
| return 0; |
| } |
| |
| /* Encode and return an argument relocation specification for |
| the given register in the location specified by arg_reloc. */ |
| |
| static unsigned int |
| pa_align_arg_reloc (unsigned int reg, unsigned int arg_reloc) |
| { |
| unsigned int new_reloc; |
| |
| new_reloc = arg_reloc; |
| switch (reg) |
| { |
| case 0: |
| new_reloc <<= 8; |
| break; |
| case 1: |
| new_reloc <<= 6; |
| break; |
| case 2: |
| new_reloc <<= 4; |
| break; |
| case 3: |
| new_reloc <<= 2; |
| break; |
| default: |
| as_bad (_("Invalid argument description: %d"), reg); |
| } |
| |
| return new_reloc; |
| } |
| |
| /* Parse a non-negated compare/subtract completer returning the |
| number (for encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_nonneg_cmpsub_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| char *save_s = *s; |
| int nullify = 0; |
| |
| cmpltr = 0; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| |
| if (strcmp (name, "=") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, "<") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, "<=") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcmp (name, "<<") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcmp (name, "<<=") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "sv") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "od") == 0) |
| { |
| cmpltr = 7; |
| } |
| /* If we have something like addb,n then there is no condition |
| completer. */ |
| else if (strcasecmp (name, "n") == 0) |
| { |
| cmpltr = 0; |
| nullify = 1; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| /* Reset pointers if this was really a ,n for a branch instruction. */ |
| if (nullify) |
| *s = save_s; |
| |
| return cmpltr; |
| } |
| |
| /* Parse a negated compare/subtract completer returning the |
| number (for encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_neg_cmpsub_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| char *save_s = *s; |
| int nullify = 0; |
| |
| cmpltr = 0; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| |
| if (strcasecmp (name, "tr") == 0) |
| { |
| cmpltr = 0; |
| } |
| else if (strcmp (name, "<>") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, ">=") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, ">") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcmp (name, ">>=") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcmp (name, ">>") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "nsv") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "ev") == 0) |
| { |
| cmpltr = 7; |
| } |
| /* If we have something like addb,n then there is no condition |
| completer. */ |
| else if (strcasecmp (name, "n") == 0) |
| { |
| cmpltr = 0; |
| nullify = 1; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| /* Reset pointers if this was really a ,n for a branch instruction. */ |
| if (nullify) |
| *s = save_s; |
| |
| return cmpltr; |
| } |
| |
| /* Parse a 64 bit compare and branch completer returning the number (for |
| encoding in instructions) of the given completer. |
| |
| Nonnegated comparisons are returned as 0-7, negated comparisons are |
| returned as 8-15. */ |
| |
| static int |
| pa_parse_cmpb_64_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| |
| cmpltr = -1; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| |
| if (strcmp (name, "*") == 0) |
| { |
| cmpltr = 0; |
| } |
| else if (strcmp (name, "*=") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, "*<") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, "*<=") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcmp (name, "*<<") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcmp (name, "*<<=") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "*sv") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "*od") == 0) |
| { |
| cmpltr = 7; |
| } |
| else if (strcasecmp (name, "*tr") == 0) |
| { |
| cmpltr = 8; |
| } |
| else if (strcmp (name, "*<>") == 0) |
| { |
| cmpltr = 9; |
| } |
| else if (strcmp (name, "*>=") == 0) |
| { |
| cmpltr = 10; |
| } |
| else if (strcmp (name, "*>") == 0) |
| { |
| cmpltr = 11; |
| } |
| else if (strcmp (name, "*>>=") == 0) |
| { |
| cmpltr = 12; |
| } |
| else if (strcmp (name, "*>>") == 0) |
| { |
| cmpltr = 13; |
| } |
| else if (strcasecmp (name, "*nsv") == 0) |
| { |
| cmpltr = 14; |
| } |
| else if (strcasecmp (name, "*ev") == 0) |
| { |
| cmpltr = 15; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| return cmpltr; |
| } |
| |
| /* Parse a 64 bit compare immediate and branch completer returning the number |
| (for encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_cmpib_64_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| |
| cmpltr = -1; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| |
| if (strcmp (name, "*<<") == 0) |
| { |
| cmpltr = 0; |
| } |
| else if (strcmp (name, "*=") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, "*<") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, "*<=") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcmp (name, "*>>=") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcmp (name, "*<>") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "*>=") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "*>") == 0) |
| { |
| cmpltr = 7; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| return cmpltr; |
| } |
| |
| /* Parse a non-negated addition completer returning the number |
| (for encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_nonneg_add_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| char *save_s = *s; |
| int nullify = 0; |
| |
| cmpltr = 0; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| if (strcmp (name, "=") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, "<") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, "<=") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcasecmp (name, "nuv") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcasecmp (name, "znv") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "sv") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "od") == 0) |
| { |
| cmpltr = 7; |
| } |
| /* If we have something like addb,n then there is no condition |
| completer. */ |
| else if (strcasecmp (name, "n") == 0) |
| { |
| cmpltr = 0; |
| nullify = 1; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| /* Reset pointers if this was really a ,n for a branch instruction. */ |
| if (nullify) |
| *s = save_s; |
| |
| return cmpltr; |
| } |
| |
| /* Parse a negated addition completer returning the number |
| (for encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_neg_add_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| char *save_s = *s; |
| int nullify = 0; |
| |
| cmpltr = 0; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| if (strcasecmp (name, "tr") == 0) |
| { |
| cmpltr = 0; |
| } |
| else if (strcmp (name, "<>") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, ">=") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, ">") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcasecmp (name, "uv") == 0) |
| { |
| cmpltr = 4; |
| } |
| else if (strcasecmp (name, "vnz") == 0) |
| { |
| cmpltr = 5; |
| } |
| else if (strcasecmp (name, "nsv") == 0) |
| { |
| cmpltr = 6; |
| } |
| else if (strcasecmp (name, "ev") == 0) |
| { |
| cmpltr = 7; |
| } |
| /* If we have something like addb,n then there is no condition |
| completer. */ |
| else if (strcasecmp (name, "n") == 0) |
| { |
| cmpltr = 0; |
| nullify = 1; |
| } |
| else |
| { |
| cmpltr = -1; |
| } |
| **s = c; |
| } |
| |
| /* Reset pointers if this was really a ,n for a branch instruction. */ |
| if (nullify) |
| *s = save_s; |
| |
| return cmpltr; |
| } |
| |
| /* Parse a 64 bit wide mode add and branch completer returning the number (for |
| encoding in instructions) of the given completer. */ |
| |
| static int |
| pa_parse_addb_64_cmpltr (char **s) |
| { |
| int cmpltr; |
| char *name = *s + 1; |
| char c; |
| char *save_s = *s; |
| int nullify = 0; |
| |
| cmpltr = 0; |
| if (**s == ',') |
| { |
| *s += 1; |
| while (**s != ',' && **s != ' ' && **s != '\t') |
| *s += 1; |
| c = **s; |
| **s = 0x00; |
| if (strcmp (name, "=") == 0) |
| { |
| cmpltr = 1; |
| } |
| else if (strcmp (name, "<") == 0) |
| { |
| cmpltr = 2; |
| } |
| else if (strcmp (name, "<=") == 0) |
| { |
| cmpltr = 3; |
| } |
| else if (strcasecmp (name, "nuv") == 0) |
| { |
| cmpltr = 4; |
| }
|