cpu: Fix broken squashAfter implementation in O3 CPU
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:45 +0000 (13:05 -0500)
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:45 +0000 (13:05 -0500)
Commit can currently both commit and squash in the same cycle. This
confuses other stages since the signals coming from the commit stage
can only signal either a squash or a commit in a cycle. This changeset
changes the behavior of squashAfter so that it commits all
instructions, including the instruction that requested the squash, in
the first cycle and then starts to squash in the next cycle.

src/cpu/o3/commit.hh
src/cpu/o3/commit_impl.hh

index fdd9609a41d1622d419ee0fab2868724886543b2..2d8d88b2185a9604bc1396b6a1c0efe569f8b43f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2010-2012 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -129,7 +129,8 @@ class DefaultCommit
         Idle,
         ROBSquashing,
         TrapPending,
-        FetchTrapPending
+        FetchTrapPending,
+        SquashAfterPending, //< Committing instructions before a squash.
     };
 
     /** Commit policy for SMT mode. */
@@ -259,13 +260,38 @@ class DefaultCommit
     /** Handles squashing due to an TC write. */
     void squashFromTC(ThreadID tid);
 
-    /** Handles squashing from instruction with SquashAfter set.
+    /** Handles a squash from a squashAfter() request. */
+    void squashFromSquashAfter(ThreadID tid);
+
+    /**
+     * Handle squashing from instruction with SquashAfter set.
+     *
      * This differs from the other squashes as it squashes following
      * instructions instead of the current instruction and doesn't
-     * clean up various status bits about traps/tc writes pending.
+     * clean up various status bits about traps/tc writes
+     * pending. Since there might have been instructions committed by
+     * the commit stage before the squashing instruction was reached
+     * and we can't commit and squash in the same cycle, we have to
+     * squash in two steps:
+     *
+     * <ol>
+     *   <li>Immediately set the commit status of the thread of
+     *       SquashAfterPending. This forces the thread to stop
+     *       committing instructions in this cycle. The last
+     *       instruction to be committed in this cycle will be the
+     *       SquashAfter instruction.
+     *   <li>In the next cycle, commit() checks for the
+     *       SquashAfterPending state and squashes <i>all</i>
+     *       in-flight instructions. Since the SquashAfter instruction
+     *       was the last instruction to be committed in the previous
+     *       cycle, this causes all subsequent instructions to be
+     *       squashed.
+     * </ol>
+     *
+     * @param tid ID of the thread to squash.
+     * @param head_inst Instruction that requested the squash.
      */
-    void squashAfter(ThreadID tid, DynInstPtr &head_inst,
-            uint64_t squash_after_seq_num);
+    void squashAfter(ThreadID tid, DynInstPtr &head_inst);
 
     /** Handles processing an interrupt. */
     void handleInterrupt();
@@ -372,6 +398,15 @@ class DefaultCommit
     /** Records if a thread has to squash this cycle due to an XC write. */
     bool tcSquash[Impl::MaxThreads];
 
+    /**
+     * Instruction passed to squashAfter().
+     *
+     * The squash after implementation needs to buffer the instruction
+     * that caused a squash since this needs to be passed to the fetch
+     * stage once squashing starts.
+     */
+    DynInstPtr squashAfterInst[Impl::MaxThreads];
+
     /** Priority List used for Commit Policy */
     std::list<ThreadID> priority_list;
 
index bff5c5ae987fc0ab352654426934fd079c952f0c..333ccc89f4b1097a3586571754d53360ff26af37 100644 (file)
@@ -144,6 +144,7 @@ DefaultCommit<Impl>::DefaultCommit(O3CPU *_cpu, DerivO3CPUParams *params)
         tcSquash[tid] = false;
         pc[tid].set(0);
         lastCommitedSeqNum[tid] = 0;
+        squashAfterInst[tid] = NULL;
     }
     interrupt = NoFault;
 }
@@ -404,6 +405,7 @@ DefaultCommit<Impl>::takeOverFrom()
         changedROBNumEntries[tid] = false;
         trapSquash[tid] = false;
         tcSquash[tid] = false;
+        squashAfterInst[tid] = NULL;
     }
     squashCounter = 0;
     rob->takeOverFrom();
@@ -587,31 +589,32 @@ DefaultCommit<Impl>::squashFromTC(ThreadID tid)
 
 template <class Impl>
 void
-DefaultCommit<Impl>::squashAfter(ThreadID tid, DynInstPtr &head_inst,
-        uint64_t squash_after_seq_num)
+DefaultCommit<Impl>::squashFromSquashAfter(ThreadID tid)
 {
-    youngestSeqNum[tid] = squash_after_seq_num;
+    DPRINTF(Commit, "Squashing after squash after request, "
+            "restarting at PC %s\n", pc[tid]);
 
-    rob->squash(squash_after_seq_num, tid);
-    changedROBNumEntries[tid] = true;
-
-    // Send back the sequence number of the squashed instruction.
-    toIEW->commitInfo[tid].doneSeqNum = squash_after_seq_num;
-
-    toIEW->commitInfo[tid].squashInst = head_inst;
-    // Send back the squash signal to tell stages that they should squash.
-    toIEW->commitInfo[tid].squash = true;
-
-    // Send back the rob squashing signal so other stages know that
-    // the ROB is in the process of squashing.
-    toIEW->commitInfo[tid].robSquashing = true;
+    squashAll(tid);
+    // Make sure to inform the fetch stage of which instruction caused
+    // the squash. It'll try to re-fetch an instruction executing in
+    // microcode unless this is set.
+    toIEW->commitInfo[tid].squashInst = squashAfterInst[tid];
+    squashAfterInst[tid] = NULL;
 
-    toIEW->commitInfo[tid].mispredictInst = NULL;
+    commitStatus[tid] = ROBSquashing;
+    cpu->activityThisCycle();
+}
 
-    toIEW->commitInfo[tid].pc = pc[tid];
+template <class Impl>
+void
+DefaultCommit<Impl>::squashAfter(ThreadID tid, DynInstPtr &head_inst)
+{
     DPRINTF(Commit, "Executing squash after for [tid:%i] inst [sn:%lli]\n",
-            tid, squash_after_seq_num);
-    commitStatus[tid] = ROBSquashing;
+            tid, head_inst->seqNum);
+
+    assert(!squashAfterInst[tid] || squashAfterInst[tid] == head_inst);
+    commitStatus[tid] = SquashAfterPending;
+    squashAfterInst[tid] = head_inst;
 }
 
 template <class Impl>
@@ -797,6 +800,11 @@ DefaultCommit<Impl>::commit()
         } else if (tcSquash[tid] == true) {
             assert(commitStatus[tid] != TrapPending);
             squashFromTC(tid);
+        } else if (commitStatus[tid] == SquashAfterPending) {
+            // A squash from the previous cycle of the commit stage (i.e.,
+            // commitInsts() called squashAfter) is pending. Squash the
+            // thread now.
+            squashFromSquashAfter(tid);
         }
 
         // Squashed sequence number must be older than youngest valid
@@ -1008,7 +1016,7 @@ DefaultCommit<Impl>::commitInsts()
                 // If this is an instruction that doesn't play nicely with
                 // others squash everything and restart fetch
                 if (head_inst->isSquashAfter())
-                    squashAfter(tid, head_inst, head_inst->seqNum);
+                    squashAfter(tid, head_inst);
 
                 int count = 0;
                 Addr oldpc;