Tom Rollet has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/50727 )

Change subject: cpu: add perfect Branch Predictor
......................................................................

cpu: add perfect Branch Predictor

This commit adds a oracle branch predictor based on a trace file.
This BP have 0 missprediction on commited branches.

The trace file is generated by an earlier run and contains in order
the pc, upc and target of each taken branch. An option was added to
generate this file with the simple and o3 cpu.

The perfect branch predictor works for now with only 1 cpu, no smt
and checkpoints are not supported.

The trace can be generated with '--trace-BP' on the simple or o3 CPU.
The trace can be used with '--perfect-BP'.

Change-Id: Ib5c9e61c26d12edc040de2e2751c62835465cb95
---
M configs/common/Options.py
M configs/common/Simulation.py
M src/cpu/BaseCPU.py
M src/cpu/base.cc
M src/cpu/base.hh
M src/cpu/o3/commit.cc
M src/cpu/o3/commit.hh
M src/cpu/pred/BranchPredictor.py
M src/cpu/pred/SConscript
M src/cpu/pred/bpred_unit.cc
M src/cpu/pred/bpred_unit.hh
A src/cpu/pred/perfect.cc
A src/cpu/pred/perfect.hh
M src/cpu/simple/base.cc
14 files changed, 510 insertions(+), 7 deletions(-)



diff --git a/configs/common/Options.py b/configs/common/Options.py
index 7d72d4a..1187dac 100644
--- a/configs/common/Options.py
+++ b/configs/common/Options.py
@@ -263,6 +263,22 @@
     parser.add_argument("-l", "--lpae", action="store_true")
     parser.add_argument("-V", "--virtualisation", action="store_true")

+    parser.add_argument("--trace-BP", action="store", type=str,
+ help="""BranchPredictor trace file name. Will record + information about all the taken branches of this run.
+                        This created file, located in the output directory,
+                        can be used by a later run by the branch predictor
+                        with the option  '--perfect-BP'.
+ By appending '.gz' to the file name the trace will be
+                        written in a compressed format (gzip)
+                        Only works with a O3 or Base CPU""")
+
+    parser.add_argument("--perfect-BP", action="store", type=str,
+                        help="""Path to the branch predictor trace file.
+                        Will be used by the branch predictor to make
+ predictions. This trace file can be generated by using
+                        the option '--trace-BP'.
+                        The file can be gzip compressed.""")
     # dist-gem5 options
     parser.add_argument("--dist", action="store_true",
                         help="Parallel distributed gem5 simulation.")
diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py
index 3b9efc0..ac9f167 100644
--- a/configs/common/Simulation.py
+++ b/configs/common/Simulation.py
@@ -444,6 +444,18 @@
     if options.repeat_switch and options.take_checkpoints:
         fatal("Can't specify both --repeat-switch and --take-checkpoints")

+    if options.trace_BP:
+        if (len(testsys.cpu) != 1 or options.smt):
+            fatal("Can't use --trace-BP with more than 1 cpu or smt")
+        testsys.cpu[0].trace_BP = True
+        testsys.cpu[0].trace_file_BP = options.trace_BP
+    if options.perfect_BP:
+        if (len(testsys.cpu) != 1 or options.smt):
+            fatal("Can't use --perfect-BP with more than 1 cpu or smt")
+        testsys.cpu[0].branchPred.perfect = True
+        testsys.cpu[0].branchPred.perfectBranchPredictorTrace\
+                = options.perfect_BP
+
     # Setup global stat filtering.
     stat_root_simobjs = []
     for stat_root_str in options.stats_root:
diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py
index fb5cbe6..cce688f 100644
--- a/src/cpu/BaseCPU.py
+++ b/src/cpu/BaseCPU.py
@@ -170,6 +170,13 @@

     tracer = Param.InstTracer(default_tracer, "Instruction tracer")

+    trace_BP = Param.Bool(False,
+        "Generate a file with the Branch Predictor trace")
+ trace_file_BP = Param.String("", "Name of the BP trace file to create. "\
+                "Add the suffix '.bz' to automatically create compressed "\
+                "traces")
+
+
     icache_port = RequestPort("Instruction Port")
     dcache_port = RequestPort("Data Port")
     _cached_ports = ['icache_port', 'dcache_port']
diff --git a/src/cpu/base.cc b/src/cpu/base.cc
index 0b7ef88..e3ab509 100644
--- a/src/cpu/base.cc
+++ b/src/cpu/base.cc
@@ -146,6 +146,13 @@
         _cpuId = cpuList.size();
     }

+    // Generate a file with the trace of the branch predictor
+    if (p.trace_BP) {
+        // Open file in binary mode with possible bz compression
+        trace_bp_file = simout.create(p.trace_file_BP, true, false);
+    }
+
+
     // add self to global list of CPUs
     cpuList.push_back(this);

@@ -728,6 +735,21 @@
     }
 }

+void
+BaseCPU::generateBranchTrace(TheISA::PCState pcstate)
+{
+    assert(trace_bp_file);
+    Addr pc = pcstate.instAddr();
+    Addr npc = pcstate.nextInstAddr();
+    MicroPC upc = pcstate.microPC();
+
+    std::ostream *const s = trace_bp_file->stream();
+
+    s->write((char*)&pc, 8);
+    s->write((char*)&upc, 2);
+    s->write((char*)&npc, 8);
+    s->flush();
+}

 BaseCPU::GlobalStats::GlobalStats(statistics::Group *parent)
     : statistics::Group(parent),
diff --git a/src/cpu/base.hh b/src/cpu/base.hh
index 8a95f70..ae41ae0 100644
--- a/src/cpu/base.hh
+++ b/src/cpu/base.hh
@@ -42,6 +42,7 @@
 #ifndef __CPU_BASE_HH__
 #define __CPU_BASE_HH__

+#include <fstream>
 #include <vector>

 // Before we do anything else, check if this build is the NULL ISA,
@@ -51,6 +52,7 @@
 #error Including BaseCPU in a system without CPU support
 #else
 #include "arch/generic/interrupts.hh"
+#include "base/output.hh"
 #include "base/statistics.hh"
 #include "mem/port_proxy.hh"
 #include "sim/clocked_object.hh"
@@ -566,6 +568,12 @@
             traceFunctionsInternal(pc);
     }

+    /**
+     * Write the pc, upc and target of pcState. This function is used to
+     * generate a trace file for the perfect Branch Predictor.
+     */
+    void generateBranchTrace(TheISA::PCState pcstate);
+
     static int numSimulatedCPUs() { return cpuList.size(); }
     static Counter numSimulatedInsts()
     {
@@ -621,6 +629,11 @@
     const Cycles pwrGatingLatency;
     const bool powerGatingOnIdle;
     EventFunctionWrapper enterPwrGatingEvent;
+
+public:
+    // Trace file used to write the BP trace
+    OutputStream *trace_bp_file = nullptr;
+
 };

 } // namespace gem5
diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc
index 421f1e5..2325118 100644
--- a/src/cpu/o3/commit.cc
+++ b/src/cpu/o3/commit.cc
@@ -58,6 +58,7 @@
 #include "cpu/o3/thread_state.hh"
 #include "cpu/timebuf.hh"
 #include "debug/Activity.hh"
+#include "debug/Branch.hh"
 #include "debug/Commit.hh"
 #include "debug/CommitRate.hh"
 #include "debug/Drain.hh"
@@ -154,7 +155,10 @@
                "The number of times commit has been forced to stall to "
                "communicate backwards"),
       ADD_STAT(branchMispredicts, statistics::units::Count::get(),
-               "The number of times a branch was mispredicted"),
+               "Number of committed branch mispredictions"),
+      ADD_STAT(squashDueToBranch, statistics::units::Count::get(),
+              "Number of time commit stage has initiated a "
+              "squash due to branch misprediction"),
       ADD_STAT(numCommittedDist, statistics::units::Count::get(),
                "Number of insts commited each cycle"),
       ADD_STAT(instsCommitted, statistics::units::Count::get(),
@@ -881,7 +885,7 @@
if (toIEW->commitInfo[tid].mispredictInst->isUncondCtrl()) {
                      toIEW->commitInfo[tid].branchTaken = true;
                 }
-                ++stats.branchMispredicts;
+                ++stats.squashDueToBranch;
             }

             toIEW->commitInfo[tid].pc = fromIEW->pc[tid];
@@ -1283,6 +1287,13 @@

     updateComInstStats(head_inst);

+    TheISA::PCState pcstate = head_inst->pcState();
+
+    if (cpu->trace_bp_file && pcstate.branching()) {
+        assert(head_inst->staticInst->isControl());
+        cpu->generateBranchTrace(pcstate);
+    }
+
     DPRINTF(Commit,
             "[tid:%i] [sn:%llu] Committing instruction with PC %s\n",
             tid, head_inst->seqNum, head_inst->pcState());
@@ -1435,6 +1446,13 @@
     if (inst->isCall())
         stats.functionCalls[tid]++;

+
+    if (inst->mispredicted()) {
+        stats.branchMispredicts++;
+        DPRINTF(Branch, "Committing mispredicted branch [sn:%lu] %lx\n",
+                inst->seqNum, inst->instAddr());
+    }
+
 }

 ////////////////////////////////////////
diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh
index bcb7c23..7352dd2 100644
--- a/src/cpu/o3/commit.hh
+++ b/src/cpu/o3/commit.hh
@@ -482,10 +482,14 @@
          * to a non-speculative instruction reaching the head of the ROB.
          */
         statistics::Scalar commitNonSpecStalls;
-        /** Stat for the total number of branch mispredicts that caused a
-         * squash.
-         */
+        /** Stat for the total number of committed branch mispredicted. */
         statistics::Scalar branchMispredicts;
+        /** Stat for the total number of time the commit stage has squashed
+          * because of a mispredicted branch.
+ * Note that the squash can be initiated by a mispredicted branch that
+          * will later be squashed by another, older mispeculation.
+          */
+        statistics::Scalar squashDueToBranch;
/** Distribution of the number of committed instructions each cycle. */
         statistics::Distribution numCommittedDist;

diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py
index c6abebb..a821d6d 100644
--- a/src/cpu/pred/BranchPredictor.py
+++ b/src/cpu/pred/BranchPredictor.py
@@ -67,6 +67,12 @@
     indirectBranchPred = Param.IndirectPredictor(SimpleIndirectPredictor(),
"Indirect branch predictor, set to NULL to disable indirect predictions")

+    perfect = Param.Bool(False, "Enable perfect branch predictor")
+    perfectBranchPredictorTrace = Param.String("",
+ "Path to the trace BP file. It can also be a gzip compressed file")
+    perfectBufferSize = Param.Unsigned(1500, "Size of the trace buffer")
+
+
 class LocalBP(BranchPredictor):
     type = 'LocalBP'
     cxx_class = 'gem5::branch_prediction::LocalBP'
diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript
index fcc32e9..1c8289a 100644
--- a/src/cpu/pred/SConscript
+++ b/src/cpu/pred/SConscript
@@ -56,6 +56,7 @@
 Source('tage_sc_l.cc')
 Source('tage_sc_l_8KB.cc')
 Source('tage_sc_l_64KB.cc')
+Source('perfect.cc')
 DebugFlag('FreeList')
 DebugFlag('Branch')
 DebugFlag('Tage')
diff --git a/src/cpu/pred/bpred_unit.cc b/src/cpu/pred/bpred_unit.cc
index 5d23a26..455aa1b 100644
--- a/src/cpu/pred/bpred_unit.cc
+++ b/src/cpu/pred/bpred_unit.cc
@@ -48,6 +48,7 @@
 #include "base/compiler.hh"
 #include "base/trace.hh"
 #include "config/the_isa.hh"
+#include "cpu/pred/perfect.hh"
 #include "debug/Branch.hh"

 namespace gem5
@@ -67,8 +68,16 @@
       RAS(numThreads),
       iPred(params.indirectBranchPred),
       stats(this),
-      instShiftAmt(params.instShiftAmt)
+      instShiftAmt(params.instShiftAmt),
+      hasPerfectBPU(params.perfect)
 {
+    if (hasPerfectBPU) {
+ fatal_if(numThreads > 1, "BP perfect option is currently only working"
+                " for 1 cpu with no smt.\n");
+        perfectBPU = new PerfectBPU(params.perfectBranchPredictorTrace,
+                params.perfectBufferSize);
+    }
+
     for (auto& r : RAS)
         r.init(params.RASSize);
 }
@@ -130,6 +139,10 @@
 BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
                    TheISA::PCState &pc, ThreadID tid)
 {
+    // Perfect branch bredictor
+    if (hasPerfectBPU)
+        return perfectBPU->perfectPredict(inst, seqNum, pc);
+
     // 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
@@ -305,7 +318,13 @@
 BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid)
 {
     DPRINTF(Branch, "[tid:%i] Committing branches until "
-            "sn:%llu]\n", tid, done_sn);
+            "[sn:%llu]\n", tid, done_sn);
+
+    if (hasPerfectBPU) {
+        perfectBPU->releaseEntries(done_sn);
+        return;
+    }
+

     while (!predHist[tid].empty() &&
            predHist[tid].back().seqNum <= done_sn) {
@@ -327,6 +346,12 @@
 void
 BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
 {
+
+    if (hasPerfectBPU) {
+        perfectBPU->squash(squashed_sn);
+        return;
+    }
+
     History &pred_hist = predHist[tid];

     if (iPred) {
diff --git a/src/cpu/pred/bpred_unit.hh b/src/cpu/pred/bpred_unit.hh
index d6b40e1..59e9914 100644
--- a/src/cpu/pred/bpred_unit.hh
+++ b/src/cpu/pred/bpred_unit.hh
@@ -48,6 +48,7 @@
 #include "base/types.hh"
 #include "cpu/pred/btb.hh"
 #include "cpu/pred/indirect.hh"
+#include "cpu/pred/perfect.hh"
 #include "cpu/pred/ras.hh"
 #include "cpu/inst_seq.hh"
 #include "cpu/static_inst.hh"
@@ -344,6 +345,16 @@
     /** Miss-predicted branches */
     probing::PMUUPtr ppMisses;

+  public:
+    /**
+     * Set to true by the param perfect. When set the BP will make perfect
+     * predictions based on a trace file.
+     */
+    bool hasPerfectBPU;
+
+    /** The object used to make perfect predictions */
+    PerfectBPU *perfectBPU;
+
     /** @} */
 };

diff --git a/src/cpu/pred/perfect.cc b/src/cpu/pred/perfect.cc
new file mode 100644
index 0000000..3ef4d0a
--- /dev/null
+++ b/src/cpu/pred/perfect.cc
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2021 Huawei Technologies Switzerland
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "cpu/pred/perfect.hh"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <zlib.h>
+
+#include "base/circular_queue.hh"
+#include "base/named.hh"
+#include "base/types.hh"
+#include "cpu/inst_seq.hh"
+#include "cpu/static_inst.hh"
+#include "debug/Branch.hh"
+
+namespace gem5
+{
+
+namespace branch_prediction
+{
+
+PerfectBPU::PerfectBPU(const std::string name, unsigned bufferSize)
+    : Named("PerfectBPU"),
+      // Must be greater than the number of of inflight branches in the CPU
+      // pipeline
+      takenBranchesTrace(bufferSize)
+{
+    // Open trace file
+    int fd = open(name.c_str(), O_RDONLY);
+    panic_if(fd <= 0, "Failed to open trace file '%s'\n",
+            name.c_str());
+
+    // Open to inflate gzip
+    // If the file is not compressed, then it will be read normally
+    trace_bp_fdz = gzdopen(fd, "rb");
+    panic_if(!trace_bp_fdz, "Failed to gzopen() trace file.\n");
+
+    // Fill the buffer with trace data
+    for (int i = 0; i < takenBranchesTrace.capacity(); ++i) {
+        if (!fetchOneEntry()) {
+            trace_file_empty = true;
+            break;
+        }
+    }
+}
+
+void PerfectBPU::releaseEntries(const InstSeqNum &seqNum)
+{
+    //Remove committed branches
+    int num_released = 0;
+    while (takenBranchesTrace.front().seqNum != 0 &&
+            takenBranchesTrace.front().seqNum <= seqNum) {
+        // Pop front only increments the head counter
+        // Thus we need to zero the entry
+        takenBranchesTrace.front() = {0, 0, 0, 0};
+        takenBranchesTrace.pop_front();
+        num_released++;
+    }
+}
+
+void
+PerfectBPU::fetchNewEntries()
+{
+    int num_fetched = 0;
+ // Fetch new entries until the buffer is full or the input file is fully
+    // read
+    while (takenBranchesTrace.size() != takenBranchesTrace.capacity()) {
+        if (!fetchOneEntry()) {
+            trace_file_empty = true;
+            break;
+        }
+        num_fetched++;
+    }
+
+    panic_if(num_fetched == 0 && !trace_file_empty,
+            "The trace BP queue is too small compared to the number "
+            "of inflight branches in the CPU pipeline."
+ "Please increase the size of the trace buffer. It can be done by " + "increasing the value of system.cpu.branchPred.perfectBufferSize");
+}
+
+void
+PerfectBPU::squash(InstSeqNum squashedSn)
+{
+
+    DPRINTF(Branch, "Rewinding trace to [sn:%llu]\n", squashedSn);
+
+    /* While -the index is not at the first entry
+     *       -the previous entry needs to be squashed
+     * Then reset the seqNum counter and decrements the index (traceOffset)
+     */
+    while ((takenBranchesTrace.head() !=
+                (traceOffset % takenBranchesTrace.capacity())) &&
+            takenBranchesTrace[traceOffset - 1].seqNum >  squashedSn) {
+
+        DPRINTF(Branch,
+                "    [sn:%llu] removed. PC: %lx, uPC: %u, Target: %lx\n",
+                takenBranchesTrace[traceOffset - 1].seqNum,
+                takenBranchesTrace[traceOffset - 1].pc,
+                takenBranchesTrace[traceOffset - 1].upc,
+                takenBranchesTrace[traceOffset - 1].target
+                );
+
+        // Clear trace seqNum
+        takenBranchesTrace[traceOffset - 1].seqNum = 0;
+
+        // Offset from circularQueue starts at 1
+        assert(traceOffset > 1);
+        traceOffset--;
+    }
+}
+
+bool
+PerfectBPU::fetchOneEntry()
+{
+    // Trace format is 64bit pc, 16 bit upc and 64bit target.
+
+
+    uint64_t pc, target;
+    uint16_t upc;
+    int read = 0;
+
+    // Read pc
+    read = gzread(trace_bp_fdz, (char*)&pc, 8);
+    // Test if the file is fully read
+    if (read == 0)
+        return false;
+
+    // Read upc
+    read = gzread(trace_bp_fdz, (char*)&upc, 2);
+    assert(read == 2);
+
+    // Read target
+    read = gzread(trace_bp_fdz, (char*)&target, 8);
+    assert(read == 8);
+
+    struct BPTraceEntry entry = BPTraceEntry{pc, upc, target, 0};
+    takenBranchesTrace.push_back(entry);
+
+    return true;
+}
+
+bool
+PerfectBPU::perfectPredict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
+                   TheISA::PCState &pc)
+{
+    // If we have read all the entries from the trace buffer and the trace
+    // file does not contain anymore entries:
+    // The branch will not be taken
+    // Typical case in SE mode: branches past the exit syscall
+    if ((traceOffset == takenBranchesTrace.tail() + 1)
+            && trace_file_empty) {
+        DPRINTF(Branch, "[sn:%lu], pc: %llx, Past the end of file\n",
+                seqNum, pc.instAddr());
+        return false;
+    }
+
+    // Match the current pc with the first unused branch pc from trace
+    // If match, this mean that the branch will be taken
+    if (pc.instAddr() == takenBranchesTrace[traceOffset].pc
+            && pc.microPC() == takenBranchesTrace[traceOffset].upc) {
+
+        DPRINTF(Branch, "[sn:%lu] PC %lx, uPC %d: Predicted taken to"
+                " %lx\n",
+                seqNum,
+                takenBranchesTrace[traceOffset].pc,
+                takenBranchesTrace[traceOffset].upc,
+                takenBranchesTrace[traceOffset].target);
+
+        //Set the seqNum for the squash and the release of entries
+        takenBranchesTrace[traceOffset].seqNum = seqNum;
+
+        //Set the current pc to the target address from trace file
+        pc.set(takenBranchesTrace[traceOffset].target);
+
+        //Inc the offset in the trace buffer
+        traceOffset++;
+
+        // If traceOffset reach the end of the current trace,
+        // fetch new entries
+        if (traceOffset == takenBranchesTrace.tail() + 1)
+            fetchNewEntries();
+
+        return true;
+    } else {
+        // Branch not taken, update pc
+        DPRINTF(Branch,
+ "[sn:%lu] PC: %lx, next taken PC: %lx Predicted not taken\n",
+                seqNum, pc.instAddr(), takenBranchesTrace[traceOffset].pc);
+        inst->advancePC(pc);
+        return false;
+    }
+}
+
+} // namespace branch_prediction
+} // namespace gem5
diff --git a/src/cpu/pred/perfect.hh b/src/cpu/pred/perfect.hh
new file mode 100644
index 0000000..10ed1cc
--- /dev/null
+++ b/src/cpu/pred/perfect.hh
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2021 Huawei Technologies Switzerland
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __CPU_PERFECT_BP_HH__
+#define __CPU_PERFECT_BP_HH__
+
+#include <zlib.h>
+
+#include "base/circular_queue.hh"
+#include "base/named.hh"
+#include "base/types.hh"
+#include "cpu/inst_seq.hh"
+#include "cpu/static_inst.hh"
+
+namespace gem5
+{
+
+namespace branch_prediction
+{
+
+ /**
+  * PerfectBPU implements a branch predictor oracle.
+  * It is done by reading a trace from an earlier execution. This trace
+  * contains the pc, upc and target from all the taken branches, in order.
+  * The trace is read from a compressed or uncompressed file into a buffer.
+  * Entries is the buffer are released when the corresponding branch has
+ * been committed. When the next branch to predict is not in the buffer, new
+  * entries from the trace file will be fetched.
+  * In the bpred unit, the predict, update and squash functions are hooked
+  * and if the perfect option is set, the functions from the perfectBPU
+  * will be used instead.
+  */
+class PerfectBPU : public Named
+{
+
+  public:
+    PerfectBPU(const std::string name, unsigned bufferSize);
+
+    /**
+     * Rewind the trace until a given sequence number.
+ * @partam squashedSn The sequence number used. All younger entries will
+     * be rewinded.
+     */
+    void squash(InstSeqNum squashedSn);
+
+    /**
+ * Returns whether or not the instructions is a taken branch, and set the
+     * target if it is taken. This is done based on a trace from an
+     * earlier execution.
+     * @param inst The branch instruction.
+     * @param PC The predicted PC is passed back through this parameter.
+     * @return Returns if the branch is taken or not.
+     */
+ bool perfectPredict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
+            TheISA::PCState &pc);
+
+    /**
+     * Release committed entries in the trace buffer.
+ * @param seqNum The sequence number from the latest committed instruction
+     */
+    void releaseEntries(const InstSeqNum &seqNum);
+
+  private:
+
+    /**
+     * File from which the trace is read
+     * It works with a gzip file or an uncompressed file
+     */
+    gzFile trace_bp_fdz;
+
+    /** Set when the trace file is fully read */
+    bool trace_file_empty = false;
+
+    /**
+     * @struct BPTraceEntry
+     * @brief Entry containing the trace information from taken branches.
+ * @var seqNum The seqNum field is set when the entry is used. In case of a
+     * rewind due to a squash, the seqNum entry will be cleared
+     */
+    struct BPTraceEntry
+    {
+        uint64_t pc;
+        uint16_t upc;
+        uint64_t target;
+        uint64_t seqNum;
+    };
+
+    /** Circular buffer with the fetched trace entries */
+    CircularQueue<BPTraceEntry> takenBranchesTrace;
+    /** Current offset in the circular trace buffer */
+    int traceOffset = 1;
+
+    /**
+ * Fetch new entries until the trace buffer is full or the file if fully
+     * read
+     */
+    void fetchNewEntries();
+
+    /**
+     * Tries to read one entry from the [compressed] file
+     * Return true only if an entry was read and put in the trace buffer
+     * Return false otherwise
+     */
+    bool fetchOneEntry();
+};
+
+
+} // namespace branch_prediction
+} // namespace gem5
+
+#endif // __CPU_PERFECT_BP_HH__
diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc
index 8854e9a..aee5351 100644
--- a/src/cpu/simple/base.cc
+++ b/src/cpu/simple/base.cc
@@ -463,6 +463,14 @@

     const bool branching(thread->pcState().branching());

+    TheISA::PCState pcstate = t_info.pcState();
+
+    if (trace_bp_file && curStaticInst && branching) {
+        assert(curStaticInst->isControl());
+        generateBranchTrace(pcstate);
+    }
+
+
     //Since we're moving to a new pc, zero out the offset
     t_info.fetchOffset = 0;
     if (fault != NoFault) {

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/50727
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: Ib5c9e61c26d12edc040de2e2751c62835465cb95
Gerrit-Change-Number: 50727
Gerrit-PatchSet: 1
Gerrit-Owner: Tom Rollet <tom.rol...@huawei.com>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to