/* tc-riscv.c -- RISC-V assembler
- Copyright 2011-2016 Free Software Foundation, Inc.
+ Copyright (C) 2011-2017 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
#include "itbl-ops.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
+#include "struc-symbol.h"
#include "elf/riscv.h"
#include "opcode/riscv.h"
static const char default_arch[] = DEFAULT_ARCH;
-unsigned xlen = 0; /* width of an x-register */
+static unsigned xlen = 0; /* width of an x-register */
+static unsigned abi_xlen = 0; /* width of a pointer in the ABI */
-#define LOAD_ADDRESS_INSN (xlen == 64 ? "ld" : "lw")
+#define LOAD_ADDRESS_INSN (abi_xlen == 64 ? "ld" : "lw")
#define ADD32_INSN (xlen == 64 ? "addiw" : "addi")
static unsigned elf_flags = 0;
{
int pic; /* Generate position-independent code. */
int rvc; /* Generate RVC code. */
+ int relax; /* Emit relocs the linker is allowed to relax. */
};
static struct riscv_set_options riscv_opts =
{
0, /* pic */
0, /* rvc */
+ 1, /* relax */
};
static void
return FALSE;
}
+static void
+riscv_clear_subsets (void)
+{
+ while (riscv_subsets != NULL)
+ {
+ struct riscv_subset *next = riscv_subsets->next;
+ free ((void *) riscv_subsets->name);
+ free (riscv_subsets);
+ riscv_subsets = next;
+ }
+}
+
static void
riscv_add_subset (const char *subset)
{
riscv_subsets = s;
}
-/* Set which ISA and extensions are available. Formally, ISA strings must
- begin with RV32 or RV64, but we allow the prefix to be omitted.
+/* Set which ISA and extensions are available. */
- FIXME: Version numbers are not supported yet. */
static void
-riscv_set_arch (const char *p)
+riscv_set_arch (const char *s)
{
- const char *all_subsets = "IMAFDC";
- const char *extension = NULL;
- int rvc = 0;
- int i;
+ const char *all_subsets = "imafdqc";
+ char *extension = NULL;
+ const char *p = s;
+
+ riscv_clear_subsets();
- if (strncasecmp (p, "RV32", 4) == 0)
+ if (strncmp (p, "rv32", 4) == 0)
{
xlen = 32;
p += 4;
}
- else if (strncasecmp (p, "RV64", 4) == 0)
+ else if (strncmp (p, "rv64", 4) == 0)
{
xlen = 64;
p += 4;
}
- else if (strncasecmp (p, "RV", 2) == 0)
- p += 2;
+ else
+ as_fatal ("-march=%s: ISA string must begin with rv32 or rv64", s);
- switch (TOUPPER(*p))
+ switch (*p)
{
- case 'I':
+ case 'i':
break;
- case 'G':
+ case 'g':
p++;
- /* Fall through. */
-
- case '\0':
- for (i = 0; all_subsets[i] != '\0'; i++)
+ for ( ; *all_subsets != 'q'; all_subsets++)
{
- const char subset[] = {all_subsets[i], '\0'};
+ const char subset[] = {*all_subsets, '\0'};
riscv_add_subset (subset);
}
break;
default:
- as_fatal ("`I' must be the first ISA subset name specified (got %c)",
- *p);
+ as_fatal ("-march=%s: first ISA subset must be `i' or `g'", s);
}
while (*p)
{
- if (TOUPPER(*p) == 'X')
+ if (*p == 'x')
{
- char *subset = xstrdup (p), *q = subset;
+ char *subset = xstrdup (p);
+ char *q = subset;
while (*++q != '\0' && *q != '_')
;
*q = '\0';
if (extension)
- as_fatal ("only one eXtension is supported (found %s and %s)",
- extension, subset);
+ as_fatal ("-march=%s: only one non-standard extension is supported"
+ " (found `%s' and `%s')", s, extension, subset);
extension = subset;
riscv_add_subset (subset);
p += strlen (subset);
- free (subset);
}
else if (*p == '_')
p++;
{
const char subset[] = {*p, 0};
riscv_add_subset (subset);
- if (TOUPPER(*p) == 'C')
- rvc = 1;
all_subsets++;
p++;
}
else
- as_fatal ("unsupported ISA subset %c", *p);
+ as_fatal ("-march=%s: unsupported ISA subset `%c'", s, *p);
}
- if (rvc)
- {
- /* Override -m[no-]rvc setting if C was explicitly listed. */
- riscv_set_rvc (TRUE);
- }
- else
- {
- /* Add RVC anyway. -m[no-]rvc toggles its availability. */
- riscv_add_subset ("C");
- }
+ free (extension);
}
/* Handle of the OPCODE hash table. */
if (fragp->fr_symbol != NULL
&& S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
{
offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
case 'C': /* RVC */
switch (c = *p++)
{
- case 'a': used_bits |= ENCODE_RVC_J_IMM(-1U); break;
+ case 'a': used_bits |= ENCODE_RVC_J_IMM (-1U); break;
case 'c': break; /* RS1, constrained to equal sp */
case 'i': used_bits |= ENCODE_RVC_SIMM3(-1U); break;
- case 'j': used_bits |= ENCODE_RVC_IMM(-1U); break;
- case 'k': used_bits |= ENCODE_RVC_LW_IMM(-1U); break;
- case 'l': used_bits |= ENCODE_RVC_LD_IMM(-1U); break;
- case 'm': used_bits |= ENCODE_RVC_LWSP_IMM(-1U); break;
- case 'n': used_bits |= ENCODE_RVC_LDSP_IMM(-1U); break;
- case 'p': used_bits |= ENCODE_RVC_B_IMM(-1U); break;
+ case 'j': used_bits |= ENCODE_RVC_IMM (-1U); break;
+ case 'o': used_bits |= ENCODE_RVC_IMM (-1U); break;
+ case 'k': used_bits |= ENCODE_RVC_LW_IMM (-1U); break;
+ case 'l': used_bits |= ENCODE_RVC_LD_IMM (-1U); break;
+ case 'm': used_bits |= ENCODE_RVC_LWSP_IMM (-1U); break;
+ case 'n': used_bits |= ENCODE_RVC_LDSP_IMM (-1U); break;
+ case 'p': used_bits |= ENCODE_RVC_B_IMM (-1U); break;
case 's': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break;
case 't': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
- case 'u': used_bits |= ENCODE_RVC_IMM(-1U); break;
- case 'v': used_bits |= ENCODE_RVC_IMM(-1U); break;
+ case 'u': used_bits |= ENCODE_RVC_IMM (-1U); break;
+ case 'v': used_bits |= ENCODE_RVC_IMM (-1U); break;
case 'w': break; /* RS1S, constrained to equal RD */
case 'x': break; /* RS2S, constrained to equal RD */
- case 'K': used_bits |= ENCODE_RVC_ADDI4SPN_IMM(-1U); break;
- case 'L': used_bits |= ENCODE_RVC_ADDI16SP_IMM(-1U); break;
- case 'M': used_bits |= ENCODE_RVC_SWSP_IMM(-1U); break;
- case 'N': used_bits |= ENCODE_RVC_SDSP_IMM(-1U); break;
+ case 'K': used_bits |= ENCODE_RVC_ADDI4SPN_IMM (-1U); break;
+ case 'L': used_bits |= ENCODE_RVC_ADDI16SP_IMM (-1U); break;
+ case 'M': used_bits |= ENCODE_RVC_SWSP_IMM (-1U); break;
+ case 'N': used_bits |= ENCODE_RVC_SDSP_IMM (-1U); break;
case 'U': break; /* RS1, constrained to equal RD */
case 'V': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
- case '<': used_bits |= ENCODE_RVC_IMM(-1U); break;
- case '>': used_bits |= ENCODE_RVC_IMM(-1U); break;
+ case '<': used_bits |= ENCODE_RVC_IMM (-1U); break;
+ case '>': used_bits |= ENCODE_RVC_IMM (-1U); break;
case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break;
case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break;
default:
case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break;
case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break;
case 'o':
- case 'j': used_bits |= ENCODE_ITYPE_IMM(-1U); break;
- case 'a': used_bits |= ENCODE_UJTYPE_IMM(-1U); break;
- case 'p': used_bits |= ENCODE_SBTYPE_IMM(-1U); break;
- case 'q': used_bits |= ENCODE_STYPE_IMM(-1U); break;
- case 'u': used_bits |= ENCODE_UTYPE_IMM(-1U); break;
+ case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
+ case 'a': used_bits |= ENCODE_UJTYPE_IMM (-1U); break;
+ case 'p': used_bits |= ENCODE_SBTYPE_IMM (-1U); break;
+ case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
+ case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
case '[': break;
case ']': break;
case '0': break;
md_begin (void)
{
int i = 0;
+ unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
- if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, 0))
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
as_warn (_("Could not set architecture and machine"));
op_hash = hash_new ();
hash_reg_names (RCLASS_FPR, riscv_fpr_names_abi, NFPR);
#define DECLARE_CSR(name, num) hash_reg_name (RCLASS_CSR, #name, num);
+#define DECLARE_CSR_ALIAS(name, num) DECLARE_CSR(name, num);
#include "opcode/riscv-opc.h"
#undef DECLARE_CSR
record_alignment (text_section, riscv_opts.rvc ? 1 : 2);
}
+static insn_t
+riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
+{
+ switch (reloc_type)
+ {
+ case BFD_RELOC_32:
+ return value;
+
+ case BFD_RELOC_RISCV_HI20:
+ return ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
+
+ case BFD_RELOC_RISCV_LO12_S:
+ return ENCODE_STYPE_IMM (value);
+
+ case BFD_RELOC_RISCV_LO12_I:
+ return ENCODE_ITYPE_IMM (value);
+
+ default:
+ abort ();
+ }
+}
+
/* Output an instruction. IP is the instruction information.
ADDRESS_EXPR is an operand of the instruction to be used with
RELOC_TYPE. */
{
reloc_howto_type *howto;
- gas_assert(address_expr);
+ gas_assert (address_expr);
if (reloc_type == BFD_RELOC_12_PCREL
|| reloc_type == BFD_RELOC_RISCV_JMP)
{
address_expr->X_add_number);
return;
}
- else if (address_expr->X_op == O_constant)
+ else
{
- switch (reloc_type)
- {
- case BFD_RELOC_32:
- ip->insn_opcode |= address_expr->X_add_number;
- goto append;
-
- case BFD_RELOC_RISCV_HI20:
- {
- insn_t imm = RISCV_CONST_HIGH_PART (address_expr->X_add_number);
- ip->insn_opcode |= ENCODE_UTYPE_IMM (imm);
- goto append;
- }
-
- case BFD_RELOC_RISCV_LO12_S:
- ip->insn_opcode |= ENCODE_STYPE_IMM (address_expr->X_add_number);
- goto append;
+ howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
+ if (howto == NULL)
+ as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
- case BFD_RELOC_RISCV_LO12_I:
- ip->insn_opcode |= ENCODE_ITYPE_IMM (address_expr->X_add_number);
- goto append;
+ ip->fixp = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr, FALSE, reloc_type);
- default:
- break;
- }
+ ip->fixp->fx_tcbit = riscv_opts.relax;
}
-
- howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
- if (howto == NULL)
- as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
-
- ip->fixp = fix_new_exp (ip->frag, ip->where,
- bfd_get_reloc_size (howto),
- address_expr, FALSE, reloc_type);
}
-append:
add_fixed_insn (ip);
install_insn (ip);
+
+ /* We need to start a new frag after any instruction that can be
+ optimized away or compressed by the linker during relaxation, to prevent
+ the assembler from computing static offsets across such an instruction.
+ This is necessary to get correct EH info. */
+ if (reloc_type == BFD_RELOC_RISCV_CALL
+ || reloc_type == BFD_RELOC_RISCV_CALL_PLT
+ || reloc_type == BFD_RELOC_RISCV_HI20
+ || reloc_type == BFD_RELOC_RISCV_PCREL_HI20
+ || reloc_type == BFD_RELOC_RISCV_TPREL_HI20
+ || reloc_type == BFD_RELOC_RISCV_TPREL_ADD)
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ }
}
/* Build an instruction created by a macro expansion. This is passed
make_internal_label (void)
{
return (symbolS *) local_symbol_make (FAKE_LABEL_NAME, now_seg,
- (valueT) frag_now_fix(), frag_now);
+ (valueT) frag_now_fix (), frag_now);
}
/* Load an entry from the GOT. */
return;
}
- if (xlen > 32 && !IS_SEXT_32BIT_NUM(ep->X_add_number))
+ if (xlen > 32 && !IS_SEXT_32BIT_NUM (ep->X_add_number))
{
/* Reduce to a signed 32-bit constant using SLLI and ADDI. */
while (((upper.X_add_number >> shift) & 1) == 0)
shift++;
upper.X_add_number = (int64_t) upper.X_add_number >> shift;
- load_const(reg, &upper);
+ load_const (reg, &upper);
macro_build (NULL, "slli", "d,s,>", reg, reg, shift);
if (lower.X_add_number != 0)
/* Check whether the output BFD supports this relocation.
If not, issue an error and fall back on something safe. */
- if (!bfd_reloc_type_lookup (stdoutput, percent_op->reloc))
+ if (*reloc != BFD_RELOC_UNUSED
+ && !bfd_reloc_type_lookup (stdoutput, *reloc))
{
as_bad ("relocation %s isn't supported by the current ABI",
percent_op->str);
return reloc_index;
}
+/* Detect and handle implicitly zero load-store offsets. For example,
+ "lw t0, (t1)" is shorthand for "lw t0, 0(t1)". Return TRUE iff such
+ an implicit offset was detected. */
+
+static bfd_boolean
+riscv_handle_implicit_zero_offset (expressionS *expr, const char *s)
+{
+ /* Check whether there is only a single bracketed expression left.
+ If so, it must be the base register and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ expr->X_op = O_constant;
+ expr->X_add_number = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/* This routine assembles an instruction into its binary format. As a
side effect, it sets the global variable imm_reloc to the type of
relocation to do if one of the operands is an address expression. */
ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'k':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_LW_IMM (imm_expr->X_add_number))
ip->insn_opcode |= ENCODE_RVC_LW_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'l':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_LD_IMM (imm_expr->X_add_number))
ip->insn_opcode |= ENCODE_RVC_LD_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'm':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_LWSP_IMM (imm_expr->X_add_number))
ENCODE_RVC_LWSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'n':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_LDSP_IMM (imm_expr->X_add_number))
ip->insn_opcode |=
ENCODE_RVC_LDSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
+ case 'o':
+ if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
+ || imm_expr->X_op != O_constant
+ /* C.addiw, c.li, and c.andi allow zero immediate.
+ C.addi allows zero immediate as hint. Otherwise this
+ is same as 'j'. */
+ || !VALID_RVC_IMM (imm_expr->X_add_number))
+ break;
+ ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number);
+ goto rvc_imm_done;
case 'K':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
ENCODE_RVC_ADDI16SP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'M':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_SWSP_IMM (imm_expr->X_add_number))
ENCODE_RVC_SWSP_IMM (imm_expr->X_add_number);
goto rvc_imm_done;
case 'N':
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
+ continue;
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
|| !VALID_RVC_SDSP_IMM (imm_expr->X_add_number))
my_getExpression (imm_expr, s);
check_absolute_expr (ip, imm_expr);
if ((unsigned long) imm_expr->X_add_number > 31)
- as_warn (_("Improper shift amount (%lu)"),
- (unsigned long) imm_expr->X_add_number);
+ as_bad (_("Improper shift amount (%lu)"),
+ (unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (SHAMTW, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
s = expr_end;
my_getExpression (imm_expr, s);
check_absolute_expr (ip, imm_expr);
if ((unsigned long) imm_expr->X_add_number >= xlen)
- as_warn (_("Improper shift amount (%lu)"),
- (unsigned long) imm_expr->X_add_number);
+ as_bad (_("Improper shift amount (%lu)"),
+ (unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (SHAMT, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
s = expr_end;
my_getExpression (imm_expr, s);
check_absolute_expr (ip, imm_expr);
if ((unsigned long) imm_expr->X_add_number > 31)
- as_warn (_("Improper CSRxI immediate (%lu)"),
- (unsigned long) imm_expr->X_add_number);
+ as_bad (_("Improper CSRxI immediate (%lu)"),
+ (unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (RS1, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
s = expr_end;
my_getExpression (imm_expr, s);
check_absolute_expr (ip, imm_expr);
if ((unsigned long) imm_expr->X_add_number > 0xfff)
- as_warn(_("Improper CSR address (%lu)"),
+ as_bad (_("Improper CSR address (%lu)"),
(unsigned long) imm_expr->X_add_number);
INSERT_OPERAND (CSR, *ip, imm_expr->X_add_number);
imm_expr->X_op = O_absent;
p = percent_op_rtype;
*imm_reloc = BFD_RELOC_UNUSED;
load_store:
- /* Check whether there is only a single bracketed expression
- left. If so, it must be the base register and the
- constant must be zero. */
- imm_expr->X_op = O_constant;
- imm_expr->X_add_number = 0;
- if (*s == '(' && strchr (s + 1, '(') == 0)
+ if (riscv_handle_implicit_zero_offset (imm_expr, s))
continue;
alu_op:
/* If this value won't fit into a 16 bit offset, then go
enum options
{
- OPTION_M32 = OPTION_MD_BASE,
- OPTION_M64,
- OPTION_MARCH,
+ OPTION_MARCH = OPTION_MD_BASE,
OPTION_PIC,
OPTION_NO_PIC,
- OPTION_MSOFT_FLOAT,
- OPTION_MHARD_FLOAT,
- OPTION_MRVC,
- OPTION_MNO_RVC,
+ OPTION_MABI,
OPTION_END_OF_ENUM
};
struct option md_longopts[] =
{
- {"m32", no_argument, NULL, OPTION_M32},
- {"m64", no_argument, NULL, OPTION_M64},
{"march", required_argument, NULL, OPTION_MARCH},
{"fPIC", no_argument, NULL, OPTION_PIC},
{"fpic", no_argument, NULL, OPTION_PIC},
{"fno-pic", no_argument, NULL, OPTION_NO_PIC},
- {"mrvc", no_argument, NULL, OPTION_MRVC},
- {"mno-rvc", no_argument, NULL, OPTION_MNO_RVC},
- {"msoft-float", no_argument, NULL, OPTION_MSOFT_FLOAT},
- {"mhard-float", no_argument, NULL, OPTION_MHARD_FLOAT},
+ {"mabi", required_argument, NULL, OPTION_MABI},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
-enum float_mode
-{
- FLOAT_MODE_DEFAULT,
- FLOAT_MODE_SOFT,
- FLOAT_MODE_HARD
+enum float_abi {
+ FLOAT_ABI_DEFAULT = -1,
+ FLOAT_ABI_SOFT,
+ FLOAT_ABI_SINGLE,
+ FLOAT_ABI_DOUBLE,
+ FLOAT_ABI_QUAD
};
-static enum float_mode float_mode = FLOAT_MODE_DEFAULT;
+static enum float_abi float_abi = FLOAT_ABI_DEFAULT;
+
+static void
+riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi)
+{
+ abi_xlen = new_xlen;
+ float_abi = new_float_abi;
+}
int
md_parse_option (int c, const char *arg)
{
switch (c)
{
- case OPTION_MRVC:
- riscv_set_rvc (TRUE);
- break;
-
- case OPTION_MNO_RVC:
- riscv_set_rvc (FALSE);
- break;
-
- case OPTION_MSOFT_FLOAT:
- float_mode = FLOAT_MODE_SOFT;
- break;
-
- case OPTION_MHARD_FLOAT:
- float_mode = FLOAT_MODE_HARD;
- break;
-
- case OPTION_M32:
- xlen = 32;
- break;
-
- case OPTION_M64:
- xlen = 64;
- break;
-
case OPTION_MARCH:
riscv_set_arch (arg);
break;
riscv_opts.pic = TRUE;
break;
+ case OPTION_MABI:
+ if (strcmp (arg, "ilp32") == 0)
+ riscv_set_abi (32, FLOAT_ABI_SOFT);
+ else if (strcmp (arg, "ilp32f") == 0)
+ riscv_set_abi (32, FLOAT_ABI_SINGLE);
+ else if (strcmp (arg, "ilp32d") == 0)
+ riscv_set_abi (32, FLOAT_ABI_DOUBLE);
+ else if (strcmp (arg, "ilp32q") == 0)
+ riscv_set_abi (32, FLOAT_ABI_QUAD);
+ else if (strcmp (arg, "lp64") == 0)
+ riscv_set_abi (64, FLOAT_ABI_SOFT);
+ else if (strcmp (arg, "lp64f") == 0)
+ riscv_set_abi (64, FLOAT_ABI_SINGLE);
+ else if (strcmp (arg, "lp64d") == 0)
+ riscv_set_abi (64, FLOAT_ABI_DOUBLE);
+ else if (strcmp (arg, "lp64q") == 0)
+ riscv_set_abi (64, FLOAT_ABI_QUAD);
+ else
+ return 0;
+ break;
+
default:
return 0;
}
void
riscv_after_parse_args (void)
{
- if (riscv_subsets == NULL)
- riscv_set_arch ("RVIMAFD");
-
if (xlen == 0)
{
if (strcmp (default_arch, "riscv32") == 0)
else
as_bad ("unknown default architecture `%s'", default_arch);
}
+
+ if (riscv_subsets == NULL)
+ riscv_set_arch (xlen == 64 ? "rv64g" : "rv32g");
+
+ /* Add the RVC extension, regardless of -march, to support .option rvc. */
+ riscv_set_rvc (FALSE);
+ if (riscv_subset_supports ("c"))
+ riscv_set_rvc (TRUE);
+ else
+ riscv_add_subset ("c");
+
+ /* Infer ABI from ISA if not specified on command line. */
+ if (abi_xlen == 0)
+ abi_xlen = xlen;
+ else if (abi_xlen > xlen)
+ as_bad ("can't have %d-bit ABI on %d-bit ISA", abi_xlen, xlen);
+ else if (abi_xlen < xlen)
+ as_bad ("%d-bit ABI not yet supported on %d-bit ISA", abi_xlen, xlen);
+
+ if (float_abi == FLOAT_ABI_DEFAULT)
+ {
+ struct riscv_subset *subset;
+
+ /* Assume soft-float unless D extension is present. */
+ float_abi = FLOAT_ABI_SOFT;
+
+ for (subset = riscv_subsets; subset != NULL; subset = subset->next)
+ {
+ if (strcasecmp (subset->name, "D") == 0)
+ float_abi = FLOAT_ABI_DOUBLE;
+ if (strcasecmp (subset->name, "Q") == 0)
+ float_abi = FLOAT_ABI_QUAD;
+ }
+ }
+
+ /* Insert float_abi into the EF_RISCV_FLOAT_ABI field of elf_flags. */
+ elf_flags |= float_abi * (EF_RISCV_FLOAT_ABI & ~(EF_RISCV_FLOAT_ABI << 1));
}
long
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
+ unsigned int subtype;
bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+ bfd_boolean relaxable = FALSE;
+ offsetT loc;
+ segT sub_segment;
/* Remember value for tc_gen_reloc. */
fixP->fx_addnumber = *valP;
switch (fixP->fx_r_type)
{
- case BFD_RELOC_RISCV_TLS_GOT_HI20:
- case BFD_RELOC_RISCV_TLS_GD_HI20:
- case BFD_RELOC_RISCV_TLS_DTPREL32:
- case BFD_RELOC_RISCV_TLS_DTPREL64:
- case BFD_RELOC_RISCV_TPREL_HI20:
- case BFD_RELOC_RISCV_TPREL_LO12_I:
- case BFD_RELOC_RISCV_TPREL_LO12_S:
- case BFD_RELOC_RISCV_TPREL_ADD:
- S_SET_THREAD_LOCAL (fixP->fx_addsy);
- /* Fall through. */
-
- case BFD_RELOC_RISCV_GOT_HI20:
- case BFD_RELOC_RISCV_PCREL_HI20:
case BFD_RELOC_RISCV_HI20:
case BFD_RELOC_RISCV_LO12_I:
case BFD_RELOC_RISCV_LO12_S:
+ bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
+ | bfd_getl32 (buf), buf);
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = TRUE;
+ relaxable = TRUE;
+ break;
+
+ case BFD_RELOC_RISCV_GOT_HI20:
case BFD_RELOC_RISCV_ADD8:
case BFD_RELOC_RISCV_ADD16:
case BFD_RELOC_RISCV_ADD32:
case BFD_RELOC_RISCV_ADD64:
+ case BFD_RELOC_RISCV_SUB6:
case BFD_RELOC_RISCV_SUB8:
case BFD_RELOC_RISCV_SUB16:
case BFD_RELOC_RISCV_SUB32:
case BFD_RELOC_RISCV_SUB64:
- gas_assert (fixP->fx_addsy != NULL);
- /* Nothing needed to do. The value comes from the reloc entry. */
+ case BFD_RELOC_RISCV_RELAX:
+ break;
+
+ case BFD_RELOC_RISCV_TPREL_HI20:
+ case BFD_RELOC_RISCV_TPREL_LO12_I:
+ case BFD_RELOC_RISCV_TPREL_LO12_S:
+ case BFD_RELOC_RISCV_TPREL_ADD:
+ relaxable = TRUE;
+ /* Fall through. */
+
+ case BFD_RELOC_RISCV_TLS_GOT_HI20:
+ case BFD_RELOC_RISCV_TLS_GD_HI20:
+ case BFD_RELOC_RISCV_TLS_DTPREL32:
+ case BFD_RELOC_RISCV_TLS_DTPREL64:
+ if (fixP->fx_addsy != NULL)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("TLS relocation against a constant"));
break;
- case BFD_RELOC_64:
case BFD_RELOC_32:
+ /* Use pc-relative relocation for FDE initial location.
+ The symbol address in .eh_frame may be adjusted in
+ _bfd_elf_discard_section_eh_frame, and the content of
+ .eh_frame will be adjusted in _bfd_elf_write_section_eh_frame.
+ Therefore, we cannot insert a relocation whose addend symbol is
+ in .eh_frame. Othrewise, the value may be adjusted twice.*/
+ if (fixP->fx_addsy && fixP->fx_subsy
+ && (sub_segment = S_GET_SEGMENT (fixP->fx_subsy))
+ && strcmp (sub_segment->name, ".eh_frame") == 0
+ && S_GET_VALUE (fixP->fx_subsy)
+ == fixP->fx_frag->fr_address + fixP->fx_where)
+ {
+ fixP->fx_r_type = BFD_RELOC_RISCV_32_PCREL;
+ fixP->fx_subsy = NULL;
+ break;
+ }
+ /* Fall through. */
+ case BFD_RELOC_64:
case BFD_RELOC_16:
case BFD_RELOC_8:
+ case BFD_RELOC_RISCV_CFA:
if (fixP->fx_addsy && fixP->fx_subsy)
{
fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
break;
+ case BFD_RELOC_RISCV_CFA:
+ /* Load the byte to get the subtype. */
+ subtype = bfd_get_8 (NULL, &((fragS *) (fixP->fx_frag->fr_opcode))->fr_literal[fixP->fx_where]);
+ loc = fixP->fx_frag->fr_fix - (subtype & 7);
+ switch (subtype)
+ {
+ case DW_CFA_advance_loc1:
+ fixP->fx_where = loc + 1;
+ fixP->fx_next->fx_where = loc + 1;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET8;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
+ break;
+
+ case DW_CFA_advance_loc2:
+ fixP->fx_size = 2;
+ fixP->fx_next->fx_size = 2;
+ fixP->fx_where = loc + 1;
+ fixP->fx_next->fx_where = loc + 1;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET16;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16;
+ break;
+
+ case DW_CFA_advance_loc4:
+ fixP->fx_size = 4;
+ fixP->fx_next->fx_size = 4;
+ fixP->fx_where = loc;
+ fixP->fx_next->fx_where = loc;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET32;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32;
+ break;
+
+ default:
+ if (subtype < 0x80 && (subtype & 0x40))
+ {
+ /* DW_CFA_advance_loc */
+ fixP->fx_frag = (fragS *) fixP->fx_frag->fr_opcode;
+ fixP->fx_next->fx_frag = fixP->fx_frag;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET6;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
+ }
+ else
+ as_fatal (_("internal error: bad CFA value #%d"), subtype);
+ break;
+ }
+ break;
+
default:
/* This case is unreachable. */
abort ();
}
break;
- case BFD_RELOC_RISCV_PCREL_LO12_S:
- case BFD_RELOC_RISCV_PCREL_LO12_I:
case BFD_RELOC_RISCV_CALL:
case BFD_RELOC_RISCV_CALL_PLT:
+ relaxable = TRUE;
+ break;
+
+ case BFD_RELOC_RISCV_PCREL_HI20:
+ case BFD_RELOC_RISCV_PCREL_LO12_S:
+ case BFD_RELOC_RISCV_PCREL_LO12_I:
+ relaxable = riscv_opts.relax;
+ break;
+
case BFD_RELOC_RISCV_ALIGN:
break;
if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
}
+
+ if (fixP->fx_subsy != NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported symbol subtraction"));
+
+ /* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */
+ if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
+ {
+ fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
+ fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX;
+ }
+}
+
+/* Because the value of .cfi_remember_state may changed after relaxation,
+ we insert a fix to relocate it again in link-time. */
+
+void
+riscv_pre_output_hook (void)
+{
+ const frchainS *frch;
+ const asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next)
+ {
+ fragS *frag;
+
+ for (frag = frch->frch_root; frag; frag = frag->fr_next)
+ {
+ if (frag->fr_type == rs_cfa)
+ {
+ expressionS exp;
+
+ symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol;
+ symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol;
+
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = add_symbol;
+ exp.X_add_number = 0;
+ exp.X_op_symbol = op_symbol;
+
+ fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0,
+ BFD_RELOC_RISCV_CFA);
+ }
+ }
+ }
}
+
/* This structure is used to hold a stack of .option values. */
struct riscv_option_stack
riscv_opts.pic = TRUE;
else if (strcmp (name, "nopic") == 0)
riscv_opts.pic = FALSE;
- else if (strcmp (name, "soft-float") == 0)
- float_mode = FLOAT_MODE_SOFT;
- else if (strcmp (name, "hard-float") == 0)
- float_mode = FLOAT_MODE_HARD;
+ else if (strcmp (name, "relax") == 0)
+ riscv_opts.relax = TRUE;
+ else if (strcmp (name, "norelax") == 0)
+ riscv_opts.relax = FALSE;
else if (strcmp (name, "push") == 0)
{
struct riscv_option_stack *s;
demand_empty_rest_of_line ();
}
-/* Align to a given power of two. */
-
static void
-s_align (int bytes_p)
+riscv_make_nops (char *buf, bfd_vma bytes)
{
- int fill_value = 0, fill_value_specified = 0;
- int min_text_alignment = riscv_opts.rvc ? 2 : 4;
- int alignment = get_absolute_expression(), bytes;
+ bfd_vma i = 0;
+
+ /* RISC-V instructions cannot begin or end on odd addresses, so this case
+ means we are not within a valid instruction sequence. It is thus safe
+ to use a zero byte, even though that is not a valid instruction. */
+ if (bytes % 2 == 1)
+ buf[i++] = 0;
- if (bytes_p)
+ /* Use at most one 2-byte NOP. */
+ if ((bytes - i) % 4 == 2)
{
- bytes = alignment;
- if (bytes < 1 || (bytes & (bytes-1)) != 0)
- as_bad (_("alignment not a power of 2: %d"), bytes);
- for (alignment = 0; bytes > 1; bytes >>= 1)
- alignment++;
+ md_number_to_chars (buf + i, RVC_NOP, 2);
+ i += 2;
}
- bytes = 1 << alignment;
+ /* Fill the remainder with 4-byte NOPs. */
+ for ( ; i < bytes; i += 4)
+ md_number_to_chars (buf + i, RISCV_NOP, 4);
+}
- if (alignment < 0 || alignment > 31)
- as_bad (_("unsatisfiable alignment: %d"), alignment);
+/* Called from md_do_align. Used to create an alignment frag in a
+ code section by emitting a worst-case NOP sequence that the linker
+ will later relax to the correct number of NOPs. We can't compute
+ the correct alignment now because of other linker relaxations. */
- if (*input_line_pointer == ',')
- {
- ++input_line_pointer;
- fill_value = get_absolute_expression ();
- fill_value_specified = 1;
- }
+bfd_boolean
+riscv_frag_align_code (int n)
+{
+ bfd_vma bytes = (bfd_vma) 1 << n;
+ bfd_vma insn_alignment = riscv_opts.rvc ? 2 : 4;
+ bfd_vma worst_case_bytes = bytes - insn_alignment;
+ char *nops;
+ expressionS ex;
+
+ /* If we are moving to a smaller alignment than the instruction size, then no
+ alignment is required. */
+ if (bytes <= insn_alignment)
+ return TRUE;
+
+ nops = frag_more (worst_case_bytes);
+
+ /* When not relaxing, riscv_handle_align handles code alignment. */
+ if (!riscv_opts.relax)
+ return FALSE;
+
+ ex.X_op = O_constant;
+ ex.X_add_number = worst_case_bytes;
+
+ riscv_make_nops (nops, worst_case_bytes);
- if (!fill_value_specified
- && subseg_text_p (now_seg)
- && bytes > min_text_alignment)
+ fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
+ &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
+
+ return TRUE;
+}
+
+/* Implement HANDLE_ALIGN. */
+
+void
+riscv_handle_align (fragS *fragP)
+{
+ switch (fragP->fr_type)
{
- /* Emit the worst-case NOP string. The linker will delete any
- unnecessary NOPs. This allows us to support code alignment
- in spite of linker relaxations. */
- bfd_vma i, worst_case_bytes = bytes - min_text_alignment;
- char *nops = frag_more (worst_case_bytes);
- for (i = 0; i < worst_case_bytes - 2; i += 4)
- md_number_to_chars (nops + i, RISCV_NOP, 4);
- if (i < worst_case_bytes)
- md_number_to_chars (nops + i, RVC_NOP, 2);
-
- expressionS ex;
- ex.X_op = O_constant;
- ex.X_add_number = worst_case_bytes;
-
- fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
- &ex, FALSE, BFD_RELOC_RISCV_ALIGN);
- }
- else if (alignment)
- frag_align (alignment, fill_value, 0);
+ case rs_align_code:
+ /* When relaxing, riscv_frag_align_code handles code alignment. */
+ if (!riscv_opts.relax)
+ {
+ bfd_signed_vma count = fragP->fr_next->fr_address
+ - fragP->fr_address - fragP->fr_fix;
- record_alignment (now_seg, alignment);
+ if (count <= 0)
+ break;
- demand_empty_rest_of_line ();
+ count &= MAX_MEM_FOR_RS_ALIGN_CODE;
+ riscv_make_nops (fragP->fr_literal + fragP->fr_fix, count);
+ fragP->fr_var = count;
+ }
+ break;
+
+ default:
+ break;
+ }
}
int
goto done;
default:
- abort();
+ abort ();
}
}
{
fprintf (stream, _("\
RISC-V options:\n\
- -m32 assemble RV32 code\n\
- -m64 assemble RV64 code (default)\n\
-fpic generate position-independent code\n\
-fno-pic don't generate position-independent code (default)\n\
- -msoft-float don't use F registers for floating-point values\n\
- -mhard-float use F registers for floating-point values (default)\n\
- -mno-rvc disable the C extension for compressed instructions (default)\n\
- -mrvc enable the C extension for compressed instructions\n\
- -march=ISA set the RISC-V architecture, RV64IMAFD by default\n\
+ -march=ISA set the RISC-V architecture\n\
+ -mabi=ABI set the RISC-V ABI\n\
"));
}
void
riscv_elf_final_processing (void)
{
- enum float_mode elf_float_mode = float_mode;
-
elf_elfheader (stdoutput)->e_flags |= elf_flags;
-
- if (elf_float_mode == FLOAT_MODE_DEFAULT)
- {
- struct riscv_subset *subset;
-
- /* Assume soft-float unless D extension is present. */
- elf_float_mode = FLOAT_MODE_SOFT;
-
- for (subset = riscv_subsets; subset != NULL; subset = subset->next)
- if (strcasecmp (subset->name, "D") == 0)
- elf_float_mode = FLOAT_MODE_HARD;
- }
-
- if (elf_float_mode == FLOAT_MODE_SOFT)
- elf_elfheader (stdoutput)->e_flags |= EF_RISCV_SOFT_FLOAT;
}
/* Parse the .sleb128 and .uleb128 pseudos. Only allow constant expressions,
{"dtprelword", s_dtprel, 4},
{"dtpreldword", s_dtprel, 8},
{"bss", s_bss, 0},
- {"align", s_align, 0},
- {"p2align", s_align, 0},
- {"balign", s_align, 1},
{"uleb128", s_riscv_leb128, 0},
{"sleb128", s_riscv_leb128, 1},