/* tc-sparc.c -- Assemble for the SPARC
- Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
- Free Software Foundation, Inc.
+ Copyright (C) 1989-2017 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
#define U0x80000000 ((((unsigned long) 1 << 16) << 15))
static int sparc_ip (char *, const struct sparc_opcode **);
+static int parse_sparc_asi (char **, const sparc_asi **);
static int parse_keyword_arg (int (*) (const char *), char **, int *);
static int parse_const_expr_arg (char **, int *);
static int get_expression (char *);
#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "sparclite"
#endif
-static char *default_arch = DEFAULT_ARCH;
+static const char *default_arch = DEFAULT_ARCH;
/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
have been set. */
/* The currently selected v9 memory model. Currently only used for
ELF. */
static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
+
+#ifndef TE_SOLARIS
+/* Bitmask of instruction types seen so far, used to populate the
+ GNU attributes section with hwcap information. */
+static bfd_uint64_t hwcap_seen;
+#endif
#endif
+static bfd_uint64_t hwcap_allowed;
+
static int architecture_requested;
static int warn_on_bump;
architecture, issue a warning. */
static enum sparc_opcode_arch_val warn_after_architecture;
-/* Non-zero if as should generate error if an undeclared g[23] register
- has been used in -64. */
+/* Non-zero if the assembler should generate error if an undeclared
+ g[23] register has been used in -64. */
static int no_undeclared_regs;
+/* Non-zero if the assembler should generate a warning if an
+ unpredictable DCTI (delayed control transfer instruction) couple is
+ found. */
+static int dcti_couples_detect;
+
/* Non-zero if we should try to relax jumps and calls. */
static int sparc_relax;
struct sparc_it
{
- char *error;
+ const char *error;
unsigned long opcode;
struct nlist *nlistp;
expressionS exp;
for this use. That table is for opcodes only. This table is for opcodes
and file formats. */
-enum sparc_arch_types {v6, v7, v8, sparclet, sparclite, sparc86x, v8plus,
+enum sparc_arch_types {v6, v7, v8, leon, sparclet, sparclite, sparc86x, v8plus,
v8plusa, v9, v9a, v9b, v9_64};
static struct sparc_arch {
- char *name;
- char *opcode_arch;
+ const char *name;
+ const char *opcode_arch;
enum sparc_arch_types arch_type;
/* Default word size, as specified during configuration.
A value of zero means can't be used to specify default architecture. */
int default_arch_size;
/* Allowable arg to -A? */
int user_option_p;
+ /* Extra hardware capabilities allowed. These are added to the
+ hardware capabilities associated with the opcode
+ architecture. */
+ int hwcap_allowed;
+ int hwcap2_allowed;
} sparc_arch_table[] = {
- { "v6", "v6", v6, 0, 1 },
- { "v7", "v7", v7, 0, 1 },
- { "v8", "v8", v8, 32, 1 },
- { "sparclet", "sparclet", sparclet, 32, 1 },
- { "sparclite", "sparclite", sparclite, 32, 1 },
- { "sparc86x", "sparclite", sparc86x, 32, 1 },
- { "v8plus", "v9", v9, 0, 1 },
- { "v8plusa", "v9a", v9, 0, 1 },
- { "v8plusb", "v9b", v9, 0, 1 },
- { "v9", "v9", v9, 0, 1 },
- { "v9a", "v9a", v9, 0, 1 },
- { "v9b", "v9b", v9, 0, 1 },
- /* This exists to allow configure.in/Makefile.in to pass one
+ { "v6", "v6", v6, 0, 1, 0, 0 },
+ { "v7", "v7", v7, 0, 1, 0, 0 },
+ { "v8", "v8", v8, 32, 1, 0, 0 },
+ { "v8a", "v8", v8, 32, 1, 0, 0 },
+ { "sparc", "v9", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "sparcvis", "v9a", v9, 0, 1, 0, 0 },
+ { "sparcvis2", "v9b", v9, 0, 1, 0, 0 },
+ { "sparcfmaf", "v9b", v9, 0, 1, HWCAP_FMAF, 0 },
+ { "sparcima", "v9b", v9, 0, 1, HWCAP_FMAF|HWCAP_IMA, 0 },
+ { "sparcvis3", "v9b", v9, 0, 1, HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC, 0 },
+ { "sparcvis3r", "v9b", v9, 0, 1, HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC|HWCAP_FJFMAU, 0 },
+
+ { "sparc4", "v9v", v9, 0, 1, 0, 0 },
+ { "sparc5", "v9m", v9, 0, 1, 0, 0 },
+ { "sparc6", "m8", v9, 0, 1, 0, 0 },
+
+ { "leon", "leon", leon, 32, 1, 0, 0 },
+ { "sparclet", "sparclet", sparclet, 32, 1, 0, 0 },
+ { "sparclite", "sparclite", sparclite, 32, 1, 0, 0 },
+ { "sparc86x", "sparclite", sparc86x, 32, 1, 0, 0 },
+
+ { "v8plus", "v9", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusa", "v9a", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusb", "v9b", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusc", "v9c", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusd", "v9d", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8pluse", "v9e", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusv", "v9v", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusm", "v9m", v9, 0, 1, HWCAP_V8PLUS, 0 },
+ { "v8plusm8", "m8", v9, 0, 1, HWCAP_V8PLUS, 0 },
+
+ { "v9", "v9", v9, 0, 1, 0, 0 },
+ { "v9a", "v9a", v9, 0, 1, 0, 0 },
+ { "v9b", "v9b", v9, 0, 1, 0, 0 },
+ { "v9c", "v9c", v9, 0, 1, 0, 0 },
+ { "v9d", "v9d", v9, 0, 1, 0, 0 },
+ { "v9e", "v9e", v9, 0, 1, 0, 0 },
+ { "v9v", "v9v", v9, 0, 1, 0, 0 },
+ { "v9m", "v9m", v9, 0, 1, 0, 0 },
+ { "v9m8", "m8", v9, 0, 1, 0, 0 },
+
+ /* This exists to allow configure.tgt to pass one
value to specify both the default machine and default word size. */
- { "v9-64", "v9", v9, 64, 0 },
- { NULL, NULL, v8, 0, 0 }
+ { "v9-64", "v9", v9, 64, 0, 0, 0 },
+ { NULL, NULL, v8, 0, 0, 0, 0 }
};
/* Variant of default_arch */
static enum sparc_arch_types default_arch_type;
static struct sparc_arch *
-lookup_arch (char *name)
+lookup_arch (const char *name)
{
struct sparc_arch *sa;
* -bump
* Warn on architecture bumps. See also -A.
*
- * -Av6, -Av7, -Av8, -Asparclite, -Asparclet
+ * -Av6, -Av7, -Av8, -Aleon, -Asparclite, -Asparclet
* Standard 32 bit architectures.
* -Av9, -Av9a, -Av9b
* Sparc64 in either a 32 or 64 bit world (-32/-64 says which).
{"relax", no_argument, NULL, OPTION_RELAX},
#define OPTION_NO_RELAX (OPTION_MD_BASE + 15)
{"no-relax", no_argument, NULL, OPTION_NO_RELAX},
+#define OPTION_DCTI_COUPLES_DETECT (OPTION_MD_BASE + 16)
+ {"dcti-couples-detect", no_argument, NULL, OPTION_DCTI_COUPLES_DETECT},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
{
/* We don't get a chance to initialize anything before we're called,
so handle that now. */
case OPTION_XARCH:
#ifdef OBJ_ELF
- if (strncmp (arg, "v9", 2) != 0)
- md_parse_option (OPTION_32, NULL);
- else
+ if (!strncmp (arg, "v9", 2))
md_parse_option (OPTION_64, NULL);
+ else
+ {
+ if (!strncmp (arg, "v8", 2)
+ || !strncmp (arg, "v7", 2)
+ || !strncmp (arg, "v6", 2)
+ || !strcmp (arg, "sparclet")
+ || !strcmp (arg, "sparclite")
+ || !strcmp (arg, "sparc86x"))
+ md_parse_option (OPTION_32, NULL);
+ }
#endif
/* Fall through. */
if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
as_fatal (_("Bad opcode table, broken assembler."));
- max_architecture = opcode_arch;
+ if (!architecture_requested
+ || opcode_arch > max_architecture)
+ max_architecture = opcode_arch;
+
+ /* The allowed hardware capabilities are the implied by the
+ opcodes arch plus any extra capabilities defined in the GAS
+ arch. */
+ hwcap_allowed
+ = (hwcap_allowed
+ | (((bfd_uint64_t) sparc_opcode_archs[opcode_arch].hwcaps2) << 32)
+ | (((bfd_uint64_t) sa->hwcap2_allowed) << 32)
+ | sparc_opcode_archs[opcode_arch].hwcaps
+ | sa->hwcap_allowed);
architecture_requested = 1;
}
break;
sparc_relax = 0;
break;
+ case OPTION_DCTI_COUPLES_DETECT:
+ dcti_couples_detect = 1;
+ break;
+
default:
return 0;
}
appropriate .register directive (default)\n\
-no-undeclared-regs force error on application global register usage\n\
without appropriate .register directive\n\
+--dcti-couples-detect warn when an unpredictable DCTI couple is found\n\
-q ignored\n\
-Qy, -Qn ignored\n\
-s ignored\n"));
}
\f
/* Native operand size opcode translation. */
-struct
+static struct
{
- char *name;
- char *name32;
- char *name64;
+ const char *name;
+ const char *name32;
+ const char *name64;
} native_op_table[] =
{
{"ldn", "ld", "ldx"},
struct priv_reg_entry
{
- char *name;
+ const char *name;
int regnum;
};
{"wstate", 14},
{"fq", 15},
{"gl", 16},
+ {"pmcdper", 23},
{"ver", 31},
- {"", -1}, /* End marker. */
+ {NULL, -1}, /* End marker. */
};
struct priv_reg_entry hpriv_reg_table[] =
{"hintp", 3},
{"htba", 5},
{"hver", 6},
+ {"hmcdper", 23},
+ {"hmcddfr", 24},
+ {"hva_mask_nz", 27},
+ {"hstick_offset", 28},
+ {"hstick_enable", 29},
{"hstick_cmpr", 31},
- {"", -1}, /* End marker. */
+ {NULL, -1}, /* End marker. */
};
-/* v9a specific asrs. This table is ordered by initial
- letter, in reverse. */
+/* v9a or later specific ancillary state registers. */
struct priv_reg_entry v9a_asr_table[] =
{
{"softint_set", 20},
{"softint", 22},
{"set_softint", 20},
+ {"pause", 27},
{"pic", 17},
{"pcr", 16},
+ {"mwait", 28},
{"gsr", 19},
{"dcr", 18},
+ {"cfr", 26},
{"clear_softint", 21},
- {"", -1}, /* End marker. */
+ {NULL, -1}, /* End marker. */
};
static int
const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg;
const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg;
- return strcmp (q->name, p->name);
+ if (p->name == q->name)
+ return 0;
+ else if (p->name == NULL)
+ return 1;
+ else if (q->name == NULL)
+ return -1;
+ else
+ return strcmp (q->name, p->name);
+}
+\f
+/* sparc %-pseudo-operations. */
+
+
+#define F_POP_V9 0x1 /* The pseudo-op is for v9 only. */
+#define F_POP_PCREL 0x2 /* The pseudo-op can be used in pc-relative
+ contexts. */
+#define F_POP_TLS_CALL 0x4 /* The pseudo-op marks a tls call. */
+#define F_POP_POSTFIX 0x8 /* The pseudo-op should appear after the
+ last operand of an
+ instruction. (Generally they can appear
+ anywhere an immediate operand is
+ expected. */
+struct pop_entry
+{
+ /* The name as it appears in assembler. */
+ const char *name;
+ /* The reloc this pseudo-op translates to. */
+ bfd_reloc_code_real_type reloc;
+ /* Flags. See F_POP_* above. */
+ int flags;
+};
+
+struct pop_entry pop_table[] =
+{
+ { "hix", BFD_RELOC_SPARC_HIX22, F_POP_V9 },
+ { "lox", BFD_RELOC_SPARC_LOX10, F_POP_V9 },
+ { "hi", BFD_RELOC_HI22, F_POP_PCREL },
+ { "lo", BFD_RELOC_LO10, F_POP_PCREL },
+ { "pc22", BFD_RELOC_SPARC_PC22, F_POP_PCREL },
+ { "pc10", BFD_RELOC_SPARC_PC10, F_POP_PCREL },
+ { "hh", BFD_RELOC_SPARC_HH22, F_POP_V9|F_POP_PCREL },
+ { "hm", BFD_RELOC_SPARC_HM10, F_POP_V9|F_POP_PCREL },
+ { "lm", BFD_RELOC_SPARC_LM22, F_POP_V9|F_POP_PCREL },
+ { "h34", BFD_RELOC_SPARC_H34, F_POP_V9 },
+ { "l34", BFD_RELOC_SPARC_L44, F_POP_V9 },
+ { "h44", BFD_RELOC_SPARC_H44, F_POP_V9 },
+ { "m44", BFD_RELOC_SPARC_M44, F_POP_V9 },
+ { "l44", BFD_RELOC_SPARC_L44, F_POP_V9 },
+ { "uhi", BFD_RELOC_SPARC_HH22, F_POP_V9 },
+ { "ulo", BFD_RELOC_SPARC_HM10, F_POP_V9 },
+ { "tgd_hi22", BFD_RELOC_SPARC_TLS_GD_HI22, 0 },
+ { "tgd_lo10", BFD_RELOC_SPARC_TLS_GD_LO10, 0 },
+ { "tldm_hi22", BFD_RELOC_SPARC_TLS_LDM_HI22, 0 },
+ { "tldm_lo10", BFD_RELOC_SPARC_TLS_LDM_LO10, 0 },
+ { "tldo_hix22", BFD_RELOC_SPARC_TLS_LDO_HIX22, 0 },
+ { "tldo_lox10", BFD_RELOC_SPARC_TLS_LDO_LOX10, 0 },
+ { "tie_hi22", BFD_RELOC_SPARC_TLS_IE_HI22, 0 },
+ { "tie_lo10", BFD_RELOC_SPARC_TLS_IE_LO10, 0 },
+ { "tle_hix22", BFD_RELOC_SPARC_TLS_LE_HIX22, 0 },
+ { "tle_lox10", BFD_RELOC_SPARC_TLS_LE_LOX10, 0 },
+ { "gdop_hix22", BFD_RELOC_SPARC_GOTDATA_OP_HIX22, 0 },
+ { "gdop_lox10", BFD_RELOC_SPARC_GOTDATA_OP_LOX10, 0 },
+ { "tgd_add", BFD_RELOC_SPARC_TLS_GD_ADD, F_POP_POSTFIX },
+ { "tgd_call", BFD_RELOC_SPARC_TLS_GD_CALL, F_POP_POSTFIX|F_POP_TLS_CALL },
+ { "tldm_add", BFD_RELOC_SPARC_TLS_LDM_ADD, F_POP_POSTFIX },
+ { "tldm_call", BFD_RELOC_SPARC_TLS_LDM_CALL, F_POP_POSTFIX|F_POP_TLS_CALL },
+ { "tldo_add", BFD_RELOC_SPARC_TLS_LDO_ADD, F_POP_POSTFIX },
+ { "tie_ldx", BFD_RELOC_SPARC_TLS_IE_LDX, F_POP_POSTFIX },
+ { "tie_ld", BFD_RELOC_SPARC_TLS_IE_LD, F_POP_POSTFIX },
+ { "tie_add", BFD_RELOC_SPARC_TLS_IE_ADD, F_POP_POSTFIX },
+ { "gdop", BFD_RELOC_SPARC_GOTDATA_OP, F_POP_POSTFIX }
+};
+\f
+/* Table of %-names that can appear in a sparc assembly program. This
+ table is initialized in md_begin and contains entries for each
+ privileged/hyperprivileged/alternate register and %-pseudo-op. */
+
+enum perc_entry_type
+{
+ perc_entry_none = 0,
+ perc_entry_reg,
+ perc_entry_post_pop,
+ perc_entry_imm_pop
+};
+
+struct perc_entry
+{
+ /* Entry type. */
+ enum perc_entry_type type;
+ /* Name of the %-entity. */
+ const char *name;
+ /* strlen (name). */
+ int len;
+ /* Value. Either a pop or a reg depending on type.*/
+ union
+ {
+ struct pop_entry *pop;
+ struct priv_reg_entry *reg;
+ };
+};
+
+#define NUM_PERC_ENTRIES \
+ (((sizeof (priv_reg_table) / sizeof (priv_reg_table[0])) - 1) \
+ + ((sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0])) - 1) \
+ + ((sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0])) - 1) \
+ + ARRAY_SIZE (pop_table) \
+ + 1)
+
+struct perc_entry perc_table[NUM_PERC_ENTRIES];
+
+static int
+cmp_perc_entry (const void *parg, const void *qarg)
+{
+ const struct perc_entry *p = (const struct perc_entry *) parg;
+ const struct perc_entry *q = (const struct perc_entry *) qarg;
+
+ if (p->name == q->name)
+ return 0;
+ else if (p->name == NULL)
+ return 1;
+ else if (q->name == NULL)
+ return -1;
+ else
+ return strcmp (q->name, p->name);
}
\f
/* This function is called once, at assembler startup time. It should
void
md_begin (void)
{
- register const char *retval = NULL;
+ const char *retval = NULL;
int lose = 0;
- register unsigned int i = 0;
+ unsigned int i = 0;
/* We don't get a chance to initialize anything before md_parse_option
is called, and it may not be called, so handle default initialization
for (i = 0; native_op_table[i].name; i++)
{
const struct sparc_opcode *insn;
- char *name = ((sparc_arch_size == 32)
+ const char *name = ((sparc_arch_size == 32)
? native_op_table[i].name32
: native_op_table[i].name64);
insn = (struct sparc_opcode *) hash_find (op_hash, name);
qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
sizeof (priv_reg_table[0]), cmp_reg_entry);
-
+ qsort (hpriv_reg_table, sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0]),
+ sizeof (hpriv_reg_table[0]), cmp_reg_entry);
+ qsort (v9a_asr_table, sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0]),
+ sizeof (v9a_asr_table[0]), cmp_reg_entry);
+
/* If -bump, record the architecture level at which we start issuing
warnings. The behaviour is different depending upon whether an
architecture was explicitly specified. If it wasn't, we issue warnings
/* `max_architecture' records the requested architecture.
Issue warnings if we go above it. */
warn_after_architecture = max_architecture;
-
- /* Find the highest architecture level that doesn't conflict with
- the requested one. */
- for (max_architecture = SPARC_OPCODE_ARCH_MAX;
- max_architecture > warn_after_architecture;
- --max_architecture)
- if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
- warn_after_architecture))
- break;
}
+
+ /* Find the highest architecture level that doesn't conflict with
+ the requested one. */
+
+ if (warn_on_bump
+ || !architecture_requested)
+ {
+ enum sparc_opcode_arch_val current_max_architecture
+ = max_architecture;
+
+ for (max_architecture = SPARC_OPCODE_ARCH_MAX;
+ max_architecture > warn_after_architecture;
+ --max_architecture)
+ if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
+ current_max_architecture))
+ break;
+ }
+
+ /* Prepare the tables of %-pseudo-ops. */
+ {
+ struct priv_reg_entry *reg_tables[]
+ = {priv_reg_table, hpriv_reg_table, v9a_asr_table, NULL};
+ struct priv_reg_entry **reg_table;
+ int entry = 0;
+
+ /* Add registers. */
+ for (reg_table = reg_tables; reg_table[0]; reg_table++)
+ {
+ struct priv_reg_entry *reg;
+ for (reg = *reg_table; reg->name; reg++)
+ {
+ struct perc_entry *p = &perc_table[entry++];
+ p->type = perc_entry_reg;
+ p->name = reg->name;
+ p->len = strlen (reg->name);
+ p->reg = reg;
+ }
+ }
+
+ /* Add %-pseudo-ops. */
+ for (i = 0; i < ARRAY_SIZE (pop_table); i++)
+ {
+ struct perc_entry *p = &perc_table[entry++];
+ p->type = (pop_table[i].flags & F_POP_POSTFIX
+ ? perc_entry_post_pop : perc_entry_imm_pop);
+ p->name = pop_table[i].name;
+ p->len = strlen (pop_table[i].name);
+ p->pop = &pop_table[i];
+ }
+
+ /* Last entry is the sentinel. */
+ perc_table[entry].type = perc_entry_none;
+
+ qsort (perc_table, sizeof (perc_table) / sizeof (perc_table[0]),
+ sizeof (perc_table[0]), cmp_perc_entry);
+
+ }
}
/* Called after all assembly has been done. */
sparc_md_end (void)
{
unsigned long mach = bfd_mach_sparc;
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ int hwcaps, hwcaps2;
+#endif
if (sparc_arch_size == 64)
switch (current_architecture)
{
case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v9a; break;
case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v9b; break;
+ case SPARC_OPCODE_ARCH_V9C: mach = bfd_mach_sparc_v9c; break;
+ case SPARC_OPCODE_ARCH_V9D: mach = bfd_mach_sparc_v9d; break;
+ case SPARC_OPCODE_ARCH_V9E: mach = bfd_mach_sparc_v9e; break;
+ case SPARC_OPCODE_ARCH_V9V: mach = bfd_mach_sparc_v9v; break;
+ case SPARC_OPCODE_ARCH_V9M: mach = bfd_mach_sparc_v9m; break;
+ case SPARC_OPCODE_ARCH_M8: mach = bfd_mach_sparc_v9m8; break;
default: mach = bfd_mach_sparc_v9; break;
}
else
case SPARC_OPCODE_ARCH_V9: mach = bfd_mach_sparc_v8plus; break;
case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v8plusa; break;
case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v8plusb; break;
+ case SPARC_OPCODE_ARCH_V9C: mach = bfd_mach_sparc_v8plusc; break;
+ case SPARC_OPCODE_ARCH_V9D: mach = bfd_mach_sparc_v8plusd; break;
+ case SPARC_OPCODE_ARCH_V9E: mach = bfd_mach_sparc_v8pluse; break;
+ case SPARC_OPCODE_ARCH_V9V: mach = bfd_mach_sparc_v8plusv; break;
+ case SPARC_OPCODE_ARCH_V9M: mach = bfd_mach_sparc_v8plusm; break;
+ case SPARC_OPCODE_ARCH_M8: mach = bfd_mach_sparc_v8plusm8; break;
/* The sparclite is treated like a normal sparc. Perhaps it shouldn't
be but for now it is (since that's the way it's always been
treated). */
default: break;
}
bfd_set_arch_mach (stdoutput, bfd_arch_sparc, mach);
+
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ hwcaps = hwcap_seen & U0xffffffff;
+ hwcaps2 = hwcap_seen >> 32;
+
+ if (hwcaps)
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_Sparc_HWCAPS, hwcaps);
+ if (hwcaps2)
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_Sparc_HWCAPS2, hwcaps2);
+#endif
}
\f
/* Return non-zero if VAL is in the range -(MAX+1) to MAX. */
static char *expr_end;
/* Values for `special_case'.
- Instructions that require wierd handling because they're longer than
+ Instructions that require weird handling because they're longer than
4 bytes. */
#define SPECIAL_CASE_NONE 0
#define SPECIAL_CASE_SET 1
output_insn (insn, &the_insn);
}
-/* Handle the setsw synthetic instruction. */
+/* Handle the setx synthetic instruction. */
static void
synthetize_setx (const struct sparc_opcode *insn)
if (insn == NULL)
return;
- /* We warn about attempts to put a floating point branch in a delay slot,
- unless the delay slot has been annulled. */
+ /* Certain instructions may not appear on delay slots. Check for
+ these situations. */
if (last_insn != NULL
- && (insn->flags & F_FBR) != 0
- && (last_insn->flags & F_DELAYED) != 0
- /* ??? This test isn't completely accurate. We assume anything with
- F_{UNBR,CONDBR,FBR} set is annullable. */
- && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
- || (last_opcode & ANNUL) == 0))
- as_warn (_("FP branch in delay slot"));
+ && (last_insn->flags & F_DELAYED) != 0)
+ {
+ /* Before SPARC V9 the effect of having a delayed branch
+ instruction in the delay slot of a conditional delayed branch
+ was undefined.
+
+ In SPARC V9 DCTI couples are well defined.
+
+ However, starting with the UltraSPARC Architecture 2005, DCTI
+ couples (of all kind) are deprecated and should not be used,
+ as they may be slow or behave differently to what the
+ programmer expects. */
+ if (dcti_couples_detect
+ && (insn->flags & F_DELAYED) != 0
+ && ((max_architecture < SPARC_OPCODE_ARCH_V9
+ && (last_insn->flags & F_CONDBR) != 0)
+ || max_architecture >= SPARC_OPCODE_ARCH_V9C))
+ as_warn (_("unpredictable DCTI couple"));
+
+
+ /* We warn about attempts to put a floating point branch in a
+ delay slot, unless the delay slot has been annulled. */
+ if ((insn->flags & F_FBR) != 0
+ /* ??? This test isn't completely accurate. We assume anything with
+ F_{UNBR,CONDBR,FBR} set is annullable. */
+ && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
+ || (last_opcode & ANNUL) == 0))
+ as_warn (_("FP branch in delay slot"));
+ }
/* SPARC before v9 requires a nop instruction between a floating
point instruction and a floating point branch. We insert one
}
}
+static const char *
+get_hwcap_name (bfd_uint64_t mask)
+{
+ if (mask & HWCAP_MUL32)
+ return "mul32";
+ if (mask & HWCAP_DIV32)
+ return "div32";
+ if (mask & HWCAP_FSMULD)
+ return "fsmuld";
+ if (mask & HWCAP_V8PLUS)
+ return "v8plus";
+ if (mask & HWCAP_POPC)
+ return "popc";
+ if (mask & HWCAP_VIS)
+ return "vis";
+ if (mask & HWCAP_VIS2)
+ return "vis2";
+ if (mask & HWCAP_ASI_BLK_INIT)
+ return "ASIBlkInit";
+ if (mask & HWCAP_FMAF)
+ return "fmaf";
+ if (mask & HWCAP_VIS3)
+ return "vis3";
+ if (mask & HWCAP_HPC)
+ return "hpc";
+ if (mask & HWCAP_RANDOM)
+ return "random";
+ if (mask & HWCAP_TRANS)
+ return "trans";
+ if (mask & HWCAP_FJFMAU)
+ return "fjfmau";
+ if (mask & HWCAP_IMA)
+ return "ima";
+ if (mask & HWCAP_ASI_CACHE_SPARING)
+ return "cspare";
+ if (mask & HWCAP_AES)
+ return "aes";
+ if (mask & HWCAP_DES)
+ return "des";
+ if (mask & HWCAP_KASUMI)
+ return "kasumi";
+ if (mask & HWCAP_CAMELLIA)
+ return "camellia";
+ if (mask & HWCAP_MD5)
+ return "md5";
+ if (mask & HWCAP_SHA1)
+ return "sha1";
+ if (mask & HWCAP_SHA256)
+ return "sha256";
+ if (mask & HWCAP_SHA512)
+ return "sha512";
+ if (mask & HWCAP_MPMUL)
+ return "mpmul";
+ if (mask & HWCAP_MONT)
+ return "mont";
+ if (mask & HWCAP_PAUSE)
+ return "pause";
+ if (mask & HWCAP_CBCOND)
+ return "cbcond";
+ if (mask & HWCAP_CRC32C)
+ return "crc32c";
+
+ mask = mask >> 32;
+ if (mask & HWCAP2_FJATHPLUS)
+ return "fjathplus";
+ if (mask & HWCAP2_VIS3B)
+ return "vis3b";
+ if (mask & HWCAP2_ADP)
+ return "adp";
+ if (mask & HWCAP2_SPARC5)
+ return "sparc5";
+ if (mask & HWCAP2_MWAIT)
+ return "mwait";
+ if (mask & HWCAP2_XMPMUL)
+ return "xmpmul";
+ if (mask & HWCAP2_XMONT)
+ return "xmont";
+ if (mask & HWCAP2_NSEC)
+ return "nsec";
+ if (mask & HWCAP2_SPARC6)
+ return "sparc6";
+ if (mask & HWCAP2_ONADDSUB)
+ return "onaddsub";
+ if (mask & HWCAP2_ONMUL)
+ return "onmul";
+ if (mask & HWCAP2_ONDIV)
+ return "ondiv";
+ if (mask & HWCAP2_DICTUNP)
+ return "dictunp";
+ if (mask & HWCAP2_FPCMPSHL)
+ return "fpcmpshl";
+ if (mask & HWCAP2_RLE)
+ return "rle";
+ if (mask & HWCAP2_SHA3)
+ return "sha3";
+
+ return "UNKNOWN";
+}
+
/* Subroutine of md_assemble to do the actual parsing. */
static int
sparc_ip (char *str, const struct sparc_opcode **pinsn)
{
- char *error_message = "";
+ const char *error_message = "";
char *s;
const char *args;
char c;
int comma = 0;
int v9_arg_p;
int special_case = SPECIAL_CASE_NONE;
+ const sparc_asi *sasi = NULL;
s = str;
if (ISLOWER (*s))
{
do
++s;
- while (ISLOWER (*s) || ISDIGIT (*s));
+ while (ISLOWER (*s) || ISDIGIT (*s) || *s == '_');
}
switch (*s)
{
while (*s == '#')
{
- int mask;
+ int jmask;
if (! parse_keyword_arg (sparc_encode_membar, &s,
- &mask))
+ &jmask))
{
error_message = _(": invalid membar mask name");
goto error;
}
- kmask |= mask;
+ kmask |= jmask;
while (*s == ' ')
++s;
if (*s == '|' || *s == '+')
/* Parse a sparc64 privileged register. */
if (*s == '%')
{
- struct priv_reg_entry *p = priv_reg_table;
+ struct priv_reg_entry *p;
unsigned int len = 9999999; /* Init to make gcc happy. */
s += 1;
- while (p->name[0] > s[0])
- p++;
- while (p->name[0] == s[0])
- {
- len = strlen (p->name);
- if (strncmp (p->name, s, len) == 0)
- break;
- p++;
- }
- if (p->name[0] != s[0])
+ for (p = priv_reg_table; p->name; p++)
+ if (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ }
+
+ if (!p->name)
{
error_message = _(": unrecognizable privileged register");
goto error;
}
- if (*args == '?')
- opcode |= (p->regnum << 14);
- else
- opcode |= (p->regnum << 25);
+
+ if (((opcode >> (*args == '?' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+ {
+ error_message = _(": unrecognizable privileged register");
+ goto error;
+ }
+
s += len;
continue;
}
/* Parse a sparc64 hyperprivileged register. */
if (*s == '%')
{
- struct priv_reg_entry *p = hpriv_reg_table;
+ struct priv_reg_entry *p;
unsigned int len = 9999999; /* Init to make gcc happy. */
s += 1;
- while (p->name[0] > s[0])
- p++;
- while (p->name[0] == s[0])
- {
- len = strlen (p->name);
- if (strncmp (p->name, s, len) == 0)
- break;
- p++;
- }
- if (p->name[0] != s[0])
+ for (p = hpriv_reg_table; p->name; p++)
+ if (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ }
+
+ if (!p->name)
{
error_message = _(": unrecognizable hyperprivileged register");
goto error;
}
- if (*args == '$')
- opcode |= (p->regnum << 14);
- else
- opcode |= (p->regnum << 25);
- s += len;
+
+ if (((opcode >> (*args == '$' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+ {
+ error_message = _(": unrecognizable hyperprivileged register");
+ goto error;
+ }
+
+ s += len;
continue;
}
else
case '_':
case '/':
- /* Parse a v9a/v9b ancillary state register. */
+ /* Parse a v9a or later ancillary state register. */
if (*s == '%')
{
- struct priv_reg_entry *p = v9a_asr_table;
+ struct priv_reg_entry *p;
unsigned int len = 9999999; /* Init to make gcc happy. */
s += 1;
- while (p->name[0] > s[0])
- p++;
- while (p->name[0] == s[0])
- {
- len = strlen (p->name);
- if (strncmp (p->name, s, len) == 0)
- break;
- p++;
- }
- if (p->name[0] != s[0])
+ for (p = v9a_asr_table; p->name; p++)
+ if (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ }
+
+ if (!p->name)
{
- error_message = _(": unrecognizable v9a or v9b ancillary state register");
+ error_message = _(": unrecognizable ancillary state register");
goto error;
}
- if (*args == '/' && (p->regnum == 20 || p->regnum == 21))
- {
- error_message = _(": rd on write only ancillary state register");
- goto error;
- }
- if (p->regnum >= 24
- && (insn->architecture
- & SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A)))
- {
- /* %sys_tick and %sys_tick_cmpr are v9bnotv9a */
- error_message = _(": unrecognizable v9a ancillary state register");
- goto error;
- }
- if (*args == '/')
- opcode |= (p->regnum << 14);
- else
- opcode |= (p->regnum << 25);
+
+ if (((opcode >> (*args == '/' ? 14 : 25)) & 0x1f) != (unsigned) p->regnum)
+ {
+ error_message = _(": unrecognizable ancillary state register");
+ goto error;
+ }
+
s += len;
continue;
}
else
{
- error_message = _(": unrecognizable v9a or v9b ancillary state register");
+ error_message = _(": unrecognizable ancillary state register");
goto error;
}
++s;
}
- if (current_architecture >= SPARC_OPCODE_ARCH_V9)
- {
- if (num < 16 || 31 < num)
- {
- error_message = _(": asr number must be between 16 and 31");
- goto error;
- }
- }
- else
- {
- if (num < 0 || 31 < num)
- {
- error_message = _(": asr number must be between 0 and 31");
- goto error;
- }
- }
+ /* We used to check here for the asr number to
+ be between 16 and 31 in V9 and later, as
+ mandated by the section C.1.1 "Register
+ Names" in the SPARC spec. However, we
+ decided to remove this restriction as a) it
+ introduces problems when new V9 asr registers
+ are introduced, b) the Solaris assembler
+ doesn't implement this restriction and c) the
+ restriction will go away in future revisions
+ of the Oracle SPARC Architecture. */
+
+ if (num < 0 || 31 < num)
+ {
+ error_message = _(": asr number must be between 0 and 31");
+ goto error;
+ }
opcode |= (*args == 'M' ? RS1 (num) : RD (num));
continue;
the_insn.reloc = BFD_RELOC_SPARC_10;
goto immediate;
+ case ')':
+ if (*s == ' ')
+ s++;
+ if ((s[0] == '0' && s[1] == 'x' && ISXDIGIT (s[2]))
+ || ISDIGIT (*s))
+ {
+ long num = 0;
+
+ if (s[0] == '0' && s[1] == 'x')
+ {
+ s += 2;
+ while (ISXDIGIT (*s))
+ {
+ num <<= 4;
+ num |= hex_value (*s);
+ ++s;
+ }
+ }
+ else
+ {
+ while (ISDIGIT (*s))
+ {
+ num = num * 10 + *s - '0';
+ ++s;
+ }
+ }
+ if (num < 0 || num > 31)
+ {
+ error_message = _(": crypto immediate must be between 0 and 31");
+ goto error;
+ }
+
+ opcode |= RS3 (num);
+ continue;
+ }
+ else
+ {
+ error_message = _(": expecting crypto immediate");
+ goto error;
+ }
+
case 'X':
/* V8 systems don't understand BFD_RELOC_SPARC_5. */
if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
the_insn.pcrel = 1;
goto immediate;
+ case '=':
+ the_insn.reloc = /* RELOC_WDISP2_8 */ BFD_RELOC_SPARC_WDISP10;
+ the_insn.pcrel = 1;
+ goto immediate;
+
case 'G':
the_insn.reloc = BFD_RELOC_SPARC_WDISP19;
the_insn.pcrel = 1;
{
++s;
}
- if (strncmp (s, "%icc", 4) == 0)
+ if ((strncmp (s, "%icc", 4) == 0)
+ || (sparc_arch_size == 32 && strncmp (s, "%ncc", 4) == 0))
{
s += 4;
continue;
{
++s;
}
- if (strncmp (s, "%xcc", 4) == 0)
+ if ((strncmp (s, "%xcc", 4) == 0)
+ || (sparc_arch_size == 64 && strncmp (s, "%ncc", 4) == 0))
{
s += 4;
continue;
case '\0': /* End of args. */
if (s[0] == ',' && s[1] == '%')
{
- static const struct ops
- {
- /* The name as it appears in assembler. */
- char *name;
- /* strlen (name), precomputed for speed */
- int len;
- /* The reloc this pseudo-op translates to. */
- int reloc;
- /* 1 if tls call. */
- int tls_call;
- }
- ops[] =
- {
- { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 },
- { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 },
- { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 },
- { "tldm_call", 9, BFD_RELOC_SPARC_TLS_LDM_CALL, 1 },
- { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 },
- { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 },
- { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 },
- { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 },
- { "gdop", 4, BFD_RELOC_SPARC_GOTDATA_OP, 0 },
- { NULL, 0, 0, 0 }
- };
- const struct ops *o;
char *s1;
int npar = 0;
+ const struct perc_entry *p;
- for (o = ops; o->name; o++)
- if (strncmp (s + 2, o->name, o->len) == 0)
- break;
- if (o->name == NULL)
- break;
+ for (p = perc_table; p->type != perc_entry_none; p++)
+ if ((p->type == perc_entry_post_pop || p->type == perc_entry_reg)
+ && strncmp (s + 2, p->name, p->len) == 0)
+ break;
+ if (p->type == perc_entry_none || p->type == perc_entry_reg)
+ break;
- if (s[o->len + 2] != '(')
+ if (s[p->len + 2] != '(')
{
- as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
return special_case;
}
- if (! o->tls_call && the_insn.reloc != BFD_RELOC_NONE)
+ if (! (p->pop->flags & F_POP_TLS_CALL)
+ && the_insn.reloc != BFD_RELOC_NONE)
{
as_bad (_("Illegal operands: %%%s cannot be used together with other relocs in the insn ()"),
- o->name);
+ p->name);
return special_case;
}
- if (o->tls_call
+ if ((p->pop->flags & F_POP_TLS_CALL)
&& (the_insn.reloc != BFD_RELOC_32_PCREL_S2
|| the_insn.exp.X_add_number != 0
|| the_insn.exp.X_add_symbol
!= symbol_find_or_make ("__tls_get_addr")))
{
as_bad (_("Illegal operands: %%%s can be only used with call __tls_get_addr"),
- o->name);
+ p->name);
return special_case;
}
- the_insn.reloc = o->reloc;
+ the_insn.reloc = p->pop->reloc;
memset (&the_insn.exp, 0, sizeof (the_insn.exp));
- s += o->len + 3;
+ s += p->len + 3;
for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
if (*s1 == '(')
if (*s1 != ')')
{
- as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
return special_case;
}
case 'e': /* next operand is a floating point register */
case 'v':
case 'V':
+ case ';':
case 'f':
case 'B':
case 'R':
+ case ':':
+ case '\'':
+
+ case '4':
+ case '5':
case 'g':
case 'H':
case 'J':
+ case '}':
+ case '^':
{
char format;
if (*s++ == '%'
- && ((format = *s) == 'f')
+ && ((format = *s) == 'f'
+ || format == 'd'
+ || format == 'q')
&& ISDIGIT (*++s))
{
for (mask = 0; ISDIGIT (*s); ++s)
if ((*args == 'v'
|| *args == 'B'
- || *args == 'H')
+ || *args == '5'
+ || *args == 'H'
+ || *args == '\''
+ || format == 'd')
&& (mask & 1))
{
+ /* register must be even numbered */
break;
- } /* register must be even numbered */
+ }
if ((*args == 'V'
|| *args == 'R'
- || *args == 'J')
+ || *args == 'J'
+ || format == 'q')
&& (mask & 3))
{
+ /* register must be multiple of 4 */
break;
- } /* register must be multiple of 4 */
+ }
+
+ if ((*args == ':'
+ || *args == ';'
+ || *args == '^')
+ && (mask & 7))
+ {
+ /* register must be multiple of 8 */
+ break;
+ }
+
+ if (*args == '\'' && mask < 48)
+ {
+ /* register must be higher or equal than %f48 */
+ break;
+ }
if (mask >= 64)
{
break;
} /* if not an 'f' register. */
+ if (*args == '}' && mask != RS2 (opcode))
+ {
+ error_message
+ = _(": Instruction requires frs2 and frsd must be the same register");
+ goto error;
+ }
+
switch (*args)
{
case 'v':
case 'V':
case 'e':
+ case ';':
opcode |= RS1 (mask);
continue;
case 'f':
case 'B':
case 'R':
+ case ':':
opcode |= RS2 (mask);
continue;
+ case '\'':
+ opcode |= RS2 (mask & 0xe);
+ continue;
+
+ case '4':
+ case '5':
+ opcode |= RS3 (mask);
+ continue;
+
case 'g':
case 'H':
case 'J':
+ case '}':
+ case '^':
opcode |= RD (mask);
continue;
} /* Pack it in. */
}
break;
+ case '(':
+ if (strncmp (s, "%efsr", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
case '0': /* 64 bit immediate (set, setsw, setx insn) */
the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */
goto immediate;
{
char *s1;
- char *op_arg = NULL;
+ const char *op_arg = NULL;
static expressionS op_exp;
bfd_reloc_code_real_type old_reloc = the_insn.reloc;
/* Check for %hi, etc. */
if (*s == '%')
{
- static const struct ops {
- /* The name as it appears in assembler. */
- char *name;
- /* strlen (name), precomputed for speed */
- int len;
- /* The reloc this pseudo-op translates to. */
- int reloc;
- /* Non-zero if for v9 only. */
- int v9_p;
- /* Non-zero if can be used in pc-relative contexts. */
- int pcrel_p;/*FIXME:wip*/
- } ops[] = {
- /* hix/lox must appear before hi/lo so %hix won't be
- mistaken for %hi. */
- { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 },
- { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 },
- { "hi", 2, BFD_RELOC_HI22, 0, 1 },
- { "lo", 2, BFD_RELOC_LO10, 0, 1 },
- { "pc22", 4, BFD_RELOC_SPARC_PC22, 0, 1 },
- { "pc10", 4, BFD_RELOC_SPARC_PC10, 0, 1 },
- { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
- { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
- { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
- { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 },
- { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 },
- { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 },
- { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 },
- { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 },
- { "tgd_hi22", 8, BFD_RELOC_SPARC_TLS_GD_HI22, 0, 0 },
- { "tgd_lo10", 8, BFD_RELOC_SPARC_TLS_GD_LO10, 0, 0 },
- { "tldm_hi22", 9, BFD_RELOC_SPARC_TLS_LDM_HI22, 0, 0 },
- { "tldm_lo10", 9, BFD_RELOC_SPARC_TLS_LDM_LO10, 0, 0 },
- { "tldo_hix22", 10, BFD_RELOC_SPARC_TLS_LDO_HIX22, 0,
- 0 },
- { "tldo_lox10", 10, BFD_RELOC_SPARC_TLS_LDO_LOX10, 0,
- 0 },
- { "tie_hi22", 8, BFD_RELOC_SPARC_TLS_IE_HI22, 0, 0 },
- { "tie_lo10", 8, BFD_RELOC_SPARC_TLS_IE_LO10, 0, 0 },
- { "tle_hix22", 9, BFD_RELOC_SPARC_TLS_LE_HIX22, 0, 0 },
- { "tle_lox10", 9, BFD_RELOC_SPARC_TLS_LE_LOX10, 0, 0 },
- { "gdop_hix22", 10, BFD_RELOC_SPARC_GOTDATA_OP_HIX22,
- 0, 0 },
- { "gdop_lox10", 10, BFD_RELOC_SPARC_GOTDATA_OP_LOX10,
- 0, 0 },
- { NULL, 0, 0, 0, 0 }
- };
- const struct ops *o;
-
- for (o = ops; o->name; o++)
- if (strncmp (s + 1, o->name, o->len) == 0)
- break;
- if (o->name == NULL)
- break;
-
- if (s[o->len + 1] != '(')
+ const struct perc_entry *p;
+
+ for (p = perc_table; p->type != perc_entry_none; p++)
+ if ((p->type == perc_entry_imm_pop || p->type == perc_entry_reg)
+ && strncmp (s + 1, p->name, p->len) == 0)
+ break;
+ if (p->type == perc_entry_none || p->type == perc_entry_reg)
+ break;
+
+ if (s[p->len + 1] != '(')
{
- as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name);
return special_case;
}
- op_arg = o->name;
- the_insn.reloc = o->reloc;
- s += o->len + 2;
- v9_arg_p = o->v9_p;
+ op_arg = p->name;
+ the_insn.reloc = p->pop->reloc;
+ s += p->len + 2;
+ v9_arg_p = p->pop->flags & F_POP_V9;
}
/* Note that if the get_expression() fails, we will still
*s1 = '\0';
(void) get_expression (s);
*s1 = ')';
+ if (expr_end != s1)
+ {
+ as_bad (_("Expression inside %%%s could not be parsed"), op_arg);
+ return special_case;
+ }
s = s1 + 1;
if (*s == ',' || *s == ']' || !*s)
continue;
{
if (s1[-2] == '%' && s1[-3] == '+')
s1 -= 3;
- else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+ else if (strchr ("golir0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
s1 -= 4;
+ else if (s1[-3] == 'r' && s1[-4] == '%' && s1[-5] == '+')
+ s1 -= 5;
else
s1 = NULL;
if (s1)
val &= 0x3ff;
break;
+ case BFD_RELOC_SPARC_H34:
+ val >>= 12;
+ val &= 0x3fffff;
+ break;
+
case BFD_RELOC_SPARC_H44:
val >>= 22;
val &= 0x3fffff;
all the various cases (e.g. in md_apply_fix and
bfd_install_relocation) so duplicating all that code
here isn't right. */
+
+ /* This is a special case to handle cbcond instructions
+ properly, which can need two relocations. The first
+ one is for the 5-bit immediate field and the latter
+ is going to be for the WDISP10 branch part. We
+ handle the R_SPARC_5 immediate directly here so that
+ we don't need to add support for multiple relocations
+ in one instruction just yet. */
+ if (the_insn.reloc == BFD_RELOC_SPARC_5
+ && ((insn->match & OP(0x3)) == 0))
+ {
+ valueT val = the_insn.exp.X_add_number;
+
+ the_insn.reloc = BFD_RELOC_NONE;
+ if (! in_bitfield_range (val, 0x1f))
+ {
+ error_message = _(": Immediate value in cbcond is out of range.");
+ goto error;
+ }
+ opcode |= val & 0x1f;
+ }
}
continue;
/* Parse an asi. */
if (*s == '#')
{
- if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
+ if (! parse_sparc_asi (&s, &sasi))
{
error_message = _(": invalid ASI name");
goto error;
}
+ asi = sasi->value;
}
else
{
s += 5;
continue;
+ case '{':
+ if (strncmp (s, "%mcdper",7) != 0)
+ break;
+ s += 7;
+ continue;
+
+ case '&':
+ if (strncmp (s, "%entropy", 8) != 0)
+ break;
+ s += 8;
+ continue;
+
case 'E':
if (strncmp (s, "%ccr", 4) != 0)
break;
s += 4;
continue;
+ case '|':
+ {
+ int imm2 = 0;
+
+ /* Parse a 2-bit immediate. */
+ if (! parse_const_expr_arg (&s, &imm2))
+ {
+ error_message = _(": non-immdiate imm2 operand");
+ goto error;
+ }
+ if ((imm2 & ~0x3) != 0)
+ {
+ error_message = _(": imm2 immediate operand out of range (0-3)");
+ goto error;
+ }
+
+ opcode |= ((imm2 & 0x2) << 3) | (imm2 & 0x1);
+ continue;
+ }
+
case 'x':
{
char *push = input_line_pointer;
else
{
/* We have a match. Now see if the architecture is OK. */
+ /* String to use in case of architecture warning. */
+ const char *msg_str = str;
int needed_arch_mask = insn->architecture;
+ /* Include the ASI architecture needed as well */
+ if (sasi && needed_arch_mask > sasi->architecture)
+ {
+ needed_arch_mask = sasi->architecture;
+ msg_str = sasi->name;
+ }
+
+ bfd_uint64_t hwcaps
+ = (((bfd_uint64_t) insn->hwcaps2) << 32) | insn->hwcaps;
+
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ if (hwcaps)
+ hwcap_seen |= hwcaps;
+#endif
if (v9_arg_p)
{
needed_arch_mask &=
as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
sparc_opcode_archs[current_architecture].name,
sparc_opcode_archs[needed_architecture].name,
- str);
+ msg_str);
warn_after_architecture = needed_architecture;
}
current_architecture = needed_architecture;
+ hwcap_allowed
+ = (hwcap_allowed
+ | hwcaps
+ | (((bfd_uint64_t) sparc_opcode_archs[current_architecture].hwcaps2) << 32)
+ | sparc_opcode_archs[current_architecture].hwcaps);
}
/* Conflict. */
/* ??? This seems to be a bit fragile. What if the next entry in
++arch;
}
- as_bad (_("Architecture mismatch on \"%s\"."), str);
- as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
+ as_bad (_("Architecture mismatch on \"%s %s\"."), str, argsStart);
+ as_tsktsk (_("(Requires %s; requested architecture is %s.)"),
required_archs,
sparc_opcode_archs[max_architecture].name);
return special_case;
}
+
+ /* Make sure the hwcaps used by the instruction are
+ currently enabled. */
+ if (hwcaps & ~hwcap_allowed)
+ {
+ const char *hwcap_name = get_hwcap_name(hwcaps & ~hwcap_allowed);
+
+ as_bad (_("Hardware capability \"%s\" not enabled for \"%s\"."),
+ hwcap_name, str);
+ return special_case;
+ }
} /* If no match. */
break;
return special_case;
}
+static char *
+skip_over_keyword (char *q)
+{
+ for (q = q + (*q == '#' || *q == '%');
+ ISALNUM (*q) || *q == '_';
+ ++q)
+ continue;
+ return q;
+}
+
+static int
+parse_sparc_asi (char **input_pointer_p, const sparc_asi **value_p)
+{
+ const sparc_asi *value;
+ char c, *p, *q;
+
+ p = *input_pointer_p;
+ q = skip_over_keyword(p);
+ c = *q;
+ *q = 0;
+ value = sparc_encode_asi (p);
+ *q = c;
+ if (value == NULL)
+ return 0;
+ *value_p = value;
+ *input_pointer_p = q;
+ return 1;
+}
+
/* Parse an argument that can be expressed as a keyword.
(eg: #StoreStore or %ccfr).
The result is a boolean indicating success.
char c, *p, *q;
p = *input_pointerP;
- for (q = p + (*p == '#' || *p == '%');
- ISALNUM (*q) || *q == '_';
- ++q)
- continue;
+ q = skip_over_keyword(p);
c = *q;
*q = 0;
value = (*lookup_fn) (p);
/* Subroutine of md_assemble to output one insn. */
static void
-output_insn (const struct sparc_opcode *insn, struct sparc_it *the_insn)
+output_insn (const struct sparc_opcode *insn, struct sparc_it *theinsn)
{
char *toP = frag_more (4);
/* Put out the opcode. */
if (INSN_BIG_ENDIAN)
- number_to_chars_bigendian (toP, (valueT) the_insn->opcode, 4);
+ number_to_chars_bigendian (toP, (valueT) theinsn->opcode, 4);
else
- number_to_chars_littleendian (toP, (valueT) the_insn->opcode, 4);
+ number_to_chars_littleendian (toP, (valueT) theinsn->opcode, 4);
/* Put out the symbol-dependent stuff. */
- if (the_insn->reloc != BFD_RELOC_NONE)
+ if (theinsn->reloc != BFD_RELOC_NONE)
{
fixS *fixP = fix_new_exp (frag_now, /* Which frag. */
(toP - frag_now->fr_literal), /* Where. */
4, /* Size. */
- &the_insn->exp,
- the_insn->pcrel,
- the_insn->reloc);
+ &theinsn->exp,
+ theinsn->pcrel,
+ theinsn->reloc);
/* Turn off overflow checking in fixup_segment. We'll do our
own overflow checking in md_apply_fix. This is necessary because
the insn size is 4 and fixup_segment will signal an overflow for
large 8 byte quantities. */
fixP->fx_no_overflow = 1;
- if (the_insn->reloc == BFD_RELOC_SPARC_OLO10)
- fixP->tc_fix_data = the_insn->exp2.X_add_number;
+ if (theinsn->reloc == BFD_RELOC_SPARC_OLO10)
+ fixP->tc_fix_data = theinsn->exp2.X_add_number;
}
last_insn = insn;
- last_opcode = the_insn->opcode;
+ last_opcode = theinsn->opcode;
#ifdef OBJ_ELF
dwarf2_emit_insn (4);
#endif
}
\f
-char *
+const char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
/* If this is a data relocation, just output VAL. */
- if (fixP->fx_r_type == BFD_RELOC_16
- || fixP->fx_r_type == BFD_RELOC_SPARC_UA16)
+ if (fixP->fx_r_type == BFD_RELOC_8)
+ {
+ md_number_to_chars (buf, val, 1);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_SPARC_UA16)
{
md_number_to_chars (buf, val, 2);
}
insn |= val & 0x3fffffff;
- /* See if we have a delay slot. */
- if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+ /* See if we have a delay slot. In that case we attempt to
+ optimize several cases transforming CALL instructions
+ into branches. But we can only do that if the relocation
+ can be completely resolved here, i.e. if no undefined
+ symbol is associated with it. */
+ if (sparc_relax && fixP->fx_addsy == NULL
+ && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
{
#define G0 0
#define O7 15
insn |= val & 0x1f;
break;
+ case BFD_RELOC_SPARC_WDISP10:
+ if ((val & 3)
+ || val >= 0x007fc
+ || val <= -(offsetT) 0x808)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= ((val & 0x300) << 11)
+ | ((val & 0xff) << 5);
+ break;
+
case BFD_RELOC_SPARC_WDISP16:
if ((val & 3)
|| val >= 0x1fffc
insn |= val & 0x3fffff;
break;
+ case BFD_RELOC_SPARC_H34:
+ if (!fixP->fx_addsy)
+ {
+ bfd_vma tval = val;
+ tval >>= 12;
+ insn |= tval & 0x3fffff;
+ }
+ break;
+
case BFD_RELOC_SPARC_H44:
if (!fixP->fx_addsy)
{
arelent *reloc;
bfd_reloc_code_real_type code;
- relocs[0] = reloc = (arelent *) xmalloc (sizeof (arelent));
+ relocs[0] = reloc = XNEW (arelent);
relocs[1] = NULL;
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
switch (fixp->fx_r_type)
{
+ case BFD_RELOC_8:
case BFD_RELOC_16:
case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ if (fixp->fx_pcrel)
+ {
+ switch (fixp->fx_size)
+ {
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("can not do %d byte pc-relative relocation"),
+ fixp->fx_size);
+ code = fixp->fx_r_type;
+ fixp->fx_pcrel = 0;
+ break;
+ case 1: code = BFD_RELOC_8_PCREL; break;
+ case 2: code = BFD_RELOC_16_PCREL; break;
+ case 4: code = BFD_RELOC_32_PCREL; break;
+#ifdef BFD64
+ case 8: code = BFD_RELOC_64_PCREL; break;
+#endif
+ }
+ if (fixp->fx_pcrel)
+ fixp->fx_addnumber = fixp->fx_offset;
+ break;
+ }
+ /* Fall through. */
case BFD_RELOC_HI22:
case BFD_RELOC_LO10:
case BFD_RELOC_32_PCREL_S2:
case BFD_RELOC_SPARC_PC22:
case BFD_RELOC_SPARC_PC10:
case BFD_RELOC_SPARC_BASE13:
+ case BFD_RELOC_SPARC_WDISP10:
case BFD_RELOC_SPARC_WDISP16:
case BFD_RELOC_SPARC_WDISP19:
case BFD_RELOC_SPARC_WDISP22:
- case BFD_RELOC_64:
case BFD_RELOC_SPARC_5:
case BFD_RELOC_SPARC_6:
case BFD_RELOC_SPARC_7:
case BFD_RELOC_SPARC_PC_HH22:
case BFD_RELOC_SPARC_PC_HM10:
case BFD_RELOC_SPARC_PC_LM22:
+ case BFD_RELOC_SPARC_H34:
case BFD_RELOC_SPARC_H44:
case BFD_RELOC_SPARC_M44:
case BFD_RELOC_SPARC_L44:
&& code != BFD_RELOC_SPARC_WDISP22
&& code != BFD_RELOC_SPARC_WDISP16
&& code != BFD_RELOC_SPARC_WDISP19
+ && code != BFD_RELOC_SPARC_WDISP10
&& code != BFD_RELOC_SPARC_WPLT30
&& code != BFD_RELOC_SPARC_TLS_GD_CALL
&& code != BFD_RELOC_SPARC_TLS_LDM_CALL)
on the same location. */
if (code == BFD_RELOC_SPARC_OLO10)
{
- relocs[1] = reloc = (arelent *) xmalloc (sizeof (arelent));
+ relocs[1] = reloc = XNEW (arelent);
relocs[2] = NULL;
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr
= symbol_get_bfdsym (section_symbol (absolute_section));
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
int temp;
symbolS *symbolP;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
p = input_line_pointer;
*p = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
if (*input_line_pointer != ',')
{
offsetT temp, size;
symbolS *symbolP;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
/* Just after name is now '\0'. */
p = input_line_pointer;
*p = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
if (*input_line_pointer != ',')
{
as_bad (_("Expected comma after symbol-name"));
{
segT old_sec;
int old_subsec;
- char *p;
int align;
old_sec = now_seg;
static int sparc_no_align_cons = 0;
-/* This static variable is set by sparc_cons to emit requested types
- of relocations in cons_fix_new_sparc. */
-
-static const char *sparc_cons_special_reloc;
-
/* This handles the unaligned space allocation pseudo-ops, such as
.uaword. .uaword is just like .word, but the value does not need
to be aligned. */
char c;
int reg;
int flags;
- const char *regname;
+ char *regname;
if (input_line_pointer[0] != '%'
|| input_line_pointer[1] != 'g'
if (*input_line_pointer == '#')
{
++input_line_pointer;
- regname = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (®name);
if (strcmp (regname, "scratch") && strcmp (regname, "ignore"))
as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
if (regname[0] == 'i')
regname = NULL;
else
- regname = "";
+ regname = (char *) "";
}
else
{
- regname = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (®name);
}
+
if (sparc_arch_size == 64)
{
if (globals[reg])
}
}
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
demand_empty_rest_of_line ();
}
sparc_cons_align (int nbytes)
{
int nalign;
- char *p;
/* Only do this if we are enforcing aligned data. */
if (! enforce_aligned_data)
return;
}
- p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
- (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+ frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
record_alignment (now_seg, nalign);
}
elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1|EF_SPARC_SUN_US3;
}
-void
+const char *
sparc_cons (expressionS *exp, int size)
{
char *save;
+ const char *sparc_cons_special_reloc = NULL;
SKIP_WHITESPACE ();
- sparc_cons_special_reloc = NULL;
save = input_line_pointer;
if (input_line_pointer[0] == '%'
&& input_line_pointer[1] == 'r'
}
if (sparc_cons_special_reloc == NULL)
expression (exp);
+ return sparc_cons_special_reloc;
}
#endif
cons_fix_new_sparc (fragS *frag,
int where,
unsigned int nbytes,
- expressionS *exp)
+ expressionS *exp,
+ const char *sparc_cons_special_reloc)
{
bfd_reloc_code_real_type r;
&& now_seg->flags & SEC_ALLOC)
r = BFD_RELOC_SPARC_REV32;
+#ifdef TE_SOLARIS
+ /* The Solaris linker does not allow R_SPARC_UA64
+ relocations for 32-bit executables. */
+ if (!target_little_endian_data
+ && sparc_arch_size != 64
+ && r == BFD_RELOC_64)
+ r = BFD_RELOC_32;
+#endif
+
if (sparc_cons_special_reloc)
{
if (*sparc_cons_special_reloc == 'd')
case 8: r = BFD_RELOC_SPARC_TLS_DTPOFF64; break;
}
}
- else if (sparc_no_align_cons)
+ else if (sparc_no_align_cons
+ || /* PR 20803 - relocs in the .eh_frame section
+ need to support unaligned access. */
+ strcmp (now_seg->name, ".eh_frame") == 0)
{
switch (nbytes)
{
case 2: r = BFD_RELOC_SPARC_UA16; break;
case 4: r = BFD_RELOC_SPARC_UA32; break;
+#ifdef TE_SOLARIS
+ /* The Solaris linker does not allow R_SPARC_UA64
+ relocations for 32-bit executables. */
+ case 8: r = sparc_arch_size == 64 ?
+ BFD_RELOC_SPARC_UA64 : BFD_RELOC_SPARC_UA32; break;
+#else
case 8: r = BFD_RELOC_SPARC_UA64; break;
+#endif
default: abort ();
}
}
fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
- sparc_cons_special_reloc = NULL;
}
void
int
sparc_regname_to_dw2regnum (char *regname)
{
- char *p, *q;
+ char *q;
+ int i;
if (!regname[0])
return -1;
- q = "goli";
- p = strchr (q, regname[0]);
- if (p)
+ switch (regname[0])
+ {
+ case 'g': i = 0; break;
+ case 'o': i = 1; break;
+ case 'l': i = 2; break;
+ case 'i': i = 3; break;
+ default: i = -1; break;
+ }
+ if (i != -1)
{
if (regname[1] < '0' || regname[1] > '8' || regname[2])
return -1;
- return (p - q) * 8 + regname[1] - '0';
+ return i * 8 + regname[1] - '0';
}
if (regname[0] == 's' && regname[1] == 'p' && !regname[2])
return 14;
unsigned int regnum;
regnum = strtoul (regname + 1, &q, 10);
- if (p == q || *q)
+ if (q == NULL || *q)
return -1;
if (regnum >= ((regname[0] == 'f'
&& SPARC_OPCODE_ARCH_V9_P (max_architecture))
void
sparc_cfi_emit_pcrel_expr (expressionS *exp, unsigned int nbytes)
{
- sparc_cons_special_reloc = "disp";
sparc_no_align_cons = 1;
- emit_expr (exp, nbytes);
+ emit_expr_with_reloc (exp, nbytes, "disp");
sparc_no_align_cons = 0;
- sparc_cons_special_reloc = NULL;
}