cpu: split LTAGE implementation into a base TAGE and a derived LTAGE
[gem5.git] / src / cpu / pred / bpred_unit.cc
index c38927c8dac7f57217c12ed52f6b90762a91cfd0..05770cba9358ca6cc4e9812c81fa69d89bebf328 100644 (file)
@@ -62,6 +62,15 @@ BPredUnit::BPredUnit(const Params *params)
           params->instShiftAmt,
           params->numThreads),
       RAS(numThreads),
+      useIndirect(params->useIndirect),
+      iPred(params->indirectHashGHR,
+            params->indirectHashTargets,
+            params->indirectSets,
+            params->indirectWays,
+            params->indirectTagSize,
+            params->indirectPathLength,
+            params->instShiftAmt,
+            params->numThreads),
       instShiftAmt(params->instShiftAmt)
 {
     for (auto& r : RAS)
@@ -71,6 +80,8 @@ BPredUnit::BPredUnit(const Params *params)
 void
 BPredUnit::regStats()
 {
+    SimObject::regStats();
+
     lookups
         .name(name() + ".lookups")
         .desc("Number of BP lookups")
@@ -117,6 +128,27 @@ BPredUnit::regStats()
         .name(name() + ".RASInCorrect")
         .desc("Number of incorrect RAS predictions.")
         ;
+
+    indirectLookups
+        .name(name() + ".indirectLookups")
+        .desc("Number of indirect predictor lookups.")
+        ;
+
+    indirectHits
+        .name(name() + ".indirectHits")
+        .desc("Number of indirect target hits.")
+        ;
+
+    indirectMisses
+        .name(name() + ".indirectMisses")
+        .desc("Number of indirect misses.")
+        ;
+
+    indirectMispredicted
+        .name(name() + "indirectMispredicted")
+        .desc("Number of mispredicted indirect branches.")
+        ;
+
 }
 
 ProbePoints::PMUUPtr
@@ -165,10 +197,10 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
         DPRINTF(Branch, "[tid:%i]: Unconditional control.\n", tid);
         pred_taken = true;
         // Tell the BP there was an unconditional branch.
-        uncondBranch(pc.instAddr(), bp_history);
+        uncondBranch(tid, pc.instAddr(), bp_history);
     } else {
         ++condPredicted;
-        pred_taken = lookup(pc.instAddr(), bp_history);
+        pred_taken = lookup(tid, pc.instAddr(), bp_history);
 
         DPRINTF(Branch, "[tid:%i]: [sn:%i] Branch predictor"
                 " predicted %i for PC %s\n", tid, seqNum,  pred_taken, pc);
@@ -216,31 +248,59 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
                         tid, pc, pc, RAS[tid].topIdx());
             }
 
-            if (BTB.valid(pc.instAddr(), tid)) {
-                ++BTBHits;
-
-                // If it's not a return, use the BTB to get the target addr.
-                target = BTB.lookup(pc.instAddr(), tid);
-
-                DPRINTF(Branch, "[tid:%i]: Instruction %s predicted"
-                        " target is %s.\n", tid, pc, target);
-
+            if (inst->isDirectCtrl() || !useIndirect) {
+                // Check BTB on direct branches
+                if (BTB.valid(pc.instAddr(), tid)) {
+                    ++BTBHits;
+
+                    // If it's not a return, use the BTB to get target addr.
+                    target = BTB.lookup(pc.instAddr(), tid);
+
+                    DPRINTF(Branch, "[tid:%i]: Instruction %s predicted"
+                            " target is %s.\n", tid, pc, target);
+
+                } else {
+                    DPRINTF(Branch, "[tid:%i]: BTB doesn't have a "
+                            "valid entry.\n",tid);
+                    pred_taken = false;
+                    // The Direction of the branch predictor is altered
+                    // because the BTB did not have an entry
+                    // The predictor needs to be updated accordingly
+                    if (!inst->isCall() && !inst->isReturn()) {
+                        btbUpdate(tid, pc.instAddr(), bp_history);
+                        DPRINTF(Branch, "[tid:%i]:[sn:%i] btbUpdate"
+                                " called for %s\n", tid, seqNum, pc);
+                    } else if (inst->isCall() && !inst->isUncondCtrl()) {
+                        RAS[tid].pop();
+                        predict_record.pushedRAS = false;
+                    }
+                    TheISA::advancePC(target, inst);
+                }
             } else {
-                DPRINTF(Branch, "[tid:%i]: BTB doesn't have a "
-                        "valid entry.\n",tid);
-                pred_taken = false;
-                // The Direction of the branch predictor is altered because the
-                // BTB did not have an entry
-                // The predictor needs to be updated accordingly
-                if (!inst->isCall() && !inst->isReturn()) {
-                      btbUpdate(pc.instAddr(), bp_history);
-                      DPRINTF(Branch, "[tid:%i]:[sn:%i] btbUpdate"
-                              " called for %s\n", tid, seqNum, pc);
-                } else if (inst->isCall() && !inst->isUncondCtrl()) {
-                      RAS[tid].pop();
-                      predict_record.pushedRAS = false;
+                predict_record.wasIndirect = true;
+                ++indirectLookups;
+                //Consult indirect predictor on indirect control
+                if (iPred.lookup(pc.instAddr(), getGHR(tid, bp_history),
+                        target, tid)) {
+                    // Indirect predictor hit
+                    ++indirectHits;
+                    DPRINTF(Branch, "[tid:%i]: Instruction %s predicted "
+                            "indirect target is %s.\n", tid, pc, target);
+                } else {
+                    ++indirectMisses;
+                    pred_taken = false;
+                    DPRINTF(Branch, "[tid:%i]: Instruction %s no indirect "
+                            "target.\n", tid, pc);
+                    if (!inst->isCall() && !inst->isReturn()) {
+
+                    } else if (inst->isCall() && !inst->isUncondCtrl()) {
+                        RAS[tid].pop();
+                        predict_record.pushedRAS = false;
+                    }
+                    TheISA::advancePC(target, inst);
                 }
-                TheISA::advancePC(target, inst);
+                iPred.recordIndirect(pc.instAddr(), target.instAddr(), seqNum,
+                        tid);
             }
         }
     } else {
@@ -260,143 +320,19 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
     return pred_taken;
 }
 
-bool
-BPredUnit::predictInOrder(const StaticInstPtr &inst, const InstSeqNum &seqNum,
-                          int asid, TheISA::PCState &instPC,
-                          TheISA::PCState &predPC, ThreadID tid)
-{
-    // See if branch predictor predicts taken.
-    // If so, get its target addr either from the BTB or the RAS.
-    // Save off record of branch stuff so the RAS can be fixed
-    // up once it's done.
-
-    using TheISA::MachInst;
-
-    bool pred_taken = false;
-    TheISA::PCState target;
-
-    ++lookups;
-    ppBranches->notify(1);
-
-    DPRINTF(Branch, "[tid:%i] [sn:%i] %s ... PC %s doing branch "
-            "prediction\n", tid, seqNum,
-            inst->disassemble(instPC.instAddr()), instPC);
-
-    void *bp_history = NULL;
-
-    if (inst->isUncondCtrl()) {
-        DPRINTF(Branch, "[tid:%i] Unconditional control.\n", tid);
-        pred_taken = true;
-        // Tell the BP there was an unconditional branch.
-        uncondBranch(instPC.instAddr(), bp_history);
-
-        if (inst->isReturn() && RAS[tid].empty()) {
-            DPRINTF(Branch, "[tid:%i] RAS is empty, predicting "
-                    "false.\n", tid);
-            pred_taken = false;
-        }
-    } else {
-        ++condPredicted;
-
-        pred_taken = lookup(predPC.instAddr(), bp_history);
-    }
-
-    PredictorHistory predict_record(seqNum, predPC.instAddr(), pred_taken,
-                                    bp_history, tid);
-
-    // Now lookup in the BTB or RAS.
-    if (pred_taken) {
-        if (inst->isReturn()) {
-            ++usedRAS;
-
-            // If it's a function return call, then look up the address
-            // in the RAS.
-            TheISA::PCState rasTop = RAS[tid].top();
-            target = TheISA::buildRetPC(instPC, rasTop);
-
-            // Record the top entry of the RAS, and its index.
-            predict_record.usedRAS = true;
-            predict_record.RASIndex = RAS[tid].topIdx();
-            predict_record.RASTarget = rasTop;
-
-            assert(predict_record.RASIndex < 16);
-
-            RAS[tid].pop();
-
-            DPRINTF(Branch, "[tid:%i]: Instruction %s is a return, "
-                    "RAS predicted target: %s, RAS index: %i.\n",
-                    tid, instPC, target,
-                    predict_record.RASIndex);
-        } else {
-            ++BTBLookups;
-
-            if (inst->isCall()) {
-
-                RAS[tid].push(instPC);
-                predict_record.pushedRAS = true;
-
-                // Record that it was a call so that the top RAS entry can
-                // be popped off if the speculation is incorrect.
-                predict_record.wasCall = true;
-
-                DPRINTF(Branch, "[tid:%i]: Instruction %s was a call"
-                        ", adding %s to the RAS index: %i.\n",
-                        tid, instPC, predPC,
-                        RAS[tid].topIdx());
-            }
-
-            if (inst->isCall() &&
-                inst->isUncondCtrl() &&
-                inst->isDirectCtrl()) {
-                target = inst->branchTarget(instPC);
-            } else if (BTB.valid(predPC.instAddr(), asid)) {
-                ++BTBHits;
-
-                // If it's not a return, use the BTB to get the target addr.
-                target = BTB.lookup(predPC.instAddr(), asid);
-
-                DPRINTF(Branch, "[tid:%i]: [asid:%i] Instruction %s "
-                        "predicted target is %s.\n",
-                        tid, asid, instPC, target);
-            } else {
-                DPRINTF(Branch, "[tid:%i]: BTB doesn't have a "
-                        "valid entry, predicting false.\n",tid);
-                pred_taken = false;
-            }
-        }
-    }
-
-    if (pred_taken) {
-        // Set the PC and the instruction's predicted target.
-        predPC = target;
-    }
-    DPRINTF(Branch, "[tid:%i]: [sn:%i]: Setting Predicted PC to %s.\n",
-            tid, seqNum, predPC);
-
-    predHist[tid].push_front(predict_record);
-
-    DPRINTF(Branch, "[tid:%i] [sn:%i] pushed onto front of predHist "
-            "...predHist.size(): %i\n",
-            tid, seqNum, predHist[tid].size());
-
-    return pred_taken;
-}
-
 void
 BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid)
 {
     DPRINTF(Branch, "[tid:%i]: Committing branches until "
             "[sn:%lli].\n", tid, done_sn);
 
+    iPred.commit(done_sn, tid);
     while (!predHist[tid].empty() &&
            predHist[tid].back().seqNum <= done_sn) {
         // Update the branch predictor with the correct results.
-        if (!predHist[tid].back().wasSquashed) {
-            update(predHist[tid].back().pc, predHist[tid].back().predTaken,
-                predHist[tid].back().bpHistory, false);
-        } else {
-            retireSquashed(predHist[tid].back().bpHistory);
-        }
+        update(tid, predHist[tid].back().pc,
+                    predHist[tid].back().predTaken,
+                    predHist[tid].back().bpHistory, false);
 
         predHist[tid].pop_back();
     }
@@ -407,6 +343,7 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
 {
     History &pred_hist = predHist[tid];
 
+    iPred.squash(squashed_sn, tid);
     while (!pred_hist.empty() &&
            pred_hist.front().seqNum > squashed_sn) {
         if (pred_hist.front().usedRAS) {
@@ -425,7 +362,7 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
         }
 
         // This call should delete the bpHistory.
-        squash(pred_hist.front().bpHistory);
+        squash(tid, pred_hist.front().bpHistory);
 
         DPRINTF(Branch, "[tid:%i]: Removing history for [sn:%i] "
                 "PC %s.\n", tid, pred_hist.front().seqNum,
@@ -485,11 +422,27 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
 
         if ((*hist_it).usedRAS) {
             ++RASIncorrect;
+            DPRINTF(Branch, "[tid:%i]: Incorrect RAS [sn:%i]\n",
+                    tid, hist_it->seqNum);
         }
 
-        update((*hist_it).pc, actually_taken,
+        // Get the underlying Global History Register
+        unsigned ghr = getGHR(tid, hist_it->bpHistory);
+
+        // There are separate functions for in-order and out-of-order
+        // branch prediction, but not for update. Therefore, this
+        // call should take into account that the mispredicted branch may
+        // be on the wrong path (i.e., OoO execution), and that the counter
+        // counter table(s) should not be updated. Thus, this call should
+        // restore the state of the underlying predictor, for instance the
+        // local/global histories. The counter tables will be updated when
+        // the branch actually commits.
+
+        // Remember the correct direction for the update at commit.
+        pred_hist.front().predTaken = actually_taken;
+
+        update(tid, (*hist_it).pc, actually_taken,
                pred_hist.front().bpHistory, true);
-        hist_it->wasSquashed = true;
 
         if (actually_taken) {
             if (hist_it->wasReturn && !hist_it->usedRAS) {
@@ -499,12 +452,15 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
                  RAS[tid].pop();
                  hist_it->usedRAS = true;
             }
+            if (hist_it->wasIndirect) {
+                ++indirectMispredicted;
+                iPred.recordTarget(hist_it->seqNum, ghr, corrTarget, tid);
+            } else {
+                DPRINTF(Branch,"[tid: %i] BTB Update called for [sn:%i]"
+                        " PC: %s\n", tid,hist_it->seqNum, hist_it->pc);
 
-            DPRINTF(Branch,"[tid: %i] BTB Update called for [sn:%i]"
-                    " PC: %s\n", tid,hist_it->seqNum, hist_it->pc);
-
-            BTB.update((*hist_it).pc, corrTarget, tid);
-
+                BTB.update((*hist_it).pc, corrTarget, tid);
+            }
         } else {
            //Actually not Taken
            if (hist_it->usedRAS) {