arch,cpu: Rearrange StaticInst flags for memory barriers.

There were three different StaticInst flags for memory barriers,
IsMemBarrier, IsReadBarrier, and IsWriteBarrier. IsReadBarrier was never
used, and IsMemBarrier was for both loads and stores, so a composite of
IsReadBarrier and IsWriteBarrier.

This change gets rid of IsMemBarrier and replaces by setting
IsReadBarrier and IsWriteBarrier at the same time. An isMemBarrier
accessor is left, but is now implemented by checking if both of the
other flags are set, and renamed to isFullMemBarrier to make it clear
that it's checking both for both types of barrier, not one or the other.

Change-Id: I702633a047f4777be4b180b42d62438ca69f52ea
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/33743
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2020-08-30 02:28:33 -07:00
parent 429b828e7b
commit faf0af7a35
26 changed files with 106 additions and 92 deletions

View File

@@ -79,7 +79,6 @@ Memory64::setExcAcRel(bool exclusive, bool acrel)
else
memAccessFlags |= ArmISA::TLB::AllowUnaligned;
if (acrel) {
flags[IsMemBarrier] = true;
flags[IsWriteBarrier] = true;
flags[IsReadBarrier] = true;
}

View File

@@ -83,7 +83,6 @@ MicroTfence64::MicroTfence64(ExtMachInst machInst)
_numVecElemDestRegs = 0;
_numIntDestRegs = 0;
_numCCDestRegs = 0;
flags[IsMemBarrier] = true;
flags[IsMicroop] = true;
flags[IsReadBarrier] = true;
flags[IsWriteBarrier] = true;

View File

@@ -91,7 +91,7 @@ let {{
self.instFlags.append("IsMicroop")
if self.flavor in ("release", "acquire_release", "acquire"):
self.instFlags.append("IsMemBarrier")
self.instFlags.extend(["IsReadBarrier", "IsWriteBarrier"])
if self.flavor in ("release", "acquire_release"):
self.instFlags.append("IsWriteBarrier")
if self.flavor in ("acquire_release", "acquire"):

View File

@@ -179,9 +179,7 @@ let {{
self.memFlags.append("Request::LLSC")
if self.flavor in ("acquire", "acex"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
"IsReadBarrier"])
self.instFlags.extend(["IsWriteBarrier", "IsReadBarrier"])
self.memFlags.append("Request::ACQUIRE")
# Disambiguate the class name for different flavors of loads
@@ -260,9 +258,7 @@ let {{
self.Name = "%s_%s" % (self.name.upper(), self.Name)
if self.flavor in ("acquire", "acex"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
"IsReadBarrier"])
self.instFlags.extend(["IsWriteBarrier", "IsReadBarrier"])
self.memFlags.append("Request::ACQUIRE")
def emit(self):

View File

@@ -91,9 +91,7 @@ let {{
self.memFlags.append("ArmISA::TLB::AllowUnaligned")
if self.flavor in ("acquire", "acex", "acexp"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
"IsReadBarrier"])
self.instFlags.extend(["IsWriteBarrier", "IsReadBarrier"])
self.memFlags.append("Request::ACQUIRE")
if self.flavor in ("acex", "exclusive", "exp", "acexp"):

View File

@@ -1238,7 +1238,8 @@ let {{
dsbIop = InstObjParams("dsb", "Dsb", "ImmOp",
{"code": dsbCode,
"predicate_test": predicateTest},
['IsMemBarrier', 'IsSerializeAfter'])
['IsReadBarrier', 'IsWriteBarrier',
'IsSerializeAfter'])
header_output += ImmOpDeclare.subst(dsbIop)
decoder_output += ImmOpConstructor.subst(dsbIop)
exec_output += PredOpExecute.subst(dsbIop)
@@ -1254,7 +1255,7 @@ let {{
dmbIop = InstObjParams("dmb", "Dmb", "ImmOp",
{"code": dmbCode,
"predicate_test": predicateTest},
['IsMemBarrier'])
['IsReadBarrier', 'IsWriteBarrier'])
header_output += ImmOpDeclare.subst(dmbIop)
decoder_output += ImmOpConstructor.subst(dmbIop)
exec_output += PredOpExecute.subst(dmbIop)

View File

@@ -173,13 +173,14 @@ let {{
exec_output += BasicExecute.subst(isbIop)
dsbIop = InstObjParams("dsb", "Dsb64", "ArmStaticInst", "",
['IsMemBarrier', 'IsSerializeAfter'])
['IsReadBarrier', 'IsWriteBarrier',
'IsSerializeAfter'])
header_output += BasicDeclare.subst(dsbIop)
decoder_output += BasicConstructor64.subst(dsbIop)
exec_output += BasicExecute.subst(dsbIop)
dmbIop = InstObjParams("dmb", "Dmb64", "ArmStaticInst", "",
['IsMemBarrier'])
['IsReadBarrier', 'IsWriteBarrier'])
header_output += BasicDeclare.subst(dmbIop)
decoder_output += BasicConstructor64.subst(dmbIop)
exec_output += BasicExecute.subst(dmbIop)

View File

@@ -187,8 +187,7 @@ let {{
self.memFlags.append("ArmISA::TLB::AllowUnaligned")
if self.flavor in ("release", "relex"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
self.instFlags.extend(["IsWriteBarrier",
"IsReadBarrier"])
self.memFlags.append("Request::RELEASE")
@@ -269,8 +268,7 @@ let {{
self.memFlags.append("ArmISA::TLB::AlignWord")
if self.flavor in ("release", "relex"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
self.instFlags.extend(["IsWriteBarrier",
"IsReadBarrier"])
self.memFlags.append("Request::RELEASE")

View File

@@ -79,8 +79,7 @@ let {{
self.instFlags.append("IsMicroop")
if self.flavor in ("release", "relex", "relexp"):
self.instFlags.extend(["IsMemBarrier",
"IsWriteBarrier",
self.instFlags.extend(["IsWriteBarrier",
"IsReadBarrier"])
self.memFlags.append("Request::RELEASE")

View File

@@ -38,8 +38,8 @@
// A new class of Semihosting constructor templates has been added.
// Their main purpose is to check if the Exception Generation
// Instructions (HLT, SVC) are actually a semihosting command.
// If that is the case, the IsMemBarrier flag is raised, so that
// in the O3 model we perform a coherent memory access during
// If that is the case, the IsReadBarrier and IsWriteBarrier flags are raised,
// so that in the O3 model we perform a coherent memory access during
// the semihosting operation.
// Please note: since we don't have a thread context pointer in the
// constructor we cannot check if semihosting is enabled in the
@@ -64,7 +64,8 @@ def template SemihostConstructor {{
auto semihost_imm = machInst.thumb? %(thumb_semihost)s :
%(arm_semihost)s;
if (_imm == semihost_imm) {
flags[IsMemBarrier] = true;
flags[IsReadBarrier] = true;
flags[IsWriteBarrier] = true;
}
}
}};
@@ -78,7 +79,8 @@ def template SemihostConstructor64 {{
// In AArch64 there is only one instruction for issuing
// semhosting commands: HLT #0xF000
if (_imm == 0xF000) {
flags[IsMemBarrier] = true;
flags[IsReadBarrier] = true;
flags[IsWriteBarrier] = true;
}
}
}};

View File

@@ -166,7 +166,7 @@ decode OPCODE_HI default Unknown::unknown() {
fault = std::make_shared<SystemCallFault>();
}});
}
0x7: sync({{ ; }}, IsMemBarrier);
0x7: sync({{ ; }}, IsReadBarrier, IsWriteBarrier);
0x5: break({{fault = std::make_shared<BreakpointFault>();}});
}

View File

@@ -343,8 +343,8 @@ decode OPCODE default Unknown::unknown() {
format MiscOp {
278: dcbt({{ }});
246: dcbtst({{ }});
598: sync({{ }}, [ IsMemBarrier ]);
854: eieio({{ }}, [ IsMemBarrier ]);
598: sync({{ }}, [ IsReadBarrier, IsWriteBarrier ]);
854: eieio({{ }}, [ IsReadBarrier, IsWriteBarrier ]);
}
}

View File

@@ -421,7 +421,7 @@ decode QUADRANT default Unknown::unknown() {
0x03: decode FUNCT3 {
format FenceOp {
0x0: fence({{
}}, uint64_t, IsMemBarrier, No_OpClass);
}}, uint64_t, IsReadBarrier, IsWriteBarrier, No_OpClass);
0x1: fence_i({{
}}, uint64_t, IsNonSpeculative, IsSerializeAfter, No_OpClass);
}

View File

@@ -100,7 +100,8 @@ def template LRSCMacroConstructor {{
if (RL) {
rel_fence = new MemFenceMicro(machInst, No_OpClass);
rel_fence->setFlag(IsFirstMicroop);
rel_fence->setFlag(IsMemBarrier);
rel_fence->setFlag(IsReadBarrier);
rel_fence->setFlag(IsWriteBarrier);
rel_fence->setFlag(IsDelayedCommit);
}
@@ -121,7 +122,8 @@ def template LRSCMacroConstructor {{
if (AQ) {
acq_fence = new MemFenceMicro(machInst, No_OpClass);
acq_fence->setFlag(IsLastMicroop);
acq_fence->setFlag(IsMemBarrier);
acq_fence->setFlag(IsReadBarrier);
acq_fence->setFlag(IsWriteBarrier);
}
if (RL && AQ) {
@@ -159,7 +161,8 @@ def template AtomicMemOpMacroConstructor {{
if (RL) {
rel_fence = new MemFenceMicro(machInst, No_OpClass);
rel_fence->setFlag(IsFirstMicroop);
rel_fence->setFlag(IsMemBarrier);
rel_fence->setFlag(IsReadBarrier);
rel_fence->setFlag(IsWriteBarrier);
rel_fence->setFlag(IsDelayedCommit);
}
@@ -180,7 +183,8 @@ def template AtomicMemOpMacroConstructor {{
if (AQ) {
acq_fence = new MemFenceMicro(machInst, No_OpClass);
acq_fence->setFlag(IsLastMicroop);
acq_fence->setFlag(IsMemBarrier);
acq_fence->setFlag(IsReadBarrier);
acq_fence->setFlag(IsWriteBarrier);
}
if (RL && AQ) {

View File

@@ -335,7 +335,8 @@ decode OP default Unknown::unknown()
// 7-14 should cause an illegal instruction exception
0x0F: decode I {
0x0: Nop::stbar(IsWriteBarrier, MemWriteOp);
0x1: Nop::membar(IsMemBarrier, MemReadOp);
0x1: Nop::membar(IsReadBarrier, IsWriteBarrier,
MemReadOp);
}
0x10: Priv::rdpcr({{Rd = Pcr;}});
0x11: Priv::rdpic({{Rd = Pic;}}, {{Pcr<0:>}});

View File

@@ -786,13 +786,12 @@
//0x6: group15();
0x6: decode MODRM_MOD {
0x3: decode MODRM_REG {
0x5: BasicOperate::LFENCE(
{{/*Nothing*/}}, IsReadBarrier,
IsSerializeAfter);
0x6: BasicOperate::MFENCE(
{{/*Nothing*/}}, IsMemBarrier);
0x7: BasicOperate::SFENCE(
{{/*Nothing*/}}, IsWriteBarrier);
0x5: BasicOperate::LFENCE({{/*Nothing*/}},
IsReadBarrier, IsSerializeAfter);
0x6: BasicOperate::MFENCE({{/*Nothing*/}},
IsReadBarrier, IsWriteBarrier);
0x7: BasicOperate::SFENCE({{/*Nothing*/}},
IsWriteBarrier);
default: Inst::UD2();
}
default: decode MODRM_REG {

View File

@@ -233,7 +233,8 @@ let {{
def __init__(self):
self.className = "Mfence"
self.mnemonic = "mfence"
self.instFlags = "| (1ULL << StaticInst::IsMemBarrier)"
self.instFlags = "| (1ULL << StaticInst::IsReadBarrier)" + \
"| (1ULL << StaticInst::IsWriteBarrier)"
def getAllocator(self, microFlags):
allocString = '''

View File

@@ -40,11 +40,6 @@ from m5.params import *
# - If IsControl is set, then exactly one of IsDirectControl or IsIndirect
# Control will be set, and exactly one of IsCondControl or IsUncondControl
# will be set.
# - IsSerializing, IsMemBarrier, and IsWriteBarrier are implemented as flags
# since in the current model there's no other way for instructions to inject
# behavior into the pipeline outside of fetch. Once we go to an exec-in-exec
# CPU model we should be able to get rid of these flags and implement this
# behavior via the execute() methods.
class StaticInstFlags(Enum):
wrapper_name = 'StaticInstFlags'
@@ -79,7 +74,6 @@ class StaticInstFlags(Enum):
# older instructions have committed.
'IsSerializeBefore',
'IsSerializeAfter',
'IsMemBarrier', # Is a memory barrier
'IsWriteBarrier', # Is a write barrier
'IsReadBarrier', # Is a read barrier

View File

@@ -553,7 +553,8 @@ class BaseDynInst : public ExecContext, public RefCounted
return staticInst->isSerializeAfter() || status[SerializeAfter];
}
bool isSquashAfter() const { return staticInst->isSquashAfter(); }
bool isMemBarrier() const { return staticInst->isMemBarrier(); }
bool isFullMemBarrier() const { return staticInst->isFullMemBarrier(); }
bool isReadBarrier() const { return staticInst->isReadBarrier(); }
bool isWriteBarrier() const { return staticInst->isWriteBarrier(); }
bool isNonSpeculative() const { return staticInst->isNonSpeculative(); }
bool isQuiesce() const { return staticInst->isQuiesce(); }

View File

@@ -781,7 +781,7 @@ Execute::issue(ThreadID thread_id)
/* Mark up barriers in the LSQ */
if (!discarded && inst->isInst() &&
inst->staticInst->isMemBarrier())
inst->staticInst->isFullMemBarrier())
{
DPRINTF(MinorMem, "Issuing memory barrier inst: %s\n", *inst);
lsq.issuedMemBarrierInst(inst);
@@ -951,7 +951,7 @@ Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue,
completed_inst = completed_mem_inst;
}
completed_mem_issue = completed_inst;
} else if (inst->isInst() && inst->staticInst->isMemBarrier() &&
} else if (inst->isInst() && inst->staticInst->isFullMemBarrier() &&
!lsq.canPushIntoStoreBuffer())
{
DPRINTF(MinorExecute, "Can't commit data barrier inst: %s yet as"
@@ -1368,7 +1368,7 @@ Execute::commit(ThreadID thread_id, bool only_commit_microops, bool discard,
ex_info.inFlightInsts->pop();
/* Complete barriers in the LSQ/move to store buffer */
if (inst->isInst() && inst->staticInst->isMemBarrier()) {
if (inst->isInst() && inst->staticInst->isFullMemBarrier()) {
DPRINTF(MinorMem, "Completing memory barrier"
" inst: %s committed: %d\n", *inst, committed_inst);
lsq.completeMemBarrierInst(inst, committed_inst);

View File

@@ -154,7 +154,7 @@ LSQ::LSQRequest::containsAddrRangeOf(LSQRequestPtr other_request)
bool
LSQ::LSQRequest::isBarrier()
{
return inst->isInst() && inst->staticInst->isMemBarrier();
return inst->isInst() && inst->staticInst->isFullMemBarrier();
}
bool
@@ -1711,7 +1711,7 @@ makePacketForRequest(const RequestPtr &request, bool isLoad,
void
LSQ::issuedMemBarrierInst(MinorDynInstPtr inst)
{
assert(inst->isInst() && inst->staticInst->isMemBarrier());
assert(inst->isInst() && inst->staticInst->isFullMemBarrier());
assert(inst->id.execSeqNum > lastMemBarrier[inst->id.threadId]);
/* Remember the barrier. We only have a notion of one

View File

@@ -1196,7 +1196,7 @@ DefaultCommit<Impl>::commitHead(const DynInstPtr &head_inst, unsigned inst_num)
// Make sure we are only trying to commit un-executed instructions we
// think are possible.
assert(head_inst->isNonSpeculative() || head_inst->isStoreConditional()
|| head_inst->isMemBarrier() || head_inst->isWriteBarrier()
|| head_inst->isReadBarrier() || head_inst->isWriteBarrier()
|| head_inst->isAtomic()
|| (head_inst->isLoad() && head_inst->strictlyOrdered()));
@@ -1462,7 +1462,7 @@ DefaultCommit<Impl>::updateComInstStats(const DynInstPtr &inst)
}
}
if (inst->isMemBarrier()) {
if (inst->isFullMemBarrier()) {
stats.membars[tid]++;
}

View File

@@ -1119,7 +1119,7 @@ DefaultIEW<Impl>::dispatchInsts(ThreadID tid)
}
toRename->iewInfo[tid].dispatchedToSQ++;
} else if (inst->isMemBarrier() || inst->isWriteBarrier()) {
} else if (inst->isReadBarrier() || inst->isWriteBarrier()) {
// Same as non-speculative stores.
inst->setCanCommit();
instQueue.insertBarrier(inst);

View File

@@ -1014,7 +1014,7 @@ InstructionQueue<Impl>::wakeDependents(const DynInstPtr &completed_inst)
++freeEntries;
completed_inst->memOpDone(true);
count[tid]--;
} else if (completed_inst->isMemBarrier() ||
} else if (completed_inst->isReadBarrier() ||
completed_inst->isWriteBarrier()) {
// Completes a non mem ref barrier
memDepUnit[tid].completeInst(completed_inst);
@@ -1245,7 +1245,7 @@ InstructionQueue<Impl>::doSquash(ThreadID tid)
DPRINTF(IQ, "[tid:%i] Instruction [sn:%llu] PC %s squashed.\n",
tid, squashed_inst->seqNum, squashed_inst->pcState());
bool is_acq_rel = squashed_inst->isMemBarrier() &&
bool is_acq_rel = squashed_inst->isFullMemBarrier() &&
(squashed_inst->isLoad() ||
(squashed_inst->isStore() &&
!squashed_inst->isStoreConditional()));
@@ -1255,7 +1255,7 @@ InstructionQueue<Impl>::doSquash(ThreadID tid)
(!squashed_inst->isNonSpeculative() &&
!squashed_inst->isStoreConditional() &&
!squashed_inst->isAtomic() &&
!squashed_inst->isMemBarrier() &&
!squashed_inst->isReadBarrier() &&
!squashed_inst->isWriteBarrier())) {
for (int src_reg_idx = 0;

View File

@@ -44,6 +44,7 @@
#include <map>
#include <vector>
#include "base/debug.hh"
#include "cpu/o3/inst_queue.hh"
#include "cpu/o3/mem_dep_unit.hh"
#include "debug/MemDepUnit.hh"
@@ -171,24 +172,31 @@ void
MemDepUnit<MemDepPred, Impl>::insertBarrierSN(const DynInstPtr &barr_inst)
{
InstSeqNum barr_sn = barr_inst->seqNum;
// Memory barriers block loads and stores, write barriers only stores.
// Required also for hardware transactional memory commands which
// can have strict ordering semantics
if (barr_inst->isMemBarrier() || barr_inst->isHtmCmd()) {
loadBarrierSNs.insert(barr_sn);
storeBarrierSNs.insert(barr_sn);
DPRINTF(MemDepUnit, "Inserted a memory barrier %s SN:%lli\n",
barr_inst->pcState(), barr_sn);
} else if (barr_inst->isWriteBarrier()) {
storeBarrierSNs.insert(barr_sn);
DPRINTF(MemDepUnit, "Inserted a write barrier %s SN:%lli\n",
barr_inst->pcState(), barr_sn);
}
if (loadBarrierSNs.size() || storeBarrierSNs.size()) {
DPRINTF(MemDepUnit, "Outstanding load barriers = %d; "
"store barriers = %d\n",
loadBarrierSNs.size(), storeBarrierSNs.size());
if (barr_inst->isReadBarrier() || barr_inst->isHtmCmd())
loadBarrierSNs.insert(barr_sn);
if (barr_inst->isWriteBarrier() || barr_inst->isHtmCmd())
storeBarrierSNs.insert(barr_sn);
if (DTRACE(MemDepUnit)) {
const char *barrier_type = nullptr;
if (barr_inst->isReadBarrier() && barr_inst->isWriteBarrier())
barrier_type = "memory";
else if (barr_inst->isReadBarrier())
barrier_type = "read";
else if (barr_inst->isWriteBarrier())
barrier_type = "write";
if (barrier_type) {
DPRINTF(MemDepUnit, "Inserted a %s barrier %s SN:%lli\n",
barrier_type, barr_inst->pcState(), barr_sn);
}
if (loadBarrierSNs.size() || storeBarrierSNs.size()) {
DPRINTF(MemDepUnit, "Outstanding load barriers = %d; "
"store barriers = %d\n",
loadBarrierSNs.size(), storeBarrierSNs.size());
}
}
}
@@ -444,18 +452,27 @@ MemDepUnit<MemDepPred, Impl>::completeInst(const DynInstPtr &inst)
completed(inst);
InstSeqNum barr_sn = inst->seqNum;
if (inst->isMemBarrier() || inst->isHtmCmd()) {
if (inst->isWriteBarrier() || inst->isHtmCmd()) {
assert(hasStoreBarrier());
storeBarrierSNs.erase(barr_sn);
}
if (inst->isReadBarrier() || inst->isHtmCmd()) {
assert(hasLoadBarrier());
assert(hasStoreBarrier());
loadBarrierSNs.erase(barr_sn);
storeBarrierSNs.erase(barr_sn);
DPRINTF(MemDepUnit, "Memory barrier completed: %s SN:%lli\n",
inst->pcState(), inst->seqNum);
} else if (inst->isWriteBarrier()) {
assert(hasStoreBarrier());
storeBarrierSNs.erase(barr_sn);
DPRINTF(MemDepUnit, "Write barrier completed: %s SN:%lli\n",
inst->pcState(), inst->seqNum);
}
if (DTRACE(MemDepUnit)) {
const char *barrier_type = nullptr;
if (inst->isWriteBarrier() && inst->isReadBarrier())
barrier_type = "Memory";
else if (inst->isWriteBarrier())
barrier_type = "Write";
else if (inst->isReadBarrier())
barrier_type = "Read";
if (barrier_type) {
DPRINTF(MemDepUnit, "%s barrier completed: %s SN:%lli\n",
barrier_type, inst->pcState(), inst->seqNum);
}
}
}
@@ -463,9 +480,8 @@ template <class MemDepPred, class Impl>
void
MemDepUnit<MemDepPred, Impl>::wakeDependents(const DynInstPtr &inst)
{
// Only stores, atomics, barriers and
// hardware transactional memory commands have dependents.
if (!inst->isStore() && !inst->isAtomic() && !inst->isMemBarrier() &&
// Only stores, atomics and barriers have dependents.
if (!inst->isStore() && !inst->isAtomic() && !inst->isReadBarrier() &&
!inst->isWriteBarrier() && !inst->isHtmCmd()) {
return;
}

View File

@@ -185,7 +185,12 @@ class StaticInst : public RefCounted, public StaticInstFlags
bool isSerializeBefore() const { return flags[IsSerializeBefore]; }
bool isSerializeAfter() const { return flags[IsSerializeAfter]; }
bool isSquashAfter() const { return flags[IsSquashAfter]; }
bool isMemBarrier() const { return flags[IsMemBarrier]; }
bool
isFullMemBarrier() const
{
return flags[IsReadBarrier] && flags[IsWriteBarrier];
}
bool isReadBarrier() const { return flags[IsReadBarrier]; }
bool isWriteBarrier() const { return flags[IsWriteBarrier]; }
bool isNonSpeculative() const { return flags[IsNonSpeculative]; }
bool isQuiesce() const { return flags[IsQuiesce]; }