RISC-V: Set EF_RISCV_TSO also on .option arch
[binutils-gdb.git] / gas / config / tc-riscv.c
index e8061217e7cd63e423c4266c61c7f2d5c1abf283..42d7bf62e4f71b7161cfa0fc465ee2b0302272ed 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
@@ -65,7 +65,19 @@ enum riscv_csr_class
   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.  */
@@ -82,13 +94,27 @@ struct riscv_csr_extra
   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
@@ -104,7 +130,7 @@ struct riscv_csr_extra
 
 /* 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
@@ -231,6 +257,14 @@ riscv_set_rvc (bool rvc_value)
   riscv_opts.rvc = rvc_value;
 }
 
+/* Turn on the tso flag for elf_flags once we have enabled ztso extension.  */
+
+static void
+riscv_set_tso ()
+{
+  elf_flags |= EF_RISCV_TSO;
+}
+
 /* This linked list records all enabled extensions, which are parsed from
    the architecture string.  The architecture string can be set by the
    -march option, the elf architecture attributes, and the --with-arch
@@ -281,6 +315,9 @@ riscv_set_arch (const char *s)
   riscv_set_rvc (false);
   if (riscv_subset_supports (&riscv_rps_as, "c"))
     riscv_set_rvc (true);
+
+  if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+    riscv_set_tso ();
 }
 
 /* Indicate -mabi option is explictly set.  */
@@ -375,7 +412,7 @@ const char EXP_CHARS[] = "eE";
 
 /* 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;
@@ -723,7 +760,7 @@ static const struct opcode_name_t opcode_name_list[] =
   {"NMADD",     0x4f},
   {"NMSUB",     0x4b},
   {"OP_FP",     0x53},
-  /*reserved    0x57.  */
+  {"OP_V",      0x57},
   {"CUSTOM_2",  0x5b},
   /* 48b        0x5f.  */
 
@@ -853,7 +890,7 @@ riscv_init_csr_hash (const char *name,
   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;
@@ -882,38 +919,79 @@ riscv_csr_address (const char *csr_name,
 {
   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)
     {
@@ -1104,7 +1182,7 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
            default:
              goto unknown_validate_operand;
            }
-         break;
+         break;  /* end RVC */
        case 'V': /* RVV */
          switch (*++oparg)
            {
@@ -1128,7 +1206,7 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
            default:
              goto unknown_validate_operand;
            }
-         break;
+         break; /* end RVV */
        case ',': break;
        case '(': break;
        case ')': break;
@@ -1159,6 +1237,7 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
        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.  */
@@ -1615,8 +1694,8 @@ load_const (int reg, expressionS *ep)
 
       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
     {
@@ -1628,13 +1707,13 @@ load_const (int reg, expressionS *ep)
          /* 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);
     }
 }
 
@@ -1887,6 +1966,15 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
       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;
@@ -2215,7 +2303,7 @@ riscv_is_priv_insn (insn_t insn)
    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)
 {
@@ -2226,9 +2314,11 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
   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;
 
@@ -2251,12 +2341,16 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
        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;
@@ -2304,14 +2398,14 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                      && 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;
 
@@ -2544,7 +2638,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                            || 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);
@@ -2605,7 +2699,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                default:
                  goto unknown_riscv_ip_operand;
                }
-             break;
+             break; /* end RVC */
 
            case 'V': /* RVV */
              switch (*++oparg)
@@ -2771,10 +2865,9 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                default:
                  goto unknown_riscv_ip_operand;
                }
-             break;
+             break; /* end RVV */
 
            case ',':
-             ++argnum;
              if (*asarg++ == *oparg)
                continue;
              asarg--;
@@ -3031,12 +3124,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
              my_getExpression (imm_expr, asarg);
              asarg = expr_end;
              if (strcmp (asarg, "@plt") == 0)
-               {
-                 *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
-                 asarg += 4;
-               }
-             else
-               *imm_reloc = BFD_RELOC_RISCV_CALL;
+               asarg += 4;
+             *imm_reloc = BFD_RELOC_RISCV_CALL_PLT;
              continue;
 
            case 'O':
@@ -3163,6 +3252,23 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
              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'"),
@@ -3220,7 +3326,7 @@ riscv_ip_hardcode (char *str,
   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");
 
@@ -3247,11 +3353,16 @@ md_assemble (char *str)
 
   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;
     }
 
@@ -3777,6 +3888,9 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
       riscv_set_rvc (false);
       if (riscv_subset_supports (&riscv_rps_as, "c"))
        riscv_set_rvc (true);
+
+      if (riscv_subset_supports (&riscv_rps_as, "ztso"))
+       riscv_set_tso ();
     }
   else if (strcmp (name, "push") == 0)
     {
@@ -3913,6 +4027,12 @@ riscv_frag_align_code (int n)
 
   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;
 }
 
@@ -4167,7 +4287,7 @@ RISC-V options:\n\
   -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\
@@ -4254,17 +4374,23 @@ s_riscv_insn (int x ATTRIBUTE_UNUSED)
 
   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);
@@ -4343,7 +4469,7 @@ riscv_set_public_attributes (void)
 /* Called after all assembly has been done.  */
 
 void
-riscv_md_end (void)
+riscv_md_finish (void)
 {
   riscv_set_public_attributes ();
 }
@@ -4477,19 +4603,18 @@ 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 (srcelf->size)
+  /* 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 (destelf->size == NULL)
-       destelf->size = XNEW (expressionS);
-      *destelf->size = *srcelf->size;
-    }
-  else
-    {
-      if (destelf->size != NULL)
-       free (destelf->size);
-      destelf->size = NULL;
+      if (srcelf->size)
+       {
+         destelf->size = XNEW (expressionS);
+         *destelf->size = *srcelf->size;
+       }
+      S_SET_SIZE (dest, S_GET_SIZE (src));
     }
-  S_SET_SIZE (dest, S_GET_SIZE (src));
 }
 
 /* RISC-V pseudo-ops table.  */
@@ -4507,6 +4632,7 @@ static const pseudo_typeS riscv_pseudo_table[] =
   {"insn", s_riscv_insn, 0},
   {"attribute", s_riscv_attribute, 0},
   {"variant_cc", s_variant_cc, 0},
+  {"float16", float_cons, 'h'},
 
   { NULL, NULL, 0 },
 };