x86: replace adhoc ambiguous operand checking for CRC32
[deliverable/binutils-gdb.git] / gas / config / tc-mmix.c
index 937b772acff16d1ebb7b7e141db131884a585a45..a7728fa7937b4e39690f584a989ce483ae1b240c 100644 (file)
@@ -1,11 +1,11 @@
 /* tc-mmix.c -- Assembler for Don Knuth's MMIX.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
+   Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
    compatible syntax, but the main purpose is to serve GCC.  */
 
 
-#include <stdio.h>
-#include <limits.h>
 #include "as.h"
+#include <limits.h>
 #include "subsegs.h"
-#include "bfd.h"
 #include "elf/mmix.h"
 #include "opcode/mmix.h"
 #include "safe-ctype.h"
    for example assert something of what it became or make a relocation.  */
 
 enum mmix_fixup_action
- {
-   mmix_fixup_byte,
-   mmix_fixup_register,
-   mmix_fixup_register_or_adjust_for_byte
- };
+{
+  mmix_fixup_byte,
+  mmix_fixup_register,
+  mmix_fixup_register_or_adjust_for_byte
+};
 
 static int get_spec_regno (char *);
 static int get_operands (int, char *, expressionS *);
@@ -110,6 +108,14 @@ static struct
    expressionS exp;
  } mmix_raw_gregs[MAX_GREGS];
 
+static struct loc_assert_s
+ {
+   segT old_seg;
+   symbolS *loc_sym;
+   fragS *frag;
+   struct loc_assert_s *next;
+ } *loc_asserts = NULL;
+
 /* Fixups for all unique GREG registers.  We store the fixups here in
    md_convert_frag, then we use the array to convert
    BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc.  The index is
@@ -180,7 +186,7 @@ int mmix_next_semicolon_is_eoln = 1;
 
 /* Do we have a BSPEC in progress?  */
 static int doing_bspec = 0;
-static char *bspec_file;
+static const char *bspec_file;
 static unsigned int bspec_line;
 
 struct option md_longopts[] =
@@ -389,9 +395,9 @@ const char line_comment_chars[] = "*#";
 
 const char line_separator_chars[] = ";";
 
-const char mmix_exp_chars[] = "eE";
+const char EXP_CHARS[] = "eE";
 
-const char mmix_flt_chars[] = "rf";
+const char FLT_CHARS[] = "rf";
 
 
 /* Fill in the offset-related part of GETA or Bcc.  */
@@ -581,8 +587,10 @@ get_putget_operands (struct mmix_opcode *insn, char *operands,
            p++;
          sregp = p;
          input_line_pointer = sregp;
-         c = get_symbol_end ();
+         c = get_symbol_name (&sregp);
          sregend = input_line_pointer;
+         if (c == '"')
+           ++ input_line_pointer;
        }
     }
   else
@@ -590,10 +598,10 @@ get_putget_operands (struct mmix_opcode *insn, char *operands,
       expp_sreg = &exp[0];
       expp_reg = &exp[1];
 
-      sregp = p;
-      c = get_symbol_end ();
-      sregend = p = input_line_pointer;
-      *p = c;
+      c = get_symbol_name (&sregp);
+      sregend = input_line_pointer;
+      restore_line_pointer (c);
+      p = input_line_pointer;
 
       /* Skip whitespace */
       while (*p == ' ' || *p == '\t')
@@ -632,7 +640,7 @@ get_putget_operands (struct mmix_opcode *insn, char *operands,
 /* Handle MMIX-specific option.  */
 
 int
-md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
@@ -1366,6 +1374,9 @@ md_assemble (char *str)
             pass expressions as symbols and use fix_new, not fix_new_exp.  */
          sym = make_expr_symbol (exp + 1);
 
+         /* Mark the symbol as being OK for a reloc.  */
+         symbol_get_bfdsym (sym)->flags |= BSF_KEEP;
+
          /* Now we know it can be a "base address plus offset".  Add
             proper fixup types so we can handle this later, when we've
             parsed everything.  */
@@ -1466,8 +1477,8 @@ md_assemble (char *str)
                && ((exp[1].X_op == O_register
                     && exp[1].X_add_number < 512)
                    || (exp[1].X_op == O_constant
-                       && exp[1].X_add_number < 0
-                       && exp[1].X_add_number > 4)
+                       && (exp[1].X_add_number < 0
+                           || exp[1].X_add_number > 4))
                    || (exp[1].X_op != O_register
                        && exp[1].X_op != O_constant))))
          {
@@ -1671,7 +1682,10 @@ md_assemble (char *str)
       break;
 
     case mmix_operands_xyz_opt:
-      /* SWYM, TRIP, TRAP: zero, one, two or three operands.  */
+      /* SWYM, TRIP, TRAP: zero, one, two or three operands.  It's
+        unspecified whether operands are registers or constants, but
+        when we find register syntax, we require operands to be literal and
+        within 0..255.  */
       if (n_operands == 0 && ! mmix_gnu_syntax)
        /* Zeros are in place - nothing needs to be done for zero
           operands.  We don't allow this in GNU syntax mode, because it
@@ -1682,7 +1696,7 @@ md_assemble (char *str)
        {
          if (exp[0].X_op == O_constant)
            {
-             if (exp[0].X_add_number > 255*255*255
+             if (exp[0].X_add_number > 255*256*256
                  || exp[0].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
@@ -1724,7 +1738,7 @@ md_assemble (char *str)
 
          if (exp[1].X_op == O_constant)
            {
-             if (exp[1].X_add_number > 255*255
+             if (exp[1].X_add_number > 255*256
                  || exp[1].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
@@ -1796,12 +1810,15 @@ md_assemble (char *str)
            fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
                         1, exp + 2, 0, BFD_RELOC_8);
        }
-      else if (n_operands <= 3
-              && (strcmp (instruction->name, "trip") == 0
-                  || strcmp (instruction->name, "trap") == 0))
+      else
        {
-         /* The meaning of operands to TRIP and TRAP are not defined, so
-            we add combinations not handled above here as we find them.  */
+         /* We can't get here for other cases.  */
+         gas_assert (n_operands <= 3);
+
+         /* The meaning of operands to TRIP and TRAP is not defined (and
+            SWYM operands aren't enforced in mmixal, so let's avoid
+            that).  We add combinations not handled above here as we find
+            them and as they're reported.  */
          if (n_operands == 3)
            {
              /* Don't require non-register operands.  Always generate
@@ -1809,49 +1826,48 @@ md_assemble (char *str)
                 maintenance problems.  TRIP is supposed to be a rare
                 instruction, so the overhead should not matter.  We
                 aren't allowed to fix_new_exp for an expression which is
-                an  O_register at this point, however.  */
+                an O_register at this point, however.
+
+                Don't use BFD_RELOC_MMIX_REG_OR_BYTE as that modifies
+                the insn for a register in the Z field and we want
+                consistency.  */
              if (exp[0].X_op == O_register)
                opcodep[1] = exp[0].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
-                            1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp, 0, BFD_RELOC_8);
              if (exp[1].X_op == O_register)
                opcodep[2] = exp[1].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
-                            1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp + 1, 0, BFD_RELOC_8);
              if (exp[2].X_op == O_register)
                opcodep[3] = exp[2].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
-                            1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp + 2, 0, BFD_RELOC_8);
            }
          else if (n_operands == 2)
            {
              if (exp[0].X_op == O_register)
-               opcodep[2] = exp[0].X_add_number;
+               opcodep[1] = exp[0].X_add_number;
              else
-               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
-                            1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+                            1, exp, 0, BFD_RELOC_8);
              if (exp[1].X_op == O_register)
                opcodep[3] = exp[1].X_add_number;
              else
-               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
-                            1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+                            2, exp + 1, 0, BFD_RELOC_16);
            }
          else
            {
-             as_bad (_("unsupported operands to %s: `%s'"),
-                     instruction->name, operands);
-             return;
+             /* We can't get here for other cases.  */
+             gas_assert (n_operands == 1 && exp[0].X_op == O_register);
+
+             opcodep[3] = exp[0].X_add_number;
            }
        }
-      else
-       {
-         as_bad (_("invalid operands to opcode %s: `%s'"),
-                 instruction->name, operands);
-         return;
-       }
       break;
 
     case mmix_operands_resume:
@@ -1925,11 +1941,9 @@ s_prefix (int unused ATTRIBUTE_UNUSED)
 
   SKIP_WHITESPACE ();
 
-  p = input_line_pointer;
-
-  c = get_symbol_end ();
-
-  /* Reseting prefix?  */
+  c = get_symbol_name (&p);
+  
+  /* Resetting prefix?  */
   if (*p == ':' && p[1] == 0)
     mmix_current_prefix = NULL;
   else
@@ -1947,7 +1961,7 @@ s_prefix (int unused ATTRIBUTE_UNUSED)
       mmix_current_prefix = p;
     }
 
-  *input_line_pointer = c;
+  (void) restore_line_pointer (c);
 
   mmix_handle_rest_of_empty_line ();
 }
@@ -1987,10 +2001,11 @@ static void
 mmix_greg_internal (char *label)
 {
   expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
+  segT section;
 
   /* Don't set the section to register contents section before the
      expression has been parsed; it may refer to the current position.  */
-  expression (expP);
+  section = expression (expP);
 
   /* FIXME: Check that no expression refers to the register contents
      section.  May need to be done in elf64-mmix.c.  */
@@ -2004,6 +2019,24 @@ mmix_greg_internal (char *label)
       expP->X_op_symbol = NULL;
     }
 
+  if (section == undefined_section)
+    {
+      /* This is an error or a LOC with an expression involving
+        forward references.  For the expression to be correctly
+        evaluated, we need to force a proper symbol; gas loses track
+        of the segment for "local symbols".  */
+      if (expP->X_op == O_add)
+       {
+         symbol_get_value_expression (expP->X_op_symbol);
+         symbol_get_value_expression (expP->X_add_symbol);
+       }
+      else
+       {
+         gas_assert (expP->X_op == O_symbol);
+         symbol_get_value_expression (expP->X_add_symbol);
+       }
+    }
+
   /* We must handle prefixes here, as we save the labels and expressions
      to be output later.  */
   mmix_raw_gregs[n_of_raw_gregs].label
@@ -2024,13 +2057,15 @@ s_greg (int unused ATTRIBUTE_UNUSED)
 {
   char *p;
   char c;
-  p = input_line_pointer;
 
   /* This will skip over what can be a symbol and zero out the next
      character, which we assume is a ',' or other meaningful delimiter.
      What comes after that is the initializer expression for the
      register.  */
-  c = get_symbol_end ();
+  c = get_symbol_name (&p);
+
+  if (c == '"')
+    c = * ++ input_line_pointer;
 
   if (! is_end_of_line[(unsigned char) c])
     input_line_pointer++;
@@ -2095,9 +2130,8 @@ s_bspec (int unused ATTRIBUTE_UNUSED)
       if (sec == NULL)
        as_fatal (_("can't create section %s"), newsecname);
 
-      if (!bfd_set_section_flags (stdoutput, sec,
-                                 bfd_get_section_flags (stdoutput, sec)
-                                 | SEC_READONLY))
+      if (!bfd_set_section_flags (sec,
+                                 bfd_section_flags (sec) | SEC_READONLY))
        as_fatal (_("can't set section flags for section %s"), newsecname);
     }
 
@@ -2106,7 +2140,7 @@ s_bspec (int unused ATTRIBUTE_UNUSED)
   subseg_set (sec, 0);
 
   /* Save position for missing ESPEC.  */
-  as_where (&bspec_file, &bspec_line);
+  bspec_file = as_where (&bspec_line);
 
   doing_bspec = 1;
 }
@@ -2237,45 +2271,17 @@ md_estimate_size_before_relax (fragS *fragP, segT segment)
    emitted is stored in *sizeP .  An error message is returned, or NULL on
    OK.  */
 
-char *
+const char *
 md_atof (int type, char *litP, int *sizeP)
 {
-  int prec;
-  LITTLENUM_TYPE words[4];
-  char *t;
-  int i;
-
-  switch (type)
-    {
-      /* FIXME: Having 'f' in mmix_flt_chars (and here) makes it
-        problematic to also have a forward reference in an expression.
-        The testsuite wants it, and it's customary.
-        We'll deal with the real problems when they come; we share the
-        problem with most other ports.  */
-    case 'f':
-    case 'r':
-      prec = 2;
-      break;
-    case 'd':
-      prec = 4;
-      break;
-    default:
-      *sizeP = 0;
-      return _("bad call to md_atof");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-
-  *sizeP = prec * 2;
-
-  for (i = 0; i < prec; i++)
-    {
-      md_number_to_chars (litP, (valueT) words[i], 2);
-      litP += 2;
-    }
-  return NULL;
+  if (type == 'r')
+    type = 'f';
+  /* FIXME: Having 'f' in FLT_CHARS (and here) makes it
+     problematic to also have a forward reference in an expression.
+     The testsuite wants it, and it's customary.
+     We'll deal with the real problems when they come; we share the
+     problem with most other ports.  */
+  return ieee_md_atof (type, litP, sizeP, TRUE);
 }
 
 /* Convert variable-sized frags into one or more fixups.  */
@@ -2365,7 +2371,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
       if (fragP->tc_frag_data == NULL)
        {
          /* We must initialize data that's supposed to be "fixed up" to
-            avoid emitting garbage, because md_apply_fix3 won't do
+            avoid emitting garbage, because md_apply_fix won't do
             anything for undefined symbols.  */
          md_number_to_chars (var_partp, 0, 8);
          tmpfixP
@@ -2413,7 +2419,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
    Note that this function isn't called when linkrelax != 0.  */
 
 void
-md_apply_fix3 (fixS *fixP, valueT *valP, segT segment)
+md_apply_fix (fixS *fixP, valueT *valP, segT segment)
 {
   char *buf  = fixP->fx_where + fixP->fx_frag->fr_literal;
   /* Note: use offsetT because it is signed, valueT is unsigned.  */
@@ -2631,7 +2637,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
      than just helping the user around this limitation here; hopefully the
      code using the local expression is around.  Putting the LOCAL
      semantics in a relocation still seems right; a section didn't do.  */
-  if (bfd_section_size (section->owner, section) == 0)
+  if (bfd_section_size (section) == 0)
     as_bad_where
       (fixP->fx_file, fixP->fx_line,
        fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
@@ -2652,7 +2658,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
 
       if (addsy == NULL || bfd_is_abs_section (addsec))
        {
-         /* Resolve this reloc now, as md_apply_fix3 would have done (not
+         /* Resolve this reloc now, as md_apply_fix would have done (not
             called if -linkrelax).  There is no point in keeping a reloc
             to an absolute symbol.  No reloc that is subject to
             relaxation must be to an absolute symbol; difference
@@ -2701,7 +2707,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
         resolve the relocation here.  */
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
-             || strcmp (bfd_get_section_name (addsec->owner, addsec),
+             || strcmp (bfd_section_name (addsec),
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
@@ -2728,7 +2734,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
 
     case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
       if (addsy != NULL
-         && strcmp (bfd_get_section_name (addsec->owner, addsec),
+         && strcmp (bfd_section_name (addsec),
                     MMIX_REG_CONTENTS_SECTION_NAME) == 0)
        {
          /* This changed into a register; the relocation is for the
@@ -2831,7 +2837,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
     case BFD_RELOC_MMIX_REG:
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
-             || strcmp (bfd_get_section_name (addsec->owner, addsec),
+             || strcmp (bfd_section_name (addsec),
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
@@ -2852,9 +2858,9 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
        }
       /* FALLTHROUGH.  */
 
-      /* The others are supposed to be handled by md_apply_fix3.
+      /* The others are supposed to be handled by md_apply_fix.
         FIXME: ... which isn't called when -linkrelax.  Move over
-        md_apply_fix3 code here for everything reasonable.  */
+        md_apply_fix code here for everything reasonable.  */
     badop:
     default:
       as_bad_where
@@ -2867,9 +2873,9 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
       return NULL;
     }
 
-  relP = (arelent *) xmalloc (sizeof (arelent));
-  assert (relP != 0);
-  relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  relP = XNEW (arelent);
+  gas_assert (relP != 0);
+  relP->sym_ptr_ptr = XNEW (asymbol *);
   *relP->sym_ptr_ptr = baddsy;
   relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
 
@@ -2900,8 +2906,8 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
 void
 mmix_handle_mmixal (void)
 {
-  char *s0 = input_line_pointer;
-  char *s;
+  char *insn;
+  char *s = input_line_pointer;
   char *label = NULL;
   char c;
 
@@ -2911,44 +2917,20 @@ mmix_handle_mmixal (void)
   if (mmix_gnu_syntax)
     return;
 
-  /* If the first character is a '.', then it's a pseudodirective, not a
-     label.  Make GAS not handle label-without-colon on this line.  We
-     also don't do mmixal-specific stuff on this line.  */
-  if (input_line_pointer[0] == '.')
-    {
-      label_without_colon_this_line = 0;
-      return;
-    }
-
-  /* Don't handle empty lines here.  */
-  while (1)
-    {
-      if (*s0 == 0 || is_end_of_line[(unsigned int) *s0])
-       return;
-
-      if (! ISSPACE (*s0))
-       break;
-
-      s0++;
-    }
-
   /* If we're on a line with a label, check if it's a mmixal fb-label.
      Save an indicator and skip the label; it must be set only after all
      fb-labels of expressions are evaluated.  */
-  if (ISDIGIT (input_line_pointer[0])
-      && input_line_pointer[1] == 'H'
-      && ISSPACE (input_line_pointer[2]))
+  if (ISDIGIT (s[0]) && s[1] == 'H' && ISSPACE (s[2]))
     {
-      char *s;
-      current_fb_label = input_line_pointer[0] - '0';
+      current_fb_label = s[0] - '0';
 
       /* We have to skip the label, but also preserve the newlineness of
         the previous character, since the caller checks that.  It's a
         mess we blame on the caller.  */
-      input_line_pointer[1] = input_line_pointer[-1];
-      input_line_pointer += 2;
+      s[1] = s[-1];
+      s += 2;
+      input_line_pointer = s;
 
-      s = input_line_pointer;
       while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s])
        s++;
 
@@ -2956,48 +2938,75 @@ mmix_handle_mmixal (void)
         caller is about to bump the counters.  Adjust the error messages.  */
       if (is_end_of_line[(unsigned int) *s])
        {
-         char *name;
          unsigned int line;
-         as_where (&name, &line);
+         const char * name = as_where (&line);
          as_bad_where (name, line + 1,
                        _("[0-9]H labels may not appear alone on a line"));
          current_fb_label = -1;
        }
       if (*s == '.')
        {
-         char *name;
          unsigned int line;
-         as_where (&name, &line);
+         const char * name  = as_where (&line);
          as_bad_where (name, line + 1,
                        _("[0-9]H labels do not mix with dot-pseudos"));
          current_fb_label = -1;
        }
+
+      /* Back off to the last space before the opcode so we don't handle
+        the opcode as a label.  */
+      s--;
     }
   else
+    current_fb_label = -1;
+
+  if (*s == '.')
     {
-      current_fb_label = -1;
-      if (is_name_beginner (input_line_pointer[0]))
-       label = input_line_pointer;
+      /* If the first character is a '.', then it's a pseudodirective, not a
+        label.  Make GAS not handle label-without-colon on this line.  We
+        also don't do mmixal-specific stuff on this line.  */
+      label_without_colon_this_line = 0;
+      return;
     }
 
-  s0 = input_line_pointer;
-  /* Skip over label.  */
-  while (*s0 && is_part_of_name (*s0))
-    s0++;
-
-  /* Remove trailing ":" off labels, as they'd otherwise be considered
-     part of the name.  But don't do it for local labels.  */
-  if (s0 != input_line_pointer && s0[-1] == ':'
-      && (s0 - 2 != input_line_pointer
-         || ! ISDIGIT (s0[-2])))
-    s0[-1] = ' ';
-  else if (label != NULL)
+  if (*s == 0 || is_end_of_line[(unsigned int) *s])
+    /* We avoid handling empty lines here.  */
+    return;
+
+  if (is_name_beginner (*s))
+    label = s;
+
+  /* If there is a label, skip over it.  */
+  while (*s && is_part_of_name (*s))
+    s++;
+
+  /* Find the start of the instruction or pseudo following the label,
+     if there is one.  */
+  for (insn = s;
+       *insn && ISSPACE (*insn) && ! is_end_of_line[(unsigned int) *insn];
+       insn++)
+    /* Empty */
+    ;
+
+  /* Remove a trailing ":" off labels, as they'd otherwise be considered
+     part of the name.  But don't do this for local labels.  */
+  if (s != input_line_pointer && s[-1] == ':'
+      && (s - 2 != input_line_pointer
+         || ! ISDIGIT (s[-2])))
+    s[-1] = ' ';
+  else if (label != NULL
+          /* For a lone label on a line, we don't attach it to the next
+             instruction or MMIXAL-pseudo (getting its alignment).  Thus
+             is acts like a "normal" :-ended label.  Ditto if it's
+             followed by a non-MMIXAL pseudo.  */
+          && !is_end_of_line[(unsigned int) *insn]
+          && *insn != '.')
     {
       /* For labels that don't end in ":", we save it so we can later give
         it the same alignment and address as the associated instruction.  */
 
       /* Make room for the label including the ending nul.  */
-      int len_0 = s0 - label + 1;
+      size_t len_0 = s - label + 1;
 
       /* Save this label on the MMIX symbol obstack.  Saving it on an
         obstack is needless for "IS"-pseudos, but it's harmless and we
@@ -3007,14 +3016,10 @@ mmix_handle_mmixal (void)
       pending_label[len_0 - 1] = 0;
     }
 
-  while (*s0 && ISSPACE (*s0) && ! is_end_of_line[(unsigned int) *s0])
-    s0++;
-
-  if (pending_label != NULL && is_end_of_line[(unsigned int) *s0])
-    /* Whoops, this was actually a lone label on a line.  Like :-ended
-       labels, we don't attach such labels to the next instruction or
-       pseudo.  */
-    pending_label = NULL;
+  /* If we have a non-MMIXAL pseudo, we have not business with the rest of
+     the line.  */
+  if (*insn == '.')
+    return;
 
   /* Find local labels of operands.  Look for "[0-9][FB]" where the
      characters before and after are not part of words.  Break if a single
@@ -3026,7 +3031,6 @@ mmix_handle_mmixal (void)
 
   /* First make sure we don't have any of the magic characters on the line
      appearing as input.  */
-  s = s0;
   while (*s)
     {
       c = *s++;
@@ -3037,7 +3041,7 @@ mmix_handle_mmixal (void)
     }
 
   /* Scan again, this time looking for ';' after operands.  */
-  s = s0;
+  s = insn;
 
   /* Skip the insn.  */
   while (*s
@@ -3077,7 +3081,9 @@ mmix_handle_mmixal (void)
        {
          if ((s[1] != 'B' && s[1] != 'F')
              || is_part_of_name (s[-1])
-             || is_part_of_name (s[2]))
+             || is_part_of_name (s[2])
+             /* Don't treat e.g. #1F as a local-label reference.  */
+             || (s != input_line_pointer && s[-1] == '#'))
            s++;
          else
            {
@@ -3103,7 +3109,7 @@ mmix_handle_mmixal (void)
 
   /* Make IS into an EQU by replacing it with "= ".  Only match upper-case
      though; let lower-case be a syntax error.  */
-  s = s0;
+  s = insn;
   if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2]))
     {
       *s = '=';
@@ -3267,10 +3273,10 @@ mmix_force_relocation (fixS *fixP)
   if (linkrelax)
     return 1;
 
-  /* All our pcrel relocations are must-keep.  Note that md_apply_fix3 is
+  /* All our pcrel relocations are must-keep.  Note that md_apply_fix is
      called *after* this, and will handle getting rid of the presumed
      reloc; a relocation isn't *forced* other than to be handled by
-     md_apply_fix3 (or tc_gen_reloc if linkrelax).  */
+     md_apply_fix (or tc_gen_reloc if linkrelax).  */
   if (fixP->fx_pcrel)
     return 1;
 
@@ -3406,7 +3412,7 @@ mmix_md_relax_frag (segT seg, fragS *fragP, long stretch)
        fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
       }
       /* FALLTHROUGH.  */
-    
+
       /* See if this PUSHJ is redirectable to a stub.  */
     case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
       {
@@ -3475,6 +3481,8 @@ mmix_md_end (void)
 {
   fragS *fragP;
   symbolS *mainsym;
+  asection *regsec;
+  struct loc_assert_s *loc_assert;
   int i;
 
   /* The first frag of GREG:s going into the register contents section.  */
@@ -3532,6 +3540,38 @@ mmix_md_end (void)
       S_SET_EXTERNAL (mainsym);
     }
 
+  /* Check that we didn't LOC into the unknown, or rather that when it
+     was unknown, we actually change sections.  */
+  for (loc_assert = loc_asserts;
+       loc_assert != NULL;
+       loc_assert = loc_assert->next)
+    {
+      segT actual_seg;
+
+      resolve_symbol_value (loc_assert->loc_sym);
+      actual_seg = S_GET_SEGMENT (loc_assert->loc_sym);
+      if (actual_seg != loc_assert->old_seg)
+       {
+         const char *fnam;
+         unsigned int line;
+         int e_valid = expr_symbol_where (loc_assert->loc_sym, &fnam, &line);
+
+         gas_assert (e_valid == 1);
+         as_bad_where (fnam, line,
+                       _("LOC to section unknown or indeterminable "
+                         "at first pass"));
+
+         /* Patch up the generic location data to avoid cascading
+            error messages from later passes.  (See original in
+            write.c:relax_segment.)  */
+         fragP = loc_assert->frag;
+         fragP->fr_type = rs_align;
+         fragP->fr_subtype = 0;
+         fragP->fr_offset = 0;
+         fragP->fr_fix = 0;
+       }
+    }
+
   if (n_of_raw_gregs != 0)
     {
       /* Emit GREGs.  They are collected in order of appearance, but must
@@ -3539,9 +3579,9 @@ mmix_md_end (void)
         and the same allocation order (within a file) as mmixal.  */
       segT this_segment = now_seg;
       subsegT this_subsegment = now_subseg;
-      asection *regsec
-       = bfd_make_section_old_way (stdoutput,
-                                   MMIX_REG_CONTENTS_SECTION_NAME);
+
+      regsec = bfd_make_section_old_way (stdoutput,
+                                        MMIX_REG_CONTENTS_SECTION_NAME);
       subseg_set (regsec, 0);
 
       /* Finally emit the initialization-value.  Emit a variable frag, which
@@ -3568,6 +3608,11 @@ mmix_md_end (void)
       subseg_set (this_segment, this_subsegment);
     }
 
+  regsec = bfd_get_section_by_name (stdoutput, MMIX_REG_CONTENTS_SECTION_NAME);
+  /* Mark the section symbol as being OK for a reloc.  */
+  if (regsec != NULL)
+    regsec->symbol->flags |= BSF_KEEP;
+
   /* Iterate over frags resulting from GREGs and move those that evidently
      have the same value together and point one to another.
 
@@ -3729,7 +3774,7 @@ mmix_frob_file (void)
 
       if (gregs == NULL)
        {
-         gregs = xmalloc (sizeof (*gregs));
+         gregs = XNEW (struct mmix_symbol_gregs);
          gregs->n_gregs = 0;
          symbol_set_tc (sym, &gregs);
          all_greg_symbols[n_greg_symbols++] = gregs;
@@ -3748,7 +3793,7 @@ mmix_frob_file (void)
   if (real_reg_section != NULL)
     {
       /* FIXME: Pass error state gracefully.  */
-      if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS)
+      if (bfd_section_flags (real_reg_section) & SEC_HAS_CONTENTS)
        as_fatal (_("register section has contents\n"));
 
       bfd_section_list_remove (stdoutput, real_reg_section);
@@ -3767,7 +3812,7 @@ int
 mmix_parse_predefined_name (char *name, expressionS *expP)
 {
   char *canon_name;
-  char *handler_charp;
+  const char *handler_charp;
   const char handler_chars[] = "DVWIOUZX";
   symbolS *symp;
 
@@ -3878,7 +3923,7 @@ mmix_md_elf_section_change_hook (void)
   if (doing_bspec)
     as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
 
-  last_alignment = bfd_get_section_alignment (now_seg->owner, now_seg);
+  last_alignment = bfd_section_alignment (now_seg);
   want_unaligned = 0;
 }
 
@@ -3905,20 +3950,39 @@ s_loc (int ignore ATTRIBUTE_UNUSED)
 
   if (exp.X_op == O_illegal
       || exp.X_op == O_absent
-      || exp.X_op == O_big
-      || section == undefined_section)
+      || exp.X_op == O_big)
     {
       as_bad (_("invalid LOC expression"));
       return;
     }
 
+  if (section == undefined_section)
+    {
+      /* This is an error or a LOC with an expression involving
+        forward references.  For the expression to be correctly
+        evaluated, we need to force a proper symbol; gas loses track
+        of the segment for "local symbols".  */
+      if (exp.X_op == O_add)
+       {
+         symbol_get_value_expression (exp.X_op_symbol);
+         symbol_get_value_expression (exp.X_add_symbol);
+       }
+      else
+       {
+         gas_assert (exp.X_op == O_symbol);
+         symbol_get_value_expression (exp.X_add_symbol);
+       }
+    }
+
   if (section == absolute_section)
     {
       /* Translate a constant into a suitable section.  */
 
       if (exp.X_add_number < ((offsetT) 0x20 << 56))
        {
-         /* Lower than Data_Segment - assume it's .text.  */
+         /* Lower than Data_Segment or in the reserved area (the
+            segment number is >= 0x80, appearing negative) - assume
+            it's .text.  */
          section = text_section;
 
          /* Save the lowest seen location, so we can pass on this
@@ -3930,8 +3994,8 @@ s_loc (int ignore ATTRIBUTE_UNUSED)
             this one), we org at (this - lower).  There's an implicit
             "LOC 0" before any entered code.  FIXME: handled by spurious
             settings of text_has_contents.  */
-         if (exp.X_add_number < 0
-             || exp.X_add_number < (offsetT) lowest_text_loc)
+         if (lowest_text_loc != (bfd_vma) -1
+             && (bfd_vma) exp.X_add_number < lowest_text_loc)
            {
              as_bad (_("LOC expression stepping backwards is not supported"));
              exp.X_op = O_absent;
@@ -3954,7 +4018,8 @@ s_loc (int ignore ATTRIBUTE_UNUSED)
        }
       else
        {
-         /* Do the same for the .data section.  */
+         /* Do the same for the .data section, except we don't have
+            to worry about exp.X_add_number carrying a sign.  */
          section = data_section;
 
          if (exp.X_add_number < (offsetT) lowest_data_loc)
@@ -3980,7 +4045,9 @@ s_loc (int ignore ATTRIBUTE_UNUSED)
        }
     }
 
-  if (section != now_seg)
+  /* If we can't deduce the section, it must be the current one.
+     Below, we arrange to assert this.  */
+  if (section != now_seg && section != undefined_section)
     {
       obj_elf_section_change_hook ();
       subseg_set (section, 0);
@@ -3991,16 +4058,41 @@ s_loc (int ignore ATTRIBUTE_UNUSED)
 
   if (exp.X_op != O_absent)
     {
+      symbolS *esym = NULL;
+
       if (exp.X_op != O_constant && exp.X_op != O_symbol)
        {
          /* Handle complex expressions.  */
-         sym = make_expr_symbol (&exp);
+         esym = sym = make_expr_symbol (&exp);
          off = 0;
        }
       else
        {
          sym = exp.X_add_symbol;
          off = exp.X_add_number;
+
+         if (section == undefined_section)
+           {
+             /* We need an expr_symbol when tracking sections.  In
+                order to make this an expr_symbol with file and line
+                tracked, we have to make the exp non-trivial; not an
+                O_symbol with .X_add_number == 0.  The constant part
+                is unused.  */
+             exp.X_add_number = 1;
+             esym = make_expr_symbol (&exp);
+           }
+       }
+
+      /* Track the LOC's where we couldn't deduce the section: assert
+        that we weren't supposed to change section.  */
+      if (section == undefined_section)
+       {
+         struct loc_assert_s *next = loc_asserts;
+         loc_asserts = XNEW (struct loc_assert_s);
+         loc_asserts->next = next;
+         loc_asserts->old_seg = now_seg;
+         loc_asserts->loc_sym = esym;
+         loc_asserts->frag = frag_now;
        }
 
       p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
@@ -4018,7 +4110,6 @@ static void
 mmix_byte (void)
 {
   unsigned int c;
-  char *start;
 
   if (now_seg == text_section)
     text_has_contents = 1;
@@ -4032,7 +4123,6 @@ mmix_byte (void)
        {
        case '\"':
          ++input_line_pointer;
-         start = input_line_pointer;
          while (is_a_char (c = next_char_of_string ()))
            {
              FRAG_APPEND_1_CHAR (c);
@@ -4108,7 +4198,6 @@ static void
 mmix_cons (int nbytes)
 {
   expressionS exp;
-  char *start;
 
   /* If we don't have any contents, then it's ok to have a specified start
      address that is not a multiple of the max data size.  We will then
@@ -4189,7 +4278,6 @@ mmix_cons (int nbytes)
               bytes.  */
          case '\"':
            ++input_line_pointer;
-           start = input_line_pointer;
            while (is_a_char (c = next_char_of_string ()))
              {
                exp.X_op = O_constant;
This page took 0.039194 seconds and 4 git commands to generate.