Added opportunistic parallelisation of adjacent instructions.
authorNick Clifton <nickc@redhat.com>
Wed, 21 Jan 1998 01:13:47 +0000 (01:13 +0000)
committerNick Clifton <nickc@redhat.com>
Wed, 21 Jan 1998 01:13:47 +0000 (01:13 +0000)
gas/ChangeLog
gas/config/tc-m32r.c

index 409db0b..98c70c4 100644 (file)
@@ -1,3 +1,13 @@
+start-sanitize-m32rx
+Tue Jan 20 17:08:53 1998  Nick Clifton  <nickc@cygnus.com>
+
+       * config/tc-m32r.c (md_assemble): Add code to swap explicitly
+       parallel instructions so that they are in the correct order.
+       (reads_from_src_reg, get_src_reg, can_make_parallel,
+       make_parallel): New functions to support opportunistic
+       parallelisation of adjacent instructions.
+
+end-sanitize-m32rx
 Fri Jan 16 14:51:48 1998  Ian Lance Taylor  <ian@cygnus.com>
 
        * read.c (dwarf_file_string): New file static variable.
index c717de5..c2e6154 100644 (file)
@@ -27,6 +27,7 @@
 /* Non-null if last insn was a 16 bit insn on a 32 bit boundary
    (i.e. was the first of two 16 bit insns).  */
 static const CGEN_INSN * prev_insn = NULL;
+static CGEN_FIELDS       prev_fields;
 
 /* Non-zero if we've seen a relaxable insn since the last 32 bit
    alignment request.  */
@@ -203,7 +204,7 @@ m32r_do_align (n, fill, len, max)
       /* Only do this special handling if aligning to at least a
         4 byte boundary.  */
       && n > 1
-      /* Only do this special handling if we're allowed to emit at
+     /* Only do this special handling if we're allowed to emit at
         least two bytes.  */
       && (max == 0 || max > 1))
     {
@@ -365,16 +366,156 @@ writes_to_dest_reg (insn)
   return c;
 }
 
-/* Returns an integer representing the destination register of
-   the given insn, or -1 if the insn has no destination.  */
+/* Returns non zero if the given instruction reads from a source register.
+   Syntax characters equal to 'ignore' are skipped as they have already been
+   processed.  (This works provided that no potential parallel instruction
+   can have more than 2 input registers).  */
 static int
-get_dest_reg (insn)
+reads_from_src_reg (insn, ignore)
      const CGEN_INSN * insn;
+     unsigned char     ignore;
 {
-  /* XXX to be done.  */
-  return -1;
+  unsigned char * syntax = CGEN_SYNTAX_STRING (CGEN_INSN_SYNTAX (insn));
+  unsigned char   c;
+  
+  /* Scan the syntax string looking for a source register.  */
+  while ((c = (* syntax ++)) != 0)
+    {
+      if (c == ignore)
+       continue;
+      
+      if (   c == 128 + M32R_OPERAND_SR
+         || c == 128 + M32R_OPERAND_SRC1
+         || c == 128 + M32R_OPERAND_SRC2)
+       break;
+    }
+
+  return c;
 }
 
+/* Returns the integer value of the destination register held in the fields. */
+#define get_dest_reg(fields) fields->f_r1
+
+/* Returns an integer representing the source register of the given type.  */
+static int
+get_src_reg (syntax, fields)
+     unsigned char syntax;
+     CGEN_FIELDS * fields;
+{
+  switch (syntax)
+    {
+    case 128 + M32R_OPERAND_SR:    return fields->f_r2;
+      /* Relies upon the fact that no instruction with a $src1 operand
+        also has a $dr operand.  */
+    case 128 + M32R_OPERAND_SRC1:  return fields->f_r1;
+    case 128 + M32R_OPERAND_SRC2:  return fields->f_r2;
+    default:                       abort(); return -1;
+    }
+}
+
+/* start-sanitize-m32rx */
+/* Returns NULL if the two 16 bit insns can be executed in parallel,
+   otherwise it returns a pointer to an error message explaining why not.  */
+static const char *
+can_make_parallel (a, a_fields, b, b_fields, test_a_inputs, test_b_inputs)
+     const CGEN_INSN * a;
+     CGEN_FIELDS *     a_fields;
+     const CGEN_INSN * b;
+     CGEN_FIELDS *     b_fields;
+     int               test_a_inputs;
+     int               test_b_inputs;
+{
+  PIPE_ATTR a_pipe;
+  PIPE_ATTR b_pipe;
+
+  /* Make sure the instructions are the right length.  */
+  if (   CGEN_FIELDS_BITSIZE (a_fields) != 16
+      || CGEN_FIELDS_BITSIZE (b_fields) != 16)
+    abort();
+  
+  a_pipe = CGEN_INSN_ATTR (a, CGEN_INSN_PIPE);
+  b_pipe = CGEN_INSN_ATTR (b, CGEN_INSN_PIPE);
+
+  if (   a_pipe == PIPE_NONE
+      || b_pipe == PIPE_NONE)
+    return "Instructions do not use parallel execution pipelines.";
+  
+  if (   a_pipe == PIPE_S
+      || b_pipe == PIPE_O)
+    return "Instructions share the same execution pipeline";
+
+  if (   writes_to_dest_reg (a)
+      && writes_to_dest_reg (b)
+      && (get_dest_reg (a_fields) == get_dest_reg (b_fields)))
+    return "Instructions write to the same destination register.";
+
+  /* If requested, make sure that the first instruction does not
+     overwrite the inputs of the second instruction.  */
+  if (test_b_inputs && writes_to_dest_reg (a))
+    {
+      unsigned char skip = 1;
+      
+      while (skip = reads_from_src_reg (b, skip))
+       {
+         if (get_src_reg (skip, b_fields) == get_dest_reg (a_fields))
+           return "First instruction writes to register read by the second instruction";
+       }
+    }
+  
+  /* Similarly, if requested, make sure that the second instruction
+     does not overwrite the inputs of the first instruction.  */
+  if (test_a_inputs && writes_to_dest_reg (b))
+    {
+      unsigned char skip = 1;
+      
+      while (skip = reads_from_src_reg (a, skip))
+       {
+         if (get_src_reg (skip, a_fields) == get_dest_reg (b_fields))
+           return "Second instruction writes to register read by the first instruction";
+       }
+    }
+  
+  return NULL;
+}
+/* end-sanitize-m32rx */
+
+
+#ifdef CGEN_INT_INSN
+static void
+make_parallel (insn, buffer)
+     const CGEN_INSN * insn;
+     cgen_insn_t *     buffer;
+{
+  /* Force the top bit of the second insn to be set.  */
+
+  bfd_vma value;
+      
+  if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG)
+    {
+      value = bfd_getb16 ((bfd_byte *) buffer);
+      value |= 0x8000;
+      bfd_putb16 (value, (char *) buffer);
+    }
+  else
+    {
+      value = bfd_getl16 ((bfd_byte *) buffer);
+      value |= 0x8000;
+      bfd_putl16 (value, (char *) buffer);
+    }
+}
+#else
+static void
+make_parallel (insn, buffer)
+     const CGEN_INSN * insn;
+     char *            buffer;
+{
+  /* Force the top bit of the second insn to be set.  */
+
+  buffer [CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG ? 0 : 1] |= 0x80;
+}
+#endif
+
+
 void
 md_assemble (str)
      char * str;
@@ -387,7 +528,6 @@ md_assemble (str)
   char                     prev_buffer [CGEN_MAX_INSN_SIZE];
 #endif
   CGEN_FIELDS              fields;
-  CGEN_FIELDS              prev_fields;
   const CGEN_INSN *        insn;
   char *                   errmsg;
   char *                   str2 = NULL;
@@ -403,13 +543,15 @@ md_assemble (str)
       
       * str2 = 0; /* Seperate the two instructions.  */
 
-      /* If there was a previous 16 bit insn, then fill the following 16 bit slot,
-        so that the parallel instruction will start on a 32 bit boundary.  */
+      /* If there was a previous 16 bit insn, then fill the following 16 bit
+        slot, so that the parallel instruction will start on a 32 bit
+        boundary.  */
       if (prev_insn)
        fill_insn (0);
 
       /* Assemble the first instruction.  */
-      prev_insn = CGEN_SYM (assemble_insn) (str, & prev_fields, prev_buffer, & errmsg);
+      prev_insn = CGEN_SYM (assemble_insn) (str, & prev_fields, prev_buffer,
+                                           & errmsg);
       if (! prev_insn)
        {
          as_bad (errmsg);
@@ -450,8 +592,7 @@ md_assemble (str)
     }
 
 /* start-sanitize-m32rx */
-  if (! enable_m32rx
-      && CGEN_INSN_ATTR (insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
+  if (! enable_m32rx && CGEN_INSN_ATTR (insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
     {
       as_bad ("instruction '%s' is for the M32RX only", str);
       return;
@@ -460,6 +601,8 @@ md_assemble (str)
   
   if (is_parallel)
     {
+      int swap = false;
+      
 /* start-sanitize-m32rx */
       if (! enable_m32rx)
        {
@@ -470,70 +613,50 @@ md_assemble (str)
              return;
            }
        }
-      /* Check to see if this is an allowable parallel insn.  */
-      if (CGEN_INSN_ATTR (insn, CGEN_INSN_PIPE) == PIPE_NONE)
-       {
-         as_bad ("instruction '%s', cannot be executed in parallel.", str);
-         return;
-       }
-
-      /* Check to see that the two instructions can be placed in parallel. */
-      if ((CGEN_INSN_ATTR (insn, CGEN_INSN_PIPE) == CGEN_INSN_ATTR (prev_insn, CGEN_INSN_PIPE))
-         && (CGEN_INSN_ATTR (insn, CGEN_INSN_PIPE) != PIPE_OS))
-       {
-         as_bad ("'%s': both instructions use the same execution pipeline", str2);
-         return;
-       }
-/* end-sanitize-m32rx */
 
-#if 0
-      /* Check that the instructions do not write to the same destination register.  */
-      if (writes_to_dest_reg (insn)
-         && writes_to_dest_reg (prev_insn) /* This test is actually redundant.  */
-         && get_dest_reg (insn) == get_dest_reg (prev_insn))
+      /* We assume that if the first instruction writes to a register that is
+        read by the second instruction it is because the programmer intended
+        this to happen, (after all they have explicitly requested that these
+        two instructions be executed in parallel).  So we do not generate an
+        error if this happens.  */
+      if (can_make_parallel (prev_insn, & prev_fields, insn,
+                            & fields, false, false) != NULL)
        {
-         as_bad ("'%s': both instructions write to the same destination register", str2);
-         return;
-       }
-#endif
-      
-      /* Force the top bit of the second insn to be set.  */
-#if 0 /*def CGEN_INT_INSN*/
-#define MAKE_PARALLEL(insn)    ((insn) |= 0x8000)
-      switch (CGEN_FIELDS_BITSIZE (& fields))
-       {
-         bfd_vma value;
-         
-       case 16:
-         if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG)
+         if ((errmsg = (char *) can_make_parallel (insn, & fields, prev_insn,
+                                                   & prev_fields, false, false)) == NULL)
            {
-             value = bfd_getb16 ((bfd_vma) * buffer);
-             MAKE_PARALLEL (value);
-             bfd_putb16 (value, buffer);
+             /* swap the two insns.  */
+             swap = true;
            }
          else
            {
-             value = bfd_getl16 ((bfd_vma) * buffer);
-             MAKE_PARALLEL (value);
-             bfd_putl16 (value, buffer);
+             as_bad ("'%s': %s", str2, errmsg);
+             return;
            }
-         break;
-       default:
-         abort ();
        }
-#else
-#define MAKE_PARALLEL(insn)    ((insn) |= 0x80)
-      MAKE_PARALLEL (buffer [CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG ? 0 : 1]);
-#endif
+/* end-sanitize-m32rx */
       
       /* Generate the parallel instructions */
-      cgen_asm_finish_insn (prev_insn, prev_buffer, CGEN_FIELDS_BITSIZE (& prev_fields));
-      cgen_asm_finish_insn (insn, buffer, CGEN_FIELDS_BITSIZE (& fields));
+      if (swap)
+       {
+         cgen_asm_finish_insn (insn, buffer, CGEN_FIELDS_BITSIZE (& fields));
+         
+         /* Force the top bit of the second insn to be set.  */
+         make_parallel (prev_insn, prev_buffer);
 
-      /* If prev_ins is relaxable (and insn is not), then swap them, so that the test
-        after the end of this if-then-else section will work.  */
-      if (CGEN_INSN_ATTR (prev_insn, CGEN_INSN_RELAXABLE))
-       insn = prev_insn;
+         cgen_asm_finish_insn (prev_insn, prev_buffer,
+                               CGEN_FIELDS_BITSIZE (& prev_fields));
+       }
+      else
+       {
+         cgen_asm_finish_insn (prev_insn, prev_buffer,
+                               CGEN_FIELDS_BITSIZE (& prev_fields));
+
+         /* Force the top bit of the second insn to be set.  */
+         make_parallel (insn, buffer);
+      
+         cgen_asm_finish_insn (insn, buffer, CGEN_FIELDS_BITSIZE (& fields));
+       }
       
       /* Clear the prev_insn variable, since it only used if the insn was the first
         16 bit insn in a 32 bit word.  */
@@ -556,9 +679,24 @@ md_assemble (str)
       /* Keep track of whether we've seen a pair of 16 bit insns.
         PREV_INSN is NULL when we're on a 32 bit boundary.  */
       if (prev_insn)
-       prev_insn = NULL;
+       {
+/* start-sanitize-m32rx */
+         if (can_make_parallel (prev_insn, & prev_fields, insn, & fields, false, true) == NULL)
+           make_parallel (insn, buffer);
+         else if (can_make_parallel (insn, & fields, prev_insn, & prev_fields, true, false) == NULL)
+           {
+             /* Swap instructions and make parallel.  */
+             /* XXX TODO .... */
+           }
+/* end-sanitize-m32rx */
+         
+         prev_insn = NULL;
+       }
       else
-       prev_insn = insn;
+       {
+         prev_insn   = insn;
+         prev_fields = fields;
+       }
       
       cgen_asm_finish_insn (insn, buffer, CGEN_FIELDS_BITSIZE (& fields));
 
@@ -567,13 +705,13 @@ md_assemble (str)
       if (prev_insn
          && CGEN_INSN_ATTR (insn, CGEN_INSN_FILL_SLOT) != 0)
        fill_insn (0);
-    }
 
-  /* If this is a relaxable insn (can be replaced with a larger version)
-     mark the fact so that we can emit an alignment directive for a following
-     32 bit insn if we see one.   */
-  if (CGEN_INSN_ATTR (insn, CGEN_INSN_RELAXABLE) != 0)
-    seen_relaxable_p = 1;
+      /* If this is a relaxable insn (can be replaced with a larger version)
+        mark the fact so that we can emit an alignment directive for a following
+        32 bit insn if we see one.   */
+      if (CGEN_INSN_ATTR (insn, CGEN_INSN_RELAXABLE) != 0)
+       seen_relaxable_p = 1;
+    }
 
   /* Set these so m32r_fill_insn can use them.  */
   prev_seg    = now_seg;
@@ -710,10 +848,13 @@ m32r_scomm (ignore)
 
       record_alignment (sbss_section, align2);
       subseg_set (sbss_section, 0);
+      
       if (align2)
        frag_align (align2, 0, 0);
+      
       if (S_GET_SEGMENT (symbolP) == sbss_section)
        symbolP->sy_frag->fr_symbol = 0;
+      
       symbolP->sy_frag = frag_now;
       pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
                        (char *) 0);
@@ -891,9 +1032,9 @@ md_estimate_size_before_relax (fragP, segment)
 
 void
 md_convert_frag (abfd, sec, fragP)
-  bfd *abfd;
-  segT sec;
-  fragS *fragP;
+  bfd *   abfd;
+  segT    sec;
+  fragS * fragP;
 {
   char * opcode;
   char * displacement;