blob: e82c0636ff15706a41be11a763358037c37dec85 [file] [log] [blame]
/* 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 (&notes, 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;
}</