X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-riscv.c;h=e50505138e95d386d51c80ca3119b5838b47e608;hb=618265039f697eab9e72bb58b95fc2d32925df58;hp=189e40d11c7f9cd82d6a1d512746305f5faa6dac;hpb=9d06997adb88a71487a2f5627de032c6fb994604;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index 189e40d11c..e50505138e 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -1,5 +1,5 @@ /* tc-riscv.c -- RISC-V assembler - Copyright (C) 2011-2017 Free Software Foundation, Inc. + Copyright (C) 2011-2019 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on MIPS target. @@ -28,8 +28,8 @@ #include "itbl-ops.h" #include "dwarf2dbg.h" #include "dw2gencfi.h" -#include "struc-symbol.h" +#include "bfd/elfxx-riscv.h" #include "elf/riscv.h" #include "opcode/riscv.h" @@ -59,10 +59,15 @@ struct riscv_cl_insn #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 */ static unsigned abi_xlen = 0; /* width of a pointer in the ABI */ +static bfd_boolean rve_abi = FALSE; #define LOAD_ADDRESS_INSN (abi_xlen == 64 ? "ld" : "lw") #define ADD32_INSN (xlen == 64 ? "addiw" : "addi") @@ -75,14 +80,18 @@ struct riscv_set_options { int pic; /* Generate position-independent code. */ 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, /* pic */ 0, /* rvc */ + 0, /* rve */ 1, /* relax */ + DEFAULT_RISCV_ATTR, /* arch_attr */ }; static void @@ -94,52 +103,46 @@ riscv_set_rvc (bfd_boolean rvc_value) riscv_opts.rvc = rvc_value; } -struct riscv_subset +static void +riscv_set_rve (bfd_boolean rve_value) { - const char *name; - - struct riscv_subset *next; -}; + riscv_opts.rve = rve_value; +} -static struct riscv_subset *riscv_subsets; +static riscv_subset_list_t riscv_subsets; static bfd_boolean riscv_subset_supports (const char *feature) { - struct riscv_subset *s; - char *p; - unsigned xlen_required = strtoul (feature, &p, 10); - - if (xlen_required && xlen != xlen_required) - return FALSE; - - for (s = riscv_subsets; s != NULL; s = s->next) - if (strcasecmp (s->name, p) == 0) - return TRUE; + if (riscv_opts.rvc && (strcasecmp (feature, "c") == 0)) + return TRUE; - return FALSE; + return riscv_lookup_subset (&riscv_subsets, feature) != NULL; } -static void -riscv_clear_subsets (void) +static bfd_boolean +riscv_multi_subset_supports (enum riscv_insn_class insn_class) { - while (riscv_subsets != NULL) + switch (insn_class) { - struct riscv_subset *next = riscv_subsets->next; - free ((void *) riscv_subsets->name); - free (riscv_subsets); - riscv_subsets = next; - } -} + 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"); -static void -riscv_add_subset (const char *subset) -{ - struct riscv_subset *s = xmalloc (sizeof *s); + case INSN_CLASS_F_AND_C: + return riscv_subset_supports ("f") && riscv_subset_supports ("c"); + + case INSN_CLASS_Q: return riscv_subset_supports ("q"); - s->name = xstrdup (subset); - s->next = riscv_subsets; - riscv_subsets = s; + default: + as_fatal ("Unreachable"); + return FALSE; + } } /* Set which ISA and extensions are available. */ @@ -147,80 +150,21 @@ riscv_add_subset (const char *subset) static void riscv_set_arch (const char *s) { - const char *all_subsets = "imafdqc"; - char *extension = NULL; - const char *p = s; - - riscv_clear_subsets(); - - if (strncmp (p, "rv32", 4) == 0) - { - xlen = 32; - p += 4; - } - else if (strncmp (p, "rv64", 4) == 0) - { - xlen = 64; - p += 4; - } - else - as_fatal ("-march=%s: ISA string must begin with rv32 or rv64", s); - - switch (*p) - { - case 'i': - break; - - case 'g': - p++; - for ( ; *all_subsets != 'q'; all_subsets++) - { - const char subset[] = {*all_subsets, '\0'}; - riscv_add_subset (subset); - } - break; - - default: - as_fatal ("-march=%s: first ISA subset must be `i' or `g'", s); - } - - while (*p) - { - if (*p == 'x') - { - char *subset = xstrdup (p); - char *q = subset; - - while (*++q != '\0' && *q != '_') - ; - *q = '\0'; - - if (extension) - 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); - } - else if (*p == '_') - p++; - else if ((all_subsets = strchr (all_subsets, *p)) != NULL) - { - const char subset[] = {*p, 0}; - riscv_add_subset (subset); - all_subsets++; - p++; - } - else - as_fatal ("-march=%s: unsupported ISA subset `%c'", s, *p); - } + riscv_parse_subset_t rps; + rps.subset_list = &riscv_subsets; + rps.error_handler = as_fatal; + rps.xlen = &xlen; - free (extension); + riscv_release_subset_list (&riscv_subsets); + riscv_parse_subset (&rps, s); } /* Handle of the OPCODE hash table. */ static struct hash_control *op_hash = NULL; +/* Handle of the type of .insn hash table. */ +static struct hash_control *insn_type_hash = NULL; + /* This array holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful */ const char comment_chars[] = "#"; @@ -245,6 +189,12 @@ const char EXP_CHARS[] = "eE"; /* 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) \ @@ -391,6 +341,111 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update) return length; } +/* Information about an opcode name, mnemonics and its value. */ +struct opcode_name_t +{ + const char *name; + unsigned int val; +}; + +/* List for all supported opcode name. */ +static const struct opcode_name_t opcode_name_list[] = +{ + {"C0", 0x0}, + {"C1", 0x1}, + {"C2", 0x2}, + + {"LOAD", 0x03}, + {"LOAD_FP", 0x07}, + {"CUSTOM_0", 0x0b}, + {"MISC_MEM", 0x0f}, + {"OP_IMM", 0x13}, + {"AUIPC", 0x17}, + {"OP_IMM_32", 0x1b}, + /* 48b 0x1f. */ + + {"STORE", 0x23}, + {"STORE_FP", 0x27}, + {"CUSTOM_1", 0x2b}, + {"AMO", 0x2f}, + {"OP", 0x33}, + {"LUI", 0x37}, + {"OP_32", 0x3b}, + /* 64b 0x3f. */ + + {"MADD", 0x43}, + {"MSUB", 0x47}, + {"NMADD", 0x4f}, + {"NMSUB", 0x4b}, + {"OP_FP", 0x53}, + /*reserved 0x57. */ + {"CUSTOM_2", 0x5b}, + /* 48b 0x5f. */ + + {"BRANCH", 0x63}, + {"JALR", 0x67}, + /*reserved 0x5b. */ + {"JAL", 0x6f}, + {"SYSTEM", 0x73}, + /*reserved 0x77. */ + {"CUSTOM_3", 0x7b}, + /* >80b 0x7f. */ + + {NULL, 0} +}; + +/* Hash table for lookup opcode name. */ +static struct hash_control *opcode_names_hash = NULL; + +/* Initialization for hash table of opcode name. */ +static void +init_opcode_names_hash (void) +{ + const char *retval; + const struct opcode_name_t *opcode; + + for (opcode = &opcode_name_list[0]; opcode->name != NULL; ++opcode) + { + retval = hash_insert (opcode_names_hash, opcode->name, (void *)opcode); + + if (retval != NULL) + as_fatal (_("internal error: can't hash `%s': %s"), + opcode->name, retval); + } +} + +/* Find `s` is a valid opcode name or not, + return the opcode name info if found. */ +static const struct opcode_name_t * +opcode_name_lookup (char **s) +{ + char *e; + char save_c; + struct opcode_name_t *o; + + /* Find end of name. */ + e = *s; + if (is_name_beginner (*e)) + ++e; + while (is_part_of_name (*e)) + ++e; + + /* Terminate name. */ + save_c = *e; + *e = '\0'; + + o = (struct opcode_name_t *) hash_find (opcode_names_hash, *s); + + /* Advance to next token if one was recognized. */ + if (o) + *s = e; + + *e = save_c; + expr_end = e; + + return o; +} + struct regname { const char *name; @@ -438,6 +493,10 @@ reg_lookup_internal (const char *s, enum reg_class class) if (r == NULL || DECODE_REG_CLASS (r) != class) return -1; + + if (riscv_opts.rve && class == RCLASS_GPR && DECODE_REG_NUM (r) > 15) + return -1; + return DECODE_REG_NUM (r); } @@ -475,6 +534,9 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop) const char *p = strchr (*s, ','); size_t i, len = p ? (size_t)(p - *s) : strlen (*s); + if (len == 0) + return FALSE; + for (i = 0; i < size; i++) if (array[i] != NULL && strncmp (array[i], *s, len) == 0) { @@ -488,15 +550,24 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop) /* For consistency checking, verify that all bits are specified either by the match/mask part of the instruction definition, or by the - operand list. */ + operand list. + + `length` could be 0, 4 or 8, 0 for auto detection. */ static bfd_boolean -validate_riscv_insn (const struct riscv_opcode *opc) +validate_riscv_insn (const struct riscv_opcode *opc, int length) { const char *p = opc->args; char c; insn_t used_bits = opc->mask; - int insn_width = 8 * riscv_insn_length (opc->match); - insn_t required_bits = ~0ULL >> (64 - insn_width); + int insn_width; + insn_t required_bits; + + if (length == 0) + insn_width = 8 * riscv_insn_length (opc->match); + else + insn_width = 8 * length; + + required_bits = ~0ULL >> (64 - insn_width); if ((used_bits & opc->match) != (opc->match & required_bits)) { @@ -528,6 +599,7 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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; @@ -536,8 +608,24 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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 '8': used_bits |= ENCODE_RVC_UIMM8 (-1U); break; + case 'S': USE_BITS (OP_MASK_CRS1S, OP_SH_CRS1S); break; case 'T': USE_BITS (OP_MASK_CRS2, OP_SH_CRS2); break; case 'D': USE_BITS (OP_MASK_CRS2S, OP_SH_CRS2S); break; + case 'F': /* funct */ + switch (c = *p++) + { + case '6': USE_BITS (OP_MASK_CFUNCT6, OP_SH_CFUNCT6); break; + case '4': USE_BITS (OP_MASK_CFUNCT4, OP_SH_CFUNCT4); break; + case '3': USE_BITS (OP_MASK_CFUNCT3, OP_SH_CFUNCT3); break; + case '2': USE_BITS (OP_MASK_CFUNCT2, OP_SH_CFUNCT2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `CF%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; default: as_bad (_("internal: bad RISC-V opcode (unknown operand type `C%c'): %s %s"), c, opc->name, opc->args); @@ -562,6 +650,7 @@ validate_riscv_insn (const struct riscv_opcode *opc) case 'm': USE_BITS (OP_MASK_RM, OP_SH_RM); break; case 's': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break; case 't': USE_BITS (OP_MASK_RS2, OP_SH_RS2); break; + case 'r': USE_BITS (OP_MASK_RS3, OP_SH_RS3); break; case 'P': USE_BITS (OP_MASK_PRED, OP_SH_PRED); break; case 'Q': USE_BITS (OP_MASK_SUCC, OP_SH_SUCC); break; case 'o': @@ -570,9 +659,36 @@ validate_riscv_insn (const struct riscv_opcode *opc) 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 'z': break; case '[': break; case ']': break; case '0': break; + case '1': break; + case 'F': /* funct */ + switch (c = *p++) + { + case '7': USE_BITS (OP_MASK_FUNCT7, OP_SH_FUNCT7); break; + case '3': USE_BITS (OP_MASK_FUNCT3, OP_SH_FUNCT3); break; + case '2': USE_BITS (OP_MASK_FUNCT2, OP_SH_FUNCT2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `F%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; + case 'O': /* opcode */ + switch (c = *p++) + { + case '4': USE_BITS (OP_MASK_OP, OP_SH_OP); break; + case '2': USE_BITS (OP_MASK_OP2, OP_SH_OP2); break; + default: + as_bad (_("internal: bad RISC-V opcode" + " (unknown operand type `F%c'): %s %s"), + c, opc->name, opc->args); + return FALSE; + } + break; default: as_bad (_("internal: bad RISC-V opcode " "(unknown operand type `%c'): %s %s"), @@ -596,53 +712,78 @@ struct percent_op_match bfd_reloc_code_real_type reloc; }; -/* This function is called once, at assembler startup time. It should set up - all the tables, etc. that the MD part of the assembler will need. */ - -void -md_begin (void) +/* Common hash table initialization function for + instruction and .insn directive. */ +static struct hash_control * +init_opcode_hash (const struct riscv_opcode *opcodes, + bfd_boolean insn_directive_p) { int i = 0; - unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32; - - if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach)) - as_warn (_("Could not set architecture and machine")); - - op_hash = hash_new (); - - while (riscv_opcodes[i].name) + int length; + struct hash_control *hash = hash_new (); + while (opcodes[i].name) { - const char *name = riscv_opcodes[i].name; + const char *name = opcodes[i].name; const char *hash_error = - hash_insert (op_hash, name, (void *) &riscv_opcodes[i]); + hash_insert (hash, name, (void *) &opcodes[i]); if (hash_error) { fprintf (stderr, _("internal error: can't hash `%s': %s\n"), - riscv_opcodes[i].name, hash_error); + opcodes[i].name, hash_error); /* Probably a memory allocation problem? Give up now. */ as_fatal (_("Broken assembler. No assembly attempted.")); } do { - if (riscv_opcodes[i].pinfo != INSN_MACRO) + if (opcodes[i].pinfo != INSN_MACRO) { - if (!validate_riscv_insn (&riscv_opcodes[i])) + if (insn_directive_p) + length = ((name[0] == 'c') ? 2 : 4); + else + length = 0; /* Let assembler determine the length. */ + if (!validate_riscv_insn (&opcodes[i], length)) as_fatal (_("Broken assembler. No assembly attempted.")); } + else + gas_assert (!insn_directive_p); ++i; } - while (riscv_opcodes[i].name && !strcmp (riscv_opcodes[i].name, name)); + while (opcodes[i].name && !strcmp (opcodes[i].name, name)); } + return hash; +} + +/* This function is called once, at assembler startup time. It should set up + all the tables, etc. that the MD part of the assembler will need. */ + +void +md_begin (void) +{ + unsigned long mach = xlen == 64 ? bfd_mach_riscv64 : bfd_mach_riscv32; + + if (! bfd_set_arch_mach (stdoutput, bfd_arch_riscv, mach)) + as_warn (_("Could not set architecture and machine")); + + op_hash = init_opcode_hash (riscv_opcodes, FALSE); + insn_type_hash = init_opcode_hash (riscv_insn_types, TRUE); + reg_names_hash = hash_new (); hash_reg_names (RCLASS_GPR, riscv_gpr_names_numeric, NGPR); hash_reg_names (RCLASS_GPR, riscv_gpr_names_abi, NGPR); hash_reg_names (RCLASS_FPR, riscv_fpr_names_numeric, NFPR); hash_reg_names (RCLASS_FPR, riscv_fpr_names_abi, NFPR); + /* Add "fp" as an alias for "s0". */ + hash_reg_name (RCLASS_GPR, "fp", 8); + + opcode_names_hash = hash_new (); + init_opcode_names_hash (); + #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 @@ -715,6 +856,21 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, 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 @@ -785,6 +941,29 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...) 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 @@ -798,13 +977,18 @@ normalize_constant_expr (expressionS *ex) - 0x80000000); } -/* Fail if an expression is not a constant. */ +/* Fail if an expression EX is not a constant. IP is the instruction using EX. + MAYBE_CSR is true if the symbol may be an unrecognized CSR name. */ static void -check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex) +check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex, + bfd_boolean maybe_csr) { if (ex->X_op == O_big) as_bad (_("unsupported large constant")); + else if (maybe_csr && ex->X_op == O_symbol) + as_bad (_("unknown CSR `%s'"), + S_GET_NAME (ex->X_add_symbol)); else if (ex->X_op != O_constant) as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name); @@ -865,6 +1049,7 @@ 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; @@ -884,9 +1069,10 @@ load_const (int reg, expressionS *ep) 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 { @@ -895,13 +1081,16 @@ load_const (int reg, expressionS *ep) 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); } } @@ -1064,6 +1253,11 @@ static const struct percent_op_match percent_op_rtype[] = {0, 0} }; +static const struct percent_op_match percent_op_null[] = +{ + {0, 0} +}; + /* Return true if *STR points to a relocation operator. When returning true, move *STR over the operator and store its relocation code in *RELOC. Leave both *STR and *RELOC alone when returning false. */ @@ -1123,11 +1317,15 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, 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; } @@ -1169,13 +1367,49 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, return reloc_index; } +/* Parse opcode name, could be an mnemonics or number. */ +static size_t +my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc, + char *str, const struct percent_op_match *percent_op) +{ + const struct opcode_name_t *o = opcode_name_lookup (&str); + + if (o != NULL) + { + ep->X_op = O_constant; + ep->X_add_number = o->val; + return 0; + } + + return my_getSmallExpression (ep, reloc, str, percent_op); +} + +/* 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 *ep, 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) + { + ep->X_op = O_constant; + ep->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. */ static const char * riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, - bfd_reloc_code_real_type *imm_reloc) + bfd_reloc_code_real_type *imm_reloc, struct hash_control *hash) { char *s; const char *args; @@ -1198,12 +1432,15 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, break; } - insn = (struct riscv_opcode *) hash_find (op_hash, str); + insn = (struct riscv_opcode *) hash_find (hash, str); argsStart = s; for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++) { - if (!riscv_subset_supports (insn->subset)) + if ((insn->xlen_requirement != 0) && (xlen != insn->xlen_requirement)) + continue; + + if (!riscv_multi_subset_supports (insn->insn_class)) continue; create_insn (ip, insn); @@ -1223,7 +1460,12 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, { if (!insn->match_func (insn, ip->insn_opcode)) break; - if (riscv_insn_length (insn->match) == 2 && !riscv_opts.rvc) + + /* For .insn, insn->match and insn->mask are 0. */ + if (riscv_insn_length ((insn->match == 0 && insn->mask == 0) + ? ip->insn_opcode + : insn->match) == 2 + && !riscv_opts.rvc) break; } if (*s != '\0') @@ -1272,6 +1514,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr, || 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 @@ -1292,6 +1539,15 @@ rvc_imm_done: break; ip->insn_opcode |= ENCODE_RVC_IMM (imm_expr->X_add_number); goto rvc_imm_done; + case '8': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || !VALID_RVC_UIMM8 (imm_expr->X_add_number) + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 256) + break; + ip->insn_opcode |= ENCODE_RVC_UIMM8 (imm_expr->X_add_number); + goto rvc_imm_done; case 'i': if (my_getSmallExpression (imm_expr, imm_reloc, s, p) || imm_expr->X_op != O_constant @@ -1309,6 +1565,8 @@ rvc_imm_done: 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)) @@ -1316,6 +1574,8 @@ rvc_imm_done: 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)) @@ -1323,6 +1583,8 @@ rvc_imm_done: 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)) @@ -1331,6 +1593,8 @@ rvc_imm_done: 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)) @@ -1341,6 +1605,9 @@ 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); @@ -1364,6 +1631,8 @@ rvc_imm_done: 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)) @@ -1372,6 +1641,8 @@ rvc_imm_done: 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)) @@ -1406,6 +1677,12 @@ rvc_lui: goto branch; case 'a': goto jump; + case 'S': /* Floating-point RS1 x8-x15. */ + if (!reg_lookup (&s, RCLASS_FPR, ®no) + || !(regno >= 8 && regno <= 15)) + break; + INSERT_OPERAND (CRS1S, *ip, regno % 8); + continue; case 'D': /* Floating-point RS2 x8-x15. */ if (!reg_lookup (&s, RCLASS_FPR, ®no) || !(regno >= 8 && regno <= 15)) @@ -1417,6 +1694,74 @@ rvc_lui: break; INSERT_OPERAND (CRS2, *ip, regno); continue; + case 'F': + switch (*++args) + { + case '6': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 64) + { + as_bad (_("bad value for funct6 field, " + "value must be 0...64")); + break; + } + + INSERT_OPERAND (CFUNCT6, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '4': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 16) + { + as_bad (_("bad value for funct4 field, " + "value must be 0...15")); + break; + } + + INSERT_OPERAND (CFUNCT4, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '3': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 8) + { + as_bad (_("bad value for funct3 field, " + "value must be 0...7")); + break; + } + INSERT_OPERAND (CFUNCT3, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 4) + { + as_bad (_("bad value for funct2 field, " + "value must be 0...3")); + break; + } + INSERT_OPERAND (CFUNCT2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + default: + as_bad (_("bad compressed FUNCT field" + " specifier 'CF%c'\n"), + *args); + } + break; + default: as_bad (_("bad RVC field specifier 'C%c'\n"), *args); } @@ -1439,7 +1784,7 @@ rvc_lui: case '<': /* Shift amount, 0 - 31. */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number > 31) as_bad (_("Improper shift amount (%lu)"), (unsigned long) imm_expr->X_add_number); @@ -1450,7 +1795,7 @@ rvc_lui: case '>': /* Shift amount, 0 - (XLEN-1). */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number >= xlen) as_bad (_("Improper shift amount (%lu)"), (unsigned long) imm_expr->X_add_number); @@ -1461,7 +1806,7 @@ rvc_lui: case 'Z': /* CSRRxI immediate. */ my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, FALSE); if ((unsigned long) imm_expr->X_add_number > 31) as_bad (_("Improper CSRxI immediate (%lu)"), (unsigned long) imm_expr->X_add_number); @@ -1476,7 +1821,7 @@ rvc_lui: else { my_getExpression (imm_expr, s); - check_absolute_expr (ip, imm_expr); + check_absolute_expr (ip, imm_expr, TRUE); if ((unsigned long) imm_expr->X_add_number > 0xfff) as_bad (_("Improper CSR address (%lu)"), (unsigned long) imm_expr->X_add_number); @@ -1510,6 +1855,7 @@ rvc_lui: case 'd': /* Destination register. */ case 's': /* Source register. */ case 't': /* Target register. */ + case 'r': /* rs3. */ if (reg_lookup (&s, RCLASS_GPR, ®no)) { c = *args; @@ -1529,6 +1875,9 @@ rvc_lui: case 't': INSERT_OPERAND (RS2, *ip, regno); break; + case 'r': + INSERT_OPERAND (RS3, *ip, regno); + break; } continue; } @@ -1586,9 +1935,20 @@ rvc_lui: s = expr_end; continue; + case 'B': + my_getExpression (imm_expr, s); + normalize_constant_expr (imm_expr); + /* The 'B' format specifier must be a symbol or a constant. */ + if (imm_expr->X_op != O_symbol && imm_expr->X_op != O_constant) + break; + if (imm_expr->X_op == O_symbol) + *imm_reloc = BFD_RELOC_32; + s = expr_end; + continue; + case 'j': /* Sign-extended immediate. */ - *imm_reloc = BFD_RELOC_RISCV_LO12_I; p = percent_op_itype; + *imm_reloc = BFD_RELOC_RISCV_LO12_I; goto alu_op; case 'q': /* Store displacement. */ p = percent_op_stype; @@ -1598,16 +1958,13 @@ rvc_lui: p = percent_op_itype; *imm_reloc = BFD_RELOC_RISCV_LO12_I; goto load_store; - case '0': /* AMO "displacement," which must be zero. */ + case '1': /* 4-operand add, must be %tprel_add. */ p = percent_op_rtype; - *imm_reloc = BFD_RELOC_UNUSED; + goto alu_op; + case '0': /* AMO "displacement," which must be zero. */ + p = percent_op_null; 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 @@ -1618,6 +1975,7 @@ alu_op: normalize_constant_expr (imm_expr); if (imm_expr->X_op != O_constant || (*args == '0' && imm_expr->X_add_number != 0) + || (*args == '1') || imm_expr->X_add_number >= (signed)RISCV_IMM_REACH/2 || imm_expr->X_add_number < -(signed)RISCV_IMM_REACH/2) break; @@ -1635,9 +1993,11 @@ branch: 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")); @@ -1666,6 +2026,108 @@ jump: else *imm_reloc = BFD_RELOC_RISCV_CALL; continue; + case 'O': + switch (*++args) + { + case '4': + if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 128 + || (imm_expr->X_add_number & 0x3) != 3) + { + as_bad (_("bad value for opcode field, " + "value must be 0...127 and " + "lower 2 bits must be 0x3")); + break; + } + + INSERT_OPERAND (OP, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getOpcodeExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 3) + { + as_bad (_("bad value for opcode field, " + "value must be 0...2")); + break; + } + + INSERT_OPERAND (OP2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + default: + as_bad (_("bad Opcode field specifier 'O%c'\n"), *args); + } + break; + + case 'F': + switch (*++args) + { + case '7': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 128) + { + as_bad (_("bad value for funct7 field, " + "value must be 0...127")); + break; + } + + INSERT_OPERAND (FUNCT7, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '3': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 8) + { + as_bad (_("bad value for funct3 field, " + "value must be 0...7")); + break; + } + + INSERT_OPERAND (FUNCT3, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + case '2': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number < 0 + || imm_expr->X_add_number >= 4) + { + as_bad (_("bad value for funct2 field, " + "value must be 0...3")); + break; + } + + INSERT_OPERAND (FUNCT2, *ip, imm_expr->X_add_number); + imm_expr->X_op = O_absent; + s = expr_end; + continue; + + default: + as_bad (_("bad FUNCT field specifier 'F%c'\n"), *args); + } + break; + + case 'z': + if (my_getSmallExpression (imm_expr, imm_reloc, s, p) + || imm_expr->X_op != O_constant + || imm_expr->X_add_number != 0) + break; + s = expr_end; + imm_expr->X_op = O_absent; + continue; default: as_fatal (_("internal error: bad argument type %c"), *args); @@ -1691,7 +2153,9 @@ md_assemble (char *str) expressionS imm_expr; bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED; - const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc); + const char *error = riscv_ip (str, &insn, &imm_expr, &imm_reloc, op_hash); + + start_assemble = TRUE; if (error) { @@ -1725,6 +2189,10 @@ enum options OPTION_PIC, OPTION_NO_PIC, OPTION_MABI, + OPTION_RELAX, + OPTION_NO_RELAX, + OPTION_ARCH_ATTR, + OPTION_NO_ARCH_ATTR, OPTION_END_OF_ENUM }; @@ -1735,6 +2203,10 @@ struct option md_longopts[] = {"fpic", no_argument, NULL, OPTION_PIC}, {"fno-pic", no_argument, NULL, OPTION_NO_PIC}, {"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} }; @@ -1750,10 +2222,11 @@ enum float_abi { static enum float_abi float_abi = FLOAT_ABI_DEFAULT; static void -riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi) +riscv_set_abi (unsigned new_xlen, enum float_abi new_float_abi, bfd_boolean rve) { abi_xlen = new_xlen; float_abi = new_float_abi; + rve_abi = rve; } int @@ -1775,25 +2248,43 @@ md_parse_option (int c, const char *arg) case OPTION_MABI: if (strcmp (arg, "ilp32") == 0) - riscv_set_abi (32, FLOAT_ABI_SOFT); + riscv_set_abi (32, FLOAT_ABI_SOFT, FALSE); + else if (strcmp (arg, "ilp32e") == 0) + riscv_set_abi (32, FLOAT_ABI_SOFT, TRUE); else if (strcmp (arg, "ilp32f") == 0) - riscv_set_abi (32, FLOAT_ABI_SINGLE); + riscv_set_abi (32, FLOAT_ABI_SINGLE, FALSE); else if (strcmp (arg, "ilp32d") == 0) - riscv_set_abi (32, FLOAT_ABI_DOUBLE); + riscv_set_abi (32, FLOAT_ABI_DOUBLE, FALSE); else if (strcmp (arg, "ilp32q") == 0) - riscv_set_abi (32, FLOAT_ABI_QUAD); + riscv_set_abi (32, FLOAT_ABI_QUAD, FALSE); else if (strcmp (arg, "lp64") == 0) - riscv_set_abi (64, FLOAT_ABI_SOFT); + riscv_set_abi (64, FLOAT_ABI_SOFT, FALSE); else if (strcmp (arg, "lp64f") == 0) - riscv_set_abi (64, FLOAT_ABI_SINGLE); + riscv_set_abi (64, FLOAT_ABI_SINGLE, FALSE); else if (strcmp (arg, "lp64d") == 0) - riscv_set_abi (64, FLOAT_ABI_DOUBLE); + riscv_set_abi (64, FLOAT_ABI_DOUBLE, FALSE); else if (strcmp (arg, "lp64q") == 0) - riscv_set_abi (64, FLOAT_ABI_QUAD); + riscv_set_abi (64, FLOAT_ABI_QUAD, FALSE); else return 0; break; + case OPTION_RELAX: + riscv_opts.relax = TRUE; + break; + + case OPTION_NO_RELAX: + 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; } @@ -1814,15 +2305,18 @@ riscv_after_parse_args (void) as_bad ("unknown default architecture `%s'", default_arch); } - if (riscv_subsets == NULL) + if (riscv_subsets.head == 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"); + + /* Enable RVE if specified by the -march option. */ + riscv_set_rve (FALSE); + if (riscv_subset_supports ("e")) + riscv_set_rve (TRUE); /* Infer ABI from ISA if not specified on command line. */ if (abi_xlen == 0) @@ -1834,12 +2328,12 @@ riscv_after_parse_args (void) if (float_abi == FLOAT_ABI_DEFAULT) { - struct riscv_subset *subset; + riscv_subset_t *subset; /* Assume soft-float unless D extension is present. */ float_abi = FLOAT_ABI_SOFT; - for (subset = riscv_subsets; subset != NULL; subset = subset->next) + for (subset = riscv_subsets.head; subset != NULL; subset = subset->next) { if (strcasecmp (subset->name, "D") == 0) float_abi = FLOAT_ABI_DOUBLE; @@ -1848,6 +2342,9 @@ riscv_after_parse_args (void) } } + if (rve_abi) + elf_flags |= EF_RISCV_RVE; + /* 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)); } @@ -2124,14 +2621,13 @@ riscv_pre_output_hook (void) if (frag->fr_type == rs_cfa) { expressionS exp; + expressionS *symval; - symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol; - symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol; - + symval = symbol_get_value_expression (frag->fr_symbol); exp.X_op = O_subtract; - exp.X_add_symbol = add_symbol; + exp.X_add_symbol = symval->X_add_symbol; exp.X_add_number = 0; - exp.X_op_symbol = op_symbol; + exp.X_op_symbol = symval->X_op_symbol; fix_new_exp (frag, (int) frag->fr_offset, 1, &exp, 0, BFD_RELOC_RISCV_CFA); @@ -2277,14 +2773,22 @@ bfd_boolean riscv_frag_align_code (int n) { bfd_vma bytes = (bfd_vma) 1 << n; - bfd_vma worst_case_bytes = bytes - 2; - char *nops = frag_more (worst_case_bytes); + 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; + /* When not relaxing, riscv_handle_align handles code alignment. */ if (!riscv_opts.relax) return FALSE; + nops = frag_more (worst_case_bytes); + ex.X_op = O_constant; ex.X_add_number = worst_case_bytes; @@ -2307,15 +2811,27 @@ riscv_handle_align (fragS *fragP) /* 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; - - if (count <= 0) + bfd_signed_vma bytes = (fragP->fr_next->fr_address + - fragP->fr_address - fragP->fr_fix); + /* We have 4 byte uncompressed nops. */ + bfd_signed_vma size = 4; + bfd_signed_vma excess = bytes % size; + char *p = fragP->fr_literal + fragP->fr_fix; + + if (bytes <= 0) break; - count &= MAX_MEM_FOR_RS_ALIGN_CODE; - riscv_make_nops (fragP->fr_literal + fragP->fr_fix, count); - fragP->fr_var = count; + /* Insert zeros or compressed nops to get 4 byte alignment. */ + if (excess) + { + riscv_make_nops (p, excess); + fragP->fr_fix += excess; + p += excess; + } + + /* Insert variable number of 4 byte uncompressed nops. */ + riscv_make_nops (p, size); + fragP->fr_var = size; } break; @@ -2502,6 +3018,10 @@ RISC-V options:\n\ -fno-pic don't generate position-independent code (default)\n\ -march=ISA set the RISC-V architecture\n\ -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\ ")); } @@ -2551,6 +3071,138 @@ s_riscv_leb128 (int sign) return s_leb128 (sign); } +/* Parse the .insn directive. */ + +static void +s_riscv_insn (int x ATTRIBUTE_UNUSED) +{ + char *str = input_line_pointer; + struct riscv_cl_insn insn; + expressionS imm_expr; + bfd_reloc_code_real_type imm_reloc = BFD_RELOC_UNUSED; + char save_c; + + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + + save_c = *input_line_pointer; + *input_line_pointer = '\0'; + + const char *error = riscv_ip (str, &insn, &imm_expr, + &imm_reloc, insn_type_hash); + + if (error) + { + as_bad ("%s `%s'", error, str); + } + else + { + gas_assert (insn.insn_mo->pinfo != INSN_MACRO); + append_insn (&insn, &imm_expr, imm_reloc); + } + + *input_line_pointer = save_c; + 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[] = @@ -2565,6 +3217,8 @@ static const pseudo_typeS riscv_pseudo_table[] = {"bss", s_bss, 0}, {"uleb128", s_riscv_leb128, 0}, {"sleb128", s_riscv_leb128, 1}, + {"insn", s_riscv_insn, 0}, + {"attribute", s_riscv_attribute, 0}, { NULL, NULL, 0 }, };