diff mbox

arc: Avoid store/load pipeline hazard

Message ID 1480025430-10806-1-git-send-email-andrew.burgess@embecosm.com
State New
Headers show

Commit Message

Andrew Burgess Nov. 24, 2016, 10:10 p.m. UTC
The patch below was primarily written (over several patches) by
Claudiu (CC'd) as part of an out-of-tree ARC GCC port.  I've pulled
the work together into a single patch, and added a test.  Claudiu is
happy for this to be posted upstream.

---

ARC700 targets have a store/load pipeline hazard, if we load within 2
cycles of a store, and the load/store are at the same address, then we
pay a multi-cycle penalty.

This commit avoids this by inserting nop instructions between the store
and the load.

gcc/ChangeLog:

	* config/arc/arc-protos.h (arc_store_addr_hazard_p): Declare.
	* config/arc/arc.c (arc_store_addr_hazard_p): New function.
	(workaround_arc_anomaly): Call arc_store_addr_hazard_p for ARC700.
	* config/arc/arc700.md: Add define_bypass for store/load.

gcc/testsuite/ChangeLog:

	* gcc.target/arc/arc700-stld-hazard.c: New file.
---
 gcc/ChangeLog                                     |  8 +++
 gcc/config/arc/arc-protos.h                       |  1 +
 gcc/config/arc/arc.c                              | 75 +++++++++++++++++++++++
 gcc/config/arc/arc700.md                          |  2 +
 gcc/testsuite/ChangeLog                           |  4 ++
 gcc/testsuite/gcc.target/arc/arc700-stld-hazard.c | 14 +++++
 6 files changed, 104 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/arc/arc700-stld-hazard.c

Comments

Claudiu Zissulescu Nov. 28, 2016, 9:29 a.m. UTC | #1
Hi Andrew,

Approved, but ...
 
> +/* Return true if a load instruction (CONSUMER) uses the same address
> +   as a store instruction (PRODUCER). This function is used to avoid
> +   st/ld address hazard in ARC700 cores. */


Missing space after dot.

> +  /* Peel the producer and the consumer for the address. */

Likewise.

> +  out_set = single_set (producer);
> +  if (out_set)
> +    {
> +      out_addr = SET_DEST (out_set);
> +      if (!out_addr)
> +	return false;
> +      if (GET_CODE (out_addr) == ZERO_EXTEND
> +	  || GET_CODE (out_addr) == SIGN_EXTEND)
> +	out_addr = XEXP (out_addr, 0);
> +
> +      if (!MEM_P(out_addr))

Space between macro and parenthesis.

> +	return false;
> +
> +      in_set = single_set (consumer);
> +      if (in_set)
> +	{
> +	  in_addr = SET_SRC (in_set);
> +	  if (!in_addr)
> +	    return false;
> +	  if (GET_CODE (in_addr) == ZERO_EXTEND
> +	      || GET_CODE (in_addr) == SIGN_EXTEND)
> +	    in_addr = XEXP (in_addr, 0);
> +
> +	  if (!MEM_P(in_addr))

Likewise.

> +	    return false;
> +	  /* Get rid of the MEM() and check if the addresses are
> +	     equivalent. */

Space, space after dot.

> +	  in_addr = XEXP (in_addr, 0);
> +	  out_addr = XEXP (out_addr, 0);
> +
> +	  return exp_equiv_p (in_addr, out_addr, 0, true);
> +	}
> +    }
> +  return false;
> +}
> +
>  /* The same functionality as arc_hazard.  It is called in machine
>     reorg before any other optimization.  Hence, the NOP size is taken
>     into account when doing branch shortening.  */
> @@ -6501,6 +6553,29 @@ workaround_arc_anomaly (void)
>  	  emit_insn_before (gen_nopv (), succ0);
>  	}
>      }
> +
> +  if (TARGET_ARC700)
> +    {
> +      rtx_insn *succ1;
> +
> +      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
> +	{
> +	  succ0 = next_real_insn(insn);

Space between function name and parenthesis.

> +	  if (arc_store_addr_hazard_p (insn, succ0))
> +	    {
> +	      emit_insn_after (gen_nopv (), insn);
> +	      emit_insn_after (gen_nopv (), insn);
> +	      continue;
> +	    }
> +
> +	  /* Avoid adding nops if the instruction between the ST and LD is
> +	     a call or jump. */
> +	  succ1 = next_real_insn(succ0);

Likewise.

> +	  if (succ0 && !JUMP_P (succ0) && !CALL_P (succ0)
> +	      && arc_store_addr_hazard_p (insn, succ1))
> +	    emit_insn_after (gen_nopv (), insn);
> +	}
> +    }
>  }

Best,
Claudiu
Andrew Burgess Nov. 30, 2016, 11:09 a.m. UTC | #2
* Claudiu Zissulescu <Claudiu.Zissulescu@synopsys.com> [2016-11-28 09:29:22 +0000]:

> Hi Andrew,
> 
> Approved, but ...

Thanks.  Committed as r243007 with the changes you suggested.

Thanks again,
Andrew




>  
> > +/* Return true if a load instruction (CONSUMER) uses the same address
> > +   as a store instruction (PRODUCER). This function is used to avoid
> > +   st/ld address hazard in ARC700 cores. */
> 
> 
> Missing space after dot.
> 
> > +  /* Peel the producer and the consumer for the address. */
> 
> Likewise.
> 
> > +  out_set = single_set (producer);
> > +  if (out_set)
> > +    {
> > +      out_addr = SET_DEST (out_set);
> > +      if (!out_addr)
> > +	return false;
> > +      if (GET_CODE (out_addr) == ZERO_EXTEND
> > +	  || GET_CODE (out_addr) == SIGN_EXTEND)
> > +	out_addr = XEXP (out_addr, 0);
> > +
> > +      if (!MEM_P(out_addr))
> 
> Space between macro and parenthesis.
> 
> > +	return false;
> > +
> > +      in_set = single_set (consumer);
> > +      if (in_set)
> > +	{
> > +	  in_addr = SET_SRC (in_set);
> > +	  if (!in_addr)
> > +	    return false;
> > +	  if (GET_CODE (in_addr) == ZERO_EXTEND
> > +	      || GET_CODE (in_addr) == SIGN_EXTEND)
> > +	    in_addr = XEXP (in_addr, 0);
> > +
> > +	  if (!MEM_P(in_addr))
> 
> Likewise.
> 
> > +	    return false;
> > +	  /* Get rid of the MEM() and check if the addresses are
> > +	     equivalent. */
> 
> Space, space after dot.
> 
> > +	  in_addr = XEXP (in_addr, 0);
> > +	  out_addr = XEXP (out_addr, 0);
> > +
> > +	  return exp_equiv_p (in_addr, out_addr, 0, true);
> > +	}
> > +    }
> > +  return false;
> > +}
> > +
> >  /* The same functionality as arc_hazard.  It is called in machine
> >     reorg before any other optimization.  Hence, the NOP size is taken
> >     into account when doing branch shortening.  */
> > @@ -6501,6 +6553,29 @@ workaround_arc_anomaly (void)
> >  	  emit_insn_before (gen_nopv (), succ0);
> >  	}
> >      }
> > +
> > +  if (TARGET_ARC700)
> > +    {
> > +      rtx_insn *succ1;
> > +
> > +      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
> > +	{
> > +	  succ0 = next_real_insn(insn);
> 
> Space between function name and parenthesis.
> 
> > +	  if (arc_store_addr_hazard_p (insn, succ0))
> > +	    {
> > +	      emit_insn_after (gen_nopv (), insn);
> > +	      emit_insn_after (gen_nopv (), insn);
> > +	      continue;
> > +	    }
> > +
> > +	  /* Avoid adding nops if the instruction between the ST and LD is
> > +	     a call or jump. */
> > +	  succ1 = next_real_insn(succ0);
> 
> Likewise.
> 
> > +	  if (succ0 && !JUMP_P (succ0) && !CALL_P (succ0)
> > +	      && arc_store_addr_hazard_p (insn, succ1))
> > +	    emit_insn_after (gen_nopv (), insn);
> > +	}
> > +    }
> >  }
> 
> Best,
> Claudiu
diff mbox

Patch

diff --git a/gcc/config/arc/arc-protos.h b/gcc/config/arc/arc-protos.h
index d1266b4..83a0b73 100644
--- a/gcc/config/arc/arc-protos.h
+++ b/gcc/config/arc/arc-protos.h
@@ -123,3 +123,4 @@  extern bool arc_legitimize_reload_address (rtx *, machine_mode, int, int);
 extern void arc_secondary_reload_conv (rtx, rtx, rtx, bool);
 extern bool insn_is_tls_gd_dispatch (rtx_insn *);
 extern void arc_cpu_cpp_builtins (cpp_reader *);
+extern bool arc_store_addr_hazard_p (rtx_insn *, rtx_insn *);
diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
index 98c7298..d778279 100644
--- a/gcc/config/arc/arc.c
+++ b/gcc/config/arc/arc.c
@@ -6483,6 +6483,58 @@  arc_invalid_within_doloop (const rtx_insn *insn)
   return NULL;
 }
 
+/* Return true if a load instruction (CONSUMER) uses the same address
+   as a store instruction (PRODUCER). This function is used to avoid
+   st/ld address hazard in ARC700 cores. */
+bool
+arc_store_addr_hazard_p (rtx_insn* producer, rtx_insn* consumer)
+{
+  rtx in_set, out_set;
+  rtx out_addr, in_addr;
+
+  if (!producer)
+    return false;
+
+  if (!consumer)
+    return false;
+
+  /* Peel the producer and the consumer for the address. */
+  out_set = single_set (producer);
+  if (out_set)
+    {
+      out_addr = SET_DEST (out_set);
+      if (!out_addr)
+	return false;
+      if (GET_CODE (out_addr) == ZERO_EXTEND
+	  || GET_CODE (out_addr) == SIGN_EXTEND)
+	out_addr = XEXP (out_addr, 0);
+
+      if (!MEM_P(out_addr))
+	return false;
+
+      in_set = single_set (consumer);
+      if (in_set)
+	{
+	  in_addr = SET_SRC (in_set);
+	  if (!in_addr)
+	    return false;
+	  if (GET_CODE (in_addr) == ZERO_EXTEND
+	      || GET_CODE (in_addr) == SIGN_EXTEND)
+	    in_addr = XEXP (in_addr, 0);
+
+	  if (!MEM_P(in_addr))
+	    return false;
+	  /* Get rid of the MEM() and check if the addresses are
+	     equivalent. */
+	  in_addr = XEXP (in_addr, 0);
+	  out_addr = XEXP (out_addr, 0);
+
+	  return exp_equiv_p (in_addr, out_addr, 0, true);
+	}
+    }
+  return false;
+}
+
 /* The same functionality as arc_hazard.  It is called in machine
    reorg before any other optimization.  Hence, the NOP size is taken
    into account when doing branch shortening.  */
@@ -6501,6 +6553,29 @@  workaround_arc_anomaly (void)
 	  emit_insn_before (gen_nopv (), succ0);
 	}
     }
+
+  if (TARGET_ARC700)
+    {
+      rtx_insn *succ1;
+
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+	{
+	  succ0 = next_real_insn(insn);
+	  if (arc_store_addr_hazard_p (insn, succ0))
+	    {
+	      emit_insn_after (gen_nopv (), insn);
+	      emit_insn_after (gen_nopv (), insn);
+	      continue;
+	    }
+
+	  /* Avoid adding nops if the instruction between the ST and LD is
+	     a call or jump. */
+	  succ1 = next_real_insn(succ0);
+	  if (succ0 && !JUMP_P (succ0) && !CALL_P (succ0)
+	      && arc_store_addr_hazard_p (insn, succ1))
+	    emit_insn_after (gen_nopv (), insn);
+	}
+    }
 }
 
 static int arc_reorg_in_progress = 0;
diff --git a/gcc/config/arc/arc700.md b/gcc/config/arc/arc700.md
index 4d9bddf..3eb5199 100644
--- a/gcc/config/arc/arc700.md
+++ b/gcc/config/arc/arc700.md
@@ -168,3 +168,5 @@ 
        (eq_attr "type" "store")
        (not (match_operand:DI 0 "" "")))
   "issue+dmp_write_port")
+
+(define_bypass 3 "data_store" "data_load" "arc_store_addr_hazard_p")
diff --git a/gcc/testsuite/gcc.target/arc/arc700-stld-hazard.c b/gcc/testsuite/gcc.target/arc/arc700-stld-hazard.c
new file mode 100644
index 0000000..bf6ae33
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/arc700-stld-hazard.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mcpu=arc700" } */
+
+volatile int a;
+volatile int b;
+
+void
+foo ()
+{
+  a = 1;
+  b = a;
+}
+
+/* { dg-final { scan-assembler "st r\[0-9\]+,\\\[@a\\\]\[^\n\]*\n\[ \t\]+nop_s\[^\n\]*\n\[ \t\]+nop_s\[^\n\]*\n\[ \t\]+ld r\[0-9\]+,\\\[@a\\\]" } } */