/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2022 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
CSR_CLASS_F, /* f-ext only */
CSR_CLASS_ZKR, /* zkr only */
CSR_CLASS_V, /* rvv only */
- CSR_CLASS_DEBUG /* debug CSR */
+ CSR_CLASS_DEBUG, /* debug CSR */
+ CSR_CLASS_H, /* hypervisor */
+ CSR_CLASS_H_32, /* hypervisor, rv32 only */
+ CSR_CLASS_SMSTATEEN, /* Smstateen only */
+ CSR_CLASS_SMSTATEEN_AND_H, /* Smstateen only (with H) */
+ CSR_CLASS_SMSTATEEN_32, /* Smstateen RV32 only */
+ CSR_CLASS_SMSTATEEN_AND_H_32, /* Smstateen RV32 only (with H) */
+ CSR_CLASS_SSCOFPMF, /* Sscofpmf only */
+ CSR_CLASS_SSCOFPMF_32, /* Sscofpmf RV32 only */
+ CSR_CLASS_SSTC, /* Sstc only */
+ CSR_CLASS_SSTC_AND_H, /* Sstc only (with H) */
+ CSR_CLASS_SSTC_32, /* Sstc RV32 only */
+ CSR_CLASS_SSTC_AND_H_32, /* Sstc RV32 only (with H) */
};
/* This structure holds all restricted conditions for a CSR. */
enum riscv_spec_class define_version;
/* Record the CSR is aborted/invalid from which versions. If it isn't
- aborted in the current version, then it should be CSR_CLASS_VDRAFT. */
+ aborted in the current version, then it should be PRIV_SPEC_CLASS_DRAFT. */
enum riscv_spec_class abort_version;
/* The CSR may have more than one setting. */
struct riscv_csr_extra *next;
};
+/* This structure contains information about errors that occur within the
+ riscv_ip function */
+struct riscv_ip_error
+{
+ /* General error message */
+ const char* msg;
+
+ /* Statement that caused the error */
+ char* statement;
+
+ /* Missing extension that needs to be enabled */
+ const char* missing_ext;
+};
+
#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "riscv64"
#endif
/* Need to sync the version with RISC-V compiler. */
#ifndef DEFAULT_RISCV_ISA_SPEC
-#define DEFAULT_RISCV_ISA_SPEC "2.2"
+#define DEFAULT_RISCV_ISA_SPEC "20191213"
#endif
#ifndef DEFAULT_RISCV_PRIV_SPEC
the architecture string. The architecture string can be set by the
-march option, the elf architecture attributes, and the --with-arch
configure option. */
-static riscv_subset_list_t riscv_subsets;
+static riscv_subset_list_t *riscv_subsets = NULL;
static riscv_parse_subset_t riscv_rps_as =
{
- &riscv_subsets, /* subset_list. */
+ NULL, /* subset_list, we will set it later once
+ riscv_opts_stack is created or updated. */
as_bad, /* error_handler. */
&xlen, /* xlen. */
&default_isa_spec, /* isa_spec. */
true, /* check_unknown_prefixed_ext. */
};
+/* This structure is used to hold a stack of .option values. */
+struct riscv_option_stack
+{
+ struct riscv_option_stack *next;
+ struct riscv_set_options options;
+ riscv_subset_list_t *subset_list;
+};
+
+static struct riscv_option_stack *riscv_opts_stack = NULL;
+
/* Set which ISA and extensions are available. */
static void
return;
}
- riscv_release_subset_list (&riscv_subsets);
+ if (riscv_subsets == NULL)
+ {
+ riscv_subsets = XNEW (riscv_subset_list_t);
+ riscv_subsets->head = NULL;
+ riscv_subsets->tail = NULL;
+ riscv_rps_as.subset_list = riscv_subsets;
+ }
+ riscv_release_subset_list (riscv_subsets);
riscv_parse_subset (&riscv_rps_as, s);
riscv_set_rvc (false);
/* Chars that mean this number is a floating point constant.
As in 0f12.456 or 0d1.2345e12. */
-const char FLT_CHARS[] = "rRsSfFdDxXpP";
+const char FLT_CHARS[] = "rRsSfFdDxXpPhH";
/* Indicate we are already assemble any instructions or not. */
static bool start_assemble = false;
{"NMADD", 0x4f},
{"NMSUB", 0x4b},
{"OP_FP", 0x53},
- /*reserved 0x57. */
+ {"OP_V", 0x57},
{"CUSTOM_2", 0x5b},
/* 48b 0x5f. */
if (!need_enrty)
return;
- entry = XNEW (struct riscv_csr_extra);
+ entry = notes_alloc (sizeof (*entry));
entry->csr_class = class;
entry->address = address;
entry->define_version = define_version;
{
struct riscv_csr_extra *saved_entry = entry;
enum riscv_csr_class csr_class = entry->csr_class;
- bool need_check_version = true;
- bool result = true;
+ bool need_check_version = false;
+ bool is_rv32_only = false;
+ bool is_h_required = false;
+ const char* extension = NULL;
switch (csr_class)
{
+ case CSR_CLASS_I_32:
+ is_rv32_only = true;
+ /* Fall through. */
case CSR_CLASS_I:
- result = riscv_subset_supports (&riscv_rps_as, "i");
+ need_check_version = true;
+ extension = "i";
break;
- case CSR_CLASS_I_32:
- result = (xlen == 32 && riscv_subset_supports (&riscv_rps_as, "i"));
+ case CSR_CLASS_H_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_H:
+ extension = "h";
break;
case CSR_CLASS_F:
- result = riscv_subset_supports (&riscv_rps_as, "f");
- need_check_version = false;
+ extension = "f";
break;
case CSR_CLASS_ZKR:
- result = riscv_subset_supports (&riscv_rps_as, "zkr");
- need_check_version = false;
+ extension = "zkr";
break;
case CSR_CLASS_V:
- result = riscv_subset_supports (&riscv_rps_as, "v");
- need_check_version = false;
+ extension = "zve32x";
+ break;
+ case CSR_CLASS_SMSTATEEN:
+ case CSR_CLASS_SMSTATEEN_AND_H:
+ case CSR_CLASS_SMSTATEEN_32:
+ case CSR_CLASS_SMSTATEEN_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SMSTATEEN_32
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SMSTATEEN_AND_H
+ || csr_class == CSR_CLASS_SMSTATEEN_AND_H_32);
+ extension = "smstateen";
+ break;
+ case CSR_CLASS_SSCOFPMF_32:
+ is_rv32_only = true;
+ /* Fall through. */
+ case CSR_CLASS_SSCOFPMF:
+ extension = "sscofpmf";
+ break;
+ case CSR_CLASS_SSTC:
+ case CSR_CLASS_SSTC_AND_H:
+ case CSR_CLASS_SSTC_32:
+ case CSR_CLASS_SSTC_AND_H_32:
+ is_rv32_only = (csr_class == CSR_CLASS_SSTC_32
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ is_h_required = (csr_class == CSR_CLASS_SSTC_AND_H
+ || csr_class == CSR_CLASS_SSTC_AND_H_32);
+ extension = "sstc";
break;
case CSR_CLASS_DEBUG:
- need_check_version = false;
break;
default:
as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
}
- if (riscv_opts.csr_check && !result)
- as_warn (_("invalid CSR `%s' for the current ISA"), csr_name);
+ if (riscv_opts.csr_check)
+ {
+ if (is_rv32_only && xlen != 32)
+ as_warn (_("invalid CSR `%s', needs rv32i extension"), csr_name);
+ if (is_h_required && !riscv_subset_supports (&riscv_rps_as, "h"))
+ as_warn (_("invalid CSR `%s', needs `h' extension"), csr_name);
+
+ if (extension != NULL
+ && !riscv_subset_supports (&riscv_rps_as, extension))
+ as_warn (_("invalid CSR `%s', needs `%s' extension"),
+ csr_name, extension);
+ }
while (entry != NULL)
{
default:
goto unknown_validate_operand;
}
- break;
+ break; /* end RVC */
case 'V': /* RVV */
switch (*++oparg)
{
default:
goto unknown_validate_operand;
}
- break;
+ break; /* end RVV */
case ',': break;
case '(': break;
case ')': break;
case 'j': used_bits |= ENCODE_ITYPE_IMM (-1U); break;
case 'a': used_bits |= ENCODE_JTYPE_IMM (-1U); break;
case 'p': used_bits |= ENCODE_BTYPE_IMM (-1U); break;
+ case 'f': /* Fall through. */
case 'q': used_bits |= ENCODE_STYPE_IMM (-1U); break;
case 'u': used_bits |= ENCODE_UTYPE_IMM (-1U); break;
case 'z': break; /* Zero immediate. */
md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
if (lower.X_add_number != 0)
- md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg,
- lower.X_add_number);
+ md_assemblef ("addi x%d, x%d, %" PRId64, reg, reg,
+ (int64_t) lower.X_add_number);
}
else
{
/* Discard low part and zero-extend upper immediate. */
upper_imm = ((uint32_t)upper.X_add_number >> shift);
- md_assemblef ("lui x%d, 0x%" BFD_VMA_FMT "x", reg, upper_imm);
+ md_assemblef ("lui x%d, 0x%" PRIx64, reg, (uint64_t) upper_imm);
hi_reg = reg;
}
if (lower.X_add_number != 0 || hi_reg == 0)
- md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
- lower.X_add_number);
+ md_assemblef ("%s x%d, x%d, %" PRId64, ADD32_INSN, reg, hi_reg,
+ (int64_t) lower.X_add_number);
}
}
vector_macro (ip);
break;
+ case M_FLH:
+ pcrel_load (rd, rs1, imm_expr, "flh",
+ BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
+ break;
+ case M_FSH:
+ pcrel_store (rs2, rs1, imm_expr, "fsh",
+ BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_S);
+ break;
+
default:
as_bad (_("internal: macro %s not implemented"), ip->insn_mo->name);
break;
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. */
-static const char *
+static struct riscv_ip_error
riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
bfd_reloc_code_real_type *imm_reloc, htab_t hash)
{
char save_c = 0;
struct riscv_opcode *insn;
unsigned int regno;
- int argnum;
const struct percent_op_match *p;
- const char *error = "unrecognized opcode";
+ struct riscv_ip_error error;
+ error.msg = "unrecognized opcode";
+ error.statement = str;
+ error.missing_ext = NULL;
/* Indicate we are assembling instruction with CSR. */
bool insn_with_csr = false;
continue;
if (!riscv_multi_subset_supports (&riscv_rps_as, insn->insn_class))
- continue;
+ {
+ error.missing_ext = riscv_multi_subset_supports_ext (&riscv_rps_as,
+ insn->insn_class);
+ continue;
+ }
/* Reset error message of the previous round. */
- error = _("illegal operands");
+ error.msg = _("illegal operands");
+ error.missing_ext = NULL;
create_insn (ip, insn);
- argnum = 1;
imm_expr->X_op = O_absent;
*imm_reloc = BFD_RELOC_UNUSED;
&& riscv_subset_supports (&riscv_rps_as, "zve32x")
&& !riscv_subset_supports (&riscv_rps_as, "zve64x"))
{
- error = _("illegal opcode for zve32x");
+ error.msg = _("illegal opcode for zve32x");
break;
}
}
if (*asarg != '\0')
break;
/* Successful assembly. */
- error = NULL;
+ error.msg = NULL;
insn_with_csr = false;
goto out;
|| imm_expr->X_add_number >= 64)
{
as_bad (_("bad value for compressed funct6 "
- "field, value must be 0...64"));
+ "field, value must be 0...63"));
break;
}
INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number);
default:
goto unknown_riscv_ip_operand;
}
- break;
+ break; /* end RVC */
case 'V': /* RVV */
switch (*++oparg)
default:
goto unknown_riscv_ip_operand;
}
- break;
+ break; /* end RVV */
case ',':
- ++argnum;
if (*asarg++ == *oparg)
continue;
asarg--;
case 'T': /* Floating point RS2. */
case 'U': /* Floating point RS1 and RS2. */
case 'R': /* Floating point RS3. */
- if (reg_lookup (&asarg, RCLASS_FPR, ®no))
+ if (reg_lookup (&asarg,
+ (riscv_subset_supports (&riscv_rps_as, "zfinx")
+ ? RCLASS_GPR : RCLASS_FPR), ®no))
{
char c = *oparg;
if (*asarg == ' ')
imm_expr->X_op = O_absent;
continue;
+ case 'f': /* Prefetch offset, pseudo S-type but lower 5-bits zero. */
+ if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
+ continue;
+ my_getExpression (imm_expr, asarg);
+ check_absolute_expr (ip, imm_expr, false);
+ if (((unsigned) (imm_expr->X_add_number) & 0x1fU)
+ || imm_expr->X_add_number >= (signed) RISCV_IMM_REACH / 2
+ || imm_expr->X_add_number < -(signed) RISCV_IMM_REACH / 2)
+ as_bad (_("improper prefetch offset (%ld)"),
+ (long) imm_expr->X_add_number);
+ ip->insn_opcode |=
+ ENCODE_STYPE_IMM ((unsigned) (imm_expr->X_add_number) &
+ ~ 0x1fU);
+ imm_expr->X_op = O_absent;
+ asarg = expr_end;
+ continue;
+
default:
unknown_riscv_ip_operand:
as_fatal (_("internal: unknown argument type `%s'"),
insn->match = values[num - 1];
create_insn (ip, insn);
unsigned int bytes = riscv_insn_length (insn->match);
- if (values[num - 1] >> (8 * bytes) != 0
+ if ((bytes < sizeof(values[0]) && values[num - 1] >> (8 * bytes) != 0)
|| (num == 2 && values[0] != bytes))
return _("value conflicts with instruction length");
riscv_mapping_state (MAP_INSN, 0);
- const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+ const struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
+ &imm_reloc, op_hash);
- if (error)
+ if (error.msg)
{
- as_bad ("%s `%s'", error, str);
+ if (error.missing_ext)
+ as_bad ("%s `%s', extension `%s' required", error.msg,
+ error.statement, error.missing_ext);
+ else
+ as_bad ("%s `%s'", error.msg, error.statement);
return;
}
subseg_set (seg, subseg);
}
-/* This structure is used to hold a stack of .option values. */
-struct riscv_option_stack
-{
- struct riscv_option_stack *next;
- struct riscv_set_options options;
-};
-
-static struct riscv_option_stack *riscv_opts_stack;
-
/* Handle the .option pseudo-op. */
static void
if (strcmp (name, "rvc") == 0)
{
- riscv_update_subset (&riscv_rps_as, "c", false);
+ riscv_update_subset (&riscv_rps_as, "+c");
riscv_set_rvc (true);
}
else if (strcmp (name, "norvc") == 0)
{
- riscv_update_subset (&riscv_rps_as, "c", true);
+ riscv_update_subset (&riscv_rps_as, "-c");
riscv_set_rvc (false);
}
else if (strcmp (name, "pic") == 0)
riscv_opts.csr_check = true;
else if (strcmp (name, "no-csr-check") == 0)
riscv_opts.csr_check = false;
+ else if (strncmp (name, "arch,", 5) == 0)
+ {
+ name += 5;
+ if (ISSPACE (*name) && *name != '\0')
+ name++;
+ riscv_update_subset (&riscv_rps_as, name);
+
+ riscv_set_rvc (false);
+ if (riscv_subset_supports (&riscv_rps_as, "c"))
+ riscv_set_rvc (true);
+ }
else if (strcmp (name, "push") == 0)
{
struct riscv_option_stack *s;
- s = (struct riscv_option_stack *) xmalloc (sizeof *s);
+ s = XNEW (struct riscv_option_stack);
s->next = riscv_opts_stack;
s->options = riscv_opts;
+ s->subset_list = riscv_subsets;
riscv_opts_stack = s;
+ riscv_subsets = riscv_copy_subset_list (s->subset_list);
+ riscv_rps_as.subset_list = riscv_subsets;
}
else if (strcmp (name, "pop") == 0)
{
as_bad (_(".option pop with no .option push"));
else
{
- riscv_opts = s->options;
+ riscv_subset_list_t *release_subsets = riscv_subsets;
riscv_opts_stack = s->next;
+ riscv_opts = s->options;
+ riscv_subsets = s->subset_list;
+ riscv_rps_as.subset_list = riscv_subsets;
+ riscv_release_subset_list (release_subsets);
free (s);
}
}
riscv_mapping_state (MAP_INSN, worst_case_bytes);
+ /* We need to start a new frag after the alignment which may be removed by
+ the linker, to prevent the assembler from computing static offsets.
+ This is necessary to get correct EH info. */
+ frag_wane (frag_now);
+ frag_new (0);
+
return true;
}
-fno-pic don't generate position-independent code (default)\n\
-march=ISA set the RISC-V architecture\n\
-misa-spec=ISAspec set the RISC-V ISA spec (2.2, 20190608, 20191213)\n\
- -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9.1, 1.10, 1.11)\n\
+ -mpriv-spec=PRIVspec set the RISC-V privilege spec (1.9.1, 1.10, 1.11, 1.12)\n\
-mabi=ABI set the RISC-V ABI\n\
-mrelax enable relax (default)\n\
-mno-relax disable relax\n\
riscv_mapping_state (MAP_INSN, 0);
- const char *error = riscv_ip (str, &insn, &imm_expr,
+ struct riscv_ip_error error = riscv_ip (str, &insn, &imm_expr,
&imm_reloc, insn_type_hash);
- if (error)
+ if (error.msg)
{
char *save_in = input_line_pointer;
- error = riscv_ip_hardcode (str, &insn, &imm_expr, error);
+ error.msg = riscv_ip_hardcode (str, &insn, &imm_expr, error.msg);
input_line_pointer = save_in;
}
- if (error)
- as_bad ("%s `%s'", error, str);
+ if (error.msg)
+ {
+ if (error.missing_ext)
+ as_bad ("%s `%s', extension `%s' required", error.msg, error.statement,
+ error.missing_ext);
+ else
+ as_bad ("%s `%s'", error.msg, error.statement);
+ }
else
{
gas_assert (insn.insn_mo->pinfo != INSN_MACRO);
unsigned int i;
/* Re-write architecture elf attribute. */
- arch_str = riscv_arch_str (xlen, &riscv_subsets);
+ arch_str = riscv_arch_str (xlen, riscv_subsets);
bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
xfree ((void *) arch_str);
/* Called after all assembly has been done. */
void
-riscv_md_end (void)
+riscv_md_finish (void)
{
riscv_set_public_attributes ();
}
}
}
+/* Mark symbol that it follows a variant CC convention. */
+
+static void
+s_variant_cc (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ symbolS *sym;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ c = get_symbol_name (&name);
+ if (!*name)
+ as_bad (_("missing symbol name for .variant_cc directive"));
+ sym = symbol_find_or_make (name);
+ restore_line_pointer (c);
+ demand_empty_rest_of_line ();
+
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfdsym);
+ gas_assert (elfsym);
+ elfsym->internal_elf_sym.st_other |= STO_RISCV_VARIANT_CC;
+}
+
+/* Same as elf_copy_symbol_attributes, but without copying st_other.
+ This is needed so RISC-V specific st_other values can be independently
+ specified for an IFUNC resolver (that is called by the dynamic linker)
+ and the symbol it resolves (aliased to the resolver). In particular,
+ if a function symbol has special st_other value set via directives,
+ then attaching an IFUNC resolver to that symbol should not override
+ the st_other setting. Requiring the directive on the IFUNC resolver
+ symbol would be unexpected and problematic in C code, where the two
+ symbols appear as two independent function declarations. */
+
+void
+riscv_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ struct elf_obj_sy *srcelf = symbol_get_obj (src);
+ struct elf_obj_sy *destelf = symbol_get_obj (dest);
+ /* If size is unset, copy size from src. Because we don't track whether
+ .size has been used, we can't differentiate .size dest, 0 from the case
+ where dest's size is unset. */
+ if (!destelf->size && S_GET_SIZE (dest) == 0)
+ {
+ if (srcelf->size)
+ {
+ destelf->size = XNEW (expressionS);
+ *destelf->size = *srcelf->size;
+ }
+ S_SET_SIZE (dest, S_GET_SIZE (src));
+ }
+}
+
/* RISC-V pseudo-ops table. */
static const pseudo_typeS riscv_pseudo_table[] =
{
{"sleb128", s_riscv_leb128, 1},
{"insn", s_riscv_insn, 0},
{"attribute", s_riscv_attribute, 0},
+ {"variant_cc", s_variant_cc, 0},
+ {"float16", float_cons, 'h'},
{ NULL, NULL, 0 },
};