/* tc-riscv.c -- RISC-V assembler
- Copyright (C) 2011-2018 Free Software Foundation, Inc.
+ Copyright (C) 2011-2019 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on MIPS target.
#define DEFAULT_ARCH "riscv64"
#endif
+#ifndef DEFAULT_RISCV_ATTR
+#define DEFAULT_RISCV_ATTR 0
+#endif
+
static const char default_arch[] = DEFAULT_ARCH;
static unsigned xlen = 0; /* width of an x-register */
int rvc; /* Generate RVC code. */
int rve; /* Generate RVE code. */
int relax; /* Emit relocs the linker is allowed to relax. */
+ int arch_attr; /* Emit arch attribute. */
};
static struct riscv_set_options riscv_opts =
0, /* rvc */
0, /* rve */
1, /* relax */
+ DEFAULT_RISCV_ATTR, /* arch_attr */
};
static void
}
static bfd_boolean
-riscv_multi_subset_supports (const char *features[])
+riscv_multi_subset_supports (enum riscv_insn_class insn_class)
{
- unsigned i = 0;
- bfd_boolean supported = TRUE;
+ switch (insn_class)
+ {
+ case INSN_CLASS_I: return riscv_subset_supports ("i");
+ case INSN_CLASS_C: return riscv_subset_supports ("c");
+ case INSN_CLASS_A: return riscv_subset_supports ("a");
+ case INSN_CLASS_M: return riscv_subset_supports ("m");
+ case INSN_CLASS_F: return riscv_subset_supports ("f");
+ case INSN_CLASS_D: return riscv_subset_supports ("d");
+ case INSN_CLASS_D_AND_C:
+ return riscv_subset_supports ("d") && riscv_subset_supports ("c");
- for (;features[i]; ++i)
- supported = supported && riscv_subset_supports (features[i]);
+ case INSN_CLASS_F_AND_C:
+ return riscv_subset_supports ("f") && riscv_subset_supports ("c");
- return supported;
+ case INSN_CLASS_Q: return riscv_subset_supports ("q");
+
+ default:
+ as_fatal ("Unreachable");
+ return FALSE;
+ }
}
/* Set which ISA and extensions are available. */
/* or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXpP";
+/* Indicate we are already assemble any instructions or not. */
+static bfd_boolean start_assemble = FALSE;
+
+/* Indicate arch attribute is explictly set. */
+static bfd_boolean explicit_arch_attr = FALSE;
+
/* Macros for encoding relaxation state for RVC branches and far jumps. */
#define RELAX_BRANCH_ENCODE(uncond, rvc, length) \
((relax_substateT) \
return o;
}
-struct regname
-{
- const char *name;
- unsigned int num;
-};
-
enum reg_class
{
RCLASS_GPR,
static unsigned int
reg_lookup_internal (const char *s, enum reg_class class)
{
- struct regname *r = (struct regname *) hash_find (reg_names_hash, s);
+ void *r = hash_find (reg_names_hash, s);
if (r == NULL || DECODE_REG_CLASS (r) != class)
return -1;
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 'z': break; /* RS2S, contrained to be x0 */
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;
append_insn (&insn, ep, r);
}
+/* Build an instruction created by a macro expansion. Like md_assemble but
+ accept a printf-style format string and arguments. */
+
+static void
+md_assemblef (const char *format, ...)
+{
+ char *buf = NULL;
+ va_list ap;
+ int r;
+
+ va_start (ap, format);
+
+ r = vasprintf (&buf, format, ap);
+
+ if (r < 0)
+ as_fatal (_("internal error: vasprintf failed"));
+
+ md_assemble (buf);
+ free(buf);
+
+ va_end (ap);
+}
+
/* Sign-extend 32-bit mode constants that have bit 31 set and all higher bits
unset. */
static void
load_const (int reg, expressionS *ep)
{
int shift = RISCV_IMM_BITS;
+ bfd_vma upper_imm;
expressionS upper = *ep, lower = *ep;
lower.X_add_number = (int32_t) ep->X_add_number << (32-shift) >> (32-shift);
upper.X_add_number -= lower.X_add_number;
upper.X_add_number = (int64_t) upper.X_add_number >> shift;
load_const (reg, &upper);
- macro_build (NULL, "slli", "d,s,>", reg, reg, shift);
+ md_assemblef ("slli x%d, x%d, 0x%x", reg, reg, shift);
if (lower.X_add_number != 0)
- macro_build (&lower, "addi", "d,s,j", reg, reg, BFD_RELOC_RISCV_LO12_I);
+ md_assemblef ("addi x%d, x%d, %" BFD_VMA_FMT "d", reg, reg,
+ lower.X_add_number);
}
else
{
if (upper.X_add_number != 0)
{
- macro_build (ep, "lui", "d,u", reg, BFD_RELOC_RISCV_HI20);
+ /* 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);
hi_reg = reg;
}
if (lower.X_add_number != 0 || hi_reg == 0)
- macro_build (ep, ADD32_INSN, "d,s,j", reg, hi_reg,
- BFD_RELOC_RISCV_LO12_I);
+ md_assemblef ("%s x%d, x%d, %" BFD_VMA_FMT "d", ADD32_INSN, reg, hi_reg,
+ lower.X_add_number);
}
}
unsigned crux_depth, str_depth, regno;
char *crux;
- /* First, check for integer registers. */
+ /* First, check for integer registers. No callers can accept a reg, but
+ we need to avoid accidentally creating a useless undefined symbol below,
+ if this is an instruction pattern that can't match. A glibc build fails
+ if this is removed. */
if (reg_lookup (&str, RCLASS_GPR, ®no))
{
ep->X_op = O_register;
ep->X_add_number = regno;
+ expr_end = str;
return 0;
}
if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement))
continue;
- if (!riscv_multi_subset_supports (insn->subset))
+ if (!riscv_multi_subset_supports (insn->insn_class))
continue;
create_insn (ip, insn);
|| regno != X_SP)
break;
continue;
+ case 'z': /* RS2, contrained to equal x0. */
+ if (!reg_lookup (&s, RCLASS_GPR, ®no)
+ || regno != 0)
+ break;
+ continue;
case '>':
if (my_getSmallExpression (imm_expr, imm_reloc, s, p)
|| imm_expr->X_op != O_constant
case 'u': /* Upper 20 bits. */
p = percent_op_utype;
- if (!my_getSmallExpression (imm_expr, imm_reloc, s, p)
- && imm_expr->X_op == O_constant)
+ if (!my_getSmallExpression (imm_expr, imm_reloc, s, p))
{
+ if (imm_expr->X_op != O_constant)
+ break;
+
if (imm_expr->X_add_number < 0
|| imm_expr->X_add_number >= (signed)RISCV_BIGIMM_REACH)
as_bad (_("lui expression not in range 0..1048575"));
const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash);
+ start_assemble = TRUE;
+
if (error)
{
as_bad ("%s `%s'", error, str);
OPTION_MABI,
OPTION_RELAX,
OPTION_NO_RELAX,
+ OPTION_ARCH_ATTR,
+ OPTION_NO_ARCH_ATTR,
OPTION_END_OF_ENUM
};
{"mabi", required_argument, NULL, OPTION_MABI},
{"mrelax", no_argument, NULL, OPTION_RELAX},
{"mno-relax", no_argument, NULL, OPTION_NO_RELAX},
+ {"march-attr", no_argument, NULL, OPTION_ARCH_ATTR},
+ {"mno-arch-attr", no_argument, NULL, OPTION_NO_ARCH_ATTR},
{NULL, no_argument, NULL, 0}
};
riscv_opts.relax = FALSE;
break;
+ case OPTION_ARCH_ATTR:
+ riscv_opts.arch_attr = TRUE;
+ break;
+
+ case OPTION_NO_ARCH_ATTR:
+ riscv_opts.arch_attr = FALSE;
+ break;
+
default:
return 0;
}
-mabi=ABI set the RISC-V ABI\n\
-mrelax enable relax (default)\n\
-mno-relax disable relax\n\
+ -march-attr generate RISC-V arch attribute\n\
+ -mno-arch-attr don't generate RISC-V arch attribute\n\
"));
}
if ((reg = reg_lookup_internal (regname, RCLASS_FPR)) >= 0)
return reg + 32;
+ /* CSRs are numbered 4096 -> 8191. */
+ if ((reg = reg_lookup_internal (regname, RCLASS_CSR)) >= 0)
+ return reg + 4096;
+
as_bad (_("unknown register `%s'"), regname);
return -1;
}
demand_empty_rest_of_line ();
}
+/* Update arch attributes. */
+
+static void
+riscv_write_out_arch_attr (void)
+{
+ const char *arch_str = riscv_arch_str (xlen, &riscv_subsets);
+
+ bfd_elf_add_proc_attr_string (stdoutput, Tag_RISCV_arch, arch_str);
+
+ xfree ((void *)arch_str);
+}
+
+/* Add the default contents for the .riscv.attributes section. */
+
+static void
+riscv_set_public_attributes (void)
+{
+ if (riscv_opts.arch_attr || explicit_arch_attr)
+ /* Re-write arch attribute to normalize the arch string. */
+ riscv_write_out_arch_attr ();
+}
+
+/* Called after all assembly has been done. */
+
+void
+riscv_md_end (void)
+{
+ riscv_set_public_attributes ();
+}
+
+/* Given a symbolic attribute NAME, return the proper integer value.
+ Returns -1 if the attribute is not known. */
+
+int
+riscv_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+ /* When you modify this table you should
+ also modify the list in doc/c-riscv.texi. */
+#define T(tag) {#tag, Tag_RISCV_##tag}, {"Tag_RISCV_" #tag, Tag_RISCV_##tag}
+ T(arch),
+ T(priv_spec),
+ T(priv_spec_minor),
+ T(priv_spec_revision),
+ T(unaligned_access),
+ T(stack_align),
+#undef T
+ };
+
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (strcmp (name, attribute_table[i].name) == 0)
+ return attribute_table[i].tag;
+
+ return -1;
+}
+
+/* Parse a .attribute directive. */
+
+static void
+s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+
+ if (tag == Tag_RISCV_arch)
+ {
+ unsigned old_xlen = xlen;
+
+ explicit_arch_attr = TRUE;
+ obj_attribute *attr;
+ attr = elf_known_obj_attributes_proc (stdoutput);
+ if (!start_assemble)
+ riscv_set_arch (attr[Tag_RISCV_arch].s);
+ else
+ as_fatal (_(".attribute arch must set before any instructions"));
+
+ if (old_xlen != xlen)
+ {
+ /* We must re-init bfd again if xlen is changed. */
+ unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32;
+ bfd_find_target (riscv_target_format (), stdoutput);
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach))
+ as_warn (_("Could not set architecture and machine"));
+ }
+ }
+}
+
/* Pseudo-op table. */
static const pseudo_typeS riscv_pseudo_table[] =
{"uleb128", s_riscv_leb128, 0},
{"sleb128", s_riscv_leb128, 1},
{"insn", s_riscv_insn, 0},
+ {"attribute", s_riscv_attribute, 0},
{ NULL, NULL, 0 },
};