* config/tc-arc.c (md_pseudo_table): Add .cpu.
authorDavid Edelsohn <dje.gcc@gmail.com>
Fri, 10 Feb 1995 02:01:01 +0000 (02:01 +0000)
committerDavid Edelsohn <dje.gcc@gmail.com>
Fri, 10 Feb 1995 02:01:01 +0000 (02:01 +0000)
(comment_chars): Add ';'.
(arc_mach_type, mach_type_specified, cpu_tables_init_p): New globals.
(md_parse_option): Delete support for -mmult.  Add -mcpu=xxx.
(md_begin): Current ARCs are little endian.
Call bfd_set_arch_mach to set the cpu type.
(init_opcode_tables): New function.
(md_begin): Ignore suffixes and registers not supported by cpu.
(md_assemble): Initialize opcode tables here.
Ignore opcodes not supported by selected cpu.
Always ask for more memory in one piece.
(arc_cpu): New function.
(md_numbers_to_chars): Support both endians (will probably be needed
eventually anyway).
(md_apply_fix): Likewise.

gas/config/tc-arc.c

index b229398a5a893283ef7dec26adc686ed845e5696..aa455920526e05ba9f8311ab97b82b52b9a7db77 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-arc.c -- Assembler for the ARC
-   Copyright (C) 1994 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
    Contributed by Doug Evans (dje@cygnus.com).
 
    This file is part of GAS, the GNU Assembler.
 #include "as.h"
 #include "subsegs.h"
 #include "opcode/arc.h"
+#include "elf/arc.h"
 
 extern int target_big_endian;
+extern int arc_get_mach PARAMS ((char *));
 
 static arc_insn arc_insert_operand PARAMS ((arc_insn insn,
                                            const struct arc_operand *operand,
@@ -33,31 +35,22 @@ static arc_insn arc_insert_operand PARAMS ((arc_insn insn,
                                            offsetT val,
                                            char *file, unsigned int line));
 
-static void s_data1 PARAMS ((void));
-static void s_seg PARAMS ((int));
-static void s_proc PARAMS ((int));
-static void s_reserve PARAMS ((int));
-static void s_common PARAMS ((int));
+static void arc_common PARAMS ((int));
+static void arc_cpu PARAMS ((int));
+/*static void arc_rename PARAMS ((int));*/
+
+static int find_mach PARAMS ((char *));
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */
-  {"common", s_common, 0},
-  {"global", s_globl, 0},
-  {"half", cons, 2},
-  {"optim", s_ignore, 0},
-  {"proc", s_proc, 0},
-  {"reserve", s_reserve, 0},
-  {"seg", s_seg, 0},
-  {"skip", s_space, 0},
-  {"word", cons, 4},
-  {"xword", cons, 8},
-  {"pushsection", obj_elf_section, 0},
-  {"popsection", obj_elf_previous, 0},
-  {"uahalf", cons, 2},
-  {"uaword", cons, 4},
-  {"uaxword", cons, 8},
-  {NULL, 0, 0},
+  { "align", s_align_bytes, 0 },       /* Defaulting is invalid (0) */
+  { "common", arc_common, 0 },
+/*{ "hword", cons, 2 }, - already exists */
+  { "word", cons, 4 },
+  { "xword", cons, 8 },
+  { "cpu", arc_cpu, 0 },
+/*{ "rename", arc_rename, 0 },*/
+  { NULL, 0, 0 },
 };
 
 const int md_short_jump_size = 4;
@@ -66,7 +59,7 @@ const int md_reloc_size = 12; /* Size of relocation record */
 
 /* This array holds the chars that always start a comment.  If the
    pre-processor is disabled, these aren't very useful */
-const char comment_chars[] = "#";
+const char comment_chars[] = "#;";
 
 /* This array holds the chars that only start a comment at the beginning of
    a line.  If the line seems to have the form '# 123 filename'
@@ -88,6 +81,16 @@ const char EXP_CHARS[] = "eE";
 /* or    0d1.2345e12 */
 const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
+/* One of bfd_mach_arc_xxx.  */
+static int arc_mach_type = bfd_mach_arc_base;
+
+/* Non-zero if the cpu type was specified on the command line.  */
+static int mach_type_specified = 0;
+
+/* Non-zero if opcode tables have been initialized.
+   A .cpu command must appear before any instructions.  */
+static int cpu_tables_init_p = 0;
+
 static const char *arc_condition_codes[] =
 {
   "al", "eq", "ne", "p", "n", "c", "nc", "v",
@@ -99,13 +102,11 @@ static struct hash_control *arc_suffix_hash = NULL;
 static struct hash_control *arc_reg_hash = NULL;
 \f
 const char *md_shortopts = "m:";
-struct option md_longopts[] = {
-  {NULL, no_argument, NULL, 0}
+struct option md_longopts[] =
+{
+  { NULL, no_argument, NULL, 0 }
 };
-size_t md_longopts_size = sizeof(md_longopts);
-
-/* Non-zero if we accept the mul/mulu and variable shift insns.  */
-static int have_mult = 0;
+size_t md_longopts_size = sizeof (md_longopts);
 
 /*
  * md_parse_option
@@ -122,14 +123,19 @@ md_parse_option (c, arg)
   switch (c)
     {
     case 'm':
-      if (strcmp (arg, "mult") == 0)
-       have_mult = 1;
-      else
+      if (strncmp (arg, "cpu=", 4) == 0)
        {
-         as_bad ("invalid architecture -m%s", arg);
-         return 0;
+         int mach = arc_get_mach (arg + 4);
+
+         if (mach != -1)
+           {
+             arc_mach_type = mach;
+             mach_type_specified = 1;
+             break;
+           }
        }
-      break;
+      as_bad ("invalid architecture -m%s", arg);
+      return 0;
 
     default:
       return 0;
@@ -144,24 +150,55 @@ md_show_usage (stream)
 {
   fprintf (stream, "\
 ARC options:\n\
--mmult                 recognize the mul/mulu and variable shift instructions\n");
+-mcpu={base,host,graphics,audio}       select cpu type\n");
 }
 
 /* This function is called once, at assembler startup time.  It should
-   set up all the tables, etc. that the MD part of the assembler will need. */
+   set up all the tables, etc. that the MD part of the assembler will need.
+   Opcode selection is defered until later because we might see a .cpu
+   command.  */
+
 void
 md_begin ()
 {
-  register unsigned int i = 0;
-  char *last;
+  /* The endianness can be chosen "at the factory".  One day we may have
+     to be bi-endian.  */
+  target_big_endian = 0;
 
-  target_big_endian = 1;
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+    as_warn ("could not set architecture and machine");
+}
+
+/* Initialize the various opcode and operand tables.
+   MACH is one of bfd_mach_arc_xxx.  */
+
+static void
+init_opcode_tables (mach)
+     int mach;
+{
+  register unsigned int i;
+  char *last;
+  /* Indexed by bfd_mach_arc_xxx.  */
+  static int cpu_type_map[] =
+    {
+      ARC_CPU_BASE,
+      ARC_CPU_HOST,
+      ARC_CPU_GRAPHICS,
+      ARC_CPU_AUDIO,
+    };
 
   if ((arc_ops_hash = hash_new ()) == NULL
       || (arc_suffix_hash = hash_new ()) == NULL
       || (arc_reg_hash = hash_new ()) == NULL)
     as_fatal ("Virtual memory exhausted");
 
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+    as_warn ("could not set architecture and machine");
+
+  /* This initializes a few things in arc-opc.c that we need.
+     This must be called before the various arc_xxx_supported fns.  */
+  arc_opcode_init_tables (cpu_type_map[mach]);
+
 #if 0
   for (i = 0; i < arc_opcodes_count; i++)
     hash_insert (arc_ops_hash, arc_opcodes[i].name, (PTR) (arc_opcodes + i));
@@ -172,6 +209,8 @@ md_begin ()
   last = "";
   for (i = 0; i < arc_suffixes_count; i++)
     {
+      if (! arc_opval_supported (&arc_suffixes[i]))
+       continue;
       if (strcmp (arc_suffixes[i].name, last) != 0)
        hash_insert (arc_suffix_hash, arc_suffixes[i].name, (PTR) (arc_suffixes + i));
       last = arc_suffixes[i].name;
@@ -179,10 +218,14 @@ md_begin ()
 
   /* ??? This is the simple version.  See tc-arm.c for something snazzier.  */
   for (i = 0; i < arc_reg_names_count; i++)
-    hash_insert (arc_reg_hash, arc_reg_names[i].name, (PTR) (arc_reg_names + i));
+    {
+      if (! arc_opval_supported (&arc_reg_names[i]))
+       continue;
+      hash_insert (arc_reg_hash, arc_reg_names[i].name, (PTR) (arc_reg_names + i));
+    }
 
-  /* This initializes a few things in arc-opc.c that we need.  */
-  arc_opcode_init_tables (have_mult ? ARC_HAVE_MULT_SHIFT : 0);
+  /* Tell `s_cpu' it's too late.  */
+  cpu_tables_init_p = 1;
 }
 \f
 /* Insert an operand value into an instruction.
@@ -275,6 +318,15 @@ md_assemble (str)
   char *start;
   arc_insn insn;
   bfd_reloc_code_real_type reloc;
+  static int init_tables_p = 0;
+
+  /* Opcode table initialization is deferred until here because we have to
+     wait for a possible .cpu command.  */
+  if (!init_tables_p)
+    {
+      init_opcode_tables (arc_mach_type);
+      init_tables_p = 1;
+    }
 
   /* Skip leading white space.  */
   while (isspace (*str))
@@ -294,6 +346,7 @@ md_assemble (str)
 
   /* Keep looking until we find a match.  If we haven't found a match, and the
      first character no longer matches, we needn't look any further.  */
+
   start = str;
   for ( ; opcode < opcode_end && *opcode->syntax == *start; ++opcode)
     {
@@ -302,6 +355,10 @@ md_assemble (str)
       struct arc_fixup fixups[MAX_INSN_FIXUPS];
       int fc,limm_reloc_p;
 
+      /* Is this opcode supported by the selected cpu?  */
+      if (! arc_opcode_supported (opcode))
+       continue;
+
       /* Scan the syntax string.  If it doesn't match, try the next one.  */
 
       arc_opcode_init_insert ();
@@ -583,18 +640,26 @@ md_assemble (str)
          if (*str != '\0')
            as_bad ("junk at end of line: `%s'", str);
 
-         /* Write out the instruction.  */
-         f = frag_more (4);
-         md_number_to_chars (f, insn, 4);
-
+         /* Write out the instruction.
+            It is important to fetch enough space in one call to `frag_more'.
+            We use (f - frag_now->fr_literal) to compute where we are and we
+            don't want frag_now to change between calls.  */
          if (arc_opcode_limm_p (&limm))
            {
-             char *f2 = frag_more (4);
-             md_number_to_chars (f2, limm, 4);
+             f = frag_more (8);
+             md_number_to_chars (f, insn, 4);
+             md_number_to_chars (f + 4, limm, 4);
            }
          else if (limm_reloc_p)
-           /* Ahh! We need a limm reloc, but the tables think we don't.  */
-           abort ();
+           {
+             /* We need a limm reloc, but the tables think we don't.  */
+             abort ();
+           }
+         else
+           {
+             f = frag_more (4);
+             md_number_to_chars (f, insn, 4);
+           }
 
          /* Create any fixups.  At this point we do not use a
             bfd_reloc_code_real_type, but instead just use the operand index.
@@ -625,150 +690,8 @@ md_assemble (str)
   as_bad ("bad instruction `%s'", start);
 }
 \f
-/*
- * sort of like s_lcomm
- *
- */
-#ifndef OBJ_ELF
-static int max_alignment = 15;
-#endif
-
-static void
-s_reserve (ignore)
-     int ignore;
-{
-  char *name;
-  char *p;
-  char c;
-  int align;
-  int size;
-  int temp;
-  symbolS *symbolP;
-
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("Expected comma after name");
-      ignore_rest_of_line ();
-      return;
-    }
-
-  ++input_line_pointer;
-
-  if ((size = get_absolute_expression ()) < 0)
-    {
-      as_bad ("BSS length (%d.) <0! Ignored.", size);
-      ignore_rest_of_line ();
-      return;
-    }                          /* bad length */
-
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
-
-  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
-      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
-    {
-      as_bad ("bad .reserve segment -- expected BSS segment");
-      return;
-    }
-
-  if (input_line_pointer[2] == '.')
-    input_line_pointer += 7;
-  else
-    input_line_pointer += 6;
-  SKIP_WHITESPACE ();
-
-  if (*input_line_pointer == ',')
-    {
-      ++input_line_pointer;
-
-      SKIP_WHITESPACE ();
-      if (*input_line_pointer == '\n')
-       {
-         as_bad ("Missing alignment");
-         return;
-       }
-
-      align = get_absolute_expression ();
-#ifndef OBJ_ELF
-      if (align > max_alignment)
-       {
-         align = max_alignment;
-         as_warn ("Alignment too large: %d. assumed.", align);
-       }
-#endif
-      if (align < 0)
-       {
-         align = 0;
-         as_warn ("Alignment negative. 0 assumed.");
-       }
-
-      record_alignment (bss_section, align);
-
-      /* convert to a power of 2 alignment */
-      for (temp = 0; (align & 1) == 0; align >>= 1, ++temp);;
-
-      if (align != 1)
-       {
-         as_bad ("Alignment not a power of 2");
-         ignore_rest_of_line ();
-         return;
-       }                       /* not a power of two */
-
-      align = temp;
-    }                          /* if has optional alignment */
-  else
-    align = 0;
-
-  if ((S_GET_SEGMENT (symbolP) == bss_section
-       || !S_IS_DEFINED (symbolP))
-#ifdef OBJ_AOUT
-      && S_GET_OTHER (symbolP) == 0
-      && S_GET_DESC (symbolP) == 0
-#endif
-      )
-    {
-      if (! need_pass_2)
-       {
-         char *pfrag;
-         segT current_seg = now_seg;
-         subsegT current_subseg = now_subseg;
-
-         subseg_set (bss_section, 1); /* switch to bss */
-
-         if (align)
-           frag_align (align, 0); /* do alignment */
-
-         /* detach from old frag */
-         if (S_GET_SEGMENT(symbolP) == bss_section)
-           symbolP->sy_frag->fr_symbol = NULL;
-
-         symbolP->sy_frag = frag_now;
-         pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
-                           size, (char *)0);
-         *pfrag = 0;
-
-         S_SET_SEGMENT (symbolP, bss_section);
-
-         subseg_set (current_seg, current_subseg);
-       }
-    }
-  else
-    {
-      as_warn("Ignoring attempt to re-define symbol %s.", name);
-    }                          /* if not redefining */
-
-  demand_empty_rest_of_line ();
-}
-
 static void
-s_common (ignore)
+arc_common (ignore)
      int ignore;
 {
   char *name;
@@ -785,7 +708,7 @@ s_common (ignore)
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
-      as_bad ("Expected comma after symbol-name");
+      as_bad ("expected comma after symbol-name");
       ignore_rest_of_line ();
       return;
     }
@@ -802,7 +725,7 @@ s_common (ignore)
   *p = c;
   if (S_IS_DEFINED (symbolP))
     {
-      as_bad ("Ignoring attempt to re-define symbol");
+      as_bad ("ignoring attempt to re-define symbol");
       ignore_rest_of_line ();
       return;
     }
@@ -824,7 +747,7 @@ s_common (ignore)
   assert (symbolP->sy_frag == &zero_address_frag);
   if (*input_line_pointer != ',')
     {
-      as_bad ("Expected comma after common length");
+      as_bad ("expected comma after common length");
       ignore_rest_of_line ();
       return;
     }
@@ -921,59 +844,105 @@ s_common (ignore)
   }
 }
 
+/* Select the cpu we're assembling for.  */
+
 static void
-s_seg (ignore)
+arc_cpu (ignore)
      int ignore;
 {
+  int mach;
+  char c;
+  char *cpu;
+  static int seen_p = 0;
 
-  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
+  /* Allow only one .cpu.  */
+  if (seen_p)
     {
-      input_line_pointer += 6;
-      s_text (0);
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
-    {
-      input_line_pointer += 6;
-      s_data (0);
+      as_bad ("only one .cpu command allowed");
+      ignore_rest_of_line ();
       return;
     }
-  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+  seen_p = 1;
+
+  /* If an instruction has already been seen, it's too late.  */
+  if (cpu_tables_init_p)
     {
-      input_line_pointer += 7;
-      s_data1 ();
+      as_bad (".cpu command must appear before any instructions");
+      ignore_rest_of_line ();
       return;
     }
-  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+
+  cpu = input_line_pointer;
+  c = get_symbol_end ();
+  mach = arc_get_mach (cpu);
+  *input_line_pointer = c;
+  if (mach == -1)
+    goto bad_cpu;
+
+  /* Kind of overkill but what the heck.  */
+  demand_empty_rest_of_line ();
+
+  /* The cpu may have been selected on the command line.
+     The choices must match.  */
+  if (mach_type_specified && mach != arc_mach_type)
+    as_bad (".cpu conflicts with -mcpu flag");
+  else
     {
-      input_line_pointer += 5;
-      /* We only support 2 segments -- text and data -- for now, so
-        things in the "bss segment" will have to go into data for now.
-        You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
-      subseg_set (data_section, 255);  /* FIXME-SOMEDAY */
-      return;
+      arc_mach_type = mach;
+      if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+       as_warn ("could not set architecture and machine");
     }
-  as_bad ("Unknown segment type");
-  demand_empty_rest_of_line ();
-}
+  return;
 
-static void
-s_data1 ()
-{
-  subseg_set (data_section, 1);
-  demand_empty_rest_of_line ();
+ bad_cpu:
+  as_bad ("bad .cpu op");
+  ignore_rest_of_line ();
 }
 
+#if 0
+/* The .rename pseudo-op.  This is used by gcc to implement
+   -mmangle-cpu-libgcc.  */
+
 static void
-s_proc (ignore)
+arc_rename (ignore)
      int ignore;
 {
-  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+  char *name,*new;
+  char c;
+  symbolS *sym;
+  int len;
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  sym = symbol_find_or_make (name);
+  *input_line_pointer = c;
+
+  if (*input_line_pointer != ',')
     {
-      ++input_line_pointer;
+      as_bad ("missing rename string");
+      ignore_rest_of_line ();
+      return;
     }
   ++input_line_pointer;
+  SKIP_WHITESPACE ();
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  if (*name == '\0')
+    {
+      *input_line_pointer = c;
+      as_bad ("invalid symbol to rename to");
+      ignore_rest_of_line ();
+      return;
+    }
+  new = (char *) xmalloc (strlen (name) + 1);
+  strcpy (new, name);
+  *input_line_pointer = c;
+  sym->sy_tc.real_name = new;
+
+  demand_empty_rest_of_line ();
 }
+#endif
 \f
 /* Turn a string in input_line_pointer into a floating point constant of type
    type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
@@ -1034,7 +1003,11 @@ md_number_to_chars (buf, val, n)
      valueT val;
      int n;
 {
-  number_to_chars_bigendian (buf, val, n);
+  /* The ARC isn't bi-endian.  Yet.  */
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
 }
 
 /* Round up a section size to the appropriate boundary. */
@@ -1106,11 +1079,8 @@ md_pcrel_from (fixP)
 {
   if (fixP->fx_addsy != (symbolS *) NULL
       && ! S_IS_DEFINED (fixP->fx_addsy))
-    {
-      /* This makes a branch to an undefined symbol be a branch to the
-        current location.  */
-      return 4;
-    }
+    /* Return offset from PC to delay slot.  Offsets are from there.  */
+    return 4;
 
   /* Return the address of the delay slot.  */
   return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
@@ -1181,10 +1151,16 @@ md_apply_fix (fixP, valueP)
       /* Fetch the instruction, insert the fully resolved operand
         value, and stuff the instruction back again.  */
       where = fixP->fx_frag->fr_literal + fixP->fx_where;
-      insn = bfd_getb32 ((unsigned char *) where);
+      if (target_big_endian)
+       insn = bfd_getb32 ((unsigned char *) where);
+      else
+       insn = bfd_getl32 ((unsigned char *) where);
       insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
                                 fixP->fx_file, fixP->fx_line);
-      bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+      if (target_big_endian)
+       bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+      else
+       bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
 
       if (fixP->fx_done)
        {
@@ -1270,3 +1246,20 @@ tc_gen_reloc (section, fixP)
 
   return reloc;
 }
+\f
+/* Frobbers.  */
+
+#if 0
+/* Set the real name if the .rename pseudo-op was used.
+   Return 1 if the symbol should not be included in the symbol table.  */
+
+int
+arc_frob_symbol (sym)
+     symbolS *sym;
+{
+  if (sym->sy_tc.real_name != (char *) NULL)
+    S_SET_NAME (sym, sym->sy_tc.real_name);
+
+  return 0;
+}
+#endif