Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (281692 => 281693)
--- trunk/Source/_javascript_Core/ChangeLog 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-08-27 14:48:36 UTC (rev 281693)
@@ -1,3 +1,16 @@
+2021-08-27 Commit Queue <commit-qu...@webkit.org>
+
+ Unreviewed, reverting r281587.
+ https://bugs.webkit.org/show_bug.cgi?id=229609
+
+ stress test failed on ARM64
+
+ Reverted changeset:
+
+ "[ARM64] Fix pre-index address mode"
+ https://bugs.webkit.org/show_bug.cgi?id=229175
+ https://commits.webkit.org/r281587
+
2021-08-26 Yusuke Suzuki <ysuz...@apple.com>
Intl.DateTimeFormat incorrectly parses patterns with 'h' literal
Modified: trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/B3CanonicalizePrePostIncrements.cpp 2021-08-27 14:48:36 UTC (rev 281693)
@@ -52,57 +52,69 @@
unsigned index { 0 };
InsertionSet insertionSet { proc };
+ BlockInsertionSet blockInsertionSet { proc };
Dominators& dominators = proc.dominators();
BackwardsDominators& backwardsDominators = proc.backwardsDominators();
- IndexSet<Value*> handledValues;
- HashMap<Value*, unsigned> memoryToIndex;
- HashMap<Value*, Vector<Value*>> addUses;
- HashMap<Value*, Vector<MemoryValue*>> baseToMemories;
+ IndexSet<Value*> ignoredValues;
+ HashMap<Value*, Vector<MemoryValue*>> baseToLoads;
+ HashMap<MemoryValue*, Value*> preIndexLoadCandidates;
+ HashMap<MemoryValue*, Value*> postIndexLoadCandidates;
HashMap<ValueKey, Vector<Value*>> baseOffsetToAddresses;
- HashMap<MemoryValue*, Vector<Value*>> preIndexCandidates;
- HashMap<MemoryValue*, Vector<Value*>> postIndexCandidates;
- HashMap<BasicBlock*, HashSet<MemoryValue*>> blockToPrePostIndexCandidates;
- // Strength Reduction will leave the IR in the form we're matching now.
- // It'll take an add(x, constant) that's an address and move the offset
- // into the load itself, and that's why we can match this redundancy.
- auto tryAddCandidates = [&] (Value* value) {
+ HashMap<Value*, Vector<MemoryValue*>> baseToStores;
+ HashMap<MemoryValue*, Value*> postIndexStoreCandidates;
+
+ auto tryAddPrePostIndexCandidate = [&] (Value* value) {
switch (value->opcode()) {
- case Load:
+ case Load: {
+ // Pre-Index Pattern:
+ // address = Add(base, offset)
+ // ...
+ // memory = Load(base, offset)
+ // Post-Index Pattern:
+ // memory = Load(base, 0)
+ // ...
+ // address = Add(base, offset)
+ auto tryAddpreIndexLoadCandidates = [&] () {
+ MemoryValue* memory = value->as<MemoryValue>();
+ if (memory->type() != Int32 && memory->type() != Int64)
+ return;
+ if (memory->offset()) {
+ if (!Arg::isValidIncrementIndexForm(memory->offset()))
+ return;
+ ValueKey baseOffsetkey = ValueKey(memory->child(0), static_cast<int64_t>(memory->offset()));
+ if (!baseOffsetToAddresses.contains(baseOffsetkey))
+ return;
+ for (Value* address : baseOffsetToAddresses.get(baseOffsetkey))
+ preIndexLoadCandidates.add(memory, address);
+ } else
+ baseToLoads.add(memory->child(0), Vector<MemoryValue*>()).iterator->value.append(memory);
+ };
+
+ tryAddpreIndexLoadCandidates();
+ break;
+ }
+
case Store: {
- MemoryValue* memory = value->as<MemoryValue>();
- Value* type = value->opcode() == Load ? memory : memory->child(0);
- if (type->type() != Int32 && type->type() != Int64)
- break;
- if (memory->offset()) {
- // Pre-Index Load/Store Pattern:
- // address = Add(base, offset)
- // ...
- // memory = MemoryValue(base, offset)
- ValueKey baseOffsetKey = ValueKey(memory->lastChild(), memory->offset());
- auto iter = baseOffsetToAddresses.find(baseOffsetKey);
- if (iter == baseOffsetToAddresses.end())
- break;
- for (Value* address : iter->value) {
- // The goal is to move the Add to this Load/Store. We only move the Add to this Memory value
- // if we guarantee it dominates all uses. If there are already other uses of the Add, it guarantees
- // this Memory value doesn't dominate those uses. This is because we're visiting the program in pre-order
- // traversal, so we visit this Memory value before all the things it dominates. So if the Add has other
- // users, we must not dominate those users. Therefore, this MemoryValue won't be a candidate.
- auto addUsesIter = addUses.find(address);
- if (addUsesIter != addUses.end() && addUsesIter->value.size() > 0)
- continue;
- // double check offsets to avoid ValueKey collisions
- if (memory->offset() != static_cast<Value::OffsetType>(address->child(1)->asIntPtr()))
- continue;
- preIndexCandidates.add(memory, Vector<Value*>()).iterator->value.append(address);
- blockToPrePostIndexCandidates.add(memory->owner, HashSet<MemoryValue*>()).iterator->value.add(memory);
- }
- } else
- baseToMemories.add(memory->lastChild(), Vector<MemoryValue*>()).iterator->value.append(memory);
- memoryToIndex.add(memory, index);
+ // Pre-Index Pattern:
+ // address = Add(base, offset)
+ // memory = Store(value, base, offset)
+ // Post-Index Pattern:
+ // memory = Store(value, base, 0)
+ // ...
+ // address = Add(base, offset)
+ auto tryUpdateBaseToStores = [&] () {
+ MemoryValue* memory = value->as<MemoryValue>();
+ if (memory->child(0)->type() != Int32 && memory->child(0)->type() != Int64)
+ return;
+ if (memory->child(0)->hasInt() || memory->offset())
+ return;
+ baseToStores.add(memory->child(1), Vector<MemoryValue*>()).iterator->value.append(memory);
+ };
+
+ tryUpdateBaseToStores();
break;
}
@@ -110,29 +122,27 @@
Value* left = value->child(0);
Value* right = value->child(1);
- if (!right->hasIntPtr() || value->type() != Int64)
- break;
- intptr_t offset = right->asIntPtr();
- Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
- if (smallOffset != offset || !Arg::isValidIncrementIndexForm(smallOffset))
- break;
+ auto tryAddpostIndexCandidates = [&] () {
+ if (!right->hasIntPtr() || value->type() != Int64)
+ return;
+ intptr_t offset = right->asIntPtr();
+ Value::OffsetType smallOffset = static_cast<Value::OffsetType>(offset);
+ if (smallOffset != offset || !Arg::isValidIncrementIndexForm(smallOffset))
+ return;
+ // so far this Add value is a valid address candidate for both prefix and postfix pattern
+ ValueKey baseOffsetkey = ValueKey(left, static_cast<int64_t>(smallOffset));
+ baseOffsetToAddresses.add(baseOffsetkey, Vector<Value*>()).iterator->value.append(value);
+ if (baseToLoads.contains(left)) {
+ for (MemoryValue* memory : baseToLoads.get(left))
+ postIndexLoadCandidates.add(memory, value);
+ }
+ if (baseToStores.contains(left)) {
+ for (MemoryValue* memory : baseToStores.get(left))
+ postIndexStoreCandidates.add(memory, value);
+ }
+ };
- // so far this Add value is a valid address candidate for both prefix and postfix pattern
- addUses.add(value, Vector<Value*>());
- ValueKey baseOffsetKey = ValueKey(left, smallOffset);
- baseOffsetToAddresses.add(baseOffsetKey, Vector<Value*>()).iterator->value.append(value);
-
- // Post-Index Load/Store Pattern:
- // memory = MemoryValue(base, 0)
- // ...
- // address = Add(base, offset)
- auto iter = baseToMemories.find(left);
- if (iter == baseToMemories.end())
- break;
- for (MemoryValue* memory : iter->value) {
- postIndexCandidates.add(memory, Vector<Value*>()).iterator->value.append(value);
- blockToPrePostIndexCandidates.add(memory->owner, HashSet<MemoryValue*>()).iterator->value.add(memory);
- }
+ tryAddpostIndexCandidates();
break;
}
@@ -139,17 +149,11 @@
default:
break;
}
-
- for (Value* child : value->children()) {
- auto iter = addUses.find(child);
- if (iter != addUses.end())
- iter->value.append(value);
- }
};
for (BasicBlock* basicBlock : proc.blocksInPreOrder()) {
for (index = 0; index < basicBlock->size(); ++index)
- tryAddCandidates(basicBlock->at(index));
+ tryAddPrePostIndexCandidate(basicBlock->at(index));
}
auto controlEquivalent = [&] (Value* v1, Value* v2) -> bool {
@@ -157,75 +161,68 @@
|| (dominators.dominates(v2->owner, v1->owner) && backwardsDominators.dominates(v1->owner, v2->owner));
};
- for (const auto& pair : blockToPrePostIndexCandidates) {
- BasicBlock* basicBlock = pair.key;
- for (MemoryValue* memory : pair.value) {
- if (handledValues.contains(memory))
- continue;
+ // This search is expensive. However, due to the greedy pattern
+ // matching, no better method can be proposed at present.
+ auto valueIndexInBasicBlock = [&] (Value* value) -> unsigned {
+ unsigned index = 0;
+ BasicBlock* block = value->owner;
+ for (index = 0; index < block->size(); ++index) {
+ if (block->at(index) == value)
+ return index;
+ }
+ return index;
+ };
- // Convert Pre-Index Load/Store Pattern to the Canonical Form:
- // address = Add(base, offset) address = Nop
- // ... ...
- // ... newAddress = Add(base, offset)
- // memory = MemoryValue(base, offset) --> memory = MemoryValue(base, offset)
- // ... ...
- // parent = B3Opcode(address, ...) parent = B3Opcode(newAddress, ...)
- for (Value* address : preIndexCandidates.get(memory)) {
- if (handledValues.contains(address) || !controlEquivalent(memory, address))
- continue;
+ for (auto pair : preIndexLoadCandidates) {
+ MemoryValue* memory = pair.key;
+ Value* address = pair.value;
+ if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
+ continue;
+ // address = Add(base, offset) address = Add(base, offset)
+ // ... --> newMemory = Load(base, offset)
+ // ... ...
+ // memory = Load(base, offset) memory = Identity(newMemory)
+ unsigned insertionIndex = valueIndexInBasicBlock(address) + 1;
+ MemoryValue* newMemory = insertionSet.insert<MemoryValue>(insertionIndex, Load, memory->type(), address->origin(), memory->lastChild());
+ newMemory->setOffset(memory->offset());
+ memory->replaceWithIdentity(newMemory);
+ insertionSet.execute(address->owner);
- auto dominatesAllAddUses = [&] () -> bool {
- auto iter = addUses.find(address);
- ASSERT(iter != addUses.end() && iter->value.size());
- for (Value* parent : iter->value) {
- if (!dominators.dominates(memory->owner, parent->owner))
- return false;
- }
- return true;
- };
+ ignoredValues.add(memory);
+ ignoredValues.add(address);
+ }
- if (!dominatesAllAddUses())
- continue;
+ auto detectPostIndex = [&] (const HashMap<MemoryValue*, Value*>& candidates) {
+ for (auto pair : candidates) {
+ MemoryValue* memory = pair.key;
+ Value* address = pair.value;
+ if (ignoredValues.contains(memory) || ignoredValues.contains(address) || !controlEquivalent(memory, address))
+ continue;
- unsigned insertionIndex = memoryToIndex.get(memory);
- Value* newAddress = insertionSet.insert<Value>(insertionIndex, Add, memory->origin(), address->child(0), address->child(1));
- for (Value* parent : addUses.get(address)) {
- for (unsigned i = 0; i < parent->numChildren(); ++i) {
- Value*& child = parent->child(i);
- if (child == address)
- child = newAddress;
- }
- }
- address->replaceWithNopIgnoringType();
+ unsigned insertionIndex = valueIndexInBasicBlock(memory);
+ Value* newOffset = insertionSet.insert<Const64Value>(insertionIndex, memory->origin(), address->child(1)->asInt());
+ Value* newAddress = insertionSet.insert<Value>(insertionIndex, Add, memory->origin(), address->child(0), newOffset);
+ address->replaceWithIdentity(newAddress);
+ insertionSet.execute(memory->owner);
- handledValues.add(memory);
- handledValues.add(address);
- }
+ ignoredValues.add(memory);
+ ignoredValues.add(address);
+ }
+ };
- // Convert Post-Index Load/Store Pattern to the Canonical Form:
- // ... newOffset = Constant
- // ... newAddress = Add(base, newOffset)
- // memory = MemoryValue(base, 0) memory = MemoryValue(base, 0)
- // ... --> ...
- // address = Add(base, offset) address = Identity(newAddress)
- for (Value* address : postIndexCandidates.get(memory)) {
- if (handledValues.contains(address) || !controlEquivalent(memory, address))
- continue;
+ // ... newOffset = Constant
+ // ... newAddress = Add(base, newOffset)
+ // memory = Load(base, 0) memory = Load(base, 0)
+ // ... --> ...
+ // address = Add(base, offset) address = Identity(newAddress)
+ detectPostIndex(postIndexLoadCandidates);
- unsigned insertionIndex = memoryToIndex.get(memory);
- Value* newOffset = insertionSet.insert<Const64Value>(insertionIndex, memory->origin(), address->child(1)->asInt());
- Value* newAddress = insertionSet.insert<Value>(insertionIndex, Add, memory->origin(), address->child(0), newOffset);
- address->replaceWithIdentity(newAddress);
-
- handledValues.add(memory);
- handledValues.add(address);
- }
- }
- // After this executes, memoryToIndex no longer contains up to date information for this block.
- // But that's ok because we never touch this block again.
- insertionSet.execute(basicBlock);
- }
-
+ // ... newOffset = Constant
+ // ... newAddress = Add(base, newOffset)
+ // memory = Store(value, base, 0) memory = Store(value, base, 0)
+ // ... --> ...
+ // address = Add(base, offset) address = Identity(newAddress)
+ detectPostIndex(postIndexStoreCandidates);
return true;
}
Modified: trunk/Source/_javascript_Core/b3/B3Generate.cpp (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/B3Generate.cpp 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/B3Generate.cpp 2021-08-27 14:48:36 UTC (rev 281693)
@@ -118,7 +118,7 @@
lowerMacrosAfterOptimizations(procedure);
legalizeMemoryOffsets(procedure);
moveConstants(procedure);
- if (procedure.optLevel() >= 2)
+ if (Options::useB3CanonicalizePrePostIncrements() && procedure.optLevel() >= 2)
canonicalizePrePostIncrements(procedure);
eliminateDeadCode(procedure);
Modified: trunk/Source/_javascript_Core/b3/B3ValueKey.h (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/B3ValueKey.h 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/B3ValueKey.h 2021-08-27 14:48:36 UTC (rev 281693)
@@ -56,7 +56,7 @@
{
}
- ValueKey(Value* child, int32_t offset);
+ ValueKey(Value* child, int64_t value);
ValueKey(Kind, Type, Value* child);
Modified: trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/B3ValueKeyInlines.h 2021-08-27 14:48:36 UTC (rev 281693)
@@ -33,14 +33,12 @@
namespace JSC { namespace B3 {
-inline ValueKey::ValueKey(Value* child, int32_t offset)
+inline ValueKey::ValueKey(Value* child, int64_t value)
{
m_kind = Oops;
m_type = Void;
- // The observation is that when both child->index() and offset are 0's,
- // HashMap would not accept the ValueKey.
- u.indices[0] = child->index() + 1;
- u.indices[1] = offset + 1;
+ u.indices[0] = child->index();
+ u.value = value;
}
inline ValueKey::ValueKey(Kind kind, Type type, Value* child)
Modified: trunk/Source/_javascript_Core/b3/testb3.h (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/testb3.h 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/testb3.h 2021-08-27 14:48:36 UTC (rev 281693)
@@ -1164,7 +1164,6 @@
bool shouldRun(const char* filter, const char* testName);
void testLoadPreIndex32();
-void testLoadWithStorePreIndex32();
void testLoadPreIndex64();
void testLoadPostIndex32();
void testLoadPostIndex64();
Modified: trunk/Source/_javascript_Core/b3/testb3_3.cpp (281692 => 281693)
--- trunk/Source/_javascript_Core/b3/testb3_3.cpp 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/b3/testb3_3.cpp 2021-08-27 14:48:36 UTC (rev 281693)
@@ -102,83 +102,6 @@
CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
}
-void testLoadWithStorePreIndex32()
-{
- if (Options::defaultB3OptLevel() < 2)
- return;
-
- int32_t nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int32_t* ptr = nums;
-
- Procedure proc;
- BasicBlock* root = proc.addBlock();
- BasicBlock* loopTest = proc.addBlock();
- BasicBlock* loopBody = proc.addBlock();
- BasicBlock* done = proc.addBlock();
-
- Variable* r = proc.addVariable(Int32);
- Variable* p = proc.addVariable(Int64);
-
- // ---------------------- Root_Block
- // r1 = 0
- // Upsilon(r1, ^r2)
- // p1 = addr
- // Upsilon(p1, ^p2)
- Value* r1 = root->appendIntConstant(proc, Origin(), Int32, 0);
- root->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r1);
- Value* p1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
- root->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p1);
- root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
-
- // ---------------------- Loop_Test_Block
- // loop:
- // p2 = Phi()
- // r2 = Phi()
- // if r2 >= 10 goto done
- Value* r2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), r);
- Value* p2 = loopTest->appendNew<VariableValue>(proc, B3::Get, Origin(), p);
- Value* cond = loopTest->appendNew<Value>(proc, AboveEqual, Origin(), r2, loopTest->appendNew<Const32Value>(proc, Origin(), 10));
- loopTest->appendNewControlValue(proc, Branch, Origin(), cond, FrequentedBlock(done), FrequentedBlock(loopBody));
-
- // ---------------------- Loop_Body_Block
- // p3 = p2 + 1
- // Upsilon(p3, ^p2)
- // p3' = &p3
- // store(5, p3')
- // r3 = r2 + load(p3)
- // Upsilon(r3, ^r2)
- // goto loop
- Value* p3 = loopBody->appendNew<Value>(proc, Add, Origin(), p2, loopBody->appendNew<Const64Value>(proc, Origin(), 4));
- loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), p, p3);
- Value* p3Prime = loopBody->appendNew<Value>(proc, Opaque, Origin(), p3);
- loopBody->appendNew<MemoryValue>(proc, Store, Origin(), loopBody->appendNew<Const32Value>(proc, Origin(), 5), p3Prime);
- Value* r3 = loopBody->appendNew<Value>(proc, Add, Origin(), r2, loopBody->appendNew<MemoryValue>(proc, Load, Int32, Origin(), p3));
- loopBody->appendNew<VariableValue>(proc, B3::Set, Origin(), r, r3);
- loopBody->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loopTest));
-
- // ---------------------- Done_Block
- // done:
- // return r2
- done->appendNewControlValue(proc, Return, Origin(), r2);
-
- proc.resetReachability();
- validate(proc);
- fixSSA(proc);
-
- auto code = compileProc(proc);
-
- auto test = [&] () -> int32_t {
- int32_t r = 0;
- while (r < 10) {
- *++ptr = 5;
- r += *ptr;
- }
- return r;
- };
-
- CHECK_EQ(invoke<int32_t>(*code, bitwise_cast<intptr_t>(ptr)), test());
-}
-
void testLoadPreIndex64()
{
if (Options::defaultB3OptLevel() < 2)
@@ -421,7 +344,6 @@
auto code = compileProc(proc);
if (isARM64())
checkUsesInstruction(*code, "#4]!");
-
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
ptr = bitwise_cast<int32_t*>(res);
CHECK_EQ(nums[2], *ptr);
@@ -446,9 +368,6 @@
root->appendNewControlValue(proc, Return, Origin(), preIncrement);
auto code = compileProc(proc);
- if (isARM64())
- checkUsesInstruction(*code, "#8]!");
-
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
ptr = bitwise_cast<int64_t*>(res);
CHECK_EQ(nums[2], *ptr);
@@ -477,7 +396,6 @@
auto code = compileProc(proc);
if (isARM64())
checkUsesInstruction(*code, "], #4");
-
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
ptr = bitwise_cast<int32_t*>(res);
CHECK_EQ(nums[1], 4);
@@ -505,7 +423,6 @@
auto code = compileProc(proc);
if (isARM64())
checkUsesInstruction(*code, "], #8");
-
intptr_t res = invoke<intptr_t>(*code, bitwise_cast<intptr_t>(ptr), 4);
ptr = bitwise_cast<int64_t*>(res);
CHECK_EQ(nums[1], 4);
@@ -4180,15 +4097,17 @@
RUN(testZShrArgImm32(0xffffffff, 1));
RUN(testZShrArgImm32(0xffffffff, 63));
- RUN(testLoadPreIndex32());
- RUN(testLoadPreIndex64());
- RUN(testLoadPostIndex32());
- RUN(testLoadPostIndex64());
+ if (Options::useB3CanonicalizePrePostIncrements()) {
+ RUN(testLoadPreIndex32());
+ RUN(testLoadPreIndex64());
+ RUN(testLoadPostIndex32());
+ RUN(testLoadPostIndex64());
- RUN(testStorePreIndex32());
- RUN(testStorePreIndex64());
- RUN(testStorePostIndex32());
- RUN(testStorePostIndex64());
+ RUN(testStorePreIndex32());
+ RUN(testStorePreIndex64());
+ RUN(testStorePostIndex32());
+ RUN(testStorePostIndex64());
+ }
}
#endif // ENABLE(B3_JIT)
Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (281692 => 281693)
--- trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-08-27 13:21:29 UTC (rev 281692)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-08-27 14:48:36 UTC (rev 281693)
@@ -437,6 +437,7 @@
v(Unsigned, maxB3TailDupBlockSize, 3, Normal, nullptr) \
v(Unsigned, maxB3TailDupBlockSuccessors, 3, Normal, nullptr) \
v(Bool, useB3HoistLoopInvariantValues, false, Normal, nullptr) \
+ v(Bool, useB3CanonicalizePrePostIncrements, false, Normal, nullptr) \
\
v(Bool, useDollarVM, false, Restricted, "installs the $vm debugging tool in global objects") \
v(OptionString, functionOverrides, nullptr, Restricted, "file with debugging overrides for function bodies") \