]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-riscv.c
2.41 Release sources
[thirdparty/binutils-gdb.git] / gas / config / tc-riscv.c
index 82dfea1bc0d6688f8b00835ea53f28b0f029d93c..297bb9b2a819b6de32574871ce314b148fa489e5 100644 (file)
@@ -171,6 +171,8 @@ static enum float_abi float_abi = FLOAT_ABI_DEFAULT;
 
 static unsigned elf_flags = 0;
 
+static bool probing_insn_operands;
+
 /* Set the default_isa_spec.  Return 0 if the spec isn't supported.
    Otherwise, return 1.  */
 
@@ -1219,6 +1221,21 @@ arg_lookup (char **s, const char *const *array, size_t size, unsigned *regnop)
   return false;
 }
 
+static bool
+flt_lookup (float f, const float *array, size_t size, unsigned *regnop)
+{
+  size_t i;
+
+  for (i = 0; i < size; i++)
+    if (array[i] == f)
+      {
+       *regnop = i;
+       return true;
+      }
+
+  return false;
+}
+
 #define USE_BITS(mask,shift) (used_bits |= ((insn_t)(mask) << (shift)))
 #define USE_IMM(n, s) \
   (used_bits |= ((insn_t)((1ull<<n)-1) << (s)))
@@ -1324,6 +1341,7 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
            case 'i':
            case 'j':
            case 'k': USE_BITS (OP_MASK_VIMM, OP_SH_VIMM); break;
+           case 'l': used_bits |= ENCODE_RVV_VI_UIMM6 (-1U); break;
            case 'm': USE_BITS (OP_MASK_VMASK, OP_SH_VMASK); break;
            case 'M': break; /* Macro operand, must be a mask register.  */
            case 'T': break; /* Macro operand, must be a vector register.  */
@@ -1399,6 +1417,14 @@ validate_riscv_insn (const struct riscv_opcode *opc, int length)
                  goto unknown_validate_operand;
                }
              break;
+           case 'f':
+             switch (*++oparg)
+               {
+               case 'v': USE_BITS (OP_MASK_RS1, OP_SH_RS1); break;
+               default:
+                 goto unknown_validate_operand;
+               }
+             break;
            default:
              goto unknown_validate_operand;
            }
@@ -2008,16 +2034,20 @@ macro (struct riscv_cl_insn *ip, expressionS *imm_expr,
 
     case M_LA:
     case M_LLA:
+    case M_LGA:
       /* Load the address of a symbol into a register.  */
       if (!IS_SEXT_32BIT_NUM (imm_expr->X_add_number))
        as_bad (_("offset too large"));
 
       if (imm_expr->X_op == O_constant)
        load_const (rd, imm_expr);
-      else if (riscv_opts.pic && mask == M_LA) /* Global PIC symbol.  */
+      /* Global PIC symbol.  */
+      else if ((riscv_opts.pic && mask == M_LA)
+              || mask == M_LGA)
        pcrel_load (rd, rd, imm_expr, LOAD_ADDRESS_INSN,
                    BFD_RELOC_RISCV_GOT_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
-      else /* Local PIC symbol, or any non-PIC symbol.  */
+      /* Local PIC symbol, or any non-PIC symbol.  */
+      else
        pcrel_load (rd, rd, imm_expr, "addi",
                    BFD_RELOC_RISCV_PCREL_HI20, BFD_RELOC_RISCV_PCREL_LO12_I);
       break;
@@ -2198,7 +2228,9 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
       {
        size_t len = 1 + strlen (percent_op->str);
 
-       if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+       while (ISSPACE ((*str)[len]))
+         ++len;
+       if ((*str)[len] != '(')
          continue;
 
        *str += len;
@@ -2242,21 +2274,10 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
                       char *str, const struct percent_op_match *percent_op)
 {
   size_t reloc_index;
-  unsigned crux_depth, str_depth, regno;
+  unsigned crux_depth, str_depth;
+  bool orig_probing = probing_insn_operands;
   char *crux;
 
-  /* 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, &regno))
-    {
-      ep->X_op = O_register;
-      ep->X_add_number = regno;
-      expr_parse_end = str;
-      return 0;
-    }
-
   /* Search for the start of the main expression.
 
      End the loop with CRUX pointing to the start of the main expression and
@@ -2279,9 +2300,26 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
         && reloc_index < 1
         && parse_relocation (&str, reloc, percent_op));
 
+  if (*str == '%')
+    {
+       /* expression() will choke on anything looking like an (unrecognized)
+         relocation specifier.  Don't even call it, avoiding multiple (and
+         perhaps redundant) error messages; our caller will issue one.  */
+       ep->X_op = O_illegal;
+       return 0;
+    }
+
+  /* Anything inside parentheses or subject to a relocation operator cannot
+     be a register and hence can be treated the same as operands to
+     directives (other than .insn).  */
+  if (str_depth || reloc_index)
+    probing_insn_operands = false;
+
   my_getExpression (ep, crux);
   str = expr_parse_end;
 
+  probing_insn_operands = orig_probing;
+
   /* Match every open bracket.  */
   while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
     if (*str++ == ')')
@@ -2299,7 +2337,7 @@ my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
 
 static size_t
 my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
-                       char *str, const struct percent_op_match *percent_op)
+                       char *str)
 {
   const struct opcode_name_t *o = opcode_name_lookup (&str);
 
@@ -2310,7 +2348,7 @@ my_getOpcodeExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
       return 0;
     }
 
-  return my_getSmallExpression (ep, reloc, str, percent_op);
+  return my_getSmallExpression (ep, reloc, str, percent_op_null);
 }
 
 /* Parse string STR as a vsetvli operand.  Store the expression in *EP.
@@ -2467,6 +2505,13 @@ riscv_is_priv_insn (insn_t insn)
          || ((insn ^ MATCH_SFENCE_VM) & MASK_SFENCE_VM) == 0);
 }
 
+static symbolS *deferred_sym_rootP;
+static symbolS *deferred_sym_lastP;
+/* Since symbols can't easily be freed, try to recycle ones which weren't
+   committed.  */
+static symbolS *orphan_sym_rootP;
+static symbolS *orphan_sym_lastP;
+
 /* 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.  */
@@ -2502,6 +2547,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
 
   insn = (struct riscv_opcode *) str_hash_find (hash, str);
 
+  probing_insn_operands = true;
+
   asargStart = asarg;
   for ( ; insn && insn->name && strcmp (insn->name, str) == 0; insn++)
     {
@@ -2518,11 +2565,22 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
       /* Reset error message of the previous round.  */
       error.msg = _("illegal operands");
       error.missing_ext = NULL;
+
+      /* Purge deferred symbols from the previous round, if any.  */
+      while (deferred_sym_rootP)
+       {
+         symbolS *sym = deferred_sym_rootP;
+
+         symbol_remove (sym, &deferred_sym_rootP, &deferred_sym_lastP);
+         symbol_append (sym, orphan_sym_lastP, &orphan_sym_rootP,
+                        &orphan_sym_lastP);
+       }
+
       create_insn (ip, insn);
 
       imm_expr->X_op = O_absent;
       *imm_reloc = BFD_RELOC_UNUSED;
-      p = percent_op_itype;
+      p = percent_op_null;
 
       for (oparg = insn->args;; ++oparg)
        {
@@ -2572,9 +2630,22 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                }
              if (*asarg != '\0')
                break;
+
              /* Successful assembly.  */
              error.msg = NULL;
              insn_with_csr = false;
+
+             /* Commit deferred symbols, if any.  */
+             while (deferred_sym_rootP)
+               {
+                 symbolS *sym = deferred_sym_rootP;
+
+                 symbol_remove (sym, &deferred_sym_rootP,
+                                &deferred_sym_lastP);
+                 symbol_append (sym, symbol_lastP, &symbol_rootP,
+                                &symbol_lastP);
+                 symbol_table_insert (sym);
+               }
              goto out;
 
            case 'C': /* RVC */
@@ -2778,8 +2849,6 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                case 'p':
                  goto branch;
                case 'a':
-                 if (oparg == insn->args + 1)
-                   goto jump_check_gpr;
                  goto jump;
                case 'S': /* Floating-point RS1 x8-x15.  */
                  if (!reg_lookup (&asarg, RCLASS_FPR, &regno)
@@ -3001,6 +3070,18 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                  asarg = expr_parse_end;
                  continue;
 
+               case 'l': /* 6-bit vector arith unsigned immediate */
+                 my_getExpression (imm_expr, asarg);
+                 check_absolute_expr (ip, imm_expr, FALSE);
+                 if (imm_expr->X_add_number < 0
+                     || imm_expr->X_add_number >= 64)
+                   as_bad (_("bad value for vector immediate field, "
+                             "value must be 0...63"));
+                 ip->insn_opcode |= ENCODE_RVV_VI_UIMM6 (imm_expr->X_add_number);
+                 imm_expr->X_op = O_absent;
+                 asarg = expr_parse_end;
+                 continue;
+
                case 'm': /* optional vector mask */
                  if (*asarg == '\0')
                    {
@@ -3238,7 +3319,6 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
              p = percent_op_rtype;
              goto alu_op;
            case '0': /* AMO displacement, which must be zero.  */
-             p = percent_op_null;
            load_store:
              if (riscv_handle_implicit_zero_offset (imm_expr, asarg))
                continue;
@@ -3284,18 +3364,6 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
              continue;
 
            case 'a': /* 20-bit PC-relative offset.  */
-             /* Like in my_getSmallExpression() we need to avoid emitting
-                a stray undefined symbol if the 1st JAL entry doesn't match,
-                but the 2nd (with 2 operands) might.  */
-             if (oparg == insn->args)
-               {
-           jump_check_gpr:
-                 asargStart = asarg;
-                 if (reg_lookup (&asarg, RCLASS_GPR, NULL)
-                     && (*asarg == ',' || (ISSPACE (*asarg) && asarg[1] == ',')))
-                   break;
-                 asarg = asargStart;
-               }
            jump:
              my_getExpression (imm_expr, asarg);
              asarg = expr_parse_end;
@@ -3314,7 +3382,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
              switch (*++oparg)
                {
                case '4':
-                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 128
@@ -3331,7 +3399,7 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                  continue;
 
                case '2':
-                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg, p)
+                 if (my_getOpcodeExpression (imm_expr, imm_reloc, asarg)
                      || imm_expr->X_op != O_constant
                      || imm_expr->X_add_number < 0
                      || imm_expr->X_add_number >= 3)
@@ -3461,6 +3529,35 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
                      goto unknown_riscv_ip_operand;
                    }
                  break;
+               case 'f':
+                 switch (*++oparg)
+                   {
+                   case 'v':
+                     /* FLI.[HSDQ] value field for 'Zfa' extension.  */
+                     if (!arg_lookup (&asarg, riscv_fli_symval,
+                                      ARRAY_SIZE (riscv_fli_symval), &regno))
+                       {
+                         /* 0.0 is not a valid entry in riscv_fli_numval.  */
+                         errno = 0;
+                         float f = strtof (asarg, &asarg);
+                         if (errno != 0 || f == 0.0
+                             || !flt_lookup (f, riscv_fli_numval,
+                                            ARRAY_SIZE(riscv_fli_numval),
+                                            &regno))
+                           {
+                             as_bad (_("bad fli constant operand, "
+                                       "supported constants must be in "
+                                       "decimal or hexadecimal floating-point "
+                                       "literal form"));
+                             break;
+                           }
+                       }
+                     INSERT_OPERAND (RS1, *ip, regno);
+                     continue;
+                   default:
+                     goto unknown_riscv_ip_operand;
+                   }
+                 break;
                default:
                  goto unknown_riscv_ip_operand;
                }
@@ -3534,6 +3631,8 @@ riscv_ip (char *str, struct riscv_cl_insn *ip, expressionS *imm_expr,
   if (save_c)
     *(asargStart  - 1) = save_c;
 
+  probing_insn_operands = false;
+
   return error;
 }
 
@@ -3659,7 +3758,7 @@ md_assemble (char *str)
 const char *
 md_atof (int type, char *litP, int *sizeP)
 {
-  return ieee_md_atof (type, litP, sizeP, TARGET_BYTES_BIG_ENDIAN);
+  return ieee_md_atof (type, litP, sizeP, target_big_endian);
 }
 
 void
@@ -3830,6 +3929,53 @@ riscv_after_parse_args (void)
     flag_dwarf_cie_version = 3;
 }
 
+bool riscv_parse_name (const char *name, struct expressionS *ep,
+                      enum expr_mode mode)
+{
+  unsigned int regno;
+  symbolS *sym;
+
+  if (!probing_insn_operands)
+    return false;
+
+  gas_assert (mode == expr_normal);
+
+  regno = reg_lookup_internal (name, RCLASS_GPR);
+  if (regno == (unsigned int)-1)
+    return false;
+
+  if (symbol_find (name) != NULL)
+    return false;
+
+  /* Create a symbol without adding it to the symbol table yet.
+     Insertion will happen only once we commit to using the insn
+     we're probing operands for.  */
+  for (sym = deferred_sym_rootP; sym; sym = symbol_next (sym))
+    if (strcmp (name, S_GET_NAME (sym)) == 0)
+      break;
+  if (!sym)
+    {
+      for (sym = orphan_sym_rootP; sym; sym = symbol_next (sym))
+       if (strcmp (name, S_GET_NAME (sym)) == 0)
+         {
+           symbol_remove (sym, &orphan_sym_rootP, &orphan_sym_lastP);
+           break;
+         }
+      if (!sym)
+       sym = symbol_create (name, undefined_section,
+                            &zero_address_frag, 0);
+
+      symbol_append (sym, deferred_sym_lastP, &deferred_sym_rootP,
+                    &deferred_sym_lastP);
+    }
+
+  ep->X_op = O_symbol;
+  ep->X_add_symbol = sym;
+  ep->X_add_number = 0;
+
+  return true;
+}
+
 long
 md_pcrel_from (fixS *fixP)
 {
@@ -3873,6 +4019,9 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_RISCV_SUB32:
     case BFD_RELOC_RISCV_SUB64:
     case BFD_RELOC_RISCV_RELAX:
+    /* cvt_frag_to_fill () has called output_leb128 ().  */
+    case BFD_RELOC_RISCV_SET_ULEB128:
+    case BFD_RELOC_RISCV_SUB_ULEB128:
       break;
 
     case BFD_RELOC_RISCV_TPREL_HI20:
@@ -4637,8 +4786,11 @@ s_riscv_leb128 (int sign)
   char *save_in = input_line_pointer;
 
   expression (&exp);
-  if (exp.X_op != O_constant)
-    as_bad (_("non-constant .%cleb128 is not supported"), sign ? 's' : 'u');
+  if (sign && exp.X_op != O_constant)
+    as_bad (_("non-constant .sleb128 is not supported"));
+  else if (!sign && exp.X_op != O_constant && exp.X_op != O_subtract)
+    as_bad (_(".uleb128 only supports constant or subtract expressions"));
+
   demand_empty_rest_of_line ();
 
   input_line_pointer = save_in;
@@ -4758,12 +4910,58 @@ riscv_set_public_attributes (void)
     riscv_write_out_attrs ();
 }
 
+/* Scan uleb128 subtraction expressions and insert fixups for them.
+   e.g., .uleb128 .L1 - .L0
+   Because relaxation may change the value of the subtraction, we
+   must resolve them at link-time.  */
+
+static void
+riscv_insert_uleb128_fixes (bfd *abfd ATTRIBUTE_UNUSED,
+                           asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  struct frag *fragP;
+
+  subseg_set (sec, 0);
+
+  for (fragP = seginfo->frchainP->frch_root;
+       fragP; fragP = fragP->fr_next)
+    {
+      expressionS *exp, *exp_dup;
+
+      if (fragP->fr_type != rs_leb128  || fragP->fr_symbol == NULL)
+       continue;
+
+      exp = symbol_get_value_expression (fragP->fr_symbol);
+
+      if (exp->X_op != O_subtract)
+       continue;
+
+      /* Only unsigned leb128 can be handled.  */
+      gas_assert (fragP->fr_subtype == 0);
+      exp_dup = xmemdup (exp, sizeof (*exp), sizeof (*exp));
+      exp_dup->X_op = O_symbol;
+      exp_dup->X_op_symbol = NULL;
+
+      /* Insert relocations to resolve the subtraction at link-time.
+        Emit the SET relocation first in riscv.  */
+      exp_dup->X_add_symbol = exp->X_add_symbol;
+      fix_new_exp (fragP, fragP->fr_fix, 0,
+                  exp_dup, 0, BFD_RELOC_RISCV_SET_ULEB128);
+      exp_dup->X_add_symbol = exp->X_op_symbol;
+      fix_new_exp (fragP, fragP->fr_fix, 0,
+                  exp_dup, 0, BFD_RELOC_RISCV_SUB_ULEB128);
+    }
+}
+
 /* Called after all assembly has been done.  */
 
 void
 riscv_md_finish (void)
 {
   riscv_set_public_attributes ();
+  if (riscv_opts.relax)
+    bfd_map_over_sections (stdoutput, riscv_insert_uleb128_fixes, NULL);
 }
 
 /* Adjust the symbol table.  */