cpu-o3: Mostly use PCStateBase instead of TheISA::PCState.

There are a few places where TheISA::PCState is still necessary,
specifically when checking if a PC is branching, and also when getting
the nextInstAddr.

It's likely that checking if a PC is branching will become part of the
base PCState interface, but nextInstAddr will likely be removed from the
ThreadContext, ExecContext, etc, interfaces, and then removed from the
interfaces in the O3 which doesn't seem to use them internally.

Change-Id: I499f31d569b9b0c665a745caf612d1e96babf37a
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52051
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Gabe Black <gabe.black@gmail.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
diff --git a/src/cpu/o3/comm.hh b/src/cpu/o3/comm.hh
index fc10848..b2da358 100644
--- a/src/cpu/o3/comm.hh
+++ b/src/cpu/o3/comm.hh
@@ -94,7 +94,7 @@
     DynInstPtr mispredictInst[MaxThreads];
     Addr mispredPC[MaxThreads];
     InstSeqNum squashedSeqNum[MaxThreads];
-    TheISA::PCState pc[MaxThreads];
+    std::unique_ptr<PCStateBase> pc[MaxThreads];
 
     bool squash[MaxThreads];
     bool branchMispredict[MaxThreads];
@@ -114,7 +114,7 @@
 {
     struct DecodeComm
     {
-        TheISA::PCState nextPC;
+        std::unique_ptr<PCStateBase> nextPC;
         DynInstPtr mispredictInst;
         DynInstPtr squashInst;
         InstSeqNum doneSeqNum;
@@ -169,7 +169,7 @@
         /// The pc of the next instruction to execute. This is the next
         /// instruction for a branch mispredict, but the same instruction for
         /// order violation and the like
-        TheISA::PCState pc; // *F
+        std::unique_ptr<PCStateBase> pc; // *F
 
         /// Provide fetch the instruction that mispredicted, if this
         /// pointer is not-null a misprediction occured
diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc
index 421f1e5..01ec0c8 100644
--- a/src/cpu/o3/commit.cc
+++ b/src/cpu/o3/commit.cc
@@ -120,7 +120,7 @@
         trapSquash[tid] = false;
         tcSquash[tid] = false;
         squashAfterInst[tid] = nullptr;
-        pc[tid].set(0);
+        pc[tid].reset(params.isa[0]->newPCState());
         youngestSeqNum[tid] = 0;
         lastCommitedSeqNum[tid] = 0;
         trapInFlight[tid] = false;
@@ -340,7 +340,7 @@
     committedStores[tid] = false;
     trapSquash[tid] = false;
     tcSquash[tid] = false;
-    pc[tid].set(0);
+    pc[tid].reset(cpu->tcBase(tid)->getIsaPtr()->newPCState());
     lastCommitedSeqNum[tid] = 0;
     squashAfterInst[tid] = NULL;
 }
@@ -382,7 +382,7 @@
      *   address mappings. This can happen on for example x86.
      */
     for (ThreadID tid = 0; tid < numThreads; tid++) {
-        if (pc[tid].microPC() != 0)
+        if (pc[tid]->microPC() != 0)
             return false;
     }
 
@@ -561,7 +561,7 @@
     toIEW->commitInfo[tid].mispredictInst = NULL;
     toIEW->commitInfo[tid].squashInst = NULL;
 
-    toIEW->commitInfo[tid].pc = pc[tid];
+    set(toIEW->commitInfo[tid].pc, pc[tid]);
 }
 
 void
@@ -569,7 +569,7 @@
 {
     squashAll(tid);
 
-    DPRINTF(Commit, "Squashing from trap, restarting at PC %s\n", pc[tid]);
+    DPRINTF(Commit, "Squashing from trap, restarting at PC %s\n", *pc[tid]);
 
     thread[tid]->trapPending = false;
     thread[tid]->noSquashFromTC = false;
@@ -586,7 +586,7 @@
 {
     squashAll(tid);
 
-    DPRINTF(Commit, "Squashing from TC, restarting at PC %s\n", pc[tid]);
+    DPRINTF(Commit, "Squashing from TC, restarting at PC %s\n", *pc[tid]);
 
     thread[tid]->noSquashFromTC = false;
     assert(!thread[tid]->trapPending);
@@ -601,7 +601,7 @@
 Commit::squashFromSquashAfter(ThreadID tid)
 {
     DPRINTF(Commit, "Squashing after squash after request, "
-            "restarting at PC %s\n", pc[tid]);
+            "restarting at PC %s\n", *pc[tid]);
 
     squashAll(tid);
     // Make sure to inform the fetch stage of which instruction caused
@@ -843,8 +843,7 @@
             }
 
             DPRINTF(Commit, "[tid:%i] Redirecting to PC %#x\n",
-                    tid,
-                    fromIEW->pc[tid].nextInstAddr());
+                    tid, *fromIEW->pc[tid]);
 
             commitStatus[tid] = ROBSquashing;
 
@@ -884,7 +883,7 @@
                 ++stats.branchMispredicts;
             }
 
-            toIEW->commitInfo[tid].pc = fromIEW->pc[tid];
+            set(toIEW->commitInfo[tid].pc, fromIEW->pc[tid]);
         }
 
         if (commitStatus[tid] == ROBSquashing) {
@@ -1014,7 +1013,7 @@
             // Record that the number of ROB entries has changed.
             changedROBNumEntries[tid] = true;
         } else {
-            pc[tid] = head_inst->pcState();
+            set(pc[tid], head_inst->pcState());
 
             // Try to commit the head instruction.
             bool commit_success = commitHead(head_inst, num_committed);
@@ -1064,9 +1063,9 @@
                     cpu->checker->verify(head_inst);
                 }
 
-                cpu->traceFunctions(pc[tid].instAddr());
+                cpu->traceFunctions(pc[tid]->instAddr());
 
-                head_inst->staticInst->advancePC(pc[tid]);
+                head_inst->staticInst->advancePC(*pc[tid]);
 
                 // Keep track of the last sequence number commited
                 lastCommitedSeqNum[tid] = head_inst->seqNum;
@@ -1077,12 +1076,12 @@
                     squashAfter(tid, head_inst);
 
                 if (drainPending) {
-                    if (pc[tid].microPC() == 0 && interrupt == NoFault &&
+                    if (pc[tid]->microPC() == 0 && interrupt == NoFault &&
                         !thread[tid]->trapPending) {
                         // Last architectually committed instruction.
                         // Squash the pipeline, stall fetch, and use
                         // drainImminent to disable interrupts
-                        DPRINTF(Drain, "Draining: %i:%s\n", tid, pc[tid]);
+                        DPRINTF(Drain, "Draining: %i:%s\n", tid, *pc[tid]);
                         squashAfter(tid, head_inst);
                         cpu->commitDrained(tid);
                         drainImminent = true;
@@ -1101,11 +1100,11 @@
                     assert(!thread[tid]->noSquashFromTC &&
                            !thread[tid]->trapPending);
                     do {
-                        oldpc = pc[tid].instAddr();
+                        oldpc = pc[tid]->instAddr();
                         thread[tid]->pcEventQueue.service(
                                 oldpc, thread[tid]->getTC());
                         count++;
-                    } while (oldpc != pc[tid].instAddr());
+                    } while (oldpc != pc[tid]->instAddr());
                     if (count > 1) {
                         DPRINTF(Commit,
                                 "PC skip function event, stopping commit\n");
diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh
index bcb7c23..8a005e2 100644
--- a/src/cpu/o3/commit.hh
+++ b/src/cpu/o3/commit.hh
@@ -306,20 +306,23 @@
 
   public:
     /** Reads the PC of a specific thread. */
-    TheISA::PCState pcState(ThreadID tid) { return pc[tid]; }
+    const PCStateBase &pcState(ThreadID tid) { return *pc[tid]; }
 
     /** Sets the PC of a specific thread. */
-    void pcState(const TheISA::PCState &val, ThreadID tid)
-    { pc[tid] = val; }
+    void pcState(const PCStateBase &val, ThreadID tid) { set(pc[tid], val); }
 
     /** Returns the PC of a specific thread. */
-    Addr instAddr(ThreadID tid) { return pc[tid].instAddr(); }
+    Addr instAddr(ThreadID tid) { return pc[tid]->instAddr(); }
 
     /** Returns the next PC of a specific thread. */
-    Addr nextInstAddr(ThreadID tid) { return pc[tid].nextInstAddr(); }
+    Addr
+    nextInstAddr(ThreadID tid)
+    {
+        return pc[tid]->as<TheISA::PCState>().nextInstAddr();
+    }
 
     /** Reads the micro PC of a specific thread. */
-    Addr microPC(ThreadID tid) { return pc[tid].microPC(); }
+    Addr microPC(ThreadID tid) { return pc[tid]->microPC(); }
 
   private:
     /** Time buffer interface. */
@@ -431,7 +434,7 @@
     /** The commit PC state of each thread.  Refers to the instruction that
      * is currently being processed/committed.
      */
-    TheISA::PCState pc[MaxThreads];
+    std::unique_ptr<PCStateBase> pc[MaxThreads];
 
     /** The sequence number of the youngest valid instruction in the ROB. */
     InstSeqNum youngestSeqNum[MaxThreads];
diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc
index a155d78..05d3a51 100644
--- a/src/cpu/o3/cpu.cc
+++ b/src/cpu/o3/cpu.cc
@@ -1310,7 +1310,7 @@
 TheISA::PCState
 CPU::pcState(ThreadID tid)
 {
-    return commit.pcState(tid);
+    return commit.pcState(tid).as<TheISA::PCState>();
 }
 
 void
diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc
index 5a27573..d10e19a 100644
--- a/src/cpu/o3/decode.cc
+++ b/src/cpu/o3/decode.cc
@@ -715,13 +715,13 @@
         {
             ++stats.branchResolved;
 
-            if (*inst->branchTarget() != inst->readPredTarg()) {
+            std::unique_ptr<PCStateBase> target = inst->branchTarget();
+            if (*target != inst->readPredTarg()) {
                 ++stats.branchMispred;
 
                 // Might want to set some sort of boolean and just do
                 // a check at the end
                 squash(inst, inst->threadNumber);
-                std::unique_ptr<PCStateBase> target = inst->branchTarget();
 
                 DPRINTF(Decode,
                         "[tid:%i] [sn:%llu] "
@@ -729,7 +729,7 @@
                         PredPC: %s\n",
                         tid, inst->seqNum, inst->readPredTarg(), *target);
                 //The micro pc after an instruction level branch should be 0
-                inst->setPredTarg(target->as<TheISA::PCState>());
+                inst->setPredTarg(*target);
                 break;
             }
         }
diff --git a/src/cpu/o3/dyn_inst.cc b/src/cpu/o3/dyn_inst.cc
index 37db1ed..8284c9a 100644
--- a/src/cpu/o3/dyn_inst.cc
+++ b/src/cpu/o3/dyn_inst.cc
@@ -53,11 +53,10 @@
 {
 
 DynInst::DynInst(const StaticInstPtr &static_inst,
-        const StaticInstPtr &_macroop, TheISA::PCState _pc,
-        TheISA::PCState pred_pc, InstSeqNum seq_num, CPU *_cpu)
-    : seqNum(seq_num), staticInst(static_inst), cpu(_cpu), pc(_pc),
+        const StaticInstPtr &_macroop, InstSeqNum seq_num, CPU *_cpu)
+    : seqNum(seq_num), staticInst(static_inst), cpu(_cpu),
       regs(staticInst->numSrcRegs(), staticInst->numDestRegs()),
-      predPC(pred_pc), macroop(_macroop)
+      macroop(_macroop)
 {
     regs.init();
 
@@ -90,9 +89,18 @@
 
 }
 
+DynInst::DynInst(const StaticInstPtr &static_inst,
+        const StaticInstPtr &_macroop, const PCStateBase &_pc,
+        const PCStateBase &pred_pc, InstSeqNum seq_num, CPU *_cpu)
+    : DynInst(static_inst, _macroop, seq_num, _cpu)
+{
+    set(pc, _pc);
+    set(predPC, pred_pc);
+}
+
 DynInst::DynInst(const StaticInstPtr &_staticInst,
         const StaticInstPtr &_macroop)
-    : DynInst(_staticInst, _macroop, {}, {}, 0, nullptr)
+    : DynInst(_staticInst, _macroop, 0, nullptr)
 {}
 
 DynInst::~DynInst()
@@ -166,8 +174,8 @@
 void
 DynInst::dump()
 {
-    cprintf("T%d : %#08d `", threadNumber, pc.instAddr());
-    std::cout << staticInst->disassemble(pc.instAddr());
+    cprintf("T%d : %#08d `", threadNumber, pc->instAddr());
+    std::cout << staticInst->disassemble(pc->instAddr());
     cprintf("'\n");
 }
 
@@ -175,8 +183,8 @@
 DynInst::dump(std::string &outstring)
 {
     std::ostringstream s;
-    s << "T" << threadNumber << " : 0x" << pc.instAddr() << " "
-      << staticInst->disassemble(pc.instAddr());
+    s << "T" << threadNumber << " : 0x" << pc->instAddr() << " "
+      << staticInst->disassemble(pc->instAddr());
 
     outstring = s.str();
 }
diff --git a/src/cpu/o3/dyn_inst.hh b/src/cpu/o3/dyn_inst.hh
index 38815b1..c7e99a4 100644
--- a/src/cpu/o3/dyn_inst.hh
+++ b/src/cpu/o3/dyn_inst.hh
@@ -75,13 +75,17 @@
 
 class DynInst : public ExecContext, public RefCounted
 {
+  private:
+    DynInst(const StaticInstPtr &staticInst, const StaticInstPtr &macroop,
+            InstSeqNum seq_num, CPU *cpu);
+
   public:
     // The list of instructions iterator type.
     typedef typename std::list<DynInstPtr>::iterator ListIt;
 
     /** BaseDynInst constructor given a binary instruction. */
     DynInst(const StaticInstPtr &staticInst, const StaticInstPtr
-            &macroop, TheISA::PCState pc, TheISA::PCState predPC,
+            &macroop, const PCStateBase &pc, const PCStateBase &pred_pc,
             InstSeqNum seq_num, CPU *cpu);
 
     /** BaseDynInst constructor given a static inst pointer. */
@@ -182,7 +186,7 @@
     std::queue<InstResult> instResult;
 
     /** PC state for this instruction. */
-    TheISA::PCState pc;
+    std::unique_ptr<PCStateBase> pc;
 
     /** Values to be written to the destination misc. registers. */
     std::vector<RegVal> _destMiscRegVal;
@@ -361,7 +365,7 @@
 
     ////////////////////// Branch Data ///////////////
     /** Predicted PC state after this instruction. */
-    TheISA::PCState predPC;
+    std::unique_ptr<PCStateBase> predPC;
 
     /** The Macroop if one exists */
     const StaticInstPtr macroop;
@@ -551,18 +555,22 @@
     bool doneTargCalc() { return false; }
 
     /** Set the predicted target of this current instruction. */
-    void setPredTarg(const TheISA::PCState &_predPC) { predPC = _predPC; }
+    void setPredTarg(const PCStateBase &pred_pc) { set(predPC, pred_pc); }
 
-    const TheISA::PCState &readPredTarg() { return predPC; }
+    const PCStateBase &readPredTarg() { return *predPC; }
 
     /** Returns the predicted PC immediately after the branch. */
-    Addr predInstAddr() { return predPC.instAddr(); }
+    Addr predInstAddr() { return predPC->instAddr(); }
 
     /** Returns the predicted PC two instructions after the branch */
-    Addr predNextInstAddr() { return predPC.nextInstAddr(); }
+    Addr
+    predNextInstAddr()
+    {
+        return predPC->as<TheISA::PCState>().nextInstAddr();
+    }
 
     /** Returns the predicted micro PC after the branch */
-    Addr predMicroPC() { return predPC.microPC(); }
+    Addr predMicroPC() { return predPC->microPC(); }
 
     /** Returns whether the instruction was predicted taken or not. */
     bool readPredTaken() { return instFlags[PredTaken]; }
@@ -577,9 +585,9 @@
     bool
     mispredicted()
     {
-        TheISA::PCState tempPC = pc;
-        staticInst->advancePC(tempPC);
-        return !(tempPC == predPC);
+        std::unique_ptr<PCStateBase> next_pc(pc->clone());
+        staticInst->advancePC(*next_pc);
+        return *next_pc != *predPC;
     }
 
     //
@@ -720,7 +728,7 @@
     std::unique_ptr<PCStateBase>
     branchTarget() const
     {
-        return staticInst->branchTarget(pc);
+        return staticInst->branchTarget(*pc);
     }
 
     /** Returns the number of source registers. */
@@ -941,19 +949,27 @@
     }
 
     /** Read the PC state of this instruction. */
-    TheISA::PCState pcState() const override { return pc; }
+    TheISA::PCState
+    pcState() const override
+    {
+        return pc->as<TheISA::PCState>();
+    }
 
     /** Set the PC state of this instruction. */
-    void pcState(const TheISA::PCState &val) override { pc = val; }
+    void pcState(const TheISA::PCState &val) override { set(pc, val); }
 
     /** Read the PC of this instruction. */
-    Addr instAddr() const { return pc.instAddr(); }
+    Addr instAddr() const { return pc->instAddr(); }
 
     /** Read the PC of the next instruction. */
-    Addr nextInstAddr() const { return pc.nextInstAddr(); }
+    Addr
+    nextInstAddr() const
+    {
+        return pc->as<TheISA::PCState>().nextInstAddr();
+    }
 
     /**Read the micro PC of this instruction. */
-    Addr microPC() const { return pc.microPC(); }
+    Addr microPC() const { return pc->microPC(); }
 
     bool readPredicate() const override { return instFlags[Predicate]; }
 
diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc
index cfc3a83..b87af34 100644
--- a/src/cpu/o3/fetch.cc
+++ b/src/cpu/o3/fetch.cc
@@ -120,7 +120,7 @@
     for (int i = 0; i < MaxThreads; i++) {
         fetchStatus[i] = Idle;
         decoder[i] = nullptr;
-        pc[i].set(0);
+        pc[i].reset(params.isa[0]->newPCState());
         fetchOffset[i] = 0;
         macroop[i] = nullptr;
         delayedCommit[i] = false;
@@ -299,7 +299,7 @@
 Fetch::clearStates(ThreadID tid)
 {
     fetchStatus[tid] = Running;
-    pc[tid] = cpu->pcState(tid);
+    set(pc[tid], cpu->pcState(tid));
     fetchOffset[tid] = 0;
     macroop[tid] = NULL;
     delayedCommit[tid] = false;
@@ -326,7 +326,7 @@
     // Setup PC and nextPC with initial state.
     for (ThreadID tid = 0; tid < numThreads; ++tid) {
         fetchStatus[tid] = Running;
-        pc[tid] = cpu->pcState(tid);
+        set(pc[tid], cpu->pcState(tid));
         fetchOffset[tid] = 0;
         macroop[tid] = NULL;
 
@@ -508,7 +508,7 @@
 }
 
 bool
-Fetch::lookupAndUpdateNextPC(const DynInstPtr &inst, TheISA::PCState &nextPC)
+Fetch::lookupAndUpdateNextPC(const DynInstPtr &inst, PCStateBase &next_pc)
 {
     // Do branch prediction check here.
     // A bit of a misnomer...next_PC is actually the current PC until
@@ -516,20 +516,20 @@
     bool predict_taken;
 
     if (!inst->isControl()) {
-        inst->staticInst->advancePC(nextPC);
-        inst->setPredTarg(nextPC);
+        inst->staticInst->advancePC(next_pc);
+        inst->setPredTarg(next_pc);
         inst->setPredTaken(false);
         return false;
     }
 
     ThreadID tid = inst->threadNumber;
     predict_taken = branchPred->predict(inst->staticInst, inst->seqNum,
-                                        nextPC, tid);
+                                        next_pc.as<TheISA::PCState>(), tid);
 
     if (predict_taken) {
         DPRINTF(Fetch, "[tid:%i] [sn:%llu] Branch at PC %#x "
                 "predicted to be taken to %s\n",
-                tid, inst->seqNum, inst->pcState().instAddr(), nextPC);
+                tid, inst->seqNum, inst->pcState().instAddr(), next_pc);
     } else {
         DPRINTF(Fetch, "[tid:%i] [sn:%llu] Branch at PC %#x "
                 "predicted to be not taken\n",
@@ -538,8 +538,8 @@
 
     DPRINTF(Fetch, "[tid:%i] [sn:%llu] Branch at PC %#x "
             "predicted to go to %s\n",
-            tid, inst->seqNum, inst->pcState().instAddr(), nextPC);
-    inst->setPredTarg(nextPC);
+            tid, inst->seqNum, inst->pcState().instAddr(), next_pc);
+    inst->setPredTarg(next_pc);
     inst->setPredTaken(predict_taken);
 
     ++fetchStats.branches;
@@ -683,15 +683,15 @@
         // Send the fault to commit.  This thread will not do anything
         // until commit handles the fault.  The only other way it can
         // wake up is if a squash comes along and changes the PC.
-        TheISA::PCState fetchPC = pc[tid];
+        const PCStateBase &fetch_pc = *pc[tid];
 
         DPRINTF(Fetch, "[tid:%i] Translation faulted, building noop.\n", tid);
         // We will use a nop in ordier to carry the fault.
         DynInstPtr instruction = buildInst(tid, nopStaticInstPtr, nullptr,
-                fetchPC, fetchPC, false);
+                fetch_pc, fetch_pc, false);
         instruction->setNotAnInst();
 
-        instruction->setPredTarg(fetchPC);
+        instruction->setPredTarg(fetch_pc);
         instruction->fault = fault;
         wroteToTimeBuffer = true;
 
@@ -702,21 +702,21 @@
 
         DPRINTF(Fetch, "[tid:%i] Blocked, need to handle the trap.\n", tid);
         DPRINTF(Fetch, "[tid:%i] fault (%s) detected @ PC %s.\n",
-                tid, fault->name(), pc[tid]);
+                tid, fault->name(), *pc[tid]);
     }
     _status = updateFetchStatus();
 }
 
 void
-Fetch::doSquash(const TheISA::PCState &newPC, const DynInstPtr squashInst,
+Fetch::doSquash(const PCStateBase &new_pc, const DynInstPtr squashInst,
         ThreadID tid)
 {
     DPRINTF(Fetch, "[tid:%i] Squashing, setting PC to: %s.\n",
-            tid, newPC);
+            tid, new_pc);
 
-    pc[tid] = newPC;
+    set(pc[tid], new_pc);
     fetchOffset[tid] = 0;
-    if (squashInst && squashInst->pcState().instAddr() == newPC.instAddr())
+    if (squashInst && squashInst->pcState().instAddr() == new_pc.instAddr())
         macroop[tid] = squashInst->macroop;
     else
         macroop[tid] = NULL;
@@ -759,12 +759,12 @@
 }
 
 void
-Fetch::squashFromDecode(const TheISA::PCState &newPC,
-        const DynInstPtr squashInst, const InstSeqNum seq_num, ThreadID tid)
+Fetch::squashFromDecode(const PCStateBase &new_pc, const DynInstPtr squashInst,
+        const InstSeqNum seq_num, ThreadID tid)
 {
     DPRINTF(Fetch, "[tid:%i] Squashing from decode.\n", tid);
 
-    doSquash(newPC, squashInst, tid);
+    doSquash(new_pc, squashInst, tid);
 
     // Tell the CPU to remove any instructions that are in flight between
     // fetch and decode.
@@ -825,12 +825,12 @@
 }
 
 void
-Fetch::squash(const TheISA::PCState &newPC, const InstSeqNum seq_num,
+Fetch::squash(const PCStateBase &new_pc, const InstSeqNum seq_num,
         DynInstPtr squashInst, ThreadID tid)
 {
     DPRINTF(Fetch, "[tid:%i] Squash from commit.\n", tid);
 
-    doSquash(newPC, squashInst, tid);
+    doSquash(new_pc, squashInst, tid);
 
     // Tell the CPU to remove any instructions that are not in the ROB.
     cpu->removeInstsNotInROB(tid);
@@ -958,7 +958,7 @@
         DPRINTF(Fetch, "[tid:%i] Squashing instructions due to squash "
                 "from commit.\n",tid);
         // In any case, squash.
-        squash(fromCommit->commitInfo[tid].pc,
+        squash(*fromCommit->commitInfo[tid].pc,
                fromCommit->commitInfo[tid].doneSeqNum,
                fromCommit->commitInfo[tid].squashInst, tid);
 
@@ -968,9 +968,8 @@
         if (fromCommit->commitInfo[tid].mispredictInst &&
             fromCommit->commitInfo[tid].mispredictInst->isControl()) {
             branchPred->squash(fromCommit->commitInfo[tid].doneSeqNum,
-                              fromCommit->commitInfo[tid].pc,
-                              fromCommit->commitInfo[tid].branchTaken,
-                              tid);
+                    fromCommit->commitInfo[tid].pc->as<TheISA::PCState>(),
+                    fromCommit->commitInfo[tid].branchTaken, tid);
         } else {
             branchPred->squash(fromCommit->commitInfo[tid].doneSeqNum,
                               tid);
@@ -991,9 +990,8 @@
         // Update the branch predictor.
         if (fromDecode->decodeInfo[tid].branchMispredict) {
             branchPred->squash(fromDecode->decodeInfo[tid].doneSeqNum,
-                              fromDecode->decodeInfo[tid].nextPC,
-                              fromDecode->decodeInfo[tid].branchTaken,
-                              tid);
+                    fromDecode->decodeInfo[tid].nextPC->as<TheISA::PCState>(),
+                    fromDecode->decodeInfo[tid].branchTaken, tid);
         } else {
             branchPred->squash(fromDecode->decodeInfo[tid].doneSeqNum,
                               tid);
@@ -1002,9 +1000,9 @@
         if (fetchStatus[tid] != Squashing) {
 
             DPRINTF(Fetch, "Squashing from decode with PC = %s\n",
-                fromDecode->decodeInfo[tid].nextPC);
+                *fromDecode->decodeInfo[tid].nextPC);
             // Squash unless we're already squashing
-            squashFromDecode(fromDecode->decodeInfo[tid].nextPC,
+            squashFromDecode(*fromDecode->decodeInfo[tid].nextPC,
                              fromDecode->decodeInfo[tid].squashInst,
                              fromDecode->decodeInfo[tid].doneSeqNum,
                              tid);
@@ -1044,32 +1042,30 @@
 
 DynInstPtr
 Fetch::buildInst(ThreadID tid, StaticInstPtr staticInst,
-        StaticInstPtr curMacroop, TheISA::PCState thisPC,
-        TheISA::PCState nextPC, bool trace)
+        StaticInstPtr curMacroop, const PCStateBase &this_pc,
+        const PCStateBase &next_pc, bool trace)
 {
     // Get a sequence number.
     InstSeqNum seq = cpu->getAndIncrementInstSeq();
 
     // Create a new DynInst from the instruction fetched.
     DynInstPtr instruction =
-        new DynInst(staticInst, curMacroop, thisPC, nextPC, seq, cpu);
+        new DynInst(staticInst, curMacroop, this_pc, next_pc, seq, cpu);
     instruction->setTid(tid);
 
     instruction->setThreadState(cpu->thread[tid]);
 
-    DPRINTF(Fetch, "[tid:%i] Instruction PC %#x (%d) created "
-            "[sn:%lli].\n", tid, thisPC.instAddr(),
-            thisPC.microPC(), seq);
+    DPRINTF(Fetch, "[tid:%i] Instruction PC %s created [sn:%lli].\n",
+            tid, this_pc, seq);
 
     DPRINTF(Fetch, "[tid:%i] Instruction is: %s\n", tid,
-            instruction->staticInst->
-            disassemble(thisPC.instAddr()));
+            instruction->staticInst->disassemble(this_pc.instAddr()));
 
 #if TRACING_ON
     if (trace) {
         instruction->traceData =
             cpu->getTracer()->getInstRecord(curTick(), cpu->tcBase(tid),
-                    instruction->staticInst, thisPC, curMacroop);
+                    instruction->staticInst, this_pc, curMacroop);
     }
 #else
     instruction->traceData = NULL;
@@ -1117,12 +1113,12 @@
     DPRINTF(Fetch, "Attempting to fetch from [tid:%i]\n", tid);
 
     // The current PC.
-    TheISA::PCState thisPC = pc[tid];
+    PCStateBase &this_pc = *pc[tid];
 
     Addr pcOffset = fetchOffset[tid];
-    Addr fetchAddr = (thisPC.instAddr() + pcOffset) & decoder[tid]->pcMask();
+    Addr fetchAddr = (this_pc.instAddr() + pcOffset) & decoder[tid]->pcMask();
 
-    bool inRom = isRomMicroPC(thisPC.microPC());
+    bool inRom = isRomMicroPC(this_pc.microPC());
 
     // If returning from the delay of a cache miss, then update the status
     // to running, otherwise do the cache access.  Possibly move this up
@@ -1143,9 +1139,9 @@
                     fetchBufferBlockPC == fetchBufferPC[tid]) && !inRom &&
                 !macroop[tid]) {
             DPRINTF(Fetch, "[tid:%i] Attempting to translate and read "
-                    "instruction, starting at PC %s.\n", tid, thisPC);
+                    "instruction, starting at PC %s.\n", tid, this_pc);
 
-            fetchCacheLine(fetchAddr, tid, thisPC.instAddr());
+            fetchCacheLine(fetchAddr, tid, this_pc.instAddr());
 
             if (fetchStatus[tid] == IcacheWaitResponse)
                 ++fetchStats.icacheStallCycles;
@@ -1154,7 +1150,8 @@
             else
                 ++fetchStats.miscStallCycles;
             return;
-        } else if (checkInterrupt(thisPC.instAddr()) && !delayedCommit[tid]) {
+        } else if (checkInterrupt(this_pc.instAddr()) &&
+                !delayedCommit[tid]) {
             // Stall CPU if an interrupt is posted and we're not issuing
             // an delayed commit micro-op currently (delayed commit
             // instructions are not interruptable by interrupts, only faults)
@@ -1174,7 +1171,7 @@
 
     ++fetchStats.cycles;
 
-    TheISA::PCState nextPC = thisPC;
+    std::unique_ptr<PCStateBase> next_pc(this_pc.clone());
 
     StaticInstPtr staticInst = NULL;
     StaticInstPtr curMacroop = macroop[tid];
@@ -1208,7 +1205,7 @@
         // StaticInst from the rom, the current macroop, or what's already
         // in the decoder.
         bool needMem = !inRom && !curMacroop && !dec_ptr->instReady();
-        fetchAddr = (thisPC.instAddr() + pcOffset) & pc_mask;
+        fetchAddr = (this_pc.instAddr() + pcOffset) & pc_mask;
         Addr fetchBufferBlockPC = fetchBufferAlignPC(fetchAddr);
 
         if (needMem) {
@@ -1226,7 +1223,7 @@
 
             memcpy(dec_ptr->moreBytesPtr(),
                     fetchBuffer[tid] + blkOffset * instSize, instSize);
-            decoder[tid]->moreBytes(thisPC, fetchAddr);
+            decoder[tid]->moreBytes(this_pc.as<TheISA::PCState>(), fetchAddr);
 
             if (dec_ptr->needMoreBytes()) {
                 blkOffset++;
@@ -1240,7 +1237,8 @@
         do {
             if (!(curMacroop || inRom)) {
                 if (dec_ptr->instReady()) {
-                    staticInst = dec_ptr->decode(thisPC);
+                    staticInst = dec_ptr->decode(
+                            this_pc.as<TheISA::PCState>());
 
                     // Increment stat of fetched instructions.
                     ++fetchStats.insts;
@@ -1263,15 +1261,15 @@
             if (curMacroop || inRom) {
                 if (inRom) {
                     staticInst = dec_ptr->fetchRomMicroop(
-                            thisPC.microPC(), curMacroop);
+                            this_pc.microPC(), curMacroop);
                 } else {
-                    staticInst = curMacroop->fetchMicroop(thisPC.microPC());
+                    staticInst = curMacroop->fetchMicroop(this_pc.microPC());
                 }
                 newMacro |= staticInst->isLastMicroop();
             }
 
-            DynInstPtr instruction =
-                buildInst(tid, staticInst, curMacroop, thisPC, nextPC, true);
+            DynInstPtr instruction = buildInst(
+                    tid, staticInst, curMacroop, this_pc, *next_pc, true);
 
             ppFetch->notify(instruction);
             numInst++;
@@ -1282,25 +1280,24 @@
             }
 #endif
 
-            nextPC = thisPC;
+            set(next_pc, this_pc);
 
             // If we're branching after this instruction, quit fetching
             // from the same block.
-            predictedBranch |= thisPC.branching();
-            predictedBranch |=
-                lookupAndUpdateNextPC(instruction, nextPC);
+            predictedBranch |= this_pc.as<TheISA::PCState>().branching();
+            predictedBranch |= lookupAndUpdateNextPC(instruction, *next_pc);
             if (predictedBranch) {
-                DPRINTF(Fetch, "Branch detected with PC = %s\n", thisPC);
+                DPRINTF(Fetch, "Branch detected with PC = %s\n", this_pc);
             }
 
-            newMacro |= thisPC.instAddr() != nextPC.instAddr();
+            newMacro |= this_pc.instAddr() != next_pc->instAddr();
 
             // Move to the next instruction, unless we have a branch.
-            thisPC = nextPC;
-            inRom = isRomMicroPC(thisPC.microPC());
+            set(this_pc, *next_pc);
+            inRom = isRomMicroPC(this_pc.microPC());
 
             if (newMacro) {
-                fetchAddr = thisPC.instAddr() & pc_mask;
+                fetchAddr = this_pc.instAddr() & pc_mask;
                 blkOffset = (fetchAddr - fetchBufferPC[tid]) / instSize;
                 pcOffset = 0;
                 curMacroop = NULL;
@@ -1320,7 +1317,7 @@
 
         // Re-evaluate whether the next instruction to fetch is in micro-op ROM
         // or not.
-        inRom = isRomMicroPC(thisPC.microPC());
+        inRom = isRomMicroPC(this_pc.microPC());
     }
 
     if (predictedBranch) {
@@ -1341,11 +1338,9 @@
         wroteToTimeBuffer = true;
     }
 
-    pc[tid] = thisPC;
-
     // pipeline a fetch if we're crossing a fetch buffer boundary and not in
     // a state that would preclude fetching
-    fetchAddr = (thisPC.instAddr() + pcOffset) & pc_mask;
+    fetchAddr = (this_pc.instAddr() + pcOffset) & pc_mask;
     Addr fetchBufferBlockPC = fetchBufferAlignPC(fetchAddr);
     issuePipelinedIfetch[tid] = fetchBufferBlockPC != fetchBufferPC[tid] &&
         fetchStatus[tid] != IcacheWaitResponse &&
@@ -1535,14 +1530,14 @@
     }
 
     // The next PC to access.
-    TheISA::PCState thisPC = pc[tid];
+    const PCStateBase &this_pc = *pc[tid];
 
-    if (isRomMicroPC(thisPC.microPC())) {
+    if (isRomMicroPC(this_pc.microPC())) {
         return;
     }
 
     Addr pcOffset = fetchOffset[tid];
-    Addr fetchAddr = (thisPC.instAddr() + pcOffset) & decoder[tid]->pcMask();
+    Addr fetchAddr = (this_pc.instAddr() + pcOffset) & decoder[tid]->pcMask();
 
     // Align the fetch PC so its at the start of a fetch buffer segment.
     Addr fetchBufferBlockPC = fetchBufferAlignPC(fetchAddr);
@@ -1550,9 +1545,9 @@
     // Unless buffer already got the block, fetch it from icache.
     if (!(fetchBufferValid[tid] && fetchBufferBlockPC == fetchBufferPC[tid])) {
         DPRINTF(Fetch, "[tid:%i] Issuing a pipelined I-cache access, "
-                "starting at PC %s.\n", tid, thisPC);
+                "starting at PC %s.\n", tid, this_pc);
 
-        fetchCacheLine(fetchAddr, tid, thisPC.instAddr());
+        fetchCacheLine(fetchAddr, tid, this_pc.instAddr());
     }
 }
 
diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh
index 5bfb01a..2d903b2 100644
--- a/src/cpu/o3/fetch.hh
+++ b/src/cpu/o3/fetch.hh
@@ -284,7 +284,7 @@
      * @param next_NPC Used for ISAs which use delay slots.
      * @return Whether or not a branch was predicted as taken.
      */
-    bool lookupAndUpdateNextPC(const DynInstPtr &inst, TheISA::PCState &pc);
+    bool lookupAndUpdateNextPC(const DynInstPtr &inst, PCStateBase &pc);
 
     /**
      * Fetches the cache line that contains the fetch PC.  Returns any
@@ -306,14 +306,14 @@
     bool checkInterrupt(Addr pc) { return interruptPending; }
 
     /** Squashes a specific thread and resets the PC. */
-    void doSquash(const TheISA::PCState &newPC,
-            const DynInstPtr squashInst, ThreadID tid);
+    void doSquash(const PCStateBase &new_pc, const DynInstPtr squashInst,
+            ThreadID tid);
 
     /** Squashes a specific thread and resets the PC. Also tells the CPU to
      * remove any instructions between fetch and decode
      *  that should be sqaushed.
      */
-    void squashFromDecode(const TheISA::PCState &newPC,
+    void squashFromDecode(const PCStateBase &new_pc,
                           const DynInstPtr squashInst,
                           const InstSeqNum seq_num, ThreadID tid);
 
@@ -329,7 +329,7 @@
      * remove any instructions that are not in the ROB. The source of this
      * squash should be the commit stage.
      */
-    void squash(const TheISA::PCState &newPC, const InstSeqNum seq_num,
+    void squash(const PCStateBase &new_pc, const InstSeqNum seq_num,
                 DynInstPtr squashInst, ThreadID tid);
 
     /** Ticks the fetch stage, processing all inputs signals and fetching
@@ -362,8 +362,8 @@
 
   private:
     DynInstPtr buildInst(ThreadID tid, StaticInstPtr staticInst,
-            StaticInstPtr curMacroop, TheISA::PCState thisPC,
-            TheISA::PCState nextPC, bool trace);
+            StaticInstPtr curMacroop, const PCStateBase &this_pc,
+            const PCStateBase &next_pc, bool trace);
 
     /** Returns the appropriate thread to fetch, given the fetch policy. */
     ThreadID getFetchingThread();
@@ -413,7 +413,7 @@
     /** BPredUnit. */
     branch_prediction::BPredUnit *branchPred;
 
-    TheISA::PCState pc[MaxThreads];
+    std::unique_ptr<PCStateBase> pc[MaxThreads];
 
     Addr fetchOffset[MaxThreads];
 
diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc
index d899e8b..79e0783 100644
--- a/src/cpu/o3/iew.cc
+++ b/src/cpu/o3/iew.cc
@@ -463,10 +463,9 @@
         toCommit->squashedSeqNum[tid] = inst->seqNum;
         toCommit->branchTaken[tid] = inst->pcState().branching();
 
-        TheISA::PCState pc = inst->pcState();
-        inst->staticInst->advancePC(pc);
+        set(toCommit->pc[tid], inst->pcState());
+        inst->staticInst->advancePC(*toCommit->pc[tid]);
 
-        toCommit->pc[tid] = pc;
         toCommit->mispredictInst[tid] = inst;
         toCommit->includeSquashInst[tid] = false;
 
@@ -491,7 +490,7 @@
         toCommit->squash[tid] = true;
 
         toCommit->squashedSeqNum[tid] = inst->seqNum;
-        toCommit->pc[tid] = inst->pcState();
+        set(toCommit->pc[tid], inst->pcState());
         toCommit->mispredictInst[tid] = NULL;
 
         // Must include the memory violator in the squash.
@@ -1301,13 +1300,13 @@
 
                 DPRINTF(IEW, "[tid:%i] [sn:%llu] Execute: "
                         "Branch mispredict detected.\n",
-                        tid,inst->seqNum);
+                        tid, inst->seqNum);
                 DPRINTF(IEW, "[tid:%i] [sn:%llu] "
                         "Predicted target was PC: %s\n",
-                        tid,inst->seqNum,inst->readPredTarg());
+                        tid, inst->seqNum, inst->readPredTarg());
                 DPRINTF(IEW, "[tid:%i] [sn:%llu] Execute: "
                         "Redirecting fetch to PC: %s\n",
-                        tid,inst->seqNum,inst->pcState());
+                        tid, inst->seqNum, inst->pcState());
                 // If incorrect, then signal the ROB that it must be squashed.
                 squashDueToBranch(inst, tid);