Commit c2c6763d authored by fpizlo@apple.com's avatar fpizlo@apple.com

Given a PutById or GetById with a proven structure, the DFG should be able to...

Given a PutById or GetById with a proven structure, the DFG should be able to emit a PutByOffset or GetByOffset instead
https://bugs.webkit.org/show_bug.cgi?id=102327

Reviewed by Mark Hahnenberg.

If the profiler tells us that a GetById or PutById may be polymorphic but our
control flow analysis proves that it isn't, we should trust the control flow
analysis over the profiler. This arises in cases where GetById or PutById were
inlined: the inlined function may have been called from other places that led
to polymorphism, but in the current inlined context, there is no polymorphism.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFor):
(JSC):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
(GetByIdStatus):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFor):
(JSC):
* bytecode/PutByIdStatus.h:
(JSC):
(JSC::PutByIdStatus::PutByIdStatus):
(PutByIdStatus):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::bestProvenStructure):
(AbstractValue):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck):
(ConstantFoldingPhase):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(Node):
(JSC::DFG::Node::convertToPutByOffset):
(JSC::DFG::Node::hasStorageResult):
* runtime/JSGlobalObject.h:
(JSC::Structure::prototypeChain):
(JSC):
(JSC::Structure::isValid):
* runtime/Operations.h:
(JSC::isPrototypeChainNormalized):
(JSC):
* runtime/Structure.h:
(Structure):
(JSC::Structure::transitionDidInvolveSpecificValue):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@135041 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3b528912
2012-11-16 Filip Pizlo <fpizlo@apple.com>
Given a PutById or GetById with a proven structure, the DFG should be able to emit a PutByOffset or GetByOffset instead
https://bugs.webkit.org/show_bug.cgi?id=102327
Reviewed by Mark Hahnenberg.
If the profiler tells us that a GetById or PutById may be polymorphic but our
control flow analysis proves that it isn't, we should trust the control flow
analysis over the profiler. This arises in cases where GetById or PutById were
inlined: the inlined function may have been called from other places that led
to polymorphism, but in the current inlined context, there is no polymorphism.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFor):
(JSC):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
(GetByIdStatus):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFor):
(JSC):
* bytecode/PutByIdStatus.h:
(JSC):
(JSC::PutByIdStatus::PutByIdStatus):
(PutByIdStatus):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::bestProvenStructure):
(AbstractValue):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck):
(ConstantFoldingPhase):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(Node):
(JSC::DFG::Node::convertToPutByOffset):
(JSC::DFG::Node::hasStorageResult):
* runtime/JSGlobalObject.h:
(JSC::Structure::prototypeChain):
(JSC):
(JSC::Structure::isValid):
* runtime/Operations.h:
(JSC::isPrototypeChainNormalized):
(JSC):
* runtime/Structure.h:
(Structure):
(JSC::Structure::transitionDidInvolveSpecificValue):
2012-11-16 Tony Chang <tony@chromium.org>
Remove ENABLE_CSS_HIERARCHIES since it's no longer in use
......
......@@ -632,11 +632,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
case op_get_callee: {
int r0 = (++it)->u.operand;
dataLog("[%4d] op_get_callee %s\n", location, registerName(exec, r0).data());
++it;
break;
}
case op_create_this: {
int r0 = (++it)->u.operand;
dataLog("[%4d] create_this %s", location, registerName(exec, r0).data());
int r1 = (++it)->u.operand;
dataLog("[%4d] create_this %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data());
dumpBytecodeCommentAndNewLine(location);
break;
}
......
......@@ -151,7 +151,7 @@ GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec
// Finally figure out if we can derive an access strategy.
GetByIdStatus result;
result.m_wasSeenInJIT = true;
result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only.
switch (stubInfo.accessType) {
case access_unset:
return computeFromLLInt(profiledBlock, bytecodeIndex, ident);
......@@ -252,5 +252,35 @@ GetByIdStatus GetByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec
#endif // ENABLE(JIT)
}
GetByIdStatus GetByIdStatus::computeFor(JSGlobalData& globalData, Structure* structure, Identifier& ident)
{
// For now we only handle the super simple self access case. We could handle the
// prototype case in the future.
if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex)
return GetByIdStatus(TakesSlowPath);
if (structure->typeInfo().overridesGetOwnPropertySlot())
return GetByIdStatus(TakesSlowPath);
if (!structure->propertyAccessesAreCacheable())
return GetByIdStatus(TakesSlowPath);
GetByIdStatus result;
result.m_wasSeenInJIT = false; // To my knowledge nobody that uses computeFor(JSGlobalData&, Structure*, Identifier&) reads this field, but I might as well be honest: no, it wasn't seen in the JIT, since I computed it statically.
unsigned attributes;
JSCell* specificValue;
result.m_offset = structure->get(globalData, ident, attributes, specificValue);
if (!isValidOffset(result.m_offset))
return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
if (attributes & Accessor)
return GetByIdStatus(MakesCalls);
if (structure->isDictionary())
specificValue = 0;
result.m_structureSet.add(structure);
result.m_specificValue = JSValue(specificValue);
return result;
}
} // namespace JSC
......@@ -51,6 +51,13 @@ public:
{
}
explicit GetByIdStatus(State state)
: m_state(state)
, m_offset(invalidOffset)
{
ASSERT(state == NoInformation || state == TakesSlowPath || state == MakesCalls);
}
GetByIdStatus(
State state, bool wasSeenInJIT, const StructureSet& structureSet = StructureSet(),
PropertyOffset offset = invalidOffset, JSValue specificValue = JSValue(), Vector<Structure*> chain = Vector<Structure*>())
......@@ -65,6 +72,7 @@ public:
}
static GetByIdStatus computeFor(CodeBlock*, unsigned bytecodeIndex, Identifier&);
static GetByIdStatus computeFor(JSGlobalData&, Structure*, Identifier&);
State state() const { return m_state; }
......
......@@ -29,6 +29,7 @@
#include "CodeBlock.h"
#include "LLIntData.h"
#include "LowLevelInterpreter.h"
#include "Operations.h"
#include "Structure.h"
#include "StructureChain.h"
......@@ -134,5 +135,77 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytec
#endif // ENABLE(JIT)
}
PutByIdStatus PutByIdStatus::computeFor(JSGlobalData& globalData, JSGlobalObject* globalObject, Structure* structure, Identifier& ident, bool isDirect)
{
if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex)
return PutByIdStatus(TakesSlowPath);
if (structure->typeInfo().overridesGetOwnPropertySlot())
return PutByIdStatus(TakesSlowPath);
if (!structure->propertyAccessesAreCacheable())
return PutByIdStatus(TakesSlowPath);
unsigned attributes;
JSCell* specificValueIgnored;
PropertyOffset offset = structure->get(globalData, ident, attributes, specificValueIgnored);
if (isValidOffset(offset)) {
if (attributes & (Accessor | ReadOnly))
return PutByIdStatus(TakesSlowPath);
return PutByIdStatus(SimpleReplace, structure, 0, 0, offset);
}
// Our hypothesis is that we're doing a transition. Before we prove that this is really
// true, we want to do some sanity checks.
// Don't cache put transitions on dictionaries.
if (structure->isDictionary())
return PutByIdStatus(TakesSlowPath);
// If the structure corresponds to something that isn't an object, then give up, since
// we don't want to be adding properties to strings.
if (structure->typeInfo().type() == StringType)
return PutByIdStatus(TakesSlowPath);
if (!isDirect) {
// If the prototype chain has setters or read-only properties, then give up.
if (structure->prototypeChainMayInterceptStoreTo(globalData, ident))
return PutByIdStatus(TakesSlowPath);
// If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries)
// then give up. The dictionary case would only happen if this structure has not been
// used in an optimized put_by_id transition. And really the only reason why we would
// bail here is that I don't really feel like having the optimizing JIT go and flatten
// dictionaries if we have evidence to suggest that those objects were never used as
// prototypes in a cacheable prototype access - i.e. there's a good chance that some of
// the other checks below will fail.
if (!isPrototypeChainNormalized(globalObject, structure))
return PutByIdStatus(TakesSlowPath);
}
// We only optimize if there is already a structure that the transition is cached to.
// Among other things, this allows us to guard against a transition with a specific
// value.
//
// - If we're storing a value that could be specific: this would only be a problem if
// the existing transition did have a specific value already, since if it didn't,
// then we would behave "as if" we were not storing a specific value. If it did
// have a specific value, then we'll know - the fact that we pass 0 for
// specificValue will tell us.
//
// - If we're not storing a value that could be specific: again, this would only be a
// problem if the existing transition did have a specific value, which we check for
// by passing 0 for the specificValue.
Structure* transition = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, 0, offset);
if (!transition)
return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above.
ASSERT(!transition->transitionDidInvolveSpecificValue());
ASSERT(isValidOffset(offset));
return PutByIdStatus(
SimpleTransition, structure, transition,
structure->prototypeChain(globalData, globalObject), offset);
}
} // namespace JSC
......@@ -33,6 +33,8 @@ namespace JSC {
class CodeBlock;
class Identifier;
class JSGlobalData;
class JSGlobalObject;
class Structure;
class StructureChain;
......@@ -60,6 +62,16 @@ public:
{
}
explicit PutByIdStatus(State state)
: m_state(state)
, m_oldStructure(0)
, m_newStructure(0)
, m_structureChain(0)
, m_offset(invalidOffset)
{
ASSERT(m_state == NoInformation || m_state == TakesSlowPath);
}
PutByIdStatus(
State state,
Structure* oldStructure,
......@@ -79,6 +91,7 @@ public:
}
static PutByIdStatus computeFor(CodeBlock*, unsigned bytecodeIndex, Identifier&);
static PutByIdStatus computeFor(JSGlobalData&, JSGlobalObject*, Structure*, Identifier&, bool isDirect);
State state() const { return m_state; }
......
......@@ -30,6 +30,8 @@
#include "CodeBlock.h"
#include "DFGBasicBlock.h"
#include "GetByIdStatus.h"
#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
......@@ -1409,8 +1411,30 @@ bool AbstractState::execute(unsigned indexInBlock)
m_isValid = false;
break;
}
if (isCellSpeculation(m_graph[node.child1()].prediction()))
if (isCellSpeculation(m_graph[node.child1()].prediction())) {
forNode(node.child1()).filter(SpecCell);
if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
GetByIdStatus status = GetByIdStatus::computeFor(
m_graph.m_globalData, structure,
m_graph.m_codeBlock->identifier(node.identifierNumber()));
if (status.isSimple()) {
// Assert things that we can't handle and that the computeFor() method
// above won't be able to return.
ASSERT(status.structureSet().size() == 1);
ASSERT(status.chain().isEmpty());
if (status.specificValue())
forNode(nodeIndex).set(status.specificValue());
else
forNode(nodeIndex).makeTop();
forNode(node.child1()).filter(status.structureSet());
m_foundConstants = true;
break;
}
}
}
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).makeTop();
break;
......@@ -1573,15 +1597,24 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
case GetByOffset:
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
if (!m_graph[node.child1()].hasStorageResult()) {
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
}
forNode(nodeIndex).makeTop();
break;
case PutByOffset:
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
case PutByOffset: {
bool canExit = false;
if (!m_graph[node.child1()].hasStorageResult()) {
canExit |= !isCellSpeculation(forNode(node.child1()).m_type);
forNode(node.child1()).filter(SpecCell);
}
canExit |= !isCellSpeculation(forNode(node.child2()).m_type);
forNode(node.child2()).filter(SpecCell);
node.setCanExit(canExit);
break;
}
case CheckFunction: {
JSValue value = forNode(node.child1()).value();
......@@ -1603,6 +1636,26 @@ bool AbstractState::execute(unsigned indexInBlock)
case PutById:
case PutByIdDirect:
node.setCanExit(true);
if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
PutByIdStatus status = PutByIdStatus::computeFor(
m_graph.m_globalData,
m_graph.globalObjectFor(node.codeOrigin),
structure,
m_graph.m_codeBlock->identifier(node.identifierNumber()),
node.op() == PutByIdDirect);
if (status.isSimpleReplace()) {
forNode(node.child1()).filter(structure);
m_foundConstants = true;
break;
}
if (status.isSimpleTransition()) {
clobberStructures(indexInBlock);
forNode(node.child1()).set(status.newStructure());
m_haveStructures = true;
m_foundConstants = true;
break;
}
}
forNode(node.child1()).filter(SpecCell);
clobberWorld(node.codeOrigin, indexInBlock);
break;
......
......@@ -342,6 +342,15 @@ struct AbstractValue {
return true;
}
Structure* bestProvenStructure() const
{
if (m_currentKnownStructure.hasSingleton())
return m_currentKnownStructure.singleton();
if (m_futurePossibleStructure.hasSingleton())
return m_futurePossibleStructure.singleton();
return 0;
}
void checkConsistency() const
{
if (!(m_type & SpecCell)) {
......
......@@ -33,6 +33,8 @@
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "GetByIdStatus.h"
#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
......@@ -139,6 +141,190 @@ private:
break;
}
case GetById:
case GetByIdFlush: {
CodeOrigin codeOrigin = node.codeOrigin;
NodeIndex child = node.child1().index();
unsigned identifierNumber = node.identifierNumber();
if (!isCellSpeculation(m_graph[child].prediction()))
break;
Structure* structure = m_state.forNode(child).bestProvenStructure();
if (!structure)
break;
bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
GetByIdStatus status = GetByIdStatus::computeFor(
globalData(), structure, codeBlock()->identifier(identifierNumber));
if (!status.isSimple())
break;
ASSERT(status.structureSet().size() == 1);
ASSERT(status.chain().isEmpty());
ASSERT(status.structureSet().singletonStructure() == structure);
// Now before we do anything else, push the CFA forward over the GetById
// and make sure we signal to the loop that it should continue and not
// do any eliminations.
m_state.execute(indexInBlock);
eliminated = true;
if (needsWatchpoint) {
ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
m_graph[child].ref();
Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child);
watchpoint.ref();
NodeIndex watchpointIndex = m_graph.size();
m_graph.append(watchpoint);
m_insertionSet.append(indexInBlock, watchpointIndex);
}
NodeIndex propertyStorageIndex;
m_graph[child].ref();
if (isInlineOffset(status.offset()))
propertyStorageIndex = child;
else {
Node getButterfly(GetButterfly, codeOrigin, child);
getButterfly.ref();
propertyStorageIndex = m_graph.size();
m_graph.append(getButterfly);
m_insertionSet.append(indexInBlock, propertyStorageIndex);
}
m_graph[nodeIndex].convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex);
StorageAccessData storageAccessData;
storageAccessData.offset = indexRelativeToBase(status.offset());
storageAccessData.identifierNumber = identifierNumber;
m_graph.m_storageAccessData.append(storageAccessData);
break;
}
case PutById:
case PutByIdDirect: {
CodeOrigin codeOrigin = node.codeOrigin;
NodeIndex child = node.child1().index();
unsigned identifierNumber = node.identifierNumber();
Structure* structure = m_state.forNode(child).bestProvenStructure();
if (!structure)
break;
bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
PutByIdStatus status = PutByIdStatus::computeFor(
globalData(),
m_graph.globalObjectFor(codeOrigin),
structure,
codeBlock()->identifier(identifierNumber),
node.op() == PutByIdDirect);
if (!status.isSimpleReplace() && !status.isSimpleTransition())
break;
ASSERT(status.oldStructure() == structure);
// Now before we do anything else, push the CFA forward over the PutById
// and make sure we signal to the loop that it should continue and not
// do any eliminations.
m_state.execute(indexInBlock);
eliminated = true;
if (needsWatchpoint) {
ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
m_graph[child].ref();
Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child);
watchpoint.ref();
NodeIndex watchpointIndex = m_graph.size();
m_graph.append(watchpoint);
m_insertionSet.append(indexInBlock, watchpointIndex);
}
StructureTransitionData* transitionData = 0;
if (status.isSimpleTransition()) {
transitionData = m_graph.addStructureTransitionData(
StructureTransitionData(structure, status.newStructure()));
if (node.op() == PutById) {
if (!structure->storedPrototype().isNull()) {
addStructureTransitionCheck(
codeOrigin, indexInBlock,
structure->storedPrototype().asCell());
}
for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
JSValue prototype = (*it)->storedPrototype();
if (prototype.isNull())
continue;
ASSERT(prototype.isCell());
addStructureTransitionCheck(
codeOrigin, indexInBlock, prototype.asCell());
}
}
}
NodeIndex propertyStorageIndex;
m_graph[child].ref();
if (isInlineOffset(status.offset()))
propertyStorageIndex = child;
else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
Node getButterfly(GetButterfly, codeOrigin, child);
getButterfly.ref();
propertyStorageIndex = m_graph.size();
m_graph.append(getButterfly);
m_insertionSet.append(indexInBlock, propertyStorageIndex);
} else if (!structure->outOfLineCapacity()) {
ASSERT(status.newStructure()->outOfLineCapacity());
ASSERT(!isInlineOffset(status.offset()));
Node allocateStorage(AllocatePropertyStorage, codeOrigin, OpInfo(transitionData), child);
allocateStorage.ref(); // Once for the use.
allocateStorage.ref(); // Twice because it's must-generate.
propertyStorageIndex = m_graph.size();
m_graph.append(allocateStorage);
m_insertionSet.append(indexInBlock, propertyStorageIndex);
} else {
ASSERT(structure->outOfLineCapacity());
ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
ASSERT(!isInlineOffset(status.offset()));
Node getButterfly(GetButterfly, codeOrigin, child);
getButterfly.ref();
NodeIndex getButterflyIndex = m_graph.size();
m_graph.append(getButterfly);
m_insertionSet.append(indexInBlock, getButterflyIndex);
m_graph[child].ref();
Node reallocateStorage(ReallocatePropertyStorage, codeOrigin, OpInfo(transitionData), child, getButterflyIndex);
reallocateStorage.ref(); // Once for the use.
reallocateStorage.ref(); // Twice because it's must-generate.
propertyStorageIndex = m_graph.size();
m_graph.append(reallocateStorage);
m_insertionSet.append(indexInBlock, propertyStorageIndex);
}
if (status.isSimpleTransition()) {
m_graph[child].ref();
Node putStructure(PutStructure, codeOrigin, OpInfo(transitionData), child);
putStructure.ref();
NodeIndex putStructureIndex = m_graph.size();
m_graph.append(putStructure);
m_insertionSet.append(indexInBlock, putStructureIndex);
}
m_graph[nodeIndex].convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex);
StorageAccessData storageAccessData;
storageAccessData.offset = indexRelativeToBase(status.offset());
storageAccessData.identifierNumber = identifierNumber;
m_graph.m_storageAccessData.append(storageAccessData);
break;
}
default:
break;
}
......@@ -228,6 +414,31 @@ private:
return changed;
}
void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
{
Node weakConstant(WeakJSConstant, codeOrigin, OpInfo(cell));
weakConstant.ref();
weakConstant.predict(speculationFromValue(cell));
NodeIndex weakConstantIndex = m_graph.size();
m_graph.append(weakConstant);
m_insertionSet.append(indexInBlock, weakConstantIndex);
if (cell->structure()->transitionWatchpointSetIsStillValid()) {
Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(cell->structure()), weakConstantIndex);
watchpoint.ref();
NodeIndex watchpointIndex = m_graph.size();
m_graph.append(watchpoint);
m_insertionSet.append(indexInBlock, watchpointIndex);
return;
}
Node check(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(cell->structure())), weakConstantIndex);
check.ref();
NodeIndex checkIndex = m_graph.size();
m_graph.append(check);
m_insertionSet.append(indexInBlock, checkIndex);
}
// This is necessary because the CFA may reach conclusions about constants based on its
// assumption that certain code must exit, but then those constants may lead future
// reexecutions of the CFA to believe that the same code will now no longer exit. Thus
......
......@@ -269,6 +269,26 @@ struct Node {
convertToStructureTransitionWatchpoint(structureSet().singletonStructure());
}
void convertToGetByOffset(unsigned storageAccessDataIndex, NodeIndex storage)
{
ASSERT(m_op == GetById || m_op == GetByIdFlush);
m_opInfo = storageAccessDataIndex;