This implements a workaround for the recently-disclosed FPU erratum on LEON3.
Tested on SPARC/Solaris, applied on the mainline and 4.8 branch.
2014-03-20 Eric Botcazou <ebotca...@adacore.com>
* config/sparc/sparc.c (sparc_do_work_around_errata): Implement work
around for store forwarding issue in the FPU on the UT699.
* config/sparc/sparc.md (in_branch_delay): Return false for single FP
loads and operations if -mfix-ut699 is specified.
(divtf3_hq): Tweak attribute.
(sqrttf2_hq): Likewise.
--
Eric Botcazou
Index: config/sparc/sparc.md
===================================================================
--- config/sparc/sparc.md (revision 208674)
+++ config/sparc/sparc.md (working copy)
@@ -462,6 +462,10 @@ (define_attr "in_branch_delay" "false,tr
(const_string "false")
(and (eq_attr "fix_ut699" "true") (eq_attr "type" "load,sload"))
(const_string "false")
+ (and (eq_attr "fix_ut699" "true")
+ (and (eq_attr "type" "fpload,fp,fpmove,fpmul,fpdivs,fpsqrts")
+ (eq_attr "fptype" "single")))
+ (const_string "false")
(eq_attr "length" "1")
(const_string "true")
] (const_string "false")))
@@ -5513,7 +5517,7 @@ (define_insn "*divtf3_hq"
(match_operand:TF 2 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fdivq\t%1, %2, %0"
- [(set_attr "type" "fpdivd")])
+ [(set_attr "type" "fpdivs")])
(define_expand "divdf3"
[(set (match_operand:DF 0 "register_operand" "=e")
@@ -5744,7 +5748,7 @@ (define_insn "*sqrttf2_hq"
(sqrt:TF (match_operand:TF 1 "register_operand" "e")))]
"TARGET_FPU && TARGET_HARD_QUAD"
"fsqrtq\t%1, %0"
- [(set_attr "type" "fpsqrtd")])
+ [(set_attr "type" "fpsqrts")])
(define_expand "sqrtdf2"
[(set (match_operand:DF 0 "register_operand" "=e")
Index: config/sparc/sparc.c
===================================================================
--- config/sparc/sparc.c (revision 208674)
+++ config/sparc/sparc.c (working copy)
@@ -1012,6 +1012,106 @@ sparc_do_work_around_errata (void)
}
}
+ /* Look for a single-word load/operation into an FP register. */
+ else if (sparc_fix_ut699
+ && NONJUMP_INSN_P (insn)
+ && (set = single_set (insn)) != NULL_RTX
+ && GET_MODE_SIZE (GET_MODE (SET_SRC (set))) == 4
+ && REG_P (SET_DEST (set))
+ && REGNO (SET_DEST (set)) > 31)
+ {
+ /* Number of instructions in the problematic window. */
+ const int n_insns = 4;
+ /* The problematic combination is with the sibling FP register. */
+ const unsigned int x = REGNO (SET_DEST (set));
+ const unsigned int y = x ^ 1;
+ rtx after;
+ int i;
+
+ next = next_active_insn (insn);
+ if (!next)
+ break;
+ /* If the insn is a branch, then it cannot be problematic. */
+ if (!NONJUMP_INSN_P (next) || GET_CODE (PATTERN (next)) == SEQUENCE)
+ continue;
+
+ /* Look for a second load/operation into the sibling FP register. */
+ if (!((set = single_set (next)) != NULL_RTX
+ && GET_MODE_SIZE (GET_MODE (SET_SRC (set))) == 4
+ && REG_P (SET_DEST (set))
+ && REGNO (SET_DEST (set)) == y))
+ continue;
+
+ /* Look for a (possible) store from the FP register in the next N
+ instructions, but bail out if it is again modified or if there
+ is a store from the sibling FP register before this store. */
+ for (after = next, i = 0; i < n_insns; i++)
+ {
+ bool branch_p;
+
+ after = next_active_insn (after);
+ if (!after)
+ break;
+
+ /* This is a branch with an empty delay slot. */
+ if (!NONJUMP_INSN_P (after))
+ {
+ if (++i == n_insns)
+ break;
+ branch_p = true;
+ after = NULL_RTX;
+ }
+ /* This is a branch with a filled delay slot. */
+ else if (GET_CODE (PATTERN (after)) == SEQUENCE)
+ {
+ if (++i == n_insns)
+ break;
+ branch_p = true;
+ after = XVECEXP (PATTERN (after), 0, 1);
+ }
+ /* This is a regular instruction. */
+ else
+ branch_p = false;
+
+ if (after && (set = single_set (after)) != NULL_RTX)
+ {
+ const rtx src = SET_SRC (set);
+ const rtx dest = SET_DEST (set);
+ const unsigned int size = GET_MODE_SIZE (GET_MODE (dest));
+
+ /* If the FP register is again modified before the store,
+ then the store isn't affected. */
+ if (REG_P (dest)
+ && (REGNO (dest) == x
+ || (REGNO (dest) == y && size == 8)))
+ break;
+
+ if (MEM_P (dest) && REG_P (src))
+ {
+ /* If there is a store from the sibling FP register
+ before the store, then the store is not affected. */
+ if (REGNO (src) == y || (REGNO (src) == x && size == 8))
+ break;
+
+ /* Otherwise, the store is affected. */
+ if (REGNO (src) == x && size == 4)
+ {
+ insert_nop = true;
+ break;
+ }
+ }
+ }
+
+ /* If we have a branch in the first M instructions, then we
+ cannot see the (M+2)th instruction so we play safe. */
+ if (branch_p && i <= (n_insns - 2))
+ {
+ insert_nop = true;
+ break;
+ }
+ }
+ }
+
else
next = NEXT_INSN (insn);