bfd, binutils, gas: Remove/mark unused variables
[binutils-gdb.git] / gas / config / tc-riscv.c
index 0b11bb3cd3de5fc191df925d396178787dd709c0..2f5ee18e451e895b990f8f4cb5dee07f54f6a451 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
@@ -235,16 +261,27 @@ riscv_set_rvc (bool rvc_value)
    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
@@ -257,7 +294,14 @@ riscv_set_arch (const char *s)
       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);
@@ -357,7 +401,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;
@@ -705,7 +749,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.  */
 
@@ -835,7 +879,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;
@@ -864,38 +908,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)
     {
@@ -1086,7 +1171,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)
            {
@@ -1110,7 +1195,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;
@@ -1141,6 +1226,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.  */
@@ -1597,8 +1683,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
     {
@@ -1610,13 +1696,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);
     }
 }
 
@@ -1869,6 +1955,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;
@@ -2197,7 +2292,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)
 {
@@ -2208,9 +2303,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;
 
@@ -2233,12 +2330,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;
@@ -2286,14 +2387,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;
 
@@ -2526,7 +2627,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);
@@ -2587,7 +2688,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)
@@ -2753,10 +2854,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--;
@@ -2879,7 +2979,9 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
            case 'T': /* Floating point RS2.  */
            case 'U': /* Floating point RS1 and RS2.  */
            case 'R': /* Floating point RS3.  */
-             if (reg_lookup (&asarg, RCLASS_FPR, &regno))
+             if (reg_lookup (&asarg,
+                             (riscv_subset_supports (&riscv_rps_as, "zfinx")
+                             ? RCLASS_GPR : RCLASS_FPR), &regno))
                {
                  char c = *oparg;
                  if (*asarg == ' ')
@@ -3143,6 +3245,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'"),
@@ -3200,7 +3319,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");
 
@@ -3227,11 +3346,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;
     }
 
@@ -3713,15 +3837,6 @@ riscv_pre_output_hook (void)
   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
@@ -3736,12 +3851,12 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
 
   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)
@@ -3756,14 +3871,28 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
     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)
     {
@@ -3774,8 +3903,12 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
        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);
        }
     }
@@ -3884,6 +4017,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;
 }
 
@@ -4138,7 +4277,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\
@@ -4225,17 +4364,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);
@@ -4260,7 +4405,7 @@ riscv_write_out_attrs (void)
   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);
 
@@ -4314,7 +4459,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 ();
 }
@@ -4409,6 +4554,59 @@ s_riscv_attribute (int ignored ATTRIBUTE_UNUSED)
     }
 }
 
+/* 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[] =
 {
@@ -4423,6 +4621,8 @@ 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 },
 };