misc,cpu: Make ThreadContext work with PCStateBase-s.

Change-Id: I92f1d79c697bb45f610604c9e84b24ea93d58776
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52058
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/arch/arm/fastmodel/iris/thread_context.cc b/src/arch/arm/fastmodel/iris/thread_context.cc
index a6b8c2a..6d76eac 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.cc
+++ b/src/arch/arm/fastmodel/iris/thread_context.cc
@@ -553,11 +553,10 @@
     _status = new_status;
 }
 
-ArmISA::PCState
+const PCStateBase &
 ThreadContext::pcState() const
 {
     ArmISA::CPSR cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
-    ArmISA::PCState pc;
 
     pc.thumb(cpsr.t);
     pc.nextThumb(pc.thumb());
@@ -579,9 +578,9 @@
     return pc;
 }
 void
-ThreadContext::pcState(const ArmISA::PCState &val)
+ThreadContext::pcState(const PCStateBase &val)
 {
-    Addr pc = val.pc();
+    Addr pc = val.instAddr();
 
     ArmISA::CPSR cpsr = readMiscRegNoEffect(ArmISA::MISCREG_CPSR);
     if (cpsr.width && cpsr.t)
diff --git a/src/arch/arm/fastmodel/iris/thread_context.hh b/src/arch/arm/fastmodel/iris/thread_context.hh
index c5e4cc3..a343658 100644
--- a/src/arch/arm/fastmodel/iris/thread_context.hh
+++ b/src/arch/arm/fastmodel/iris/thread_context.hh
@@ -169,6 +169,8 @@
     iris::IrisCppAdapter &call() const { return client.irisCall(); }
     iris::IrisCppAdapter &noThrow() const { return client.irisCallNoThrow(); }
 
+    mutable ArmISA::PCState pc;
+
     void readMem(iris::MemorySpaceId space,
                  Addr addr, void *p, size_t size);
     void writeMem(iris::MemorySpaceId space,
@@ -345,11 +347,11 @@
         setCCRegFlat(reg_idx, val);
     }
 
-    void pcStateNoRecord(const ArmISA::PCState &val) override { pcState(val); }
+    void pcStateNoRecord(const PCStateBase &val) override { pcState(val); }
     MicroPC microPC() const override { return 0; }
 
-    ArmISA::PCState pcState() const override;
-    void pcState(const ArmISA::PCState &val) override;
+    const PCStateBase &pcState() const override;
+    void pcState(const PCStateBase &val) override;
     Addr instAddr() const override;
 
     RegVal readMiscRegNoEffect(RegIndex misc_reg) const override;
diff --git a/src/arch/arm/faults.cc b/src/arch/arm/faults.cc
index b8ca6d2..64a07cb 100644
--- a/src/arch/arm/faults.cc
+++ b/src/arch/arm/faults.cc
@@ -534,8 +534,8 @@
     saved_cpsr.v = tc->readCCReg(CCREG_V);
     saved_cpsr.ge = tc->readCCReg(CCREG_GE);
 
-    [[maybe_unused]] Addr cur_pc = tc->pcState().pc();
-    ITSTATE it = tc->pcState().itstate();
+    [[maybe_unused]] Addr cur_pc = tc->pcState().as<PCState>().pc();
+    ITSTATE it = tc->pcState().as<PCState>().itstate();
     saved_cpsr.it2 = it.top6;
     saved_cpsr.it1 = it.bottom2;
 
@@ -688,7 +688,7 @@
         spsr.t = 0;
     } else {
         spsr.ge = tc->readCCReg(CCREG_GE);
-        ITSTATE it = tc->pcState().itstate();
+        ITSTATE it = tc->pcState().as<PCState>().itstate();
         spsr.it2 = it.top6;
         spsr.it1 = it.bottom2;
         spsr.uao = 0;
@@ -696,7 +696,7 @@
     tc->setMiscReg(spsr_idx, spsr);
 
     // Save preferred return address into ELR_ELx
-    Addr curr_pc = tc->pcState().pc();
+    Addr curr_pc = tc->pcState().instAddr();
     Addr ret_addr = curr_pc;
     if (from64)
         ret_addr += armPcElrOffset();
@@ -887,7 +887,7 @@
     tc->getSystemPtr()->workload->syscall(tc);
 
     // Advance the PC since that won't happen automatically.
-    PCState pc = tc->pcState();
+    PCState pc = tc->pcState().as<PCState>();
     assert(inst);
     inst->advancePC(pc);
     tc->pcState(pc);
diff --git a/src/arch/arm/fs_workload.cc b/src/arch/arm/fs_workload.cc
index 6bbe664..6a79aa0 100644
--- a/src/arch/arm/fs_workload.cc
+++ b/src/arch/arm/fs_workload.cc
@@ -57,18 +57,18 @@
 void
 SkipFunc::returnFromFuncIn(ThreadContext *tc)
 {
-    PCState newPC = tc->pcState();
+    PCState new_pc = tc->pcState().as<PCState>();
     if (inAArch64(tc)) {
-        newPC.set(tc->readIntReg(INTREG_X30));
+        new_pc.set(tc->readIntReg(INTREG_X30));
     } else {
-        newPC.set(tc->readIntReg(ReturnAddressReg) & ~1ULL);
+        new_pc.set(tc->readIntReg(ReturnAddressReg) & ~1ULL);
     }
 
     CheckerCPU *checker = tc->getCheckerCpuPtr();
     if (checker) {
-        tc->pcStateNoRecord(newPC);
+        tc->pcStateNoRecord(new_pc);
     } else {
-        tc->pcState(newPC);
+        tc->pcState(new_pc);
     }
 }
 
diff --git a/src/arch/arm/htm.cc b/src/arch/arm/htm.cc
index e94e437..84ef4a1 100644
--- a/src/arch/arm/htm.cc
+++ b/src/arch/arm/htm.cc
@@ -90,7 +90,7 @@
     }
     fpcr = tc->readMiscReg(MISCREG_FPCR);
     fpsr = tc->readMiscReg(MISCREG_FPSR);
-    pcstateckpt = tc->pcState();
+    pcstateckpt = tc->pcState().as<PCState>();
 
     BaseHTMCheckpoint::save(tc);
 }
diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc
index 7e047f1..a2d9700 100644
--- a/src/arch/arm/isa.cc
+++ b/src/arch/arm/isa.cc
@@ -607,12 +607,11 @@
 ISA::readMiscReg(int misc_reg)
 {
     CPSR cpsr = 0;
-    PCState pc(0);
     SCR scr = 0;
 
     if (misc_reg == MISCREG_CPSR) {
         cpsr = miscRegs[misc_reg];
-        pc = tc->pcState();
+        auto pc = tc->pcState().as<PCState>();
         cpsr.j = pc.jazelle() ? 1 : 0;
         cpsr.t = pc.thumb() ? 1 : 0;
         return cpsr;
@@ -959,7 +958,7 @@
 
         DPRINTF(Arm, "Updating CPSR from %#x to %#x f:%d i:%d a:%d mode:%#x\n",
                 miscRegs[misc_reg], cpsr, cpsr.f, cpsr.i, cpsr.a, cpsr.mode);
-        PCState pc = tc->pcState();
+        PCState pc = tc->pcState().as<PCState>();
         pc.nextThumb(cpsr.t);
         pc.nextJazelle(cpsr.j);
         pc.illegalExec(cpsr.il == 1);
@@ -2602,7 +2601,7 @@
 
     auto req = std::make_shared<Request>(
         val, 0, flags,  Request::funcRequestorId,
-        tc->pcState().pc(), tc->contextId());
+        tc->pcState().instAddr(), tc->contextId());
 
     Fault fault = getMMUPtr(tc)->translateFunctional(
         req, tc, mode, tran_type);
@@ -2653,7 +2652,7 @@
 
     auto req = std::make_shared<Request>(
         val, 0, flags,  Request::funcRequestorId,
-        tc->pcState().pc(), tc->contextId());
+        tc->pcState().instAddr(), tc->contextId());
 
     Fault fault = getMMUPtr(tc)->translateFunctional(
         req, tc, mode, tran_type);
diff --git a/src/arch/arm/kvm/arm_cpu.cc b/src/arch/arm/kvm/arm_cpu.cc
index 4f47a15..e462e4f 100644
--- a/src/arch/arm/kvm/arm_cpu.cc
+++ b/src/arch/arm/kvm/arm_cpu.cc
@@ -820,7 +820,7 @@
 
     // We update the PC state after we have updated the CPSR the
     // contents of the CPSR affects how the npc is updated.
-    PCState pc = tc->pcState();
+    PCState pc = tc->pcState().as<PCState>();
     pc.set(getOneRegU32(REG_CORE32(usr_regs.ARM_pc)));
     tc->pcState(pc);
 
diff --git a/src/arch/arm/nativetrace.cc b/src/arch/arm/nativetrace.cc
index 3cafcf7..8d0313a 100644
--- a/src/arch/arm/nativetrace.cc
+++ b/src/arch/arm/nativetrace.cc
@@ -114,7 +114,7 @@
     }
 
     //R15, aliased with the PC
-    newState[STATE_PC] = tc->pcState().npc();
+    newState[STATE_PC] = tc->pcState().as<ArmISA::PCState>().npc();
     changed[STATE_PC] = (newState[STATE_PC] != oldState[STATE_PC]);
 
     //CPSR
@@ -142,7 +142,7 @@
     ThreadContext *tc = record->getThread();
     // This area is read only on the target. It can't stop there to tell us
     // what's going on, so we should skip over anything there also.
-    if (tc->pcState().npc() > 0xffff0000)
+    if (tc->pcState().as<ArmISA::PCState>().npc() > 0xffff0000)
         return;
     nState.update(this);
     mState.update(tc);
diff --git a/src/arch/arm/remote_gdb.cc b/src/arch/arm/remote_gdb.cc
index 5382d51..2efa82f 100644
--- a/src/arch/arm/remote_gdb.cc
+++ b/src/arch/arm/remote_gdb.cc
@@ -223,7 +223,7 @@
     for (int i = 0; i < 31; ++i)
         r.x[i] = context->readIntReg(INTREG_X0 + i);
     r.spx = context->readIntReg(INTREG_SPX);
-    r.pc = context->pcState().pc();
+    r.pc = context->pcState().instAddr();
     r.cpsr = context->readMiscRegNoEffect(MISCREG_CPSR);
 
     size_t base = 0;
@@ -245,7 +245,7 @@
 
     for (int i = 0; i < 31; ++i)
         context->setIntReg(INTREG_X0 + i, r.x[i]);
-    auto pc_state = context->pcState();
+    auto pc_state = context->pcState().as<PCState>();
     pc_state.set(r.pc);
     context->pcState(pc_state);
     context->setMiscRegNoEffect(MISCREG_CPSR, r.cpsr);
@@ -287,7 +287,7 @@
     r.gpr[12] = context->readIntReg(INTREG_R12);
     r.gpr[13] = context->readIntReg(INTREG_SP);
     r.gpr[14] = context->readIntReg(INTREG_LR);
-    r.gpr[15] = context->pcState().pc();
+    r.gpr[15] = context->pcState().instAddr();
     r.cpsr = context->readMiscRegNoEffect(MISCREG_CPSR);
 
     // One day somebody will implement transfer of FPRs correctly.
@@ -317,7 +317,7 @@
     context->setIntReg(INTREG_R12, r.gpr[12]);
     context->setIntReg(INTREG_SP, r.gpr[13]);
     context->setIntReg(INTREG_LR, r.gpr[14]);
-    auto pc_state = context->pcState();
+    PCState pc_state = context->pcState().as<PCState>();
     pc_state.set(r.gpr[15]);
     context->pcState(pc_state);
 
diff --git a/src/arch/arm/self_debug.cc b/src/arch/arm/self_debug.cc
index 551abbb..2029bdc 100644
--- a/src/arch/arm/self_debug.cc
+++ b/src/arch/arm/self_debug.cc
@@ -89,7 +89,7 @@
 
     ExceptionLevel el = (ExceptionLevel) currEL(tc);
     for (auto &p: arBrkPoints){
-        PCState pcst = tc->pcState();
+        PCState pcst = tc->pcState().as<PCState>();
         Addr pc = vaddr;
         if (pcst.itstate() != 0x0)
             pc = pcst.pc();
@@ -676,8 +676,7 @@
 bool
 SoftwareStep::advanceSS(ThreadContext * tc)
 {
-
-    PCState pc = tc->pcState();
+    PCState pc = tc->pcState().as<PCState>();
     bool res = false;
     switch (stateSS) {
       case INACTIVE_STATE:
diff --git a/src/arch/generic/debugfaults.hh b/src/arch/generic/debugfaults.hh
index f54bd7c..d5976cc 100644
--- a/src/arch/generic/debugfaults.hh
+++ b/src/arch/generic/debugfaults.hh
@@ -61,9 +61,9 @@
     advancePC(ThreadContext *tc, const StaticInstPtr &inst)
     {
         if (inst) {
-            auto pc = tc->pcState();
-            inst->advancePC(pc);
-            tc->pcState(pc);
+            std::unique_ptr<PCStateBase> pc(tc->pcState().clone());
+            inst->advancePC(*pc);
+            tc->pcState(*pc);
         }
     }
 
diff --git a/src/arch/mips/faults.cc b/src/arch/mips/faults.cc
index a9f5239..c1884ee 100644
--- a/src/arch/mips/faults.cc
+++ b/src/arch/mips/faults.cc
@@ -114,7 +114,7 @@
     tc->setMiscRegNoEffect(MISCREG_STATUS, status);
 
     // write EPC
-    PCState pc = tc->pcState();
+    auto pc = tc->pcState().as<PCState>();
     DPRINTF(MipsPRA, "PC: %s\n", pc);
     bool delay_slot = pc.pc() + sizeof(MachInst) != pc.npc();
     tc->setMiscRegNoEffect(MISCREG_EPC,
diff --git a/src/arch/mips/mt.hh b/src/arch/mips/mt.hh
index 91fcf50..adbbf52 100644
--- a/src/arch/mips/mt.hh
+++ b/src/arch/mips/mt.hh
@@ -39,6 +39,7 @@
 
 #include "arch/mips/faults.hh"
 #include "arch/mips/mt_constants.hh"
+#include "arch/mips/pcstate.hh"
 #include "arch/mips/pra_constants.hh"
 #include "arch/mips/regs/misc.hh"
 #include "base/bitfield.hh"
@@ -140,7 +141,7 @@
         // Save last known PC in TCRestart
         // @TODO: Needs to check if this is a branch and if so,
         // take previous instruction
-        PCState pc = tc->pcState();
+        auto &pc = tc->pcState().template as<MipsISA::PCState>();
         tc->setMiscReg(MISCREG_TC_RESTART, pc.npc());
 
         warn("%i: Halting thread %i in %s @ PC %x, setting restart PC to %x",
diff --git a/src/arch/mips/remote_gdb.cc b/src/arch/mips/remote_gdb.cc
index bf845ba..ad39300 100644
--- a/src/arch/mips/remote_gdb.cc
+++ b/src/arch/mips/remote_gdb.cc
@@ -179,7 +179,7 @@
     r.hi = context->readIntReg(INTREG_HI);
     r.badvaddr = context->readMiscRegNoEffect(MISCREG_BADVADDR);
     r.cause = context->readMiscRegNoEffect(MISCREG_CAUSE);
-    r.pc = context->pcState().pc();
+    r.pc = context->pcState().instAddr();
     for (int i = 0; i < 32; i++) r.fpr[i] = context->readFloatReg(i);
     r.fsr = context->readFloatReg(FLOATREG_FCCR);
     r.fir = context->readFloatReg(FLOATREG_FIR);
diff --git a/src/arch/power/faults.cc b/src/arch/power/faults.cc
index 3b8851e..be1796e 100644
--- a/src/arch/power/faults.cc
+++ b/src/arch/power/faults.cc
@@ -44,7 +44,7 @@
 {
     panic_if(tc->getSystemPtr()->trapToGdb(SIGILL, tc->contextId()),
              "Unimplemented opcode encountered at virtual address %#x\n",
-             tc->pcState().pc());
+             tc->pcState().instAddr());
 }
 
 void
@@ -59,7 +59,7 @@
 {
     panic_if(tc->getSystemPtr()->trapToGdb(SIGTRAP, tc->contextId()),
              "Trap encountered at virtual address %#x\n",
-             tc->pcState().pc());
+             tc->pcState().instAddr());
 }
 
 } // namespace PowerISA
diff --git a/src/arch/power/insts/branch.cc b/src/arch/power/insts/branch.cc
index 2cab370..8540cef 100644
--- a/src/arch/power/insts/branch.cc
+++ b/src/arch/power/insts/branch.cc
@@ -65,7 +65,7 @@
     if (aa)
         addr = li;
     else
-        addr = tc->pcState().pc() + li;
+        addr = tc->pcState().instAddr() + li;
 
     return std::make_unique<PowerISA::PCState>(
             msr.sf ? addr : addr & UINT32_MAX);
@@ -114,7 +114,7 @@
     if (aa)
         addr = bd;
     else
-        addr = tc->pcState().pc() + bd;
+        addr = tc->pcState().instAddr() + bd;
 
     return std::make_unique<PowerISA::PCState>(
             msr.sf ? addr : addr & UINT32_MAX);
diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc
index ca489d8..8ed4b75 100644
--- a/src/arch/power/process.cc
+++ b/src/arch/power/process.cc
@@ -103,7 +103,7 @@
     initVirtMem->readBlob(getStartPC(), &entryPoint, sizeof(Addr));
 
     // Update the PC state
-    auto pc = tc->pcState();
+    auto pc = tc->pcState().as<PowerISA::PCState>();
     pc.byteOrder(byteOrder);
     pc.set(gtoh(entryPoint, byteOrder));
     tc->pcState(pc);
@@ -356,7 +356,7 @@
     msr.le = isLittleEndian;
     tc->setIntReg(INTREG_MSR, msr);
 
-    auto pc = tc->pcState();
+    auto pc = tc->pcState().as<PowerISA::PCState>();
     pc.set(getStartPC());
     pc.byteOrder(byteOrder);
     tc->pcState(pc);
diff --git a/src/arch/power/remote_gdb.cc b/src/arch/power/remote_gdb.cc
index 0accb6a..702439d 100644
--- a/src/arch/power/remote_gdb.cc
+++ b/src/arch/power/remote_gdb.cc
@@ -192,7 +192,7 @@
     for (int i = 0; i < NumFloatArchRegs; i++)
         r.fpr[i] = context->readFloatReg(i);
 
-    r.pc = htog((uint32_t)context->pcState().pc(), order);
+    r.pc = htog((uint32_t)context->pcState().instAddr(), order);
     r.msr = 0; // MSR is privileged, hence not exposed here
     r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
     r.lr = htog((uint32_t)context->readIntReg(INTREG_LR), order);
@@ -215,7 +215,7 @@
     for (int i = 0; i < NumFloatArchRegs; i++)
         context->setFloatReg(i, r.fpr[i]);
 
-    auto pc = context->pcState();
+    auto pc = context->pcState().as<PowerISA::PCState>();
     pc.byteOrder(order);
     pc.set(gtoh(r.pc, order));
     context->pcState(pc);
@@ -246,7 +246,7 @@
     for (int i = 0; i < NumFloatArchRegs; i++)
         r.fpr[i] = context->readFloatReg(i);
 
-    r.pc = htog(context->pcState().pc(), order);
+    r.pc = htog(context->pcState().instAddr(), order);
     r.msr = 0; // MSR is privileged, hence not exposed here
     r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
     r.lr = htog(context->readIntReg(INTREG_LR), order);
@@ -269,7 +269,7 @@
     for (int i = 0; i < NumFloatArchRegs; i++)
         context->setFloatReg(i, r.fpr[i]);
 
-    auto pc = context->pcState();
+    auto pc = context->pcState().as<PowerISA::PCState>();
     pc.byteOrder(order);
     pc.set(gtoh(r.pc, order));
     context->pcState(pc);
diff --git a/src/arch/riscv/faults.cc b/src/arch/riscv/faults.cc
index 01f6827..703b0ee 100644
--- a/src/arch/riscv/faults.cc
+++ b/src/arch/riscv/faults.cc
@@ -51,16 +51,16 @@
 void
 RiscvFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst)
 {
-    panic("Fault %s encountered at pc 0x%016llx.", name(), tc->pcState().pc());
+    panic("Fault %s encountered at pc %s.", name(), tc->pcState());
 }
 
 void
 RiscvFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
 {
-    PCState pcState = tc->pcState();
+    auto pc_state = tc->pcState().as<PCState>();
 
     DPRINTFS(Fault, tc->getCpuPtr(), "Fault (%s) at PC: %s\n",
-             name(), pcState);
+             name(), pc_state);
 
     if (FullSystem) {
         PrivilegeMode pp = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV);
@@ -156,12 +156,12 @@
         Addr addr = mbits(tc->readMiscReg(tvec), 63, 2);
         if (isInterrupt() && bits(tc->readMiscReg(tvec), 1, 0) == 1)
             addr += 4 * _code;
-        pcState.set(addr);
+        pc_state.set(addr);
     } else {
         invokeSE(tc, inst);
-        inst->advancePC(pcState);
+        inst->advancePC(pc_state);
     }
-    tc->pcState(pcState);
+    tc->pcState(pc_state);
 }
 
 void
@@ -184,31 +184,29 @@
 UnknownInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst)
 {
     auto *rsi = static_cast<RiscvStaticInst *>(inst.get());
-    panic("Unknown instruction 0x%08x at pc 0x%016llx", rsi->machInst,
-        tc->pcState().pc());
+    panic("Unknown instruction 0x%08x at pc %s", rsi->machInst,
+        tc->pcState());
 }
 
 void
 IllegalInstFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst)
 {
     auto *rsi = static_cast<RiscvStaticInst *>(inst.get());
-    panic("Illegal instruction 0x%08x at pc 0x%016llx: %s", rsi->machInst,
-        tc->pcState().pc(), reason.c_str());
+    panic("Illegal instruction 0x%08x at pc %s: %s", rsi->machInst,
+        tc->pcState(), reason.c_str());
 }
 
 void
-UnimplementedFault::invokeSE(ThreadContext *tc,
-        const StaticInstPtr &inst)
+UnimplementedFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst)
 {
-    panic("Unimplemented instruction %s at pc 0x%016llx", instName,
-        tc->pcState().pc());
+    panic("Unimplemented instruction %s at pc %s", instName, tc->pcState());
 }
 
 void
 IllegalFrmFault::invokeSE(ThreadContext *tc, const StaticInstPtr &inst)
 {
-    panic("Illegal floating-point rounding mode 0x%x at pc 0x%016llx.",
-            frm, tc->pcState().pc());
+    panic("Illegal floating-point rounding mode 0x%x at pc %s.",
+            frm, tc->pcState());
 }
 
 void
diff --git a/src/arch/riscv/isa.cc b/src/arch/riscv/isa.cc
index 0358889..c8b752c 100644
--- a/src/arch/riscv/isa.cc
+++ b/src/arch/riscv/isa.cc
@@ -466,8 +466,10 @@
                 // only allow to disable compressed instructions
                 // if the following instruction is 4-byte aligned
                 if ((val & ISA_EXT_C_MASK) == 0 &&
-                    bits(tc->pcState().npc(), 2, 0) != 0)
+                        bits(tc->pcState().as<RiscvISA::PCState>().npc(),
+                            2, 0) != 0) {
                     val |= cur_val & ISA_EXT_C_MASK;
+                }
                 setMiscRegNoEffect(misc_reg, val);
             }
             break;
diff --git a/src/arch/riscv/isa/formats/standard.isa b/src/arch/riscv/isa/formats/standard.isa
index 3c16f60..9345c1f 100644
--- a/src/arch/riscv/isa/formats/standard.isa
+++ b/src/arch/riscv/isa/formats/standard.isa
@@ -255,9 +255,10 @@
     std::unique_ptr<PCStateBase>
     %(class_name)s::branchTarget(ThreadContext *tc) const
     {
-        PCState pc = tc->pcState();
-        pc.set((tc->readIntReg(srcRegIdx(0).index()) + imm)&~0x1);
-        return std::unique_ptr<PCStateBase>{pc.clone()};
+        PCStateBase *pc_ptr = tc->pcState().clone();
+        pc_ptr->as<PCState>().set(
+                (tc->readIntReg(srcRegIdx(0).index()) + imm) & ~0x1);
+        return std::unique_ptr<PCStateBase>{pc_ptr};
     }
 
     std::string
diff --git a/src/arch/riscv/process.cc b/src/arch/riscv/process.cc
index bdbb7cc..cbd13b0 100644
--- a/src/arch/riscv/process.cc
+++ b/src/arch/riscv/process.cc
@@ -114,7 +114,7 @@
     for (ContextID ctx: contextIds) {
         auto *tc = system->threads[ctx];
         tc->setMiscRegNoEffect(MISCREG_PRV, PRV_U);
-        PCState pc = tc->pcState();
+        PCState pc = tc->pcState().as<PCState>();
         pc.rv32(true);
         tc->pcState(pc);
     }
diff --git a/src/arch/riscv/remote_gdb.cc b/src/arch/riscv/remote_gdb.cc
index ec3eb5a..50b0ba7 100644
--- a/src/arch/riscv/remote_gdb.cc
+++ b/src/arch/riscv/remote_gdb.cc
@@ -195,7 +195,7 @@
     {
         r.gpr[i] = context->readIntReg(i);
     }
-    r.pc = context->pcState().pc();
+    r.pc = context->pcState().instAddr();
 
     // Floating point registers
     for (int i = 0; i < NumFloatRegs; i++)
diff --git a/src/arch/sparc/faults.cc b/src/arch/sparc/faults.cc
index 6ead79a..ec7386f 100644
--- a/src/arch/sparc/faults.cc
+++ b/src/arch/sparc/faults.cc
@@ -311,7 +311,7 @@
     RegVal CWP = tc->readMiscRegNoEffect(MISCREG_CWP);
     RegVal CANSAVE = tc->readMiscRegNoEffect(INTREG_CANSAVE);
     RegVal GL = tc->readMiscRegNoEffect(MISCREG_GL);
-    PCState pc = tc->pcState();
+    auto &pc = tc->pcState().as<PCState>();
 
     TL++;
 
@@ -390,7 +390,7 @@
     RegVal CWP = tc->readMiscRegNoEffect(MISCREG_CWP);
     RegVal CANSAVE = tc->readIntReg(INTREG_CANSAVE);
     RegVal GL = tc->readMiscRegNoEffect(MISCREG_GL);
-    PCState pc = tc->pcState();
+    auto &pc = tc->pcState().as<PCState>();
 
     // Increment the trap level
     TL++;
@@ -825,7 +825,7 @@
 
     // We need to explicitly advance the pc, since that's not done for us
     // on a faulting instruction
-    PCState pc = tc->pcState();
+    PCState pc = tc->pcState().as<PCState>();
     pc.advance();
     tc->pcState(pc);
 }
diff --git a/src/arch/sparc/nativetrace.cc b/src/arch/sparc/nativetrace.cc
index 752e316..0179fe4 100644
--- a/src/arch/sparc/nativetrace.cc
+++ b/src/arch/sparc/nativetrace.cc
@@ -68,7 +68,7 @@
         checkReg(*(regName++), regVal, realRegVal);
     }
 
-    SparcISA::PCState pc = tc->pcState();
+    auto &pc = tc->pcState().as<SparcISA::PCState>();
     // PC
     read(&realRegVal, sizeof(realRegVal));
     realRegVal = betoh(realRegVal);
diff --git a/src/arch/sparc/remote_gdb.cc b/src/arch/sparc/remote_gdb.cc
index 83ef55b..9e1e06e 100644
--- a/src/arch/sparc/remote_gdb.cc
+++ b/src/arch/sparc/remote_gdb.cc
@@ -176,8 +176,9 @@
 RemoteGDB::SPARCGdbRegCache::getRegs(ThreadContext *context)
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
-    for (int i = 0; i < 32; i++) r.gpr[i] = htobe((uint32_t)context->readIntReg(i));
-    PCState pc = context->pcState();
+    for (int i = 0; i < 32; i++)
+        r.gpr[i] = htobe((uint32_t)context->readIntReg(i));
+    auto &pc = context->pcState().as<SparcISA::PCState>();
     r.pc = htobe((uint32_t)pc.pc());
     r.npc = htobe((uint32_t)pc.npc());
     r.y = htobe((uint32_t)context->readIntReg(INTREG_Y));
@@ -191,9 +192,11 @@
 RemoteGDB::SPARC64GdbRegCache::getRegs(ThreadContext *context)
 {
     DPRINTF(GDBAcc, "getRegs in remotegdb \n");
-    for (int i = 0; i < 32; i++) r.gpr[i] = htobe(context->readIntReg(i));
-    for (int i = 0; i < 32; i++) r.fpr[i] = 0;
-    PCState pc = context->pcState();
+    for (int i = 0; i < 32; i++)
+        r.gpr[i] = htobe(context->readIntReg(i));
+    for (int i = 0; i < 32; i++)
+        r.fpr[i] = 0;
+    auto &pc = context->pcState().as<SparcISA::PCState>();
     r.pc = htobe(pc.pc());
     r.npc = htobe(pc.npc());
     r.fsr = htobe(context->readMiscReg(MISCREG_FSR));
@@ -210,7 +213,8 @@
 void
 RemoteGDB::SPARCGdbRegCache::setRegs(ThreadContext *context) const
 {
-    for (int i = 0; i < 32; i++) context->setIntReg(i, r.gpr[i]);
+    for (int i = 0; i < 32; i++)
+        context->setIntReg(i, r.gpr[i]);
     PCState pc;
     pc.pc(r.pc);
     pc.npc(r.npc);
@@ -226,7 +230,8 @@
 void
 RemoteGDB::SPARC64GdbRegCache::setRegs(ThreadContext *context) const
 {
-    for (int i = 0; i < 32; i++) context->setIntReg(i, r.gpr[i]);
+    for (int i = 0; i < 32; i++)
+        context->setIntReg(i, r.gpr[i]);
     PCState pc;
     pc.pc(r.pc);
     pc.npc(r.npc);
diff --git a/src/arch/sparc/se_workload.cc b/src/arch/sparc/se_workload.cc
index 7c1e125..c87244f 100644
--- a/src/arch/sparc/se_workload.cc
+++ b/src/arch/sparc/se_workload.cc
@@ -54,7 +54,7 @@
 void
 SEWorkload::handleTrap(ThreadContext *tc, int trapNum)
 {
-    PCState pc = tc->pcState();
+    auto &pc = tc->pcState().as<PCState>();
     switch (trapNum) {
       case 0x01: // Software breakpoint
         warn("Software breakpoint encountered at pc %#x.", pc.pc());
diff --git a/src/arch/x86/faults.cc b/src/arch/x86/faults.cc
index d925bd7..ae9586b 100644
--- a/src/arch/x86/faults.cc
+++ b/src/arch/x86/faults.cc
@@ -64,9 +64,8 @@
         return;
     }
 
-    PCState pcState = tc->pcState();
-    Addr pc = pcState.pc();
-    DPRINTF(Faults, "RIP %#x: vector %d: %s\n", pc, vector, describe());
+    PCState pc = tc->pcState().as<PCState>();
+    DPRINTF(Faults, "RIP %#x: vector %d: %s\n", pc.pc(), vector, describe());
     using namespace X86ISAInst::rom_labels;
     HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
     MicroPC entry;
@@ -77,7 +76,7 @@
         entry = extern_label_legacyModeInterrupt;
     }
     tc->setIntReg(INTREG_MICRO(1), vector);
-    tc->setIntReg(INTREG_MICRO(7), pc);
+    tc->setIntReg(INTREG_MICRO(7), pc.pc());
     if (errorCode != (uint64_t)(-1)) {
         if (m5reg.mode == LongMode) {
             entry = extern_label_longModeInterruptWithError;
@@ -90,9 +89,9 @@
         assert(!isSoft());
         tc->setIntReg(INTREG_MICRO(15), errorCode);
     }
-    pcState.upc(romMicroPC(entry));
-    pcState.nupc(romMicroPC(entry) + 1);
-    tc->pcState(pcState);
+    pc.upc(romMicroPC(entry));
+    pc.nupc(romMicroPC(entry) + 1);
+    tc->pcState(pc);
 }
 
 std::string
@@ -109,14 +108,9 @@
 void
 X86Trap::invoke(ThreadContext *tc, const StaticInstPtr &inst)
 {
-    X86FaultBase::invoke(tc);
-    if (!FullSystem)
-        return;
-
     // This is the same as a fault, but it happens -after- the
     // instruction.
-    PCState pc = tc->pcState();
-    pc.uEnd();
+    X86FaultBase::invoke(tc);
 }
 
 void
@@ -168,8 +162,8 @@
             panic("Tried to %s unmapped address %#x.", modeStr, addr);
         } else {
             panic("Tried to %s unmapped address %#x.\nPC: %#x, Instr: %s",
-                  modeStr, addr, tc->pcState().pc(),
-                  inst->disassemble(tc->pcState().pc(),
+                  modeStr, addr, tc->pcState(),
+                  inst->disassemble(tc->pcState().instAddr(),
                       &loader::debugSymbolTable));
         }
     }
diff --git a/src/arch/x86/linux/se_workload.cc b/src/arch/x86/linux/se_workload.cc
index 46a6327..f5fa519 100644
--- a/src/arch/x86/linux/se_workload.cc
+++ b/src/arch/x86/linux/se_workload.cc
@@ -116,7 +116,7 @@
     if (dynamic_cast<X86_64Process *>(process)) {
         syscallDescs64.get(rax)->doSyscall(tc);
     } else if (auto *proc32 = dynamic_cast<I386Process *>(process)) {
-        PCState pc = tc->pcState();
+        PCState pc = tc->pcState().as<PCState>();
         Addr eip = pc.pc();
         const auto &vsyscall = proc32->getVSyscallPage();
         if (eip >= vsyscall.base && eip < vsyscall.base + vsyscall.size) {
@@ -133,10 +133,10 @@
 EmuLinux::event(ThreadContext *tc)
 {
     Process *process = tc->getProcessPtr();
-    auto pcState = tc->pcState();
+    Addr pc = tc->pcState().instAddr();
 
     if (process->kvmInSE) {
-        Addr pc_page = mbits(pcState.pc(), 63, 12);
+        Addr pc_page = mbits(pc, 63, 12);
         if (pc_page == syscallCodeVirtAddr) {
             syscall(tc);
             return;
@@ -145,7 +145,7 @@
             return;
         }
     }
-    warn("Unexpected workload event at pc %#x.", pcState.pc());
+    warn("Unexpected workload event at pc %#x.", pc);
 }
 
 void
diff --git a/src/arch/x86/nativetrace.cc b/src/arch/x86/nativetrace.cc
index 9e357a8..abb1a32 100644
--- a/src/arch/x86/nativetrace.cc
+++ b/src/arch/x86/nativetrace.cc
@@ -87,7 +87,7 @@
     r13 = tc->readIntReg(X86ISA::INTREG_R13);
     r14 = tc->readIntReg(X86ISA::INTREG_R14);
     r15 = tc->readIntReg(X86ISA::INTREG_R15);
-    rip = tc->pcState().npc();
+    rip = tc->pcState().as<X86ISA::PCState>().npc();
     //This should be expanded if x87 registers are considered
     for (int i = 0; i < 8; i++)
         mmx[i] = tc->readFloatReg(X86ISA::FLOATREG_MMX(i));
diff --git a/src/arch/x86/remote_gdb.cc b/src/arch/x86/remote_gdb.cc
index 41b4525..abb2154 100644
--- a/src/arch/x86/remote_gdb.cc
+++ b/src/arch/x86/remote_gdb.cc
@@ -141,7 +141,7 @@
     r.r13 = context->readIntReg(INTREG_R13);
     r.r14 = context->readIntReg(INTREG_R14);
     r.r15 = context->readIntReg(INTREG_R15);
-    r.rip = context->pcState().pc();
+    r.rip = context->pcState().instAddr();
     r.eflags = context->readMiscRegNoEffect(MISCREG_RFLAGS);
     r.cs = context->readMiscRegNoEffect(MISCREG_CS);
     r.ss = context->readMiscRegNoEffect(MISCREG_SS);
@@ -163,7 +163,7 @@
     r.ebp = context->readIntReg(INTREG_RBP);
     r.esi = context->readIntReg(INTREG_RSI);
     r.edi = context->readIntReg(INTREG_RDI);
-    r.eip = context->pcState().pc();
+    r.eip = context->pcState().instAddr();
     r.eflags = context->readMiscRegNoEffect(MISCREG_RFLAGS);
     r.cs = context->readMiscRegNoEffect(MISCREG_CS);
     r.ss = context->readMiscRegNoEffect(MISCREG_SS);
diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh
index 2471bae..3d29d05 100644
--- a/src/cpu/checker/cpu.hh
+++ b/src/cpu/checker/cpu.hh
@@ -354,19 +354,17 @@
         return (thread->htmTransactionStarts - thread->htmTransactionStops);
     }
 
-    mutable TheISA::PCState tempPCState;
     const PCStateBase &
     pcState() const override
     {
-        set(tempPCState, thread->pcState());
-        return tempPCState;
+        return thread->pcState();
     }
     void
     pcState(const PCStateBase &val) override
     {
         DPRINTF(Checker, "Changing PC to %s, old PC %s.\n",
                          val, thread->pcState());
-        thread->pcState(val.as<TheISA::PCState>());
+        thread->pcState(val);
     }
     Addr instAddr() { return thread->instAddr(); }
     MicroPC microPC() { return thread->microPC(); }
diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh
index 385ee2a..42b53cd 100644
--- a/src/cpu/checker/cpu_impl.hh
+++ b/src/cpu/checker/cpu_impl.hh
@@ -74,10 +74,10 @@
         if (curStaticInst) {
             if (curStaticInst->isLastMicroop())
                 curMacroStaticInst = nullStaticInstPtr;
-            TheISA::PCState pcState = thread->pcState();
-            curStaticInst->advancePC(pcState);
-            thread->pcState(pcState);
-            DPRINTF(Checker, "Advancing PC to %s.\n", thread->pcState());
+            std::unique_ptr<PCStateBase> pc_ptr(thread->pcState().clone());
+            curStaticInst->advancePC(*pc_ptr);
+            thread->pcState(*pc_ptr);
+            DPRINTF(Checker, "Advancing PC to %s.\n", *pc_ptr);
         }
     }
 }
@@ -282,29 +282,32 @@
             }
 
             if (fault == NoFault) {
-                TheISA::PCState pcState = thread->pcState();
+                std::unique_ptr<PCStateBase> pc_state(
+                        thread->pcState().clone());
 
-                if (isRomMicroPC(pcState.microPC())) {
+                if (isRomMicroPC(pc_state->microPC())) {
                     fetchDone = true;
                     curStaticInst = decoder.fetchRomMicroop(
-                            pcState.microPC(), nullptr);
+                            pc_state->microPC(), nullptr);
                 } else if (!curMacroStaticInst) {
                     //We're not in the middle of a macro instruction
                     StaticInstPtr instPtr = nullptr;
 
                     //Predecode, ie bundle up an ExtMachInst
                     //If more fetch data is needed, pass it in.
-                    Addr fetchPC =
-                        (pcState.instAddr() & pc_mask) + fetchOffset;
-                    decoder.moreBytes(pcState, fetchPC);
+                    Addr fetch_pc =
+                        (pc_state->instAddr() & pc_mask) + fetchOffset;
+                    decoder.moreBytes(pc_state->as<TheISA::PCState>(),
+                            fetch_pc);
 
                     //If an instruction is ready, decode it.
                     //Otherwise, we'll have to fetch beyond the
                     //memory chunk at the current pc.
                     if (decoder.instReady()) {
                         fetchDone = true;
-                        instPtr = decoder.decode(pcState);
-                        thread->pcState(pcState);
+                        instPtr = decoder.decode(
+                                pc_state->as<TheISA::PCState>());
+                        thread->pcState(*pc_state);
                     } else {
                         fetchDone = false;
                         fetchOffset += decoder.moreBytesSize();
@@ -315,14 +318,14 @@
                     if (instPtr && instPtr->isMacroop()) {
                         curMacroStaticInst = instPtr;
                         curStaticInst =
-                            instPtr->fetchMicroop(pcState.microPC());
+                            instPtr->fetchMicroop(pc_state->microPC());
                     } else {
                         curStaticInst = instPtr;
                     }
                 } else {
                     // Read the next micro op from the macro-op
                     curStaticInst =
-                        curMacroStaticInst->fetchMicroop(pcState.microPC());
+                        curMacroStaticInst->fetchMicroop(pc_state->microPC());
                     fetchDone = true;
                 }
             }
diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh
index 62abb21..b210fea 100644
--- a/src/cpu/checker/thread_context.hh
+++ b/src/cpu/checker/thread_context.hh
@@ -321,11 +321,11 @@
     }
 
     /** Reads this thread's PC state. */
-    TheISA::PCState pcState() const override { return actualTC->pcState(); }
+    const PCStateBase &pcState() const override { return actualTC->pcState(); }
 
     /** Sets this thread's PC state. */
     void
-    pcState(const TheISA::PCState &val) override
+    pcState(const PCStateBase &val) override
     {
         DPRINTF(Checker, "Changing PC to %s, old PC %s\n",
                          val, checkerTC->pcState());
@@ -342,7 +342,7 @@
     }
 
     void
-    pcStateNoRecord(const TheISA::PCState &val) override
+    pcStateNoRecord(const PCStateBase &val) override
     {
         return actualTC->pcState(val);
     }
diff --git a/src/cpu/minor/exec_context.hh b/src/cpu/minor/exec_context.hh
index 2c5a4b2..2773f9e 100644
--- a/src/cpu/minor/exec_context.hh
+++ b/src/cpu/minor/exec_context.hh
@@ -299,18 +299,16 @@
         return 0;
     }
 
-    mutable TheISA::PCState tempPCState;
     const PCStateBase &
     pcState() const override
     {
-        set(tempPCState, thread.pcState());
-        return tempPCState;
+        return thread.pcState();
     }
 
     void
     pcState(const PCStateBase &val) override
     {
-        thread.pcState(val.as<TheISA::PCState>());
+        thread.pcState(val);
     }
 
     RegVal
diff --git a/src/cpu/minor/execute.cc b/src/cpu/minor/execute.cc
index 39a3ba4..2b3cb8d 100644
--- a/src/cpu/minor/execute.cc
+++ b/src/cpu/minor/execute.cc
@@ -225,7 +225,7 @@
 {
     ThreadContext *thread = cpu.getContext(inst->id.threadId);
     const std::unique_ptr<PCStateBase> pc_before(inst->pc->clone());
-    TheISA::PCState target = thread->pcState();
+    std::unique_ptr<PCStateBase> target(thread->pcState().clone());
 
     /* Force a branch for SerializeAfter/SquashAfter instructions
      * at the end of micro-op sequence when we're not suspended */
@@ -236,10 +236,10 @@
          inst->staticInst->isSquashAfter());
 
     DPRINTF(Branch, "tryToBranch before: %s after: %s%s\n",
-        *pc_before, target, (force_branch ? " (forcing)" : ""));
+        *pc_before, *target, (force_branch ? " (forcing)" : ""));
 
     /* Will we change the PC to something other than the next instruction? */
-    bool must_branch = *pc_before != target ||
+    bool must_branch = *pc_before != *target ||
         fault != NoFault ||
         force_branch;
 
@@ -247,11 +247,11 @@
     BranchData::Reason reason = BranchData::NoBranch;
 
     if (fault == NoFault) {
-        inst->staticInst->advancePC(target);
-        thread->pcState(target);
+        inst->staticInst->advancePC(*target);
+        thread->pcState(*target);
 
         DPRINTF(Branch, "Advancing current PC from: %s to: %s\n",
-            *pc_before, target);
+            *pc_before, *target);
     }
 
     if (inst->predictedTaken && !force_branch) {
@@ -265,7 +265,7 @@
                 *inst);
 
             reason = BranchData::BadlyPredictedBranch;
-        } else if (*inst->predictedTarget == target) {
+        } else if (*inst->predictedTarget == *target) {
             /* Branch prediction got the right target, kill the branch and
              *  carry on.
              *  Note that this information to the branch predictor might get
@@ -281,14 +281,14 @@
             DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x"
                     " but got the wrong target (actual: 0x%x) inst: %s\n",
                     inst->pc->instAddr(), inst->predictedTarget->instAddr(),
-                    target.instAddr(), *inst);
+                    target->instAddr(), *inst);
 
             reason = BranchData::BadlyPredictedBranchTarget;
         }
     } else if (must_branch) {
         /* Unpredicted branch */
         DPRINTF(Branch, "Unpredicted branch from 0x%x to 0x%x inst: %s\n",
-            inst->pc->instAddr(), target.instAddr(), *inst);
+            inst->pc->instAddr(), target->instAddr(), *inst);
 
         reason = BranchData::UnpredictedBranch;
     } else {
@@ -296,14 +296,14 @@
         reason = BranchData::NoBranch;
     }
 
-    updateBranchData(inst->id.threadId, reason, inst, target, branch);
+    updateBranchData(inst->id.threadId, reason, inst, target.get(), branch);
 }
 
 void
 Execute::updateBranchData(
     ThreadID tid,
     BranchData::Reason reason,
-    MinorDynInstPtr inst, const TheISA::PCState &target,
+    MinorDynInstPtr inst, const PCStateBase *target,
     BranchData &branch)
 {
     if (reason != BranchData::NoBranch) {
@@ -443,7 +443,7 @@
         /* Assume that an interrupt *must* cause a branch.  Assert this? */
 
         updateBranchData(thread_id, BranchData::Interrupt,
-            MinorDynInst::bubble(), cpu.getContext(thread_id)->pcState(),
+            MinorDynInst::bubble(), &cpu.getContext(thread_id)->pcState(),
             branch);
     }
 
@@ -465,7 +465,7 @@
         issued = false;
     } else {
         ThreadContext *thread = cpu.getContext(inst->id.threadId);
-        TheISA::PCState old_pc = thread->pcState();
+        std::unique_ptr<PCStateBase> old_pc(thread->pcState().clone());
 
         ExecContext context(cpu, *cpu.threads[inst->id.threadId],
             *this, inst, zeroReg);
@@ -517,7 +517,7 @@
         }
 
         /* Restore thread PC */
-        thread->pcState(old_pc);
+        thread->pcState(*old_pc);
         issued = true;
     }
 
@@ -1022,7 +1022,7 @@
             !isInterrupted(thread_id)) /* Don't suspend if we have
                 interrupts */
         {
-            TheISA::PCState resume_pc = cpu.getContext(thread_id)->pcState();
+            auto &resume_pc = cpu.getContext(thread_id)->pcState();
 
             assert(resume_pc.microPC() == 0);
 
@@ -1032,7 +1032,7 @@
             cpu.stats.numFetchSuspends++;
 
             updateBranchData(thread_id, BranchData::SuspendThread, inst,
-                resume_pc, branch);
+                &resume_pc, branch);
         }
     }
 
@@ -1140,7 +1140,7 @@
 
             /* Branch as there was a change in PC */
             updateBranchData(thread_id, BranchData::UnpredictedBranch,
-                MinorDynInst::bubble(), thread->pcState(), branch);
+                MinorDynInst::bubble(), &thread->pcState(), branch);
         } else if (mem_response &&
             num_mem_refs_committed < memoryCommitLimit)
         {
@@ -1495,7 +1495,7 @@
              *  the bag */
             if (commit_info.drainState == DrainHaltFetch) {
                 updateBranchData(commit_tid, BranchData::HaltFetch,
-                        MinorDynInst::bubble(), TheISA::PCState(0), branch);
+                        MinorDynInst::bubble(), nullptr, branch);
 
                 cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
                 setDrainState(commit_tid, DrainAllInsts);
diff --git a/src/cpu/minor/execute.hh b/src/cpu/minor/execute.hh
index 56966ba..21720bb 100644
--- a/src/cpu/minor/execute.hh
+++ b/src/cpu/minor/execute.hh
@@ -232,8 +232,7 @@
     /** Actually create a branch to communicate to Fetch1/Fetch2 and,
      *  if that is a stream-changing branch update the streamSeqNum */
     void updateBranchData(ThreadID tid, BranchData::Reason reason,
-        MinorDynInstPtr inst, const TheISA::PCState &target,
-        BranchData &branch);
+        MinorDynInstPtr inst, const PCStateBase *target, BranchData &branch);
 
     /** Handle extracting mem ref responses from the memory queues and
      *  completing the associated instructions.
diff --git a/src/cpu/minor/fetch2.cc b/src/cpu/minor/fetch2.cc
index 58b68de..612b9e1 100644
--- a/src/cpu/minor/fetch2.cc
+++ b/src/cpu/minor/fetch2.cc
@@ -223,7 +223,7 @@
         BranchData new_branch = BranchData(BranchData::BranchPrediction,
             inst->id.threadId,
             inst->id.streamSeqNum, thread.predictionSeqNum + 1,
-            *inst->predictedTarget, inst);
+            inst->predictedTarget.get(), inst);
 
         /* Mark with a new prediction number by the stream number of the
          *  instruction causing the prediction */
diff --git a/src/cpu/minor/lsq.cc b/src/cpu/minor/lsq.cc
index e4c97ea..e4c000b 100644
--- a/src/cpu/minor/lsq.cc
+++ b/src/cpu/minor/lsq.cc
@@ -80,7 +80,7 @@
 LSQ::LSQRequest::tryToSuppressFault()
 {
     SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
-    TheISA::PCState old_pc = thread.pcState();
+    std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
     ExecContext context(port.cpu, thread, port.execute, inst, zeroReg);
     [[maybe_unused]] Fault fault = inst->translationFault;
 
@@ -92,7 +92,7 @@
     } else {
         assert(inst->translationFault == fault);
     }
-    thread.pcState(old_pc);
+    thread.pcState(*old_pc);
 }
 
 void
@@ -102,14 +102,14 @@
              *inst);
 
     SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
-    TheISA::PCState old_pc = thread.pcState();
+    std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
 
     ExecContext context(port.cpu, thread, port.execute, inst, zeroReg);
 
     context.setMemAccPredicate(false);
     inst->staticInst->completeAcc(nullptr, &context, inst->traceData);
 
-    thread.pcState(old_pc);
+    thread.pcState(*old_pc);
 }
 
 void
@@ -1131,7 +1131,7 @@
 
         SimpleThread &thread = *cpu.threads[request->inst->id.threadId];
 
-        TheISA::PCState old_pc = thread.pcState();
+        std::unique_ptr<PCStateBase> old_pc(thread.pcState().clone());
         ExecContext context(cpu, thread, execute, request->inst, zeroReg);
 
         /* Handle LLSC requests and tests */
@@ -1146,7 +1146,7 @@
                     "access for store conditional\n");
             }
         }
-        thread.pcState(old_pc);
+        thread.pcState(*old_pc);
     }
 
     /* See the do_access comment above */
diff --git a/src/cpu/minor/pipe_data.hh b/src/cpu/minor/pipe_data.hh
index b85f9be..b736ea9 100644
--- a/src/cpu/minor/pipe_data.hh
+++ b/src/cpu/minor/pipe_data.hh
@@ -130,20 +130,23 @@
 
     BranchData(Reason reason_, ThreadID thread_id,
             InstSeqNum new_stream_seq_num, InstSeqNum new_prediction_seq_num,
-            const PCStateBase &target, MinorDynInstPtr inst_) :
+            const PCStateBase *_target, MinorDynInstPtr inst_) :
         reason(reason_), threadId(thread_id),
         newStreamSeqNum(new_stream_seq_num),
         newPredictionSeqNum(new_prediction_seq_num),
-        target(target.clone()), inst(inst_)
-    {}
+        inst(inst_)
+    {
+        set(target, _target);
+    }
 
     BranchData(const BranchData &other) :
         reason(other.reason), threadId(other.threadId),
         newStreamSeqNum(other.newStreamSeqNum),
         newPredictionSeqNum(other.newPredictionSeqNum),
-        target(other.target->clone()),
         inst(other.inst)
-    {}
+    {
+        set(target, other.target);
+    }
     BranchData &
     operator=(const BranchData &other)
     {
diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc
index 69c6ed8..462c029 100644
--- a/src/cpu/o3/cpu.cc
+++ b/src/cpu/o3/cpu.cc
@@ -1307,14 +1307,14 @@
     regFile.setCCReg(phys_reg, val);
 }
 
-TheISA::PCState
+const PCStateBase &
 CPU::pcState(ThreadID tid)
 {
-    return commit.pcState(tid).as<TheISA::PCState>();
+    return commit.pcState(tid);
 }
 
 void
-CPU::pcState(const TheISA::PCState &val, ThreadID tid)
+CPU::pcState(const PCStateBase &val, ThreadID tid)
 {
     commit.pcState(val, tid);
 }
diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh
index edb694e..e7dd065 100644
--- a/src/cpu/o3/cpu.hh
+++ b/src/cpu/o3/cpu.hh
@@ -385,10 +385,10 @@
     void setArchCCReg(int reg_idx, RegVal val, ThreadID tid);
 
     /** Sets the commit PC state of a specific thread. */
-    void pcState(const TheISA::PCState &newPCState, ThreadID tid);
+    void pcState(const PCStateBase &new_pc_state, ThreadID tid);
 
     /** Reads the commit PC state of a specific thread. */
-    TheISA::PCState pcState(ThreadID tid);
+    const PCStateBase &pcState(ThreadID tid);
 
     /** Reads the commit PC of a specific thread. */
     Addr instAddr(ThreadID tid);
diff --git a/src/cpu/o3/thread_context.cc b/src/cpu/o3/thread_context.cc
index 0842062..9154012 100644
--- a/src/cpu/o3/thread_context.cc
+++ b/src/cpu/o3/thread_context.cc
@@ -248,7 +248,7 @@
 }
 
 void
-ThreadContext::pcState(const TheISA::PCState &val)
+ThreadContext::pcState(const PCStateBase &val)
 {
     cpu->pcState(val, thread->threadId());
 
@@ -256,7 +256,7 @@
 }
 
 void
-ThreadContext::pcStateNoRecord(const TheISA::PCState &val)
+ThreadContext::pcStateNoRecord(const PCStateBase &val)
 {
     cpu->pcState(val, thread->threadId());
 
diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh
index 9c38680..c50371e 100644
--- a/src/cpu/o3/thread_context.hh
+++ b/src/cpu/o3/thread_context.hh
@@ -277,16 +277,16 @@
     }
 
     /** Reads this thread's PC state. */
-    TheISA::PCState
+    const PCStateBase &
     pcState() const override
     {
         return cpu->pcState(thread->threadId());
     }
 
     /** Sets this thread's PC state. */
-    void pcState(const TheISA::PCState &val) override;
+    void pcState(const PCStateBase &val) override;
 
-    void pcStateNoRecord(const TheISA::PCState &val) override;
+    void pcStateNoRecord(const PCStateBase &val) override;
 
     /** Reads this thread's PC. */
     Addr
diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc
index de1a733..8ee10b6 100644
--- a/src/cpu/simple/atomic.cc
+++ b/src/cpu/simple/atomic.cc
@@ -650,10 +650,9 @@
 
         Fault fault = NoFault;
 
-        TheISA::PCState pcState = thread->pcState();
+        const PCStateBase &pc = thread->pcState();
 
-        bool needToFetch = !isRomMicroPC(pcState.microPC()) &&
-                           !curMacroStaticInst;
+        bool needToFetch = !isRomMicroPC(pc.microPC()) && !curMacroStaticInst;
         if (needToFetch) {
             ifetch_req->taskId(taskId());
             setupFetchRequest(ifetch_req);
diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc
index 7ddd100..549e745 100644
--- a/src/cpu/simple/base.cc
+++ b/src/cpu/simple/base.cc
@@ -112,7 +112,8 @@
         checker->setSystem(p.system);
         // Manipulate thread context
         ThreadContext *cpu_tc = threadContexts[0];
-        threadContexts[0] = new CheckerThreadContext<ThreadContext>(cpu_tc, this->checker);
+        threadContexts[0] = new CheckerThreadContext<ThreadContext>(
+                cpu_tc, this->checker);
     } else {
         checker = NULL;
     }
@@ -312,31 +313,31 @@
     t_info.setMemAccPredicate(true);
 
     // decode the instruction
-    TheISA::PCState pcState = thread->pcState();
+    std::unique_ptr<PCStateBase> pc_state(thread->pcState().clone());
 
     auto &decoder = thread->decoder;
 
-    if (isRomMicroPC(pcState.microPC())) {
+    if (isRomMicroPC(pc_state->microPC())) {
         t_info.stayAtPC = false;
         curStaticInst = decoder.fetchRomMicroop(
-                pcState.microPC(), curMacroStaticInst);
+                pc_state->microPC(), curMacroStaticInst);
     } else if (!curMacroStaticInst) {
         //We're not in the middle of a macro instruction
         StaticInstPtr instPtr = NULL;
 
         //Predecode, ie bundle up an ExtMachInst
         //If more fetch data is needed, pass it in.
-        Addr fetchPC =
-            (pcState.instAddr() & decoder.pcMask()) + t_info.fetchOffset;
+        Addr fetch_pc =
+            (pc_state->instAddr() & decoder.pcMask()) + t_info.fetchOffset;
 
-        decoder.moreBytes(pcState, fetchPC);
+        decoder.moreBytes(pc_state->as<TheISA::PCState>(), fetch_pc);
 
         //Decode an instruction if one is ready. Otherwise, we'll have to
         //fetch beyond the MachInst at the current pc.
-        instPtr = decoder.decode(pcState);
+        instPtr = decoder.decode(pc_state->as<TheISA::PCState>());
         if (instPtr) {
             t_info.stayAtPC = false;
-            thread->pcState(pcState);
+            thread->pcState(*pc_state);
         } else {
             t_info.stayAtPC = true;
             t_info.fetchOffset += decoder.moreBytesSize();
@@ -347,13 +348,13 @@
         if (instPtr && instPtr->isMacroop()) {
             curMacroStaticInst = instPtr;
             curStaticInst =
-                curMacroStaticInst->fetchMicroop(pcState.microPC());
+                curMacroStaticInst->fetchMicroop(pc_state->microPC());
         } else {
             curStaticInst = instPtr;
         }
     } else {
         //Read the next micro op from the macro op
-        curStaticInst = curMacroStaticInst->fetchMicroop(pcState.microPC());
+        curStaticInst = curMacroStaticInst->fetchMicroop(pc_state->microPC());
     }
 
     //If we decoded an instruction this "tick", record information about it.
@@ -460,7 +461,8 @@
     SimpleExecContext &t_info = *threadInfo[curThread];
     SimpleThread* thread = t_info.thread;
 
-    const bool branching(thread->pcState().branching());
+    const bool branching =
+        thread->pcState().as<TheISA::PCState>().branching();
 
     //Since we're moving to a new pc, zero out the offset
     t_info.fetchOffset = 0;
@@ -472,9 +474,9 @@
         if (curStaticInst) {
             if (curStaticInst->isLastMicroop())
                 curMacroStaticInst = nullStaticInstPtr;
-            TheISA::PCState pcState = thread->pcState();
-            curStaticInst->advancePC(pcState);
-            thread->pcState(pcState);
+            std::unique_ptr<PCStateBase> pc(thread->pcState().clone());
+            curStaticInst->advancePC(*pc);
+            thread->pcState(*pc);
         }
     }
 
@@ -483,7 +485,7 @@
         // instruction in flight at the same time.
         const InstSeqNum cur_sn(0);
 
-        if (t_info.predPC->as<TheISA::PCState>() == thread->pcState()) {
+        if (*t_info.predPC == thread->pcState()) {
             // Correctly predicted branch
             branchPred->update(cur_sn, curThread);
         } else {
diff --git a/src/cpu/simple/exec_context.hh b/src/cpu/simple/exec_context.hh
index 305f110..b0fe779 100644
--- a/src/cpu/simple/exec_context.hh
+++ b/src/cpu/simple/exec_context.hh
@@ -467,18 +467,16 @@
         thread->setMiscReg(misc_reg, val);
     }
 
-    mutable TheISA::PCState tempPCState;
     const PCStateBase &
     pcState() const override
     {
-        set(tempPCState, thread->pcState());
-        return tempPCState;
+        return thread->pcState();
     }
 
     void
     pcState(const PCStateBase &val) override
     {
-        thread->pcState(val.as<TheISA::PCState>());
+        thread->pcState(val);
     }
 
     Fault
diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc
index f05b7c4..9f1ce4a 100644
--- a/src/cpu/simple/timing.cc
+++ b/src/cpu/simple/timing.cc
@@ -693,9 +693,8 @@
     if (_status == Idle)
         return;
 
-    TheISA::PCState pcState = thread->pcState();
-    bool needToFetch = !isRomMicroPC(pcState.microPC()) &&
-                       !curMacroStaticInst;
+    MicroPC upc = thread->pcState().microPC();
+    bool needToFetch = !isRomMicroPC(upc) && !curMacroStaticInst;
 
     if (needToFetch) {
         _status = BaseSimpleCPU::Running;
diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh
index 258d376..31e02f7 100644
--- a/src/cpu/simple_thread.hh
+++ b/src/cpu/simple_thread.hh
@@ -105,7 +105,7 @@
     std::vector<RegVal> ccRegs;
     TheISA::ISA *const isa;    // one "instance" of the current ISA.
 
-    TheISA::PCState _pcState;
+    std::unique_ptr<PCStateBase> _pcState;
 
     // hardware transactional memory
     std::unique_ptr<BaseHTMCheckpoint> _htmCheckpoint;
@@ -249,7 +249,7 @@
     void
     clearArchRegs() override
     {
-        _pcState.set(0);
+        set(_pcState, isa->newPCState());
         std::fill(intRegs.begin(), intRegs.end(), 0);
         std::fill(floatRegs.begin(), floatRegs.end(), 0);
         for (auto &vec_reg: vecRegs)
@@ -420,17 +420,17 @@
         setCCRegFlat(flatIndex, val);
     }
 
-    TheISA::PCState pcState() const override { return _pcState; }
-    void pcState(const TheISA::PCState &val) override { _pcState = val; }
+    const PCStateBase &pcState() const override { return *_pcState; }
+    void pcState(const PCStateBase &val) override { set(_pcState, val); }
 
     void
-    pcStateNoRecord(const TheISA::PCState &val) override
+    pcStateNoRecord(const PCStateBase &val) override
     {
-        _pcState = val;
+        set(_pcState, val);
     }
 
-    Addr instAddr() const override  { return _pcState.instAddr(); }
-    MicroPC microPC() const override { return _pcState.microPC(); }
+    Addr instAddr() const override  { return _pcState->instAddr(); }
+    MicroPC microPC() const override { return _pcState->microPC(); }
     bool readPredicate() const { return predicate; }
     void setPredicate(bool val) { predicate = val; }
 
diff --git a/src/cpu/thread_context.cc b/src/cpu/thread_context.cc
index 4852111..40df84a 100644
--- a/src/cpu/thread_context.cc
+++ b/src/cpu/thread_context.cc
@@ -116,7 +116,7 @@
             panic("CC reg idx %d doesn't match, one: %#x, two: %#x",
                   i, t1, t2);
     }
-    if (!(one->pcState() == two->pcState()))
+    if (one->pcState() != two->pcState())
         panic("PC state doesn't match.");
     int id1 = one->cpuId();
     int id2 = two->cpuId();
@@ -243,9 +243,9 @@
             tc.setCCRegFlat(i, ccRegs[i]);
     }
 
-    TheISA::PCState pcState;
-    pcState.unserialize(cp);
-    tc.pcState(pcState);
+    std::unique_ptr<PCStateBase> pc_state(tc.pcState().clone());
+    pc_state->unserialize(cp);
+    tc.pcState(*pc_state);
 
     // thread_num and cpu_id are deterministic from the config
 }
diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh
index 2fd22ff..9e4d495 100644
--- a/src/cpu/thread_context.hh
+++ b/src/cpu/thread_context.hh
@@ -223,24 +223,25 @@
 
     virtual void setCCReg(RegIndex reg_idx, RegVal val) = 0;
 
-    virtual TheISA::PCState pcState() const = 0;
+    virtual const PCStateBase &pcState() const = 0;
 
-    virtual void pcState(const TheISA::PCState &val) = 0;
+    virtual void pcState(const PCStateBase &val) = 0;
     void
     pcState(Addr addr)
     {
-        pcState(getIsaPtr()->newPCState(addr)->as<TheISA::PCState>());
+        std::unique_ptr<PCStateBase> new_pc(getIsaPtr()->newPCState(addr));
+        pcState(*new_pc);
     }
 
     void
     setNPC(Addr val)
     {
-        TheISA::PCState pc_state = pcState();
-        pc_state.setNPC(val);
-        pcState(pc_state);
+        std::unique_ptr<PCStateBase> pc_state(pcState().clone());
+        pc_state->as<TheISA::PCState>().setNPC(val);
+        pcState(*pc_state);
     }
 
-    virtual void pcStateNoRecord(const TheISA::PCState &val) = 0;
+    virtual void pcStateNoRecord(const PCStateBase &val) = 0;
 
     virtual Addr instAddr() const = 0;
 
diff --git a/src/sim/faults.cc b/src/sim/faults.cc
index 77956d8..f7ca203 100644
--- a/src/sim/faults.cc
+++ b/src/sim/faults.cc
@@ -73,9 +73,9 @@
 {
     tc->getSystemPtr()->workload->syscall(tc);
     // Move the PC forward since that doesn't happen automatically.
-    TheISA::PCState pc = tc->pcState();
-    inst->advancePC(pc);
-    tc->pcState(pc);
+    std::unique_ptr<PCStateBase> pc(tc->pcState().clone());
+    inst->advancePC(*pc);
+    tc->pcState(*pc);
 }
 
 void
diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh
index d78f4ac..54bd54b 100644
--- a/src/sim/syscall_emul.hh
+++ b/src/sim/syscall_emul.hh
@@ -1661,9 +1661,9 @@
 
     desc->returnInto(ctc, 0);
 
-    TheISA::PCState cpc = tc->pcState();
-    cpc.advance();
-    ctc->pcState(cpc);
+    std::unique_ptr<PCStateBase> cpc(tc->pcState().clone());
+    cpc->as<TheISA::PCState>().advance();
+    ctc->pcState(*cpc);
     ctc->activate();
 
     if (flags & OS::TGT_CLONE_VFORK) {
@@ -2225,8 +2225,9 @@
     new_p->init();
     new_p->initState();
     tc->activate();
-    TheISA::PCState pcState = tc->pcState();
-    tc->setNPC(pcState.instAddr());
+    std::unique_ptr<PCStateBase> pc_state(tc->pcState().clone());
+    pc_state->as<TheISA::PCState>().advance();
+    tc->pcState(*pc_state);
 
     return SyscallReturn();
 }