Get rid of anonymous stack slots
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Feb 2016 23:21:12 +0000 (23:21 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Feb 2016 23:21:12 +0000 (23:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151128

Reviewed by Mark Lam.

Source/JavaScriptCore:

When I first designed stack slots, the idea was that an "anonymous" stack slot was one that
behaved exactly like a C variable: if it never escaped, it would not need to get stack space
for the entire lifetime of the function - it could get any slab of stack so long as it
didn't interfere with other stack slots that would be live at the same time. The reason I
called them "anonymous" is that external code could not get its address. This felt like it
gave the stack slot anonymity. But it was never a good name for this concept.

Then I had the register allocator lower temporaries to anonymous stack slots when it spilled
them. Spilling became the sole client of anonymous stack slots.

Then I realized that there was an aspect of how spill slots work that make them want
slightly different semantics than a normal C variable. A C variable is a proper memory
location - you could do a store to only some bytes in the variable, and it's reasonable to
expect that this will not destroy the other bytes in the variable. But that means that to
compute their liveness, you have to do something like a per-byte liveness. That's overkill
for spill slots. You want any store to the spill slot to kill the whole slot even if it
writes to just part of the slot. This matches how temporaries work. So rather than implement
per-byte liveness, I decided to change the semantics of anonymous stack slots to make them
work like how I wanted spill slots to work. This was quite dirty, and put B3 in the awkward
situation that B3's anonymous stack slots behaved like spill slots. But it was OK since
nobody used anonymous stack slots in B3.

Then I added tail duplication, which required having a mechanism for introducing non-SSA
variables in B3. I decided to use anonymous stack slots for this purpose. All of a sudden
this all felt like it made sense: anonymous stack slots were just like variables! Hooray for
the amazing foresight of anonymous stack slots!

But then I realized that this was all very bad. We want B3 to be able to optimize Store and
Load operations by reasoning about how they affect bytes in memory. For example, if you do
a Load of a 64-bit value, and then you modify just the low 32 bits of that value, and then
you do a 64-bit store back to the same location, then it would be better to transform this
into 32-bit operations. We don't do this optimization yet, but it's the kind of thing that
we want B3 to be able to do. To do it, we need Store to mean that it only affects N bytes
starting at the pointer, where N is the size of the thing being stored. But that's not what
Store means for anonymous stack slots. For anonymous slots, storing to any byte in the slot
clobbers all bytes in the slot. We were never clear if you need to store directly to an
anonymous slot to get this behavior, or if any pointer that points to an anoymous slot must
exhibit this behavior when stored to. Neither kinds of semantics make sense to me.

This change fixes the problem by eradicating anonymous stack slots. In B3, they are replaced
with Variables. In Air, they are replaced with a different stack slot kind, called Spill.
There is no such thing as stack slot kinds in B3 anymore, all B3 stack slots are locked. In
Air, there is still the concept of stack slot kind - Locked or Spill.

B3 Variables are awesome. They are exactly what they seem to be. They have a type. They are
declared at the top level in the Procedure. You can access them with new opcodes, Get and
Set. This greatly simplifies demoting SSA values to variables and promoting them back to
SSA. I even made the instruction selector do the right things for variables, which means
that introducing variables won't hurt instruction selection (there will be extra moves, but
IRC will kill them). It's great to have non-SSA variables as an explicit concept in IR
because it means that you don't have to do any magic to use them - they Just Work.

Air spill slots behave almost like anonymous stack slots, with one exception: you cannot
escape them. We validate this by making it illegal to UseAddr on a spill slot. This removes
the need to answer awkward questions like: does a 32-bit Def on a pointer that may point to
a 64-bit spill slot do anything to the 32 bits above the pointer?  Does it write zero to it?
Does it write zero to it just when the pointer actually points to a spill slot or always?
These are silly questions, and we don't have to answer them because the only way to refer to
a spill slot is directly. No escaping means no aliasing.

This doesn't affect performance. It just makes the compiler more fun to work with by
removing some cognitive dissonance.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3ArgumentRegValue.h:
* b3/B3CCallValue.h:
* b3/B3CheckValue.cpp:
(JSC::B3::CheckValue::cloneImpl):
(JSC::B3::CheckValue::CheckValue):
* b3/B3CheckValue.h:
* b3/B3Const32Value.h:
* b3/B3Const64Value.h:
* b3/B3ConstDoubleValue.h:
* b3/B3ConstFloatValue.h:
* b3/B3ConstPtrValue.h:
(JSC::B3::ConstPtrValue::ConstPtrValue):
* b3/B3ControlValue.cpp:
(JSC::B3::ControlValue::convertToJump):
(JSC::B3::ControlValue::convertToOops):
(JSC::B3::ControlValue::dumpMeta):
* b3/B3ControlValue.h:
* b3/B3Effects.cpp:
(JSC::B3::Effects::interferes):
(JSC::B3::Effects::dump):
* b3/B3Effects.h:
(JSC::B3::Effects::mustExecute):
* b3/B3EliminateCommonSubexpressions.cpp:
* b3/B3FixSSA.cpp:
(JSC::B3::demoteValues):
(JSC::B3::fixSSA):
* b3/B3FixSSA.h:
* b3/B3IndexMap.h:
(JSC::B3::IndexMap::resize):
(JSC::B3::IndexMap::clear):
(JSC::B3::IndexMap::size):
(JSC::B3::IndexMap::operator[]):
* b3/B3IndexSet.h:
(JSC::B3::IndexSet::contains):
(JSC::B3::IndexSet::size):
(JSC::B3::IndexSet::isEmpty):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::run):
(JSC::B3::Air::LowerToAir::lower):
* b3/B3MemoryValue.h:
* b3/B3Opcode.cpp:
(WTF::printInternal):
* b3/B3Opcode.h:
* b3/B3PatchpointValue.cpp:
(JSC::B3::PatchpointValue::cloneImpl):
(JSC::B3::PatchpointValue::PatchpointValue):
* b3/B3PatchpointValue.h:
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::Procedure):
(JSC::B3::Procedure::addBlock):
(JSC::B3::Procedure::addStackSlot):
(JSC::B3::Procedure::addVariable):
(JSC::B3::Procedure::clone):
(JSC::B3::Procedure::addIntConstant):
(JSC::B3::Procedure::dump):
(JSC::B3::Procedure::deleteStackSlot):
(JSC::B3::Procedure::deleteVariable):
(JSC::B3::Procedure::deleteValue):
(JSC::B3::Procedure::deleteOrphans):
(JSC::B3::Procedure::calleeSaveRegisters):
(JSC::B3::Procedure::addValueImpl):
(JSC::B3::Procedure::setBlockOrderImpl):
(JSC::B3::Procedure::addAnonymousStackSlot): Deleted.
(JSC::B3::Procedure::addStackSlotIndex): Deleted.
(JSC::B3::Procedure::addValueIndex): Deleted.
* b3/B3Procedure.h:
(JSC::B3::Procedure::setBlockOrder):
(JSC::B3::Procedure::stackSlots):
(JSC::B3::Procedure::variables):
(JSC::B3::Procedure::values):
(JSC::B3::Procedure::StackSlotsCollection::StackSlotsCollection): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::size): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::at): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::operator[]): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::iterator): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::operator*): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::operator++): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::operator==): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::operator!=): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::iterator::findNext): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::begin): Deleted.
(JSC::B3::Procedure::StackSlotsCollection::end): Deleted.
(JSC::B3::Procedure::ValuesCollection::ValuesCollection): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::iterator): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::operator*): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::operator++): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::operator==): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::operator!=): Deleted.
(JSC::B3::Procedure::ValuesCollection::iterator::findNext): Deleted.
(JSC::B3::Procedure::ValuesCollection::begin): Deleted.
(JSC::B3::Procedure::ValuesCollection::end): Deleted.
(JSC::B3::Procedure::ValuesCollection::size): Deleted.
(JSC::B3::Procedure::ValuesCollection::at): Deleted.
(JSC::B3::Procedure::ValuesCollection::operator[]): Deleted.
* b3/B3ProcedureInlines.h:
(JSC::B3::Procedure::add):
* b3/B3ReduceStrength.cpp:
* b3/B3SlotBaseValue.h:
* b3/B3SparseCollection.h: Added.
(JSC::B3::SparseCollection::SparseCollection):
(JSC::B3::SparseCollection::add):
(JSC::B3::SparseCollection::addNew):
(JSC::B3::SparseCollection::remove):
(JSC::B3::SparseCollection::size):
(JSC::B3::SparseCollection::isEmpty):
(JSC::B3::SparseCollection::at):
(JSC::B3::SparseCollection::operator[]):
(JSC::B3::SparseCollection::iterator::iterator):
(JSC::B3::SparseCollection::iterator::operator*):
(JSC::B3::SparseCollection::iterator::operator++):
(JSC::B3::SparseCollection::iterator::operator==):
(JSC::B3::SparseCollection::iterator::operator!=):
(JSC::B3::SparseCollection::iterator::findNext):
(JSC::B3::SparseCollection::begin):
(JSC::B3::SparseCollection::end):
* b3/B3StackSlot.cpp:
(JSC::B3::StackSlot::deepDump):
(JSC::B3::StackSlot::StackSlot):
* b3/B3StackSlot.h:
(JSC::B3::StackSlot::byteSize):
(JSC::B3::StackSlot::index):
(JSC::B3::StackSlot::setOffsetFromFP):
(JSC::B3::StackSlot::kind): Deleted.
(JSC::B3::StackSlot::isLocked): Deleted.
* b3/B3StackSlotKind.cpp: Removed.
* b3/B3StackSlotKind.h: Removed.
* b3/B3StackmapValue.cpp:
(JSC::B3::StackmapValue::dumpMeta):
(JSC::B3::StackmapValue::StackmapValue):
* b3/B3StackmapValue.h:
* b3/B3SwitchValue.cpp:
(JSC::B3::SwitchValue::cloneImpl):
(JSC::B3::SwitchValue::SwitchValue):
* b3/B3SwitchValue.h:
* b3/B3UpsilonValue.h:
* b3/B3Validate.cpp:
* b3/B3Value.cpp:
(JSC::B3::Value::replaceWithIdentity):
(JSC::B3::Value::replaceWithNop):
(JSC::B3::Value::replaceWithPhi):
(JSC::B3::Value::dump):
(JSC::B3::Value::effects):
(JSC::B3::Value::checkOpcode):
* b3/B3Value.h:
* b3/B3Variable.cpp: Added.
(JSC::B3::Variable::~Variable):
(JSC::B3::Variable::dump):
(JSC::B3::Variable::deepDump):
(JSC::B3::Variable::Variable):
* b3/B3Variable.h: Added.
(JSC::B3::Variable::type):
(JSC::B3::Variable::index):
(JSC::B3::DeepVariableDump::DeepVariableDump):
(JSC::B3::DeepVariableDump::dump):
(JSC::B3::deepDump):
* b3/B3VariableValue.cpp: Added.
(JSC::B3::VariableValue::~VariableValue):
(JSC::B3::VariableValue::dumpMeta):
(JSC::B3::VariableValue::cloneImpl):
(JSC::B3::VariableValue::VariableValue):
* b3/B3VariableValue.h: Added.
* b3/air/AirAllocateStack.cpp:
(JSC::B3::Air::allocateStack):
* b3/air/AirCode.cpp:
(JSC::B3::Air::Code::addStackSlot):
(JSC::B3::Air::Code::addSpecial):
(JSC::B3::Air::Code::cCallSpecial):
* b3/air/AirCode.h:
(JSC::B3::Air::Code::begin):
(JSC::B3::Air::Code::end):
(JSC::B3::Air::Code::stackSlots):
(JSC::B3::Air::Code::specials):
(JSC::B3::Air::Code::forAllTmps):
(JSC::B3::Air::Code::StackSlotsCollection::StackSlotsCollection): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::size): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::at): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::operator[]): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::iterator::iterator): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::iterator::operator*): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::iterator::operator++): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::iterator::operator==): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::iterator::operator!=): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::begin): Deleted.
(JSC::B3::Air::Code::StackSlotsCollection::end): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::SpecialsCollection): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::size): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::at): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::operator[]): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::iterator::iterator): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::iterator::operator*): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::iterator::operator++): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::iterator::operator==): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::iterator::operator!=): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::begin): Deleted.
(JSC::B3::Air::Code::SpecialsCollection::end): Deleted.
* b3/air/AirFixObviousSpills.cpp:
* b3/air/AirInstInlines.h:
* b3/air/AirIteratedRegisterCoalescing.cpp:
* b3/air/AirLiveness.h:
* b3/air/AirLowerAfterRegAlloc.cpp:
(JSC::B3::Air::lowerAfterRegAlloc):
* b3/air/AirSpecial.cpp:
(JSC::B3::Air::Special::Special):
* b3/air/AirSpecial.h:
* b3/air/AirSpillEverything.cpp:
(JSC::B3::Air::spillEverything):
* b3/air/AirStackSlot.cpp:
(JSC::B3::Air::StackSlot::dump):
(JSC::B3::Air::StackSlot::deepDump):
(JSC::B3::Air::StackSlot::StackSlot):
* b3/air/AirStackSlot.h:
(JSC::B3::Air::StackSlot::byteSize):
(JSC::B3::Air::StackSlot::kind):
(JSC::B3::Air::StackSlot::isLocked):
(JSC::B3::Air::StackSlot::isSpill):
(JSC::B3::Air::StackSlot::index):
(JSC::B3::Air::StackSlot::ensureSize):
* b3/air/AirStackSlotKind.cpp: Copied from Source/JavaScriptCore/b3/B3StackSlotKind.cpp.
(WTF::printInternal):
* b3/air/AirStackSlotKind.h: Copied from Source/JavaScriptCore/b3/B3StackSlotKind.h.
* b3/air/opcode_generator.rb:
* b3/air/testair.cpp:
(JSC::B3::Air::testShuffleBroadcastAllRegs):
(JSC::B3::Air::testShuffleShiftAllRegs):
(JSC::B3::Air::testShuffleRotateAllRegs):
* b3/testb3.cpp:
(JSC::B3::testStackSlot):
(JSC::B3::testStoreLoadStackSlot):
* ftl/FTLB3Output.cpp:
(JSC::FTL::Output::lockedStackSlot):
(JSC::FTL::Output::neg):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):

Websites/webkit.org:

This changes the documentation to account for the addition of Variables and the Get and Set
opcodes, and the removal of anonymous stack slots from B3 IR.

* docs/b3/intermediate-representation.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196032 268f45cc-cd09-0410-ab3c-d52691b4dbfc

69 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/b3/B3ArgumentRegValue.h
Source/JavaScriptCore/b3/B3CCallValue.h
Source/JavaScriptCore/b3/B3CheckValue.cpp
Source/JavaScriptCore/b3/B3CheckValue.h
Source/JavaScriptCore/b3/B3Const32Value.h
Source/JavaScriptCore/b3/B3Const64Value.h
Source/JavaScriptCore/b3/B3ConstDoubleValue.h
Source/JavaScriptCore/b3/B3ConstFloatValue.h
Source/JavaScriptCore/b3/B3ConstPtrValue.h
Source/JavaScriptCore/b3/B3ControlValue.cpp
Source/JavaScriptCore/b3/B3ControlValue.h
Source/JavaScriptCore/b3/B3Effects.cpp
Source/JavaScriptCore/b3/B3Effects.h
Source/JavaScriptCore/b3/B3EliminateCommonSubexpressions.cpp
Source/JavaScriptCore/b3/B3FixSSA.cpp
Source/JavaScriptCore/b3/B3FixSSA.h
Source/JavaScriptCore/b3/B3IndexMap.h
Source/JavaScriptCore/b3/B3IndexSet.h
Source/JavaScriptCore/b3/B3LowerToAir.cpp
Source/JavaScriptCore/b3/B3MemoryValue.h
Source/JavaScriptCore/b3/B3Opcode.cpp
Source/JavaScriptCore/b3/B3Opcode.h
Source/JavaScriptCore/b3/B3PatchpointValue.cpp
Source/JavaScriptCore/b3/B3PatchpointValue.h
Source/JavaScriptCore/b3/B3Procedure.cpp
Source/JavaScriptCore/b3/B3Procedure.h
Source/JavaScriptCore/b3/B3ProcedureInlines.h
Source/JavaScriptCore/b3/B3ReduceStrength.cpp
Source/JavaScriptCore/b3/B3SlotBaseValue.h
Source/JavaScriptCore/b3/B3SparseCollection.h [new file with mode: 0644]
Source/JavaScriptCore/b3/B3StackSlot.cpp
Source/JavaScriptCore/b3/B3StackSlot.h
Source/JavaScriptCore/b3/B3StackmapValue.cpp
Source/JavaScriptCore/b3/B3StackmapValue.h
Source/JavaScriptCore/b3/B3SwitchValue.cpp
Source/JavaScriptCore/b3/B3SwitchValue.h
Source/JavaScriptCore/b3/B3UpsilonValue.h
Source/JavaScriptCore/b3/B3Validate.cpp
Source/JavaScriptCore/b3/B3Value.cpp
Source/JavaScriptCore/b3/B3Value.h
Source/JavaScriptCore/b3/B3Variable.cpp [new file with mode: 0644]
Source/JavaScriptCore/b3/B3Variable.h [new file with mode: 0644]
Source/JavaScriptCore/b3/B3VariableValue.cpp [new file with mode: 0644]
Source/JavaScriptCore/b3/B3VariableValue.h [new file with mode: 0644]
Source/JavaScriptCore/b3/air/AirAllocateStack.cpp
Source/JavaScriptCore/b3/air/AirCode.cpp
Source/JavaScriptCore/b3/air/AirCode.h
Source/JavaScriptCore/b3/air/AirFixObviousSpills.cpp
Source/JavaScriptCore/b3/air/AirInstInlines.h
Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp
Source/JavaScriptCore/b3/air/AirLiveness.h
Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp
Source/JavaScriptCore/b3/air/AirSpecial.cpp
Source/JavaScriptCore/b3/air/AirSpecial.h
Source/JavaScriptCore/b3/air/AirSpillEverything.cpp
Source/JavaScriptCore/b3/air/AirStackSlot.cpp
Source/JavaScriptCore/b3/air/AirStackSlot.h
Source/JavaScriptCore/b3/air/AirStackSlotKind.cpp [moved from Source/JavaScriptCore/b3/B3StackSlotKind.cpp with 93% similarity]
Source/JavaScriptCore/b3/air/AirStackSlotKind.h [moved from Source/JavaScriptCore/b3/B3StackSlotKind.h with 67% similarity]
Source/JavaScriptCore/b3/air/opcode_generator.rb
Source/JavaScriptCore/b3/air/testair.cpp
Source/JavaScriptCore/b3/testb3.cpp
Source/JavaScriptCore/ftl/FTLB3Output.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Websites/webkit.org/ChangeLog
Websites/webkit.org/docs/b3/intermediate-representation.html

index 915e41a..e9b59f6 100644 (file)
@@ -97,6 +97,7 @@ set(JavaScriptCore_SOURCES
     b3/air/AirSpecial.cpp
     b3/air/AirSpillEverything.cpp
     b3/air/AirStackSlot.cpp
+    b3/air/AirStackSlotKind.cpp
     b3/air/AirTmp.cpp
     b3/air/AirTmpWidth.cpp
     b3/air/AirValidate.cpp
@@ -151,7 +152,6 @@ set(JavaScriptCore_SOURCES
     b3/B3StackmapGenerationParams.cpp
     b3/B3StackmapSpecial.cpp
     b3/B3StackmapValue.cpp
-    b3/B3StackSlotKind.cpp
     b3/B3StackSlot.cpp
     b3/B3SwitchCase.cpp
     b3/B3SwitchValue.cpp
@@ -163,6 +163,8 @@ set(JavaScriptCore_SOURCES
     b3/B3Value.cpp
     b3/B3ValueKey.cpp
     b3/B3ValueRep.cpp
+    b3/B3Variable.cpp
+    b3/B3VariableValue.cpp
 
     bindings/ScriptFunctionCall.cpp
     bindings/ScriptObject.cpp
index 49d9d3c..3e251ce 100644 (file)
@@ -1,3 +1,309 @@
+2016-02-02  Filip Pizlo  <fpizlo@apple.com>
+
+        Get rid of anonymous stack slots
+        https://bugs.webkit.org/show_bug.cgi?id=151128
+
+        Reviewed by Mark Lam.
+
+        When I first designed stack slots, the idea was that an "anonymous" stack slot was one that
+        behaved exactly like a C variable: if it never escaped, it would not need to get stack space
+        for the entire lifetime of the function - it could get any slab of stack so long as it
+        didn't interfere with other stack slots that would be live at the same time. The reason I
+        called them "anonymous" is that external code could not get its address. This felt like it
+        gave the stack slot anonymity. But it was never a good name for this concept.
+
+        Then I had the register allocator lower temporaries to anonymous stack slots when it spilled
+        them. Spilling became the sole client of anonymous stack slots.
+
+        Then I realized that there was an aspect of how spill slots work that make them want
+        slightly different semantics than a normal C variable. A C variable is a proper memory
+        location - you could do a store to only some bytes in the variable, and it's reasonable to
+        expect that this will not destroy the other bytes in the variable. But that means that to
+        compute their liveness, you have to do something like a per-byte liveness. That's overkill
+        for spill slots. You want any store to the spill slot to kill the whole slot even if it
+        writes to just part of the slot. This matches how temporaries work. So rather than implement
+        per-byte liveness, I decided to change the semantics of anonymous stack slots to make them
+        work like how I wanted spill slots to work. This was quite dirty, and put B3 in the awkward
+        situation that B3's anonymous stack slots behaved like spill slots. But it was OK since
+        nobody used anonymous stack slots in B3.
+
+        Then I added tail duplication, which required having a mechanism for introducing non-SSA
+        variables in B3. I decided to use anonymous stack slots for this purpose. All of a sudden
+        this all felt like it made sense: anonymous stack slots were just like variables! Hooray for
+        the amazing foresight of anonymous stack slots!
+
+        But then I realized that this was all very bad. We want B3 to be able to optimize Store and
+        Load operations by reasoning about how they affect bytes in memory. For example, if you do
+        a Load of a 64-bit value, and then you modify just the low 32 bits of that value, and then
+        you do a 64-bit store back to the same location, then it would be better to transform this
+        into 32-bit operations. We don't do this optimization yet, but it's the kind of thing that
+        we want B3 to be able to do. To do it, we need Store to mean that it only affects N bytes
+        starting at the pointer, where N is the size of the thing being stored. But that's not what
+        Store means for anonymous stack slots. For anonymous slots, storing to any byte in the slot
+        clobbers all bytes in the slot. We were never clear if you need to store directly to an
+        anonymous slot to get this behavior, or if any pointer that points to an anoymous slot must
+        exhibit this behavior when stored to. Neither kinds of semantics make sense to me.
+
+        This change fixes the problem by eradicating anonymous stack slots. In B3, they are replaced
+        with Variables. In Air, they are replaced with a different stack slot kind, called Spill.
+        There is no such thing as stack slot kinds in B3 anymore, all B3 stack slots are locked. In
+        Air, there is still the concept of stack slot kind - Locked or Spill.
+
+        B3 Variables are awesome. They are exactly what they seem to be. They have a type. They are
+        declared at the top level in the Procedure. You can access them with new opcodes, Get and
+        Set. This greatly simplifies demoting SSA values to variables and promoting them back to
+        SSA. I even made the instruction selector do the right things for variables, which means
+        that introducing variables won't hurt instruction selection (there will be extra moves, but
+        IRC will kill them). It's great to have non-SSA variables as an explicit concept in IR
+        because it means that you don't have to do any magic to use them - they Just Work.
+
+        Air spill slots behave almost like anonymous stack slots, with one exception: you cannot
+        escape them. We validate this by making it illegal to UseAddr on a spill slot. This removes
+        the need to answer awkward questions like: does a 32-bit Def on a pointer that may point to
+        a 64-bit spill slot do anything to the 32 bits above the pointer?  Does it write zero to it?
+        Does it write zero to it just when the pointer actually points to a spill slot or always?
+        These are silly questions, and we don't have to answer them because the only way to refer to
+        a spill slot is directly. No escaping means no aliasing.
+
+        This doesn't affect performance. It just makes the compiler more fun to work with by
+        removing some cognitive dissonance.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3ArgumentRegValue.h:
+        * b3/B3CCallValue.h:
+        * b3/B3CheckValue.cpp:
+        (JSC::B3::CheckValue::cloneImpl):
+        (JSC::B3::CheckValue::CheckValue):
+        * b3/B3CheckValue.h:
+        * b3/B3Const32Value.h:
+        * b3/B3Const64Value.h:
+        * b3/B3ConstDoubleValue.h:
+        * b3/B3ConstFloatValue.h:
+        * b3/B3ConstPtrValue.h:
+        (JSC::B3::ConstPtrValue::ConstPtrValue):
+        * b3/B3ControlValue.cpp:
+        (JSC::B3::ControlValue::convertToJump):
+        (JSC::B3::ControlValue::convertToOops):
+        (JSC::B3::ControlValue::dumpMeta):
+        * b3/B3ControlValue.h:
+        * b3/B3Effects.cpp:
+        (JSC::B3::Effects::interferes):
+        (JSC::B3::Effects::dump):
+        * b3/B3Effects.h:
+        (JSC::B3::Effects::mustExecute):
+        * b3/B3EliminateCommonSubexpressions.cpp:
+        * b3/B3FixSSA.cpp:
+        (JSC::B3::demoteValues):
+        (JSC::B3::fixSSA):
+        * b3/B3FixSSA.h:
+        * b3/B3IndexMap.h:
+        (JSC::B3::IndexMap::resize):
+        (JSC::B3::IndexMap::clear):
+        (JSC::B3::IndexMap::size):
+        (JSC::B3::IndexMap::operator[]):
+        * b3/B3IndexSet.h:
+        (JSC::B3::IndexSet::contains):
+        (JSC::B3::IndexSet::size):
+        (JSC::B3::IndexSet::isEmpty):
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::run):
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3MemoryValue.h:
+        * b3/B3Opcode.cpp:
+        (WTF::printInternal):
+        * b3/B3Opcode.h:
+        * b3/B3PatchpointValue.cpp:
+        (JSC::B3::PatchpointValue::cloneImpl):
+        (JSC::B3::PatchpointValue::PatchpointValue):
+        * b3/B3PatchpointValue.h:
+        * b3/B3Procedure.cpp:
+        (JSC::B3::Procedure::Procedure):
+        (JSC::B3::Procedure::addBlock):
+        (JSC::B3::Procedure::addStackSlot):
+        (JSC::B3::Procedure::addVariable):
+        (JSC::B3::Procedure::clone):
+        (JSC::B3::Procedure::addIntConstant):
+        (JSC::B3::Procedure::dump):
+        (JSC::B3::Procedure::deleteStackSlot):
+        (JSC::B3::Procedure::deleteVariable):
+        (JSC::B3::Procedure::deleteValue):
+        (JSC::B3::Procedure::deleteOrphans):
+        (JSC::B3::Procedure::calleeSaveRegisters):
+        (JSC::B3::Procedure::addValueImpl):
+        (JSC::B3::Procedure::setBlockOrderImpl):
+        (JSC::B3::Procedure::addAnonymousStackSlot): Deleted.
+        (JSC::B3::Procedure::addStackSlotIndex): Deleted.
+        (JSC::B3::Procedure::addValueIndex): Deleted.
+        * b3/B3Procedure.h:
+        (JSC::B3::Procedure::setBlockOrder):
+        (JSC::B3::Procedure::stackSlots):
+        (JSC::B3::Procedure::variables):
+        (JSC::B3::Procedure::values):
+        (JSC::B3::Procedure::StackSlotsCollection::StackSlotsCollection): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::size): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::at): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::operator[]): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::iterator): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::operator*): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::operator++): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::operator==): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::operator!=): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::iterator::findNext): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::begin): Deleted.
+        (JSC::B3::Procedure::StackSlotsCollection::end): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::ValuesCollection): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::iterator): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::operator*): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::operator++): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::operator==): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::operator!=): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::iterator::findNext): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::begin): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::end): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::size): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::at): Deleted.
+        (JSC::B3::Procedure::ValuesCollection::operator[]): Deleted.
+        * b3/B3ProcedureInlines.h:
+        (JSC::B3::Procedure::add):
+        * b3/B3ReduceStrength.cpp:
+        * b3/B3SlotBaseValue.h:
+        * b3/B3SparseCollection.h: Added.
+        (JSC::B3::SparseCollection::SparseCollection):
+        (JSC::B3::SparseCollection::add):
+        (JSC::B3::SparseCollection::addNew):
+        (JSC::B3::SparseCollection::remove):
+        (JSC::B3::SparseCollection::size):
+        (JSC::B3::SparseCollection::isEmpty):
+        (JSC::B3::SparseCollection::at):
+        (JSC::B3::SparseCollection::operator[]):
+        (JSC::B3::SparseCollection::iterator::iterator):
+        (JSC::B3::SparseCollection::iterator::operator*):
+        (JSC::B3::SparseCollection::iterator::operator++):
+        (JSC::B3::SparseCollection::iterator::operator==):
+        (JSC::B3::SparseCollection::iterator::operator!=):
+        (JSC::B3::SparseCollection::iterator::findNext):
+        (JSC::B3::SparseCollection::begin):
+        (JSC::B3::SparseCollection::end):
+        * b3/B3StackSlot.cpp:
+        (JSC::B3::StackSlot::deepDump):
+        (JSC::B3::StackSlot::StackSlot):
+        * b3/B3StackSlot.h:
+        (JSC::B3::StackSlot::byteSize):
+        (JSC::B3::StackSlot::index):
+        (JSC::B3::StackSlot::setOffsetFromFP):
+        (JSC::B3::StackSlot::kind): Deleted.
+        (JSC::B3::StackSlot::isLocked): Deleted.
+        * b3/B3StackSlotKind.cpp: Removed.
+        * b3/B3StackSlotKind.h: Removed.
+        * b3/B3StackmapValue.cpp:
+        (JSC::B3::StackmapValue::dumpMeta):
+        (JSC::B3::StackmapValue::StackmapValue):
+        * b3/B3StackmapValue.h:
+        * b3/B3SwitchValue.cpp:
+        (JSC::B3::SwitchValue::cloneImpl):
+        (JSC::B3::SwitchValue::SwitchValue):
+        * b3/B3SwitchValue.h:
+        * b3/B3UpsilonValue.h:
+        * b3/B3Validate.cpp:
+        * b3/B3Value.cpp:
+        (JSC::B3::Value::replaceWithIdentity):
+        (JSC::B3::Value::replaceWithNop):
+        (JSC::B3::Value::replaceWithPhi):
+        (JSC::B3::Value::dump):
+        (JSC::B3::Value::effects):
+        (JSC::B3::Value::checkOpcode):
+        * b3/B3Value.h:
+        * b3/B3Variable.cpp: Added.
+        (JSC::B3::Variable::~Variable):
+        (JSC::B3::Variable::dump):
+        (JSC::B3::Variable::deepDump):
+        (JSC::B3::Variable::Variable):
+        * b3/B3Variable.h: Added.
+        (JSC::B3::Variable::type):
+        (JSC::B3::Variable::index):
+        (JSC::B3::DeepVariableDump::DeepVariableDump):
+        (JSC::B3::DeepVariableDump::dump):
+        (JSC::B3::deepDump):
+        * b3/B3VariableValue.cpp: Added.
+        (JSC::B3::VariableValue::~VariableValue):
+        (JSC::B3::VariableValue::dumpMeta):
+        (JSC::B3::VariableValue::cloneImpl):
+        (JSC::B3::VariableValue::VariableValue):
+        * b3/B3VariableValue.h: Added.
+        * b3/air/AirAllocateStack.cpp:
+        (JSC::B3::Air::allocateStack):
+        * b3/air/AirCode.cpp:
+        (JSC::B3::Air::Code::addStackSlot):
+        (JSC::B3::Air::Code::addSpecial):
+        (JSC::B3::Air::Code::cCallSpecial):
+        * b3/air/AirCode.h:
+        (JSC::B3::Air::Code::begin):
+        (JSC::B3::Air::Code::end):
+        (JSC::B3::Air::Code::stackSlots):
+        (JSC::B3::Air::Code::specials):
+        (JSC::B3::Air::Code::forAllTmps):
+        (JSC::B3::Air::Code::StackSlotsCollection::StackSlotsCollection): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::size): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::at): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::operator[]): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::iterator::iterator): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::iterator::operator*): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::iterator::operator++): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::iterator::operator==): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::iterator::operator!=): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::begin): Deleted.
+        (JSC::B3::Air::Code::StackSlotsCollection::end): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::SpecialsCollection): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::size): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::at): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::operator[]): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::iterator::iterator): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::iterator::operator*): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::iterator::operator++): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::iterator::operator==): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::iterator::operator!=): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::begin): Deleted.
+        (JSC::B3::Air::Code::SpecialsCollection::end): Deleted.
+        * b3/air/AirFixObviousSpills.cpp:
+        * b3/air/AirInstInlines.h:
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        * b3/air/AirLiveness.h:
+        * b3/air/AirLowerAfterRegAlloc.cpp:
+        (JSC::B3::Air::lowerAfterRegAlloc):
+        * b3/air/AirSpecial.cpp:
+        (JSC::B3::Air::Special::Special):
+        * b3/air/AirSpecial.h:
+        * b3/air/AirSpillEverything.cpp:
+        (JSC::B3::Air::spillEverything):
+        * b3/air/AirStackSlot.cpp:
+        (JSC::B3::Air::StackSlot::dump):
+        (JSC::B3::Air::StackSlot::deepDump):
+        (JSC::B3::Air::StackSlot::StackSlot):
+        * b3/air/AirStackSlot.h:
+        (JSC::B3::Air::StackSlot::byteSize):
+        (JSC::B3::Air::StackSlot::kind):
+        (JSC::B3::Air::StackSlot::isLocked):
+        (JSC::B3::Air::StackSlot::isSpill):
+        (JSC::B3::Air::StackSlot::index):
+        (JSC::B3::Air::StackSlot::ensureSize):
+        * b3/air/AirStackSlotKind.cpp: Copied from Source/JavaScriptCore/b3/B3StackSlotKind.cpp.
+        (WTF::printInternal):
+        * b3/air/AirStackSlotKind.h: Copied from Source/JavaScriptCore/b3/B3StackSlotKind.h.
+        * b3/air/opcode_generator.rb:
+        * b3/air/testair.cpp:
+        (JSC::B3::Air::testShuffleBroadcastAllRegs):
+        (JSC::B3::Air::testShuffleShiftAllRegs):
+        (JSC::B3::Air::testShuffleRotateAllRegs):
+        * b3/testb3.cpp:
+        (JSC::B3::testStackSlot):
+        (JSC::B3::testStoreLoadStackSlot):
+        * ftl/FTLB3Output.cpp:
+        (JSC::FTL::Output::lockedStackSlot):
+        (JSC::FTL::Output::neg):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
+
 2016-02-02  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Introduce BytecodeIntrinsic constant rep like @undefined
index ee3e2be..afd5ea4 100644 (file)
                0F2B9CF719D0BAC100B1D1B5 /* FTLExitTimeObjectMaterialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B9CF119D0BAC100B1D1B5 /* FTLExitTimeObjectMaterialization.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F2B9CF819D0BAC100B1D1B5 /* FTLOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2B9CF219D0BAC100B1D1B5 /* FTLOperations.cpp */; };
                0F2B9CF919D0BAC100B1D1B5 /* FTLOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B9CF319D0BAC100B1D1B5 /* FTLOperations.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F2BBD961C5FF3F50023EF23 /* B3SparseCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BBD911C5FF3F50023EF23 /* B3SparseCollection.h */; };
+               0F2BBD971C5FF3F50023EF23 /* B3Variable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2BBD921C5FF3F50023EF23 /* B3Variable.cpp */; };
+               0F2BBD981C5FF3F50023EF23 /* B3Variable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BBD931C5FF3F50023EF23 /* B3Variable.h */; };
+               0F2BBD991C5FF3F50023EF23 /* B3VariableValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2BBD941C5FF3F50023EF23 /* B3VariableValue.cpp */; };
+               0F2BBD9A1C5FF3F50023EF23 /* B3VariableValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BBD951C5FF3F50023EF23 /* B3VariableValue.h */; };
+               0F2BBD9D1C5FF4050023EF23 /* AirStackSlotKind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2BBD9B1C5FF4050023EF23 /* AirStackSlotKind.cpp */; };
+               0F2BBD9E1C5FF4050023EF23 /* AirStackSlotKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BBD9C1C5FF4050023EF23 /* AirStackSlotKind.h */; };
                0F2BDC15151C5D4D00CD8910 /* DFGFixupPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2BDC12151C5D4A00CD8910 /* DFGFixupPhase.cpp */; };
                0F2BDC16151C5D4F00CD8910 /* DFGFixupPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BDC13151C5D4A00CD8910 /* DFGFixupPhase.h */; };
                0F2BDC21151E803B00CD8910 /* DFGInsertionSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2BDC1F151E803800CD8910 /* DFGInsertionSet.h */; };
                0FEC852D1BDACDAC0080FF74 /* B3ProcedureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84E31BDACDAC0080FF74 /* B3ProcedureInlines.h */; };
                0FEC85301BDACDAC0080FF74 /* B3StackmapSpecial.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC84E61BDACDAC0080FF74 /* B3StackmapSpecial.cpp */; };
                0FEC85311BDACDAC0080FF74 /* B3StackmapSpecial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84E71BDACDAC0080FF74 /* B3StackmapSpecial.h */; };
-               0FEC85321BDACDAC0080FF74 /* B3StackSlotKind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC84E81BDACDAC0080FF74 /* B3StackSlotKind.cpp */; };
-               0FEC85331BDACDAC0080FF74 /* B3StackSlotKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84E91BDACDAC0080FF74 /* B3StackSlotKind.h */; };
                0FEC85341BDACDAC0080FF74 /* B3SlotBaseValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC84EA1BDACDAC0080FF74 /* B3SlotBaseValue.cpp */; };
                0FEC85351BDACDAC0080FF74 /* B3SlotBaseValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84EB1BDACDAC0080FF74 /* B3SlotBaseValue.h */; };
                0FEC85361BDACDAC0080FF74 /* B3SuccessorCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84EC1BDACDAC0080FF74 /* B3SuccessorCollection.h */; };
                0F2B9CF119D0BAC100B1D1B5 /* FTLExitTimeObjectMaterialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLExitTimeObjectMaterialization.h; path = ftl/FTLExitTimeObjectMaterialization.h; sourceTree = "<group>"; };
                0F2B9CF219D0BAC100B1D1B5 /* FTLOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLOperations.cpp; path = ftl/FTLOperations.cpp; sourceTree = "<group>"; };
                0F2B9CF319D0BAC100B1D1B5 /* FTLOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLOperations.h; path = ftl/FTLOperations.h; sourceTree = "<group>"; };
+               0F2BBD911C5FF3F50023EF23 /* B3SparseCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3SparseCollection.h; path = b3/B3SparseCollection.h; sourceTree = "<group>"; };
+               0F2BBD921C5FF3F50023EF23 /* B3Variable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3Variable.cpp; path = b3/B3Variable.cpp; sourceTree = "<group>"; };
+               0F2BBD931C5FF3F50023EF23 /* B3Variable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3Variable.h; path = b3/B3Variable.h; sourceTree = "<group>"; };
+               0F2BBD941C5FF3F50023EF23 /* B3VariableValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3VariableValue.cpp; path = b3/B3VariableValue.cpp; sourceTree = "<group>"; };
+               0F2BBD951C5FF3F50023EF23 /* B3VariableValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3VariableValue.h; path = b3/B3VariableValue.h; sourceTree = "<group>"; };
+               0F2BBD9B1C5FF4050023EF23 /* AirStackSlotKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirStackSlotKind.cpp; path = b3/air/AirStackSlotKind.cpp; sourceTree = "<group>"; };
+               0F2BBD9C1C5FF4050023EF23 /* AirStackSlotKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirStackSlotKind.h; path = b3/air/AirStackSlotKind.h; sourceTree = "<group>"; };
                0F2BDC12151C5D4A00CD8910 /* DFGFixupPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGFixupPhase.cpp; path = dfg/DFGFixupPhase.cpp; sourceTree = "<group>"; };
                0F2BDC13151C5D4A00CD8910 /* DFGFixupPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGFixupPhase.h; path = dfg/DFGFixupPhase.h; sourceTree = "<group>"; };
                0F2BDC1F151E803800CD8910 /* DFGInsertionSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGInsertionSet.h; path = dfg/DFGInsertionSet.h; sourceTree = "<group>"; };
                0FEC84E31BDACDAC0080FF74 /* B3ProcedureInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3ProcedureInlines.h; path = b3/B3ProcedureInlines.h; sourceTree = "<group>"; };
                0FEC84E61BDACDAC0080FF74 /* B3StackmapSpecial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3StackmapSpecial.cpp; path = b3/B3StackmapSpecial.cpp; sourceTree = "<group>"; };
                0FEC84E71BDACDAC0080FF74 /* B3StackmapSpecial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3StackmapSpecial.h; path = b3/B3StackmapSpecial.h; sourceTree = "<group>"; };
-               0FEC84E81BDACDAC0080FF74 /* B3StackSlotKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3StackSlotKind.cpp; path = b3/B3StackSlotKind.cpp; sourceTree = "<group>"; };
-               0FEC84E91BDACDAC0080FF74 /* B3StackSlotKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3StackSlotKind.h; path = b3/B3StackSlotKind.h; sourceTree = "<group>"; };
                0FEC84EA1BDACDAC0080FF74 /* B3SlotBaseValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3SlotBaseValue.cpp; path = b3/B3SlotBaseValue.cpp; sourceTree = "<group>"; };
                0FEC84EB1BDACDAC0080FF74 /* B3SlotBaseValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3SlotBaseValue.h; path = b3/B3SlotBaseValue.h; sourceTree = "<group>"; };
                0FEC84EC1BDACDAC0080FF74 /* B3SuccessorCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3SuccessorCollection.h; path = b3/B3SuccessorCollection.h; sourceTree = "<group>"; };
                                0FEC85B81BE1462F0080FF74 /* B3ReduceStrength.h */,
                                0FEC84EA1BDACDAC0080FF74 /* B3SlotBaseValue.cpp */,
                                0FEC84EB1BDACDAC0080FF74 /* B3SlotBaseValue.h */,
+                               0F2BBD911C5FF3F50023EF23 /* B3SparseCollection.h */,
                                0F6B8ADA1C4EFAC300969052 /* B3SSACalculator.cpp */,
                                0F6B8ADB1C4EFAC300969052 /* B3SSACalculator.h */,
                                0F33FCF51C136E2500323F67 /* B3StackmapGenerationParams.cpp */,
                                0F338DF01BE93AD10013C88F /* B3StackmapValue.h */,
                                0F9495851C57F47500413A48 /* B3StackSlot.cpp */,
                                0F9495861C57F47500413A48 /* B3StackSlot.h */,
-                               0FEC84E81BDACDAC0080FF74 /* B3StackSlotKind.cpp */,
-                               0FEC84E91BDACDAC0080FF74 /* B3StackSlotKind.h */,
                                0FEC84EC1BDACDAC0080FF74 /* B3SuccessorCollection.h */,
                                0FEC84ED1BDACDAC0080FF74 /* B3SwitchCase.cpp */,
                                0FEC84EE1BDACDAC0080FF74 /* B3SwitchCase.h */,
                                0F338E0A1BF0276C0013C88F /* B3ValueKeyInlines.h */,
                                0FEC84FC1BDACDAC0080FF74 /* B3ValueRep.cpp */,
                                0FEC84FD1BDACDAC0080FF74 /* B3ValueRep.h */,
+                               0F2BBD921C5FF3F50023EF23 /* B3Variable.cpp */,
+                               0F2BBD931C5FF3F50023EF23 /* B3Variable.h */,
+                               0F2BBD941C5FF3F50023EF23 /* B3VariableValue.cpp */,
+                               0F2BBD951C5FF3F50023EF23 /* B3VariableValue.h */,
                                0FEC85AE1BDB5D5E0080FF74 /* testb3.cpp */,
                        );
                        name = b3;
                                0FEC85651BDACDC70080FF74 /* AirSpillEverything.h */,
                                0FEC85661BDACDC70080FF74 /* AirStackSlot.cpp */,
                                0FEC85671BDACDC70080FF74 /* AirStackSlot.h */,
+                               0F2BBD9B1C5FF4050023EF23 /* AirStackSlotKind.cpp */,
+                               0F2BBD9C1C5FF4050023EF23 /* AirStackSlotKind.h */,
                                0FEC85681BDACDC70080FF74 /* AirTmp.cpp */,
                                0FEC85691BDACDC70080FF74 /* AirTmp.h */,
                                0FEC856A1BDACDC70080FF74 /* AirTmpInlines.h */,
                                0FEC852D1BDACDAC0080FF74 /* B3ProcedureInlines.h in Headers */,
                                0FEC85BD1BE1462F0080FF74 /* B3ReduceStrength.h in Headers */,
                                0FEC85311BDACDAC0080FF74 /* B3StackmapSpecial.h in Headers */,
-                               0FEC85331BDACDAC0080FF74 /* B3StackSlotKind.h in Headers */,
                                0FEC85351BDACDAC0080FF74 /* B3SlotBaseValue.h in Headers */,
                                0FEC85361BDACDAC0080FF74 /* B3SuccessorCollection.h in Headers */,
                                0FEC85381BDACDAC0080FF74 /* B3SwitchCase.h in Headers */,
                                0F15F15F14B7A73E005DE37D /* CommonSlowPaths.h in Headers */,
                                6553A33217A1F1EE008CF6F3 /* CommonSlowPathsExceptions.h in Headers */,
                                0FD82E39141AB14D00179C94 /* CompactJITCodeMap.h in Headers */,
+                               0F2BBD9E1C5FF4050023EF23 /* AirStackSlotKind.h in Headers */,
                                A7E5A3A81797432D00E893C0 /* CompilationResult.h in Headers */,
                                BC18C3F40E16F5CD00B34460 /* Completion.h in Headers */,
                                0F6FC751196110A800E1D02D /* ComplexGetStatus.h in Headers */,
                                43422A631C158E6D00E2EB98 /* B3ConstFloatValue.h in Headers */,
                                0FFB921A16D02EC50055A5DB /* DFGBasicBlockInlines.h in Headers */,
                                A7D89CF417A0B8CC00773AD8 /* DFGBlockInsertionSet.h in Headers */,
+                               0F2BBD961C5FF3F50023EF23 /* B3SparseCollection.h in Headers */,
                                0FC3CCFC19ADA410006AC72A /* DFGBlockMap.h in Headers */,
                                0FC3CCFD19ADA410006AC72A /* DFGBlockMapInlines.h in Headers */,
                                0FC3CCFE19ADA410006AC72A /* DFGBlockSet.h in Headers */,
                                0F2B66AE17B6B54500A7AE3F /* GCIncomingRefCountedSet.h in Headers */,
                                0F2B66AF17B6B54500A7AE3F /* GCIncomingRefCountedSetInlines.h in Headers */,
                                2AABCDE718EF294200002096 /* GCLogging.h in Headers */,
+                               0F2BBD981C5FF3F50023EF23 /* B3Variable.h in Headers */,
                                A54E8EB018BFFBBB00556D28 /* GCSegmentedArray.h in Headers */,
                                A54E8EB118BFFBBE00556D28 /* GCSegmentedArrayInlines.h in Headers */,
                                9959E9311BD18272001AA413 /* generate-combined-inspector-json.py in Headers */,
                                0F6B8ADD1C4EFAC300969052 /* B3SSACalculator.h in Headers */,
                                7B0247591B868EB700542440 /* WASMFunctionSyntaxChecker.h in Headers */,
                                7B39F76E1B62DE3200360FB4 /* WASMModuleParser.h in Headers */,
+                               0F2BBD9A1C5FF3F50023EF23 /* B3VariableValue.h in Headers */,
                                7B39F7701B62DE3200360FB4 /* WASMReader.h in Headers */,
                                FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */,
                                0F919D2615853CE3004A4E7D /* Watchpoint.h in Headers */,
                                43422A621C158E6A00E2EB98 /* B3ConstFloatValue.cpp in Sources */,
                                0FEC85BC1BE1462F0080FF74 /* B3ReduceStrength.cpp in Sources */,
                                0FEC85301BDACDAC0080FF74 /* B3StackmapSpecial.cpp in Sources */,
-                               0FEC85321BDACDAC0080FF74 /* B3StackSlotKind.cpp in Sources */,
                                0FEC85341BDACDAC0080FF74 /* B3SlotBaseValue.cpp in Sources */,
                                0FEC85371BDACDAC0080FF74 /* B3SwitchCase.cpp in Sources */,
                                0FEC85391BDACDAC0080FF74 /* B3SwitchValue.cpp in Sources */,
                                0FC97F4118202119002C9B26 /* DFGWatchpointCollectionPhase.cpp in Sources */,
                                0FDB2CE7174830A2007B3C1B /* DFGWorklist.cpp in Sources */,
                                0FE050171AA9091100D33B33 /* DirectArguments.cpp in Sources */,
+                               0F2BBD9D1C5FF4050023EF23 /* AirStackSlotKind.cpp in Sources */,
                                0FE050151AA9091100D33B33 /* DirectArgumentsOffset.cpp in Sources */,
                                0F9D3370165DBB90005AD387 /* Disassembler.cpp in Sources */,
                                A70447ED17A0BD7000F5898E /* DumpContext.cpp in Sources */,
                                2A4EC90B1860D6C20094F782 /* WriteBarrierBuffer.cpp in Sources */,
                                0FC8150B14043C0E00CFA603 /* WriteBarrierSupport.cpp in Sources */,
                                A7E5AB3A1799E4B200D2833D /* X86Disassembler.cpp in Sources */,
+                               0F2BBD971C5FF3F50023EF23 /* B3Variable.cpp in Sources */,
                                863C6D9C1521111A00585E4E /* YarrCanonicalizeUCS2.cpp in Sources */,
                                86704B8412DBA33700A9FE7B /* YarrInterpreter.cpp in Sources */,
                                86704B8612DBA33700A9FE7B /* YarrJIT.cpp in Sources */,
                                86704B8912DBA33700A9FE7B /* YarrPattern.cpp in Sources */,
                                86704B4212DB8A8100A9FE7B /* YarrSyntaxChecker.cpp in Sources */,
+                               0F2BBD991C5FF3F50023EF23 /* B3VariableValue.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index a6bdcc9..132e1dd 100644 (file)
@@ -49,8 +49,8 @@ protected:
 private:
     friend class Procedure;
 
-    ArgumentRegValue(unsigned index, Origin origin, Reg reg)
-        : Value(index, CheckedOpcode, ArgumentReg, reg.isGPR() ? pointerType() : Double, origin)
+    ArgumentRegValue(Origin origin, Reg reg)
+        : Value(CheckedOpcode, ArgumentReg, reg.isGPR() ? pointerType() : Double, origin)
         , m_reg(reg)
     {
         ASSERT(reg.isSet());
index 6155527..92194be 100644 (file)
@@ -48,15 +48,15 @@ private:
     friend class Procedure;
 
     template<typename... Arguments>
-    CCallValue(unsigned index, Type type, Origin origin, Arguments... arguments)
-        : Value(index, CheckedOpcode, CCall, type, origin, arguments...)
+    CCallValue(Type type, Origin origin, Arguments... arguments)
+        : Value(CheckedOpcode, CCall, type, origin, arguments...)
     {
         RELEASE_ASSERT(numChildren() >= 1);
     }
 
     template<typename... Arguments>
-    CCallValue(unsigned index, Type type, Origin origin, const Effects& effects, Arguments... arguments)
-        : Value(index, CheckedOpcode, CCall, type, origin, arguments...)
+    CCallValue(Type type, Origin origin, const Effects& effects, Arguments... arguments)
+        : Value(CheckedOpcode, CCall, type, origin, arguments...)
         , effects(effects)
     {
         RELEASE_ASSERT(numChildren() >= 1);
index 69576fa..b8b27cc 100644 (file)
@@ -46,8 +46,8 @@ Value* CheckValue::cloneImpl() const
 }
 
 // Use this form for CheckAdd, CheckSub, and CheckMul.
-CheckValue::CheckValue(unsigned index, Opcode opcode, Origin origin, Value* left, Value* right)
-    : StackmapValue(index, CheckedOpcode, opcode, left->type(), origin)
+CheckValue::CheckValue(Opcode opcode, Origin origin, Value* left, Value* right)
+    : StackmapValue(CheckedOpcode, opcode, left->type(), origin)
 {
     ASSERT(B3::isInt(type()));
     ASSERT(left->type() == right->type());
@@ -57,8 +57,8 @@ CheckValue::CheckValue(unsigned index, Opcode opcode, Origin origin, Value* left
 }
 
 // Use this form for Check.
-CheckValue::CheckValue(unsigned index, Opcode opcode, Origin origin, Value* predicate)
-    : StackmapValue(index, CheckedOpcode, opcode, Void, origin)
+CheckValue::CheckValue(Opcode opcode, Origin origin, Value* predicate)
+    : StackmapValue(CheckedOpcode, opcode, Void, origin)
 {
     ASSERT(opcode == Check);
     append(ConstrainedValue(predicate, ValueRep::WarmAny));
index c2fab6f..8802b1f 100644 (file)
@@ -58,10 +58,10 @@ private:
     friend class Procedure;
 
     // Use this form for CheckAdd, CheckSub, and CheckMul.
-    JS_EXPORT_PRIVATE CheckValue(unsigned index, Opcode, Origin, Value* left, Value* right);
+    JS_EXPORT_PRIVATE CheckValue(Opcode, Origin, Value* left, Value* right);
 
     // Use this form for Check.
-    JS_EXPORT_PRIVATE CheckValue(unsigned index, Opcode, Origin, Value* predicate);
+    JS_EXPORT_PRIVATE CheckValue(Opcode, Origin, Value* predicate);
 };
 
 } } // namespace JSC::B3
index 7d564b6..d5a0ffe 100644 (file)
@@ -77,8 +77,8 @@ protected:
 
     friend class Procedure;
 
-    Const32Value(unsigned index, Origin origin, int32_t value)
-        : Value(index, CheckedOpcode, Const32, Int32, origin)
+    Const32Value(Origin origin, int32_t value)
+        : Value(CheckedOpcode, Const32, Int32, origin)
         , m_value(value)
     {
     }
index 406c451..bf5a4e2 100644 (file)
@@ -77,8 +77,8 @@ protected:
 
     friend class Procedure;
 
-    Const64Value(unsigned index, Origin origin, int64_t value)
-        : Value(index, CheckedOpcode, Const64, Int64, origin)
+    Const64Value(Origin origin, int64_t value)
+        : Value(CheckedOpcode, Const64, Int64, origin)
         , m_value(value)
     {
     }
index ddd3c5c..b2cb246 100644 (file)
@@ -70,8 +70,8 @@ protected:
 private:
     friend class Procedure;
 
-    ConstDoubleValue(unsigned index, Origin origin, double value)
-        : Value(index, CheckedOpcode, ConstDouble, Double, origin)
+    ConstDoubleValue(Origin origin, double value)
+        : Value(CheckedOpcode, ConstDouble, Double, origin)
         , m_value(value)
     {
     }
index c705460..440bd61 100644 (file)
@@ -68,8 +68,8 @@ protected:
 private:
     friend class Procedure;
 
-    ConstFloatValue(unsigned index, Origin origin, float value)
-        : Value(index, CheckedOpcode, ConstFloat, Float, origin)
+    ConstFloatValue(Origin origin, float value)
+        : Value(CheckedOpcode, ConstFloat, Float, origin)
         , m_value(value)
     {
     }
index 5fcf898..f5484bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -54,13 +54,13 @@ private:
     friend class Procedure;
 
     template<typename T>
-    ConstPtrValue(unsigned index, Origin origin, T* pointer)
-        : ConstPtrValueBase(index, origin, bitwise_cast<intptr_t>(pointer))
+    ConstPtrValue(Origin origin, T* pointer)
+        : ConstPtrValueBase(origin, bitwise_cast<intptr_t>(pointer))
     {
     }
     template<typename T>
-    ConstPtrValue(unsigned index, Origin origin, T pointer)
-        : ConstPtrValueBase(index, origin, static_cast<intptr_t>(pointer))
+    ConstPtrValue(Origin origin, T pointer)
+        : ConstPtrValueBase(origin, static_cast<intptr_t>(pointer))
     {
     }
 };
index ea86a68..2dd9f94 100644 (file)
@@ -59,9 +59,10 @@ void ControlValue::convertToJump(BasicBlock* destination)
 
     this->ControlValue::~ControlValue();
 
-    new (this) ControlValue(index, Jump, origin, FrequentedBlock(destination));
+    new (this) ControlValue(Jump, origin, FrequentedBlock(destination));
 
     this->owner = owner;
+    this->m_index = index;
 }
 
 void ControlValue::convertToOops()
@@ -72,9 +73,10 @@ void ControlValue::convertToOops()
 
     this->ControlValue::~ControlValue();
 
-    new (this) ControlValue(index, Oops, origin);
+    new (this) ControlValue(Oops, origin);
 
     this->owner = owner;
+    this->m_index = index;
 }
 
 void ControlValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
index 9b838ad..17832ae 100644 (file)
@@ -109,8 +109,8 @@ protected:
 
     // Use this for subclasses.
     template<typename... Arguments>
-    ControlValue(unsigned index, Opcode opcode, Type type, Origin origin, Arguments... arguments)
-        : Value(index, CheckedOpcode, opcode, type, origin, arguments...)
+    ControlValue(Opcode opcode, Type type, Origin origin, Arguments... arguments)
+        : Value(CheckedOpcode, opcode, type, origin, arguments...)
     {
         ASSERT(accepts(opcode));
     }
@@ -122,22 +122,22 @@ private:
     friend class Procedure;
 
     // Use this for Oops.
-    ControlValue(unsigned index, Opcode opcode, Origin origin)
-        : Value(index, CheckedOpcode, opcode, Void, origin)
+    ControlValue(Opcode opcode, Origin origin)
+        : Value(CheckedOpcode, opcode, Void, origin)
     {
         ASSERT(opcode == Oops);
     }
 
     // Use this for Return.
-    ControlValue(unsigned index, Opcode opcode, Origin origin, Value* result)
-        : Value(index, CheckedOpcode, opcode, Void, origin, result)
+    ControlValue(Opcode opcode, Origin origin, Value* result)
+        : Value(CheckedOpcode, opcode, Void, origin, result)
     {
         ASSERT(opcode == Return);
     }
 
     // Use this for Jump.
-    ControlValue(unsigned index, Opcode opcode, Origin origin, const FrequentedBlock& target)
-        : Value(index, CheckedOpcode, opcode, Void, origin)
+    ControlValue(Opcode opcode, Origin origin, const FrequentedBlock& target)
+        : Value(CheckedOpcode, opcode, Void, origin)
     {
         ASSERT(opcode == Jump);
         m_successors.append(target);
@@ -145,9 +145,9 @@ private:
 
     // Use this for Branch.
     ControlValue(
-        unsigned index, Opcode opcode, Origin origin, Value* predicate,
+        Opcode opcode, Origin origin, Value* predicate,
         const FrequentedBlock& yes, const FrequentedBlock& no)
-        : Value(index, CheckedOpcode, opcode, Void, origin, predicate)
+        : Value(CheckedOpcode, opcode, Void, origin, predicate)
     {
         ASSERT(opcode == Branch);
         m_successors.append(yes);
index 2efc482..dea0ece 100644 (file)
@@ -44,7 +44,7 @@ bool interferesWithTerminal(const Effects& terminal, const Effects& other)
 {
     if (!terminal.terminal)
         return false;
-    return other.terminal || other.controlDependent || other.writesSSAState || other.writes;
+    return other.terminal || other.controlDependent || other.writesLocalState || other.writes;
 }
 
 bool interferesWithExitSideways(const Effects& exitsSideways, const Effects& other)
@@ -54,11 +54,11 @@ bool interferesWithExitSideways(const Effects& exitsSideways, const Effects& oth
     return other.controlDependent || other.writes;
 }
 
-bool interferesWithWritesSSAState(const Effects& writesSSAState, const Effects& other)
+bool interferesWithWritesLocalState(const Effects& writesLocalState, const Effects& other)
 {
-    if (!writesSSAState.writesSSAState)
+    if (!writesLocalState.writesLocalState)
         return false;
-    return other.writesSSAState || other.readsSSAState;
+    return other.writesLocalState || other.readsLocalState;
 }
 
 } // anonymous namespace
@@ -69,7 +69,7 @@ bool Effects::interferes(const Effects& other) const
         return true;
     if (interferesWithExitSideways(*this, other) || interferesWithExitSideways(other, *this))
         return true;
-    if (interferesWithWritesSSAState(*this, other) || interferesWithWritesSSAState(other, *this))
+    if (interferesWithWritesLocalState(*this, other) || interferesWithWritesLocalState(other, *this))
         return true;
     return writes.overlaps(other.writes)
         || writes.overlaps(other.reads)
@@ -85,10 +85,10 @@ void Effects::dump(PrintStream& out) const
         out.print(comma, "ExitsSideways");
     if (controlDependent)
         out.print(comma, "ControlDependent");
-    if (writesSSAState)
-        out.print(comma, "WritesSSAState");
-    if (readsSSAState)
-        out.print(comma, "ReadsSSAState");
+    if (writesLocalState)
+        out.print(comma, "WritesLocalState");
+    if (readsLocalState)
+        out.print(comma, "ReadsLocalState");
     if (writes)
         out.print(comma, "Writes:", writes);
     if (reads)
index 146c196..9d96fab 100644 (file)
@@ -48,14 +48,14 @@ struct Effects {
     // True if the instruction may change semantics if hoisted above some control flow.
     bool controlDependent { false };
 
-    // True if this writes to the SSA state. Operations that write SSA state don't write to anything
-    // in "memory" but they have a side-effect anyway. This is for modeling Upsilons. You can ignore
-    // this if you have your own way of modeling Upsilons or if you intend to just rebuild them
+    // True if this writes to the local state. Operations that write local state don't write to anything
+    // in "memory" but they have a side-effect anyway. This is for modeling Upsilons and Sets. You can ignore
+    // this if you have your own way of modeling Upsilons and Sets or if you intend to just rebuild them
     // anyway.
-    bool writesSSAState { false };
+    bool writesLocalState { false };
 
-    // True if this reads from the SSA state. This is only used for Phi.
-    bool readsSSAState { false };
+    // True if this reads from the local state. This is only used for Phi and Get.
+    bool readsLocalState { false };
 
     HeapRange writes;
     HeapRange reads;
@@ -77,7 +77,7 @@ struct Effects {
 
     bool mustExecute() const
     {
-        return terminal || exitsSideways || writesSSAState || writes;
+        return terminal || exitsSideways || writesLocalState || writes;
     }
 
     // Returns true if reordering instructions with these respective effects would change program
index 0bfe0bb..c0041bb 100644 (file)
@@ -41,6 +41,8 @@
 #include "B3StackSlot.h"
 #include "B3ValueKey.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
+#include "B3VariableValue.h"
 #include "DFGGraph.h"
 #include <wtf/CommaPrinter.h>
 #include <wtf/HashMap.h>
@@ -131,12 +133,10 @@ public:
             m_impureBlockData[m_block] = m_data;
         }
 
-        for (SlotBaseValue* stack : m_stacks)
-            m_insertionSet.insertValue(0, stack);
         for (BasicBlock* block : m_proc) {
             for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
-                auto iter = m_stores.find(block->at(valueIndex));
-                if (iter == m_stores.end())
+                auto iter = m_sets.find(block->at(valueIndex));
+                if (iter == m_sets.end())
                     continue;
 
                 for (Value* value : iter->value)
@@ -369,21 +369,19 @@ private:
         // FIXME: It would be way better if this phase just did SSA calculation directly.
         // Right now we're relying on the fact that CSE's position in the phase order is
         // almost right before SSA fixup.
-            
-        SlotBaseValue* stack = m_proc.add<SlotBaseValue>(
-            m_value->origin(), m_proc.addAnonymousStackSlot(m_value->type()));
-        m_stacks.append(stack);
 
-        MemoryValue* load = m_insertionSet.insert<MemoryValue>(
-            m_index, Load, m_value->type(), m_value->origin(), stack);
+        Variable* variable = m_proc.addVariable(m_value->type());
+
+        VariableValue* get = m_insertionSet.insert<VariableValue>(
+            m_index, Get, m_value->origin(), variable);
         if (verbose)
-            dataLog("    Inserting load of value: ", *load, "\n");
-        m_value->replaceWithIdentity(load);
+            dataLog("    Inserting get of value: ", *get, "\n");
+        m_value->replaceWithIdentity(get);
             
         for (MemoryValue* match : matches) {
-            Vector<Value*>& stores = m_stores.add(match, Vector<Value*>()).iterator->value;
+            Vector<Value*>& sets = m_sets.add(match, Vector<Value*>()).iterator->value;
 
-            Value* value = replace(match, stores);
+            Value* value = replace(match, sets);
             if (!value) {
                 if (match->isStore())
                     value = match->child(0);
@@ -391,8 +389,8 @@ private:
                     value = match;
             }
                 
-            Value* store = m_proc.add<MemoryValue>(Store, m_value->origin(), value, stack);
-            stores.append(store);
+            Value* set = m_proc.add<VariableValue>(Set, m_value->origin(), variable, value);
+            sets.append(set);
         }
 
         return true;
@@ -499,8 +497,7 @@ private:
     unsigned m_index;
     Value* m_value;
 
-    Vector<SlotBaseValue*> m_stacks;
-    HashMap<Value*, Vector<Value*>> m_stores;
+    HashMap<Value*, Vector<Value*>> m_sets;
 
     InsertionSet m_insertionSet;
 
index 8c74219..f3eada9 100644 (file)
 #include "B3Dominators.h"
 #include "B3IndexSet.h"
 #include "B3InsertionSetInlines.h"
-#include "B3MemoryValue.h"
 #include "B3PhaseScope.h"
 #include "B3ProcedureInlines.h"
 #include "B3SSACalculator.h"
-#include "B3SlotBaseValue.h"
-#include "B3StackSlot.h"
 #include "B3UpsilonValue.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
+#include "B3VariableValue.h"
 #include <wtf/CommaPrinter.h>
 
 namespace JSC { namespace B3 {
@@ -52,23 +51,16 @@ const bool verbose = false;
 
 void demoteValues(Procedure& proc, const IndexSet<Value>& values)
 {
-    HashMap<Value*, SlotBaseValue*> map;
-    HashMap<Value*, SlotBaseValue*> phiMap;
+    HashMap<Value*, Variable*> map;
+    HashMap<Value*, Variable*> phiMap;
 
     // Create stack slots.
-    InsertionSet insertionSet(proc);
     for (Value* value : values.values(proc.values())) {
-        SlotBaseValue* stack = insertionSet.insert<SlotBaseValue>(
-            0, value->origin(), proc.addAnonymousStackSlot(value->type()));
-        map.add(value, stack);
-
-        if (value->opcode() == Phi) {
-            SlotBaseValue* phiStack = insertionSet.insert<SlotBaseValue>(
-                0, value->origin(), proc.addAnonymousStackSlot(value->type()));
-            phiMap.add(value, phiStack);
-        }
+        map.add(value, proc.addVariable(value->type()));
+
+        if (value->opcode() == Phi)
+            phiMap.add(value, proc.addVariable(value->type()));
     }
-    insertionSet.execute(proc[0]);
 
     if (verbose) {
         dataLog("Demoting values as follows:\n");
@@ -85,36 +77,37 @@ void demoteValues(Procedure& proc, const IndexSet<Value>& values)
     }
 
     // Change accesses to the values to accesses to the stack slots.
+    InsertionSet insertionSet(proc);
     for (BasicBlock* block : proc) {
         for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
             Value* value = block->at(valueIndex);
 
             if (value->opcode() == Phi) {
-                if (SlotBaseValue* slotBase = phiMap.get(value)) {
+                if (Variable* variable = phiMap.get(value)) {
                     value->replaceWithIdentity(
-                        insertionSet.insert<MemoryValue>(
-                            valueIndex, Load, value->type(), value->origin(), slotBase));
+                        insertionSet.insert<VariableValue>(
+                            valueIndex, Get, value->origin(), variable));
                 }
             } else {
                 for (Value*& child : value->children()) {
-                    if (SlotBaseValue* slotBase = map.get(child)) {
-                        child = insertionSet.insert<MemoryValue>(
-                            valueIndex, Load, child->type(), value->origin(), slotBase);
+                    if (Variable* variable = map.get(child)) {
+                        child = insertionSet.insert<VariableValue>(
+                            valueIndex, Get, value->origin(), variable);
                     }
                 }
 
                 if (UpsilonValue* upsilon = value->as<UpsilonValue>()) {
-                    if (SlotBaseValue* slotBase = phiMap.get(upsilon->phi())) {
-                        insertionSet.insert<MemoryValue>(
-                            valueIndex, Store, upsilon->origin(), upsilon->child(0), slotBase);
+                    if (Variable* variable = phiMap.get(upsilon->phi())) {
+                        insertionSet.insert<VariableValue>(
+                            valueIndex, Set, upsilon->origin(), variable, upsilon->child(0));
                         value->replaceWithNop();
                     }
                 }
             }
 
-            if (SlotBaseValue* slotBase = map.get(value)) {
-                insertionSet.insert<MemoryValue>(
-                    valueIndex + 1, Store, value->origin(), value, slotBase);
+            if (Variable* variable = map.get(value)) {
+                insertionSet.insert<VariableValue>(
+                    valueIndex + 1, Set, value->origin(), variable, value);
             }
         }
         insertionSet.execute(block);
@@ -124,80 +117,22 @@ void demoteValues(Procedure& proc, const IndexSet<Value>& values)
 bool fixSSA(Procedure& proc)
 {
     PhaseScope phaseScope(proc, "fixSSA");
-    
-    // Collect the stack "variables". If there aren't any, then we don't have anything to do.
-    // That's a fairly common case.
-    HashMap<StackSlot*, Type> stackVariable;
-    for (Value* value : proc.values()) {
-        if (SlotBaseValue* slotBase = value->as<SlotBaseValue>()) {
-            StackSlot* stack = slotBase->slot();
-            if (stack->kind() == StackSlotKind::Anonymous)
-                stackVariable.add(stack, Void);
-        }
-    }
-
-    if (stackVariable.isEmpty())
-        return false;
 
-    // Make sure that we know how to optimize all of these. We only know how to handle Load and
-    // Store on anonymous variables.
+    // Just for sanity, remove any unused variables first. It's unlikely that this code has any
+    // bugs having to do with dead variables, but it would be silly to have to fix such a bug if
+    // it did arise.
+    IndexSet<Variable> liveVariables;
     for (Value* value : proc.values()) {
-        auto reject = [&] (Value* value) {
-            if (SlotBaseValue* slotBase = value->as<SlotBaseValue>())
-                stackVariable.remove(slotBase->slot());
-        };
-        
-        auto handleAccess = [&] (Value* access, Type type) {
-            SlotBaseValue* slotBase = access->lastChild()->as<SlotBaseValue>();
-            if (!slotBase)
-                return;
-
-            StackSlot* stack = slotBase->slot();
-            
-            if (value->as<MemoryValue>()->offset()) {
-                stackVariable.remove(stack);
-                return;
-            }
-
-            auto result = stackVariable.find(stack);
-            if (result == stackVariable.end())
-                return;
-            if (result->value == Void) {
-                result->value = type;
-                return;
-            }
-            if (result->value == type)
-                return;
-            stackVariable.remove(result);
-        };
-        
-        switch (value->opcode()) {
-        case Load:
-            // We're OK with loads from stack variables at an offset of zero.
-            handleAccess(value, value->type());
-            break;
-        case Store:
-            // We're OK with stores to stack variables, but not storing stack variables.
-            reject(value->child(0));
-            handleAccess(value, value->child(0)->type());
-            break;
-        default:
-            for (Value* child : value->children())
-                reject(child);
-            break;
-        }
+        if (VariableValue* variableValue = value->as<VariableValue>())
+            liveVariables.add(variableValue->variable());
     }
 
-    Vector<StackSlot*> deadSlots;
-    for (auto& entry : stackVariable) {
-        if (entry.value == Void)
-            deadSlots.append(entry.key);
+    for (Variable* variable : proc.variables()) {
+        if (!liveVariables.contains(variable))
+            proc.deleteVariable(variable);
     }
 
-    for (StackSlot* deadSlot : deadSlots)
-        stackVariable.remove(deadSlot);
-
-    if (stackVariable.isEmpty())
+    if (proc.variables().isEmpty())
         return false;
 
     // We know that we have variables to optimize, so do that now.
@@ -205,43 +140,38 @@ bool fixSSA(Procedure& proc)
 
     SSACalculator ssa(proc);
 
-    // Create a SSACalculator::Variable for every stack variable.
-    Vector<StackSlot*> variableToStack;
-    HashMap<StackSlot*, SSACalculator::Variable*> stackToVariable;
+    // Create a SSACalculator::Variable ("calcVar") for every variable.
+    Vector<Variable*> calcVarToVariable;
+    IndexMap<Variable, SSACalculator::Variable*> variableToCalcVar(proc.variables().size());
 
-    for (auto& entry : stackVariable) {
-        StackSlot* stack = entry.key;
-        SSACalculator::Variable* variable = ssa.newVariable();
-        RELEASE_ASSERT(variable->index() == variableToStack.size());
-        variableToStack.append(stack);
-        stackToVariable.add(stack, variable);
+    for (Variable* variable : proc.variables()) {
+        SSACalculator::Variable* calcVar = ssa.newVariable();
+        RELEASE_ASSERT(calcVar->index() == calcVarToVariable.size());
+        calcVarToVariable.append(variable);
+        variableToCalcVar[variable] = calcVar;
     }
 
     // Create Defs for all of the stores to the stack variable.
     for (BasicBlock* block : proc) {
         for (Value* value : *block) {
-            if (value->opcode() != Store)
+            if (value->opcode() != Set)
                 continue;
 
-            SlotBaseValue* slotBase = value->child(1)->as<SlotBaseValue>();
-            if (!slotBase)
-                continue;
-
-            StackSlot* stack = slotBase->slot();
+            Variable* variable = value->as<VariableValue>()->variable();
 
-            if (SSACalculator::Variable* variable = stackToVariable.get(stack))
-                ssa.newDef(variable, block, value->child(0));
+            if (SSACalculator::Variable* calcVar = variableToCalcVar[variable])
+                ssa.newDef(calcVar, block, value->child(0));
         }
     }
 
     // Decide where Phis are to be inserted. This creates them but does not insert them.
     ssa.computePhis(
-        [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Value* {
-            StackSlot* stack = variableToStack[variable->index()];
-            Value* phi = proc.add<Value>(Phi, stackVariable.get(stack), block->at(0)->origin());
+        [&] (SSACalculator::Variable* calcVar, BasicBlock* block) -> Value* {
+            Variable* variable = calcVarToVariable[calcVar->index()];
+            Value* phi = proc.add<Value>(Phi, variable->type(), block->at(0)->origin());
             if (verbose) {
                 dataLog(
-                    "Adding Phi for ", pointerDump(stack), " at ", *block, ": ",
+                    "Adding Phi for ", pointerDump(variable), " at ", *block, ": ",
                     deepDump(proc, phi), "\n");
             }
             return phi;
@@ -249,24 +179,24 @@ bool fixSSA(Procedure& proc)
 
     // Now perform the conversion.
     InsertionSet insertionSet(proc);
-    HashMap<StackSlot*, Value*> mapping;
+    IndexMap<Variable, Value*> mapping(proc.variables().size());
     for (BasicBlock* block : proc.blocksInPreOrder()) {
         mapping.clear();
 
-        for (auto& entry : stackToVariable) {
-            StackSlot* stack = entry.key;
-            SSACalculator::Variable* variable = entry.value;
+        for (unsigned index = calcVarToVariable.size(); index--;) {
+            Variable* variable = calcVarToVariable[index];
+            SSACalculator::Variable* calcVar = ssa.variable(index);
 
-            SSACalculator::Def* def = ssa.reachingDefAtHead(block, variable);
+            SSACalculator::Def* def = ssa.reachingDefAtHead(block, calcVar);
             if (def)
-                mapping.set(stack, def->value());
+                mapping[variable] = def->value();
         }
 
         for (SSACalculator::Def* phiDef : ssa.phisForBlock(block)) {
-            StackSlot* stack = variableToStack[phiDef->variable()->index()];
+            Variable* variable = calcVarToVariable[phiDef->variable()->index()];
 
             insertionSet.insertValue(0, phiDef->value());
-            mapping.set(stack, phiDef->value());
+            mapping[variable] = phiDef->value();
         }
 
         for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) {
@@ -274,23 +204,25 @@ bool fixSSA(Procedure& proc)
             value->performSubstitution();
 
             switch (value->opcode()) {
-            case Load: {
-                if (SlotBaseValue* slotBase = value->child(0)->as<SlotBaseValue>()) {
-                    StackSlot* stack = slotBase->slot();
-                    if (Value* replacement = mapping.get(stack))
-                        value->replaceWithIdentity(replacement);
+            case Get: {
+                VariableValue* variableValue = value->as<VariableValue>();
+                Variable* variable = variableValue->variable();
+
+                if (Value* replacement = mapping[variable])
+                    value->replaceWithIdentity(replacement);
+                else {
+                    value->replaceWithIdentity(
+                        insertionSet.insertBottom(valueIndex, value));
                 }
                 break;
             }
                 
-            case Store: {
-                if (SlotBaseValue* slotBase = value->child(1)->as<SlotBaseValue>()) {
-                    StackSlot* stack = slotBase->slot();
-                    if (stackToVariable.contains(stack)) {
-                        mapping.set(stack, value->child(0));
-                        value->replaceWithNop();
-                    }
-                }
+            case Set: {
+                VariableValue* variableValue = value->as<VariableValue>();
+                Variable* variable = variableValue->variable();
+
+                mapping[variable] = value->child(0);
+                value->replaceWithNop();
                 break;
             }
 
@@ -304,14 +236,14 @@ bool fixSSA(Procedure& proc)
         for (BasicBlock* successorBlock : block->successorBlocks()) {
             for (SSACalculator::Def* phiDef : ssa.phisForBlock(successorBlock)) {
                 Value* phi = phiDef->value();
-                SSACalculator::Variable* variable = phiDef->variable();
-                StackSlot* stack = variableToStack[variable->index()];
+                SSACalculator::Variable* calcVar = phiDef->variable();
+                Variable* variable = calcVarToVariable[calcVar->index()];
 
-                Value* mappedValue = mapping.get(stack);
+                Value* mappedValue = mapping[variable];
                 if (verbose) {
                     dataLog(
-                        "Mapped value for ", *stack, " with successor Phi ", *phi, " at end of ",
-                        *block, ": ", pointerDump(mappedValue), "\n");
+                        "Mapped value for ", *variable, " with successor Phi ", *phi,
+                        " at end of ", *block, ": ", pointerDump(mappedValue), "\n");
                 }
                 
                 if (!mappedValue)
index 1b412ae..45d5ffc 100644 (file)
@@ -36,8 +36,8 @@ namespace JSC { namespace B3 {
 
 class Procedure;
 
-// Turns all mentions of the given values into accesses to anonymous stack slots. This is meant
-// to be used from phases that don't like SSA for whatever reason.
+// Turns all mentions of the given values into accesses to variables. This is meant to be used
+// from phases that don't like SSA for whatever reason.
 void demoteValues(Procedure&, const IndexSet<Value>&);
 
 // This fixes SSA for you. Use this after you have done demoteValues() and you have performed
index 01b49f8..b0d1a4e 100644 (file)
@@ -49,6 +49,11 @@ public:
         m_vector.fill(Value(), size);
     }
 
+    void clear()
+    {
+        m_vector.fill(Value(), m_vector.size());
+    }
+
     size_t size() const { return m_vector.size(); }
 
     Value& operator[](size_t index)
@@ -71,11 +76,6 @@ public:
         return m_vector[key->index()];
     }
 
-    void clear()
-    {
-        m_vector.clear();
-    }
-    
 private:
     Vector<Value> m_vector;
 };
index e5c6ded..ab9b2b4 100644 (file)
@@ -69,6 +69,16 @@ public:
         return m_set.get(value->index());
     }
 
+    size_t size() const
+    {
+        return m_set.bitCount();
+    }
+
+    bool isEmpty() const
+    {
+        return !size();
+    }
+
     template<typename CollectionType>
     class Iterable {
     public:
index 51ffbc7..38b5b5d 100644 (file)
@@ -53,6 +53,8 @@
 #include "B3UpsilonValue.h"
 #include "B3UseCounts.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
+#include "B3VariableValue.h"
 #include <wtf/ListDump.h>
 
 #if COMPILER(GCC) && ASSERT_DISABLED
@@ -102,6 +104,8 @@ public:
 
         for (B3::StackSlot* stack : m_procedure.stackSlots())
             m_stackToStack.add(stack, m_code.addStackSlot(stack));
+        for (Variable* variable : m_procedure.variables())
+            m_variableToTmp.add(variable, m_code.newTmp(Arg::typeForB3Type(variable->type())));
 
         // Figure out which blocks are not rare.
         m_fastWorklist.push(m_procedure[0]);
@@ -2225,6 +2229,21 @@ private:
             return;
         }
 
+        case Set: {
+            Value* value = m_value->child(0);
+            append(
+                relaxedMoveForType(value->type()), immOrTmp(value),
+                m_variableToTmp.get(m_value->as<VariableValue>()->variable()));
+            return;
+        }
+
+        case Get: {
+            append(
+                relaxedMoveForType(m_value->type()),
+                m_variableToTmp.get(m_value->as<VariableValue>()->variable()), tmp(m_value));
+            return;
+        }
+
         case Branch: {
             m_insts.last().append(createBranch(m_value->child(0)));
             return;
@@ -2316,6 +2335,7 @@ private:
     IndexMap<Value, Tmp> m_phiToTmp; // Each Phi gets its own Tmp.
     IndexMap<B3::BasicBlock, Air::BasicBlock*> m_blockToBlock;
     HashMap<B3::StackSlot*, Air::StackSlot*> m_stackToStack;
+    HashMap<Variable*, Tmp> m_variableToTmp;
 
     UseCounts m_useCounts;
     PhiChildren m_phiChildren;
index 4ea7aa4..5c813e8 100644 (file)
@@ -75,10 +75,8 @@ private:
 
     // Use this form for Load (but not Load8Z, Load8S, or any of the Loads that have a suffix that
     // describes the returned type).
-    MemoryValue(
-        unsigned index, Opcode opcode, Type type, Origin origin, Value* pointer,
-        int32_t offset = 0)
-        : Value(index, CheckedOpcode, opcode, type, origin, pointer)
+    MemoryValue(Opcode opcode, Type type, Origin origin, Value* pointer, int32_t offset = 0)
+        : Value(CheckedOpcode, opcode, type, origin, pointer)
         , m_offset(offset)
         , m_range(HeapRange::top())
     {
@@ -104,16 +102,14 @@ private:
     }
 
     // Use this form for loads where the return type is implied.
-    MemoryValue(unsigned index, Opcode opcode, Origin origin, Value* pointer, int32_t offset = 0)
-        : MemoryValue(index, opcode, Int32, origin, pointer, offset)
+    MemoryValue(Opcode opcode, Origin origin, Value* pointer, int32_t offset = 0)
+        : MemoryValue(opcode, Int32, origin, pointer, offset)
     {
     }
 
     // Use this form for stores.
-    MemoryValue(
-        unsigned index, Opcode opcode, Origin origin, Value* value, Value* pointer,
-        int32_t offset = 0)
-        : Value(index, CheckedOpcode, opcode, Void, origin, value, pointer)
+    MemoryValue(Opcode opcode, Origin origin, Value* value, Value* pointer, int32_t offset = 0)
+        : Value(CheckedOpcode, opcode, Void, origin, value, pointer)
         , m_offset(offset)
         , m_range(HeapRange::top())
     {
index 3ad606a..7c925ad 100644 (file)
@@ -95,6 +95,12 @@ void printInternal(PrintStream& out, Opcode opcode)
     case ConstFloat:
         out.print("ConstFloat");
         return;
+    case Get:
+        out.print("Get");
+        return;
+    case Set:
+        out.print("Set");
+        return;
     case SlotBase:
         out.print("SlotBase");
         return;
index e605846..9d9bf18 100644 (file)
@@ -48,9 +48,14 @@ enum Opcode : int16_t {
     ConstDouble,
     ConstFloat,
 
-    // The magical stack slot. This is viewed as executing at the top of the program regardless of
-    // where in control flow you put it. Each instance of a StackSlot Value gets a disjoint range of
-    // stack memory. Use the StackSlotValue class.
+    // B3 supports non-SSA variables. These are accessed using Get and Set opcodes. Use the
+    // VariableValue class. It's a good idea to run fixSSA() to turn these into SSA. The
+    // optimizer will do that eventually, but if your input tends to use these opcodes, you
+    // should run fixSSA() directly before launching the optimizer.
+    Set,
+    Get,
+
+    // Gets the base address of a StackSlot.
     SlotBase,
 
     // The magical argument register. This is viewed as executing at the top of the program
index 1125a8d..b33c558 100644 (file)
@@ -49,8 +49,8 @@ Value* PatchpointValue::cloneImpl() const
     return new PatchpointValue(*this);
 }
 
-PatchpointValue::PatchpointValue(unsigned index, Type type, Origin origin)
-    : Base(index, CheckedOpcode, Patchpoint, type, origin)
+PatchpointValue::PatchpointValue(Type type, Origin origin)
+    : Base(CheckedOpcode, Patchpoint, type, origin)
     , effects(Effects::forCall())
     , resultConstraint(type == Void ? ValueRep::WarmAny : ValueRep::SomeRegister)
 {
index 99c9d52..2977fb5 100644 (file)
@@ -70,7 +70,7 @@ protected:
 private:
     friend class Procedure;
 
-    JS_EXPORT_PRIVATE PatchpointValue(unsigned index, Type, Origin);
+    JS_EXPORT_PRIVATE PatchpointValue(Type, Origin);
 };
 
 } } // namespace JSC::B3
index 7debaed..c32d11e 100644 (file)
@@ -38,6 +38,7 @@
 #include "B3OpaqueByproducts.h"
 #include "B3StackSlot.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
 
 namespace JSC { namespace B3 {
 
@@ -46,7 +47,6 @@ Procedure::Procedure()
     , m_lastPhaseName("initial")
     , m_byproducts(std::make_unique<OpaqueByproducts>())
     , m_code(new Air::Code(*this))
-    , m_valuesCollection(*this)
 {
 }
 
@@ -70,51 +70,22 @@ BasicBlock* Procedure::addBlock(double frequency)
     return result;
 }
 
-void Procedure::setBlockOrderImpl(Vector<BasicBlock*>& blocks)
+StackSlot* Procedure::addStackSlot(unsigned byteSize)
 {
-    IndexSet<BasicBlock> blocksSet;
-    blocksSet.addAll(blocks);
-
-    for (BasicBlock* block : *this) {
-        if (!blocksSet.contains(block))
-            blocks.append(block);
-    }
-
-    // Place blocks into this's block list by first leaking all of the blocks and then readopting
-    // them.
-    for (auto& entry : m_blocks)
-        entry.release();
-
-    m_blocks.resize(blocks.size());
-    for (unsigned i = 0; i < blocks.size(); ++i) {
-        BasicBlock* block = blocks[i];
-        block->m_index = i;
-        m_blocks[i] = std::unique_ptr<BasicBlock>(block);
-    }
-}
-
-StackSlot* Procedure::addStackSlot(unsigned byteSize, StackSlotKind kind)
-{
-    size_t index = addStackSlotIndex();
-    std::unique_ptr<StackSlot> slot(new StackSlot(index, byteSize, kind));
-    StackSlot* result = slot.get();
-    m_stackSlots[index] = WTFMove(slot);
-    return result;
+    return m_stackSlots.addNew(byteSize);
 }
 
-StackSlot* Procedure::addAnonymousStackSlot(Type type)
+Variable* Procedure::addVariable(Type type)
 {
-    return addStackSlot(sizeofType(type), StackSlotKind::Anonymous);
+    return m_variables.addNew(type); 
 }
 
 Value* Procedure::clone(Value* value)
 {
     std::unique_ptr<Value> clone(value->cloneImpl());
-    Value* result = clone.get();
-    clone->m_index = addValueIndex();
+    clone->m_index = UINT_MAX;
     clone->owner = nullptr;
-    m_values[clone->m_index] = WTFMove(clone);
-    return result;
+    return m_values.add(WTFMove(clone));
 }
 
 Value* Procedure::addIntConstant(Origin origin, Type type, int64_t value)
@@ -208,6 +179,11 @@ void Procedure::dump(PrintStream& out) const
         }
         dataLog("    ", deepDump(*this, value), "\n");
     }
+    if (variables().size()) {
+        out.print("Variables:\n");
+        for (Variable* variable : variables())
+            out.print("    ", deepDump(variable), "\n");
+    }
     if (stackSlots().size()) {
         out.print("Stack slots:\n");
         for (StackSlot* slot : stackSlots())
@@ -229,17 +205,17 @@ Vector<BasicBlock*> Procedure::blocksInPostOrder()
 
 void Procedure::deleteStackSlot(StackSlot* stackSlot)
 {
-    RELEASE_ASSERT(m_stackSlots[stackSlot->index()].get() == stackSlot);
-    RELEASE_ASSERT(!stackSlot->isLocked());
-    m_stackSlotIndexFreeList.append(stackSlot->index());
-    m_stackSlots[stackSlot->index()] = nullptr;
+    m_stackSlots.remove(stackSlot);
+}
+
+void Procedure::deleteVariable(Variable* variable)
+{
+    m_variables.remove(variable);
 }
 
 void Procedure::deleteValue(Value* value)
 {
-    RELEASE_ASSERT(m_values[value->index()].get() == value);
-    m_valueIndexFreeList.append(value->index());
-    m_values[value->index()] = nullptr;
+    m_values.remove(value);
 }
 
 void Procedure::deleteOrphans()
@@ -311,26 +287,32 @@ const RegisterAtOffsetList& Procedure::calleeSaveRegisters() const
     return code().calleeSaveRegisters();
 }
 
-size_t Procedure::addStackSlotIndex()
+Value* Procedure::addValueImpl(Value* value)
 {
-    if (m_stackSlotIndexFreeList.isEmpty()) {
-        size_t index = m_stackSlots.size();
-        m_stackSlots.append(nullptr);
-        return index;
-    }
-    
-    return m_stackSlotIndexFreeList.takeLast();
+    return m_values.add(std::unique_ptr<Value>(value));
 }
 
-size_t Procedure::addValueIndex()
+void Procedure::setBlockOrderImpl(Vector<BasicBlock*>& blocks)
 {
-    if (m_valueIndexFreeList.isEmpty()) {
-        size_t index = m_values.size();
-        m_values.append(nullptr);
-        return index;
+    IndexSet<BasicBlock> blocksSet;
+    blocksSet.addAll(blocks);
+
+    for (BasicBlock* block : *this) {
+        if (!blocksSet.contains(block))
+            blocks.append(block);
+    }
+
+    // Place blocks into this's block list by first leaking all of the blocks and then readopting
+    // them.
+    for (auto& entry : m_blocks)
+        entry.release();
+
+    m_blocks.resize(blocks.size());
+    for (unsigned i = 0; i < blocks.size(); ++i) {
+        BasicBlock* block = blocks[i];
+        block->m_index = i;
+        m_blocks[i] = std::unique_ptr<BasicBlock>(block);
     }
-    
-    return m_valueIndexFreeList.takeLast();
 }
 
 } } // namespace JSC::B3
index b622842..7bd7f8d 100644 (file)
@@ -31,7 +31,7 @@
 #include "B3OpaqueByproducts.h"
 #include "B3Origin.h"
 #include "B3PCToOriginMap.h"
-#include "B3StackSlotKind.h"
+#include "B3SparseCollection.h"
 #include "B3Type.h"
 #include "B3ValueKey.h"
 #include "PureNaN.h"
@@ -53,6 +53,7 @@ class CFG;
 class Dominators;
 class StackSlot;
 class Value;
+class Variable;
 
 namespace Air { class Code; }
 
@@ -94,8 +95,8 @@ public:
         setBlockOrderImpl(blocks);
     }
 
-    JS_EXPORT_PRIVATE StackSlot* addStackSlot(unsigned byteSize, StackSlotKind);
-    JS_EXPORT_PRIVATE StackSlot* addAnonymousStackSlot(Type);
+    JS_EXPORT_PRIVATE StackSlot* addStackSlot(unsigned byteSize);
+    JS_EXPORT_PRIVATE Variable* addVariable(Type);
     
     template<typename ValueType, typename... Arguments>
     ValueType* add(Arguments...);
@@ -178,143 +179,22 @@ public:
     Vector<BasicBlock*> blocksInPreOrder();
     Vector<BasicBlock*> blocksInPostOrder();
 
-    class StackSlotsCollection {
-    public:
-        StackSlotsCollection(const Procedure& proc)
-            : m_proc(proc)
-        {
-        }
-
-        unsigned size() const { return m_proc.m_stackSlots.size(); }
-        StackSlot* at(unsigned index) const { return m_proc.m_stackSlots[index].get(); }
-        StackSlot* operator[](unsigned index) const { return at(index); }
-
-        class iterator {
-        public:
-            iterator()
-                : m_collection(nullptr)
-                , m_index(0)
-            {
-            }
-
-            iterator(const StackSlotsCollection& collection, unsigned index)
-                : m_collection(&collection)
-                , m_index(findNext(index))
-            {
-            }
-
-            StackSlot* operator*()
-            {
-                return m_collection->at(m_index);
-            }
-
-            iterator& operator++()
-            {
-                m_index = findNext(m_index + 1);
-                return *this;
-            }
-
-            bool operator==(const iterator& other) const
-            {
-                return m_index == other.m_index;
-            }
-
-            bool operator!=(const iterator& other) const
-            {
-                return !(*this == other);
-            }
-
-        private:
-            unsigned findNext(unsigned index)
-            {
-                while (index < m_collection->size() && !m_collection->at(index))
-                    index++;
-                return index;
-            }
-            
-            const StackSlotsCollection* m_collection;
-            unsigned m_index;
-        };
-
-        iterator begin() const { return iterator(*this, 0); }
-        iterator end() const { return iterator(*this, size()); }
-
-    private:
-        const Procedure& m_proc;
-    };
-
-    StackSlotsCollection stackSlots() const { return StackSlotsCollection(*this); }
+    SparseCollection<StackSlot>& stackSlots() { return m_stackSlots; }
+    const SparseCollection<StackSlot>& stackSlots() const { return m_stackSlots; }
 
+    // Short for stackSlots().remove(). It's better to call this method since it's out of line.
     void deleteStackSlot(StackSlot*);
-    
-    class ValuesCollection {
-    public:
-        ValuesCollection(const Procedure& procedure)
-            : m_procedure(procedure)
-        {
-        }
 
-        class iterator {
-        public:
-            iterator()
-                : m_procedure(nullptr)
-                , m_index(0)
-            {
-            }
-
-            iterator(const Procedure& procedure, unsigned index)
-                : m_procedure(&procedure)
-                , m_index(findNext(index))
-            {
-            }
-
-            Value* operator*() const
-            {
-                return m_procedure->m_values[m_index].get();
-            }
-
-            iterator& operator++()
-            {
-                m_index = findNext(m_index + 1);
-                return *this;
-            }
-
-            bool operator==(const iterator& other) const
-            {
-                ASSERT(m_procedure == other.m_procedure);
-                return m_index == other.m_index;
-            }
-
-            bool operator!=(const iterator& other) const
-            {
-                return !(*this == other);
-            }
-
-        private:
-            unsigned findNext(unsigned index)
-            {
-                while (index < m_procedure->m_values.size() && !m_procedure->m_values[index])
-                    index++;
-                return index;
-            }
-
-            const Procedure* m_procedure;
-            unsigned m_index;
-        };
-
-        iterator begin() const { return iterator(m_procedure, 0); }
-        iterator end() const { return iterator(m_procedure, m_procedure.m_values.size()); }
-
-        unsigned size() const { return m_procedure.m_values.size(); }
-        Value* at(unsigned index) const { return m_procedure.m_values[index].get(); }
-        Value* operator[](unsigned index) const { return at(index); }
-        
-    private:
-        const Procedure& m_procedure;
-    };
+    SparseCollection<Variable>& variables() { return m_variables; }
+    const SparseCollection<Variable>& variables() const { return m_variables; }
 
-    const ValuesCollection& values() const { return m_valuesCollection; }
+    // Short for variables().remove(). It's better to call this method since it's out of line.
+    void deleteVariable(Variable*);
 
+    SparseCollection<Value>& values() { return m_values; }
+    const SparseCollection<Value>& values() const { return m_values; }
+
+    // Short for values().remove(). It's better to call this method since it's out of line.
     void deleteValue(Value*);
 
     // A valid procedure cannot contain any orphan values. An orphan is a value that is not in
@@ -367,17 +247,14 @@ public:
 
 private:
     friend class BlockInsertionSet;
-    
-    void setBlockOrderImpl(Vector<BasicBlock*>&);
 
-    size_t addStackSlotIndex();
-    JS_EXPORT_PRIVATE size_t addValueIndex();
+    JS_EXPORT_PRIVATE Value* addValueImpl(Value*);
+    void setBlockOrderImpl(Vector<BasicBlock*>&);
 
-    Vector<std::unique_ptr<StackSlot>> m_stackSlots;
+    SparseCollection<StackSlot> m_stackSlots;
+    SparseCollection<Variable> m_variables;
     Vector<std::unique_ptr<BasicBlock>> m_blocks;
-    Vector<std::unique_ptr<Value>> m_values;
-    Vector<size_t> m_stackSlotIndexFreeList;
-    Vector<size_t> m_valueIndexFreeList;
+    SparseCollection<Value> m_values;
     std::unique_ptr<CFG> m_cfg;
     std::unique_ptr<Dominators> m_dominators;
     HashSet<ValueKey> m_fastConstants;
@@ -386,7 +263,6 @@ private:
     std::unique_ptr<Air::Code> m_code;
     RefPtr<SharedTask<void(PrintStream&, Origin)>> m_originPrinter;
     const void* m_frontendData;
-    ValuesCollection m_valuesCollection;
     PCToOriginMap m_pcToOriginMap;
 };
 
index 2ea5c30..8a57462 100644 (file)
@@ -36,11 +36,7 @@ namespace JSC { namespace B3 {
 template<typename ValueType, typename... Arguments>
 ValueType* Procedure::add(Arguments... arguments)
 {
-    size_t index = addValueIndex();
-    std::unique_ptr<ValueType> value(new ValueType(index, arguments...));
-    ValueType* result = value.get();
-    m_values[index] = WTFMove(value);
-    return result;
+    return static_cast<ValueType*>(addValueImpl(new ValueType(arguments...)));
 }
 
 } } // namespace JSC::B3
index ad9223f..83cb21f 100644 (file)
@@ -45,6 +45,8 @@
 #include "B3UpsilonValue.h"
 #include "B3ValueKeyInlines.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
+#include "B3VariableValue.h"
 #include <wtf/GraphNodeWorklist.h>
 #include <wtf/HashMap.h>
 
@@ -2283,13 +2285,15 @@ private:
         Vector<UpsilonValue*, 64> upsilons;
         for (BasicBlock* block : m_proc) {
             for (Value* value : *block) {
-                Effects effects = value->effects();
-                // We don't care about SSA Effects, since we model them more accurately than the
-                // effects() method does.
-                effects.writesSSAState = false;
-                effects.readsSSAState = false;
+                Effects effects;
+                // We don't care about effects of SSA operations, since we model them more
+                // accurately than the effects() method does.
+                if (value->opcode() != Phi && value->opcode() != Upsilon)
+                    effects = value->effects();
+                
                 if (effects.mustExecute())
                     worklist.push(value);
+                
                 if (UpsilonValue* upsilon = value->as<UpsilonValue>())
                     upsilons.append(upsilon);
             }
@@ -2314,7 +2318,7 @@ private:
                 break;
         }
 
-        IndexSet<StackSlot> liveStackSlots;
+        IndexSet<Variable> liveVariables;
         
         for (BasicBlock* block : m_proc) {
             size_t sourceIndex = 0;
@@ -2322,8 +2326,8 @@ private:
             while (sourceIndex < block->size()) {
                 Value* value = block->at(sourceIndex++);
                 if (worklist.saw(value)) {
-                    if (SlotBaseValue* slotBase = value->as<SlotBaseValue>())
-                        liveStackSlots.add(slotBase->slot());
+                    if (VariableValue* variableValue = value->as<VariableValue>())
+                        liveVariables.add(variableValue->variable());
                     block->at(targetIndex++) = value;
                 } else {
                     m_proc.deleteValue(value);
@@ -2338,10 +2342,9 @@ private:
             block->values().resize(targetIndex);
         }
 
-        for (StackSlot* slot : m_proc.stackSlots()) {
-            if (slot->isLocked() || liveStackSlots.contains(slot))
-                continue;
-            m_proc.deleteStackSlot(slot);
+        for (Variable* variable : m_proc.variables()) {
+            if (!liveVariables.contains(variable))
+                m_proc.deleteVariable(variable);
         }
     }
 
index e05f6a6..aaab1b3 100644 (file)
@@ -50,8 +50,8 @@ protected:
 private:
     friend class Procedure;
 
-    SlotBaseValue(unsigned index, Origin origin, StackSlot* slot)
-        : Value(index, CheckedOpcode, SlotBase, pointerType(), origin)
+    SlotBaseValue(Origin origin, StackSlot* slot)
+        : Value(CheckedOpcode, SlotBase, pointerType(), origin)
         , m_slot(slot)
     {
     }
diff --git a/Source/JavaScriptCore/b3/B3SparseCollection.h b/Source/JavaScriptCore/b3/B3SparseCollection.h
new file mode 100644 (file)
index 0000000..d027a48
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef B3SparseCollection_h
+#define B3SparseCollection_h
+
+#if ENABLE(B3_JIT)
+
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+namespace JSC { namespace B3 {
+
+// B3::Procedure and Air::Code have a lot of collections of indexed things. This has all of the
+// logic.
+
+template<typename T>
+class SparseCollection {
+    typedef Vector<std::unique_ptr<T>> VectorType;
+    
+public:
+    SparseCollection()
+    {
+    }
+
+    T* add(std::unique_ptr<T> value)
+    {
+        T* result = value.get();
+        
+        size_t index;
+        if (m_indexFreeList.isEmpty()) {
+            index = m_vector.size();
+            m_vector.append(nullptr);
+        } else
+            index = m_indexFreeList.takeLast();
+
+        value->m_index = index;
+
+        m_vector[index] = WTFMove(value);
+
+        return result;
+    }
+
+    template<typename... Arguments>
+    T* addNew(Arguments&&... arguments)
+    {
+        return add(std::unique_ptr<T>(new T(std::forward<Arguments>(arguments)...)));
+    }
+
+    void remove(T* value)
+    {
+        RELEASE_ASSERT(m_vector[value->m_index].get() == value);
+        m_indexFreeList.append(value->m_index);
+        m_vector[value->m_index] = nullptr;
+    }
+
+    unsigned size() const { return m_vector.size(); }
+    bool isEmpty() const { return m_vector.isEmpty(); }
+    
+    T* at(unsigned index) const { return m_vector[index].get(); }
+    T* operator[](unsigned index) const { return at(index); }
+
+    class iterator {
+    public:
+        iterator()
+            : m_collection(nullptr)
+            , m_index(0)
+        {
+        }
+
+        iterator(const SparseCollection& collection, unsigned index)
+            : m_collection(&collection)
+            , m_index(findNext(index))
+        {
+        }
+
+        T* operator*()
+        {
+            return m_collection->at(m_index);
+        }
+
+        iterator& operator++()
+        {
+            m_index = findNext(m_index + 1);
+            return *this;
+        }
+
+        bool operator==(const iterator& other) const
+        {
+            ASSERT(m_collection == other.m_collection);
+            return m_index == other.m_index;
+        }
+
+        bool operator!=(const iterator& other) const
+        {
+            return !(*this == other);
+        }
+
+    private:
+        unsigned findNext(unsigned index)
+        {
+            while (index < m_collection->size() && !m_collection->at(index))
+                index++;
+            return index;
+        }
+
+        const SparseCollection* m_collection;
+        unsigned m_index;
+    };
+
+    iterator begin() const { return iterator(*this, 0); }
+    iterator end() const { return iterator(*this, size()); }
+
+private:
+    Vector<std::unique_ptr<T>> m_vector;
+    Vector<size_t> m_indexFreeList;
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
+#endif // B3SparseCollection_h
+
index 8efd963..4e22014 100644 (file)
@@ -41,13 +41,11 @@ void StackSlot::dump(PrintStream& out) const
 
 void StackSlot::deepDump(PrintStream& out) const
 {
-    out.print("byteSize = ", m_byteSize, ", offsetFromFP = ", m_offsetFromFP, ", kind = ", m_kind);
+    out.print("byteSize = ", m_byteSize, ", offsetFromFP = ", m_offsetFromFP);
 }
 
-StackSlot::StackSlot(unsigned index, unsigned byteSize, StackSlotKind kind)
-    : m_index(index)
-    , m_byteSize(byteSize)
-    , m_kind(kind)
+StackSlot::StackSlot(unsigned byteSize)
+    : m_byteSize(byteSize)
 {
 }
 
index 928dfca..60436ce 100644 (file)
@@ -28,7 +28,7 @@
 
 #if ENABLE(B3_JIT)
 
-#include "B3StackSlotKind.h"
+#include "B3SparseCollection.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/PrintStream.h>
@@ -49,8 +49,6 @@ public:
     ~StackSlot();
 
     unsigned byteSize() const { return m_byteSize; }
-    StackSlotKind kind() const { return m_kind; }
-    bool isLocked() const { return m_kind == StackSlotKind::Locked; }
     unsigned index() const { return m_index; }
 
     // This gets assigned at the end of compilation. But, you can totally pin stack slots. Use the
@@ -63,18 +61,18 @@ public:
         m_offsetFromFP = value;
     }
 
-    void dump(PrintStream& out) const;
+    void dump(PrintStream&) const;
     void deepDump(PrintStream&) const;
 
 private:
     friend class Air::StackSlot;
     friend class Procedure;
+    friend class SparseCollection<StackSlot>;
 
-    StackSlot(unsigned index, unsigned byteSize, StackSlotKind);
+    StackSlot(unsigned byteSize);
 
-    unsigned m_index;
-    unsigned m_byteSize;
-    StackSlotKind m_kind;
+    unsigned m_index { UINT_MAX };
+    unsigned m_byteSize { 0 };
     intptr_t m_offsetFromFP { 0 };
 };
 
index 746a17e..b822ea9 100644 (file)
@@ -83,8 +83,8 @@ void StackmapValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
         ", lateClobbered = ", m_lateClobbered, ", usedRegisters = ", m_usedRegisters);
 }
 
-StackmapValue::StackmapValue(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin)
-    : Value(index, CheckedOpcode, opcode, type, origin)
+StackmapValue::StackmapValue(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin)
+    : Value(CheckedOpcode, opcode, type, origin)
 {
     ASSERT(accepts(opcode));
 }
index c8c84b2..da59581 100644 (file)
@@ -284,7 +284,7 @@ protected:
     void dumpChildren(CommaPrinter&, PrintStream&) const override;
     void dumpMeta(CommaPrinter&, PrintStream&) const override;
 
-    StackmapValue(unsigned index, CheckedOpcodeTag, Opcode, Type, Origin);
+    StackmapValue(CheckedOpcodeTag, Opcode, Type, Origin);
 
 private:
     friend class CheckSpecial;
index 5e09d04..45a2437 100644 (file)
@@ -68,9 +68,8 @@ Value* SwitchValue::cloneImpl() const
     return new SwitchValue(*this);
 }
 
-SwitchValue::SwitchValue(
-    unsigned index, Origin origin, Value* child, const FrequentedBlock& fallThrough)
-    : ControlValue(index, Switch, Void, origin, child)
+SwitchValue::SwitchValue(Origin origin, Value* child, const FrequentedBlock& fallThrough)
+    : ControlValue(Switch, Void, origin, child)
 {
     m_successors.append(fallThrough);
 }
index d0d5e1f..31268fa 100644 (file)
@@ -121,8 +121,7 @@ protected:
 private:
     friend class Procedure;
 
-    JS_EXPORT_PRIVATE SwitchValue(
-        unsigned index, Origin, Value* child, const FrequentedBlock& fallThrough);
+    JS_EXPORT_PRIVATE SwitchValue(Origin, Value* child, const FrequentedBlock& fallThrough);
 
     Vector<int64_t> m_values;
 };
index 0fbbbf7..4ab74a6 100644 (file)
@@ -57,8 +57,8 @@ private:
     // Note that passing the Phi during construction is optional. A valid pattern is to first create
     // the Upsilons without the Phi, then create the Phi, then go back and tell the Upsilons about
     // the Phi. This allows you to emit code in its natural order.
-    UpsilonValue(unsigned index, Origin origin, Value* value, Value* phi = nullptr)
-        : Value(index, CheckedOpcode, Upsilon, Void, origin, value)
+    UpsilonValue(Origin origin, Value* value, Value* phi = nullptr)
+        : Value(CheckedOpcode, Upsilon, Void, origin, value)
         , m_phi(phi)
     {
         if (phi)
index 6fb06b2..47a494d 100644 (file)
@@ -37,6 +37,8 @@
 #include "B3StackSlot.h"
 #include "B3UpsilonValue.h"
 #include "B3ValueInlines.h"
+#include "B3Variable.h"
+#include "B3VariableValue.h"
 #include <wtf/HashSet.h>
 #include <wtf/StringPrintStream.h>
 #include <wtf/text/CString.h>
@@ -153,6 +155,14 @@ public:
                 VALIDATE(!value->numChildren(), ("At ", *value));
                 VALIDATE(value->type() == Float, ("At ", *value));
                 break;
+            case Set:
+                VALIDATE(value->numChildren() == 1, ("At ", *value));
+                VALIDATE(value->child(0)->type() == value->as<VariableValue>()->variable()->type(), ("At ", *value));
+                break;
+            case Get:
+                VALIDATE(!value->numChildren(), ("At ", *value));
+                VALIDATE(value->type() == value->as<VariableValue>()->variable()->type(), ("At ", *value));
+                break;
             case SlotBase:
             case FramePointer:
                 VALIDATE(!value->numChildren(), ("At ", *value));
@@ -375,6 +385,9 @@ public:
 
             VALIDATE(!(value->effects().writes && value->key()), ("At ", *value));
         }
+
+        for (Variable* variable : m_procedure.variables())
+            VALIDATE(variable->type() != Void, ("At ", *variable));
     }
 
 private:
index fe6c560..1c6d9bc 100644 (file)
@@ -39,6 +39,7 @@
 #include "B3UpsilonValue.h"
 #include "B3ValueInlines.h"
 #include "B3ValueKeyInlines.h"
+#include "B3VariableValue.h"
 #include <wtf/CommaPrinter.h>
 #include <wtf/StringPrintStream.h>
 
@@ -72,9 +73,10 @@ void Value::replaceWithIdentity(Value* value)
 
     this->Value::~Value();
 
-    new (this) Value(index, Identity, type, origin, value);
+    new (this) Value(Identity, type, origin, value);
 
     this->owner = owner;
+    this->m_index = index;
 }
 
 void Value::replaceWithNop()
@@ -85,9 +87,10 @@ void Value::replaceWithNop()
 
     this->Value::~Value();
 
-    new (this) Value(index, Nop, Void, origin);
+    new (this) Value(Nop, Void, origin);
 
     this->owner = owner;
+    this->m_index = index;
 }
 
 void Value::replaceWithPhi()
@@ -104,9 +107,10 @@ void Value::replaceWithPhi()
 
     this->Value::~Value();
 
-    new (this) Value(index, Phi, type, origin);
+    new (this) Value(Phi, type, origin);
 
     this->owner = owner;
+    this->m_index = index;
 }
 
 void Value::dump(PrintStream& out) const
@@ -484,10 +488,12 @@ Effects Value::effects() const
         result.reads = HeapRange::top();
         break;
     case Upsilon:
-        result.writesSSAState = true;
+    case Set:
+        result.writesLocalState = true;
         break;
     case Phi:
-        result.readsSSAState = true;
+    case Get:
+        result.readsLocalState = true;
         break;
     case Jump:
     case Branch:
@@ -598,6 +604,7 @@ void Value::checkOpcode(Opcode opcode)
     ASSERT(!PatchpointValue::accepts(opcode));
     ASSERT(!SlotBaseValue::accepts(opcode));
     ASSERT(!UpsilonValue::accepts(opcode));
+    ASSERT(!VariableValue::accepts(opcode));
 }
 #endif // !ASSERT_DISABLED
 
index d3a6ec5..cbbf63c 100644 (file)
@@ -32,6 +32,7 @@
 #include "B3Effects.h"
 #include "B3Opcode.h"
 #include "B3Origin.h"
+#include "B3SparseCollection.h"
 #include "B3Type.h"
 #include "B3ValueKey.h"
 #include <wtf/CommaPrinter.h>
@@ -226,6 +227,7 @@ protected:
 
 private:
     friend class Procedure;
+    friend class SparseCollection<Value>;
 
     // Checks that this opcode is valid for use with B3::Value.
 #if ASSERT_DISABLED
@@ -243,27 +245,24 @@ protected:
     // Instantiate values via Procedure.
     // This form requires specifying the type explicitly:
     template<typename... Arguments>
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, Value* firstChild, Arguments... arguments)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, Value* firstChild, Arguments... arguments)
+        : m_opcode(opcode)
         , m_type(type)
         , m_origin(origin)
         , m_children{ firstChild, arguments... }
     {
     }
     // This form is for specifying the type explicitly when the opcode has no children:
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin)
+        : m_opcode(opcode)
         , m_type(type)
         , m_origin(origin)
     {
     }
     // This form is for those opcodes that can infer their type from the opcode and first child:
     template<typename... Arguments>
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild)
+        : m_opcode(opcode)
         , m_type(typeFor(opcode, firstChild))
         , m_origin(origin)
         , m_children{ firstChild }
@@ -271,9 +270,8 @@ protected:
     }
     // This form is for those opcodes that can infer their type from the opcode and first and second child:
     template<typename... Arguments>
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild, Value* secondChild, Arguments... arguments)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild, Value* secondChild, Arguments... arguments)
+        : m_opcode(opcode)
         , m_type(typeFor(opcode, firstChild, secondChild))
         , m_origin(origin)
         , m_children{ firstChild, secondChild, arguments... }
@@ -281,25 +279,22 @@ protected:
     }
     // This form is for those opcodes that can infer their type from the opcode alone, and that don't
     // take any arguments:
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Origin origin)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin)
+        : m_opcode(opcode)
         , m_type(typeFor(opcode, nullptr))
         , m_origin(origin)
     {
     }
     // Use this form for varargs.
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, const AdjacencyList& children)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, const AdjacencyList& children)
+        : m_opcode(opcode)
         , m_type(type)
         , m_origin(origin)
         , m_children(children)
     {
     }
-    explicit Value(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, AdjacencyList&& children)
-        : m_index(index)
-        , m_opcode(opcode)
+    explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, AdjacencyList&& children)
+        : m_opcode(opcode)
         , m_type(type)
         , m_origin(origin)
         , m_children(WTFMove(children))
@@ -309,8 +304,8 @@ protected:
     // This is the constructor you end up actually calling, if you're instantiating Value
     // directly.
     template<typename... Arguments>
-    explicit Value(unsigned index, Opcode opcode, Arguments&&... arguments)
-        : Value(index, CheckedOpcode, opcode, std::forward<Arguments>(arguments)...)
+    explicit Value(Opcode opcode, Arguments&&... arguments)
+        : Value(CheckedOpcode, opcode, std::forward<Arguments>(arguments)...)
     {
         checkOpcode(opcode);
     }
@@ -321,7 +316,9 @@ private:
     static Type typeFor(Opcode, Value* firstChild, Value* secondChild = nullptr);
 
     // This group of fields is arranged to fit in 64 bits.
-    unsigned m_index;
+protected:
+    unsigned m_index { UINT_MAX };
+private:
     Opcode m_opcode;
     Type m_type;
     
diff --git a/Source/JavaScriptCore/b3/B3Variable.cpp b/Source/JavaScriptCore/b3/B3Variable.cpp
new file mode 100644 (file)
index 0000000..2314ee2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "B3Variable.h"
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+Variable::~Variable()
+{
+}
+
+void Variable::dump(PrintStream& out) const
+{
+    out.print("var", m_index);
+}
+
+void Variable::deepDump(PrintStream& out) const
+{
+    out.print(m_type, " var", m_index);
+}
+
+Variable::Variable(Type type)
+    : m_type(type)
+{
+    ASSERT(type != Void);
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
diff --git a/Source/JavaScriptCore/b3/B3Variable.h b/Source/JavaScriptCore/b3/B3Variable.h
new file mode 100644 (file)
index 0000000..e9c9965
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef B3Variable_h
+#define B3Variable_h
+
+#if ENABLE(B3_JIT)
+
+#include "B3SparseCollection.h"
+#include "B3Type.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PrintStream.h>
+
+namespace JSC { namespace B3 {
+
+class Procedure;
+
+class Variable {
+    WTF_MAKE_NONCOPYABLE(Variable);
+    WTF_MAKE_FAST_ALLOCATED;
+
+public:
+    ~Variable();
+
+    Type type() const { return m_type; }
+    unsigned index() const { return m_index; }
+
+    void dump(PrintStream&) const;
+    void deepDump(PrintStream&) const;
+
+private:
+    friend class Procedure;
+    friend class SparseCollection<Variable>;
+
+    Variable(Type);
+    
+    unsigned m_index;
+    Type m_type;
+};
+
+class DeepVariableDump {
+public:
+    DeepVariableDump(const Variable* variable)
+        : m_variable(variable)
+    {
+    }
+
+    void dump(PrintStream& out) const
+    {
+        if (m_variable)
+            m_variable->deepDump(out);
+        else
+            out.print("<null>");
+    }
+
+private:
+    const Variable* m_variable;
+};
+
+inline DeepVariableDump deepDump(const Variable* variable)
+{
+    return DeepVariableDump(variable);
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
+#endif // B3Variable_h
+
diff --git a/Source/JavaScriptCore/b3/B3VariableValue.cpp b/Source/JavaScriptCore/b3/B3VariableValue.cpp
new file mode 100644 (file)
index 0000000..01d541c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "B3VariableValue.h"
+
+#if ENABLE(B3_JIT)
+
+#include "B3Variable.h"
+
+namespace JSC { namespace B3 {
+
+VariableValue::~VariableValue()
+{
+}
+
+void VariableValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
+{
+    out.print(comma, pointerDump(m_variable));
+}
+
+Value* VariableValue::cloneImpl() const
+{
+    return new VariableValue(*this);
+}
+
+VariableValue::VariableValue(Opcode opcode, Origin origin, Variable* variable, Value* value)
+    : Value(CheckedOpcode, opcode, Void, origin, value)
+    , m_variable(variable)
+{
+    ASSERT(opcode == Set);
+}
+
+VariableValue::VariableValue(Opcode opcode, Origin origin, Variable* variable)
+    : Value(CheckedOpcode, opcode, variable->type(), origin)
+    , m_variable(variable)
+{
+    ASSERT(opcode == Get);
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
diff --git a/Source/JavaScriptCore/b3/B3VariableValue.h b/Source/JavaScriptCore/b3/B3VariableValue.h
new file mode 100644 (file)
index 0000000..395925b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef B3VariableValue_h
+#define B3VariableValue_h
+
+#if ENABLE(B3_JIT)
+
+#include "B3Value.h"
+
+namespace JSC { namespace B3 {
+
+class Variable;
+
+class JS_EXPORT_PRIVATE VariableValue : public Value {
+public:
+    static bool accepts(Opcode opcode) { return opcode == Get || opcode == Set; }
+
+    ~VariableValue();
+
+    Variable* variable() const { return m_variable; }
+
+protected:
+    void dumpMeta(CommaPrinter&, PrintStream&) const override;
+
+    Value* cloneImpl() const override;
+
+private:
+    friend class Procedure;
+
+    // Use this for Set.
+    VariableValue(Opcode, Origin, Variable*, Value*);
+
+    // Use this for Get.
+    VariableValue(Opcode, Origin, Variable*);
+
+    Variable* m_variable;
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
+#endif // B3VariableValue_h
+
index 37fa26c..de73fd0 100644 (file)
@@ -95,30 +95,13 @@ void allocateStack(Code& code)
 {
     PhaseScope phaseScope(code, "allocateStack");
 
-    // Perform an escape analysis over stack slots. An escaping stack slot is one that is locked or
-    // is explicitly escaped in the code.
-    IndexSet<StackSlot> escapingStackSlots;
-    for (StackSlot* slot : code.stackSlots()) {
-        if (slot->isLocked())
-            escapingStackSlots.add(slot);
-    }
-    for (BasicBlock* block : code) {
-        for (Inst& inst : *block) {
-            inst.forEachArg(
-                [&] (Arg& arg, Arg::Role role, Arg::Type, Arg::Width) {
-                    if (role == Arg::UseAddr && arg.isStack())
-                        escapingStackSlots.add(arg.stackSlot());
-                });
-        }
-    }
-
     // Allocate all of the escaped slots in order. This is kind of a crazy algorithm to allow for
     // the possibility of stack slots being assigned frame offsets before we even get here.
     ASSERT(!code.frameSize());
     Vector<StackSlot*> assignedEscapedStackSlots;
     Vector<StackSlot*> escapedStackSlotsWorklist;
     for (StackSlot* slot : code.stackSlots()) {
-        if (escapingStackSlots.contains(slot)) {
+        if (slot->isLocked()) {
             if (slot->offsetFromFP())
                 assignedEscapedStackSlots.append(slot);
             else
@@ -136,7 +119,7 @@ void allocateStack(Code& code)
         assignedEscapedStackSlots.append(slot);
     }
 
-    // Now we handle the anonymous slots.
+    // Now we handle the spill slots.
     StackSlotLiveness liveness(code);
     IndexMap<StackSlot, HashSet<StackSlot*>> interference(code.stackSlots().size());
     Vector<StackSlot*> slots;
@@ -154,7 +137,7 @@ void allocateStack(Code& code)
                     if (!arg.isStack())
                         return;
                     StackSlot* slot = arg.stackSlot();
-                    if (slot->kind() != StackSlotKind::Anonymous)
+                    if (slot->kind() != StackSlotKind::Spill)
                         return;
 
                     for (StackSlot* otherSlot : localCalc.live()) {
@@ -187,7 +170,7 @@ void allocateStack(Code& code)
                             return;
                         }
                         StackSlot* slot = arg.stackSlot();
-                        if (slot->kind() != StackSlotKind::Anonymous) {
+                        if (slot->kind() != StackSlotKind::Spill) {
                             ok = false;
                             return;
                         }
@@ -287,7 +270,7 @@ void allocateStack(Code& code)
                     case Arg::Stack: {
                         StackSlot* slot = arg.stackSlot();
                         if (Arg::isZDef(role)
-                            && slot->kind() == StackSlotKind::Anonymous
+                            && slot->kind() == StackSlotKind::Spill
                             && slot->byteSize() > Arg::bytes(width)) {
                             // Currently we only handle this simple case because it's the only one
                             // that arises: ZDef's are only 32-bit right now. So, when we hit these
index 9d8b834..6664186 100644 (file)
@@ -54,23 +54,18 @@ BasicBlock* Code::addBlock(double frequency)
 
 StackSlot* Code::addStackSlot(unsigned byteSize, StackSlotKind kind, B3::StackSlot* b3Slot)
 {
-    std::unique_ptr<StackSlot> slot(new StackSlot(byteSize, m_stackSlots.size(), kind, b3Slot));
-    StackSlot* result = slot.get();
-    m_stackSlots.append(WTFMove(slot));
-    return result;
+    return m_stackSlots.addNew(byteSize, kind, b3Slot);
 }
 
 StackSlot* Code::addStackSlot(B3::StackSlot* b3Slot)
 {
-    return addStackSlot(b3Slot->byteSize(), b3Slot->kind(), b3Slot);
+    return addStackSlot(b3Slot->byteSize(), StackSlotKind::Locked, b3Slot);
 }
 
 Special* Code::addSpecial(std::unique_ptr<Special> special)
 {
-    Special* result = special.get();
-    result->m_code = this;
-    m_specials.append(WTFMove(special));
-    return result;
+    special->m_code = this;
+    return m_specials.add(WTFMove(special));
 }
 
 CCallSpecial* Code::cCallSpecial()
index 82ee42c..a647add 100644 (file)
@@ -33,6 +33,7 @@
 #include "AirSpecial.h"
 #include "AirStackSlot.h"
 #include "AirTmp.h"
+#include "B3SparseCollection.h"
 #include "RegisterAtOffsetList.h"
 #include "StackAlignment.h"
 
@@ -184,125 +185,11 @@ public:
     iterator begin() const { return iterator(*this, 0); }
     iterator end() const { return iterator(*this, size()); }
 
-    class StackSlotsCollection {
-    public:
-        StackSlotsCollection(const Code& code)
-            : m_code(code)
-        {
-        }
-
-        unsigned size() const { return m_code.m_stackSlots.size(); }
-        StackSlot* at(unsigned index) const { return m_code.m_stackSlots[index].get(); }
-        StackSlot* operator[](unsigned index) const { return at(index); }
-
-        class iterator {
-        public:
-            iterator()
-                : m_collection(nullptr)
-                , m_index(0)
-            {
-            }
-
-            iterator(const StackSlotsCollection& collection, unsigned index)
-                : m_collection(&collection)
-                , m_index(index)
-            {
-            }
-
-            StackSlot* operator*()
-            {
-                return m_collection->at(m_index);
-            }
-
-            iterator& operator++()
-            {
-                m_index++;
-                return *this;
-            }
-
-            bool operator==(const iterator& other) const
-            {
-                return m_index == other.m_index;
-            }
-
-            bool operator!=(const iterator& other) const
-            {
-                return !(*this == other);
-            }
-
-        private:
-            const StackSlotsCollection* m_collection;
-            unsigned m_index;
-        };
-
-        iterator begin() const { return iterator(*this, 0); }
-        iterator end() const { return iterator(*this, size()); }
-
-    private:
-        const Code& m_code;
-    };
-
-    StackSlotsCollection stackSlots() const { return StackSlotsCollection(*this); }
-    
-    class SpecialsCollection {
-    public:
-        SpecialsCollection(const Code& code)
-            : m_code(code)
-        {
-        }
-
-        unsigned size() const { return m_code.m_specials.size(); }
-        Special* at(unsigned index) const { return m_code.m_specials[index].get(); }
-        Special* operator[](unsigned index) const { return at(index); }
-
-        class iterator {
-        public:
-            iterator()
-                : m_collection(nullptr)
-                , m_index(0)
-            {
-            }
-
-            iterator(const SpecialsCollection& collection, unsigned index)
-                : m_collection(&collection)
-                , m_index(index)
-            {
-            }
-
-            Special* operator*()
-            {
-                return m_collection->at(m_index);
-            }
-
-            iterator& operator++()
-            {
-                m_index++;
-                return *this;
-            }
-
-            bool operator==(const iterator& other) const
-            {
-                return m_index == other.m_index;
-            }
-
-            bool operator!=(const iterator& other) const
-            {
-                return !(*this == other);
-            }
-
-        private:
-            const SpecialsCollection* m_collection;
-            unsigned m_index;
-        };
-
-        iterator begin() const { return iterator(*this, 0); }
-        iterator end() const { return iterator(*this, size()); }
-
-    private:
-        const Code& m_code;
-    };
+    const SparseCollection<StackSlot>& stackSlots() const { return m_stackSlots; }
+    SparseCollection<StackSlot>& stackSlots() { return m_stackSlots; }
 
-    SpecialsCollection specials() const { return SpecialsCollection(*this); }
+    const SparseCollection<Special>& specials() const { return m_specials; }
+    SparseCollection<Special>& specials() { return m_specials; }
 
     template<typename Callback>
     void forAllTmps(const Callback& callback) const
@@ -331,9 +218,9 @@ private:
     Code(Procedure&);
 
     Procedure& m_proc; // Some meta-data, like byproducts, is stored in the Procedure.
-    Vector<std::unique_ptr<StackSlot>> m_stackSlots;
+    SparseCollection<StackSlot> m_stackSlots;
     Vector<std::unique_ptr<BasicBlock>> m_blocks;
-    Vector<std::unique_ptr<Special>> m_specials;
+    SparseCollection<Special> m_specials;
     HashSet<Tmp> m_fastTmps;
     CCallSpecial* m_cCallSpecial { nullptr };
     unsigned m_numGPTmps { 0 };
index 1a4d0f6..e8b0375 100644 (file)
@@ -261,7 +261,7 @@ private:
     
     static bool isSpillSlot(const Arg& arg)
     {
-        return arg.isStack() && !arg.stackSlot()->isLocked();
+        return arg.isStack() && arg.stackSlot()->isSpill();
     }
     
     struct RegConst {
index 5ca9f2b..95e68ae 100644 (file)
@@ -31,6 +31,7 @@
 #include "AirInst.h"
 #include "AirOpcodeUtils.h"
 #include "AirSpecial.h"
+#include "AirStackSlot.h"
 #include "B3Value.h"
 
 namespace JSC { namespace B3 { namespace Air {
index 148366c..8bd0cb8 100644 (file)
@@ -1233,7 +1233,7 @@ private:
 
             // Allocate stack slot for each spilled value.
             StackSlot* stackSlot = m_code.addStackSlot(
-                m_tmpWidth.width(tmp) <= Arg::Width32 ? 4 : 8, StackSlotKind::Anonymous);
+                m_tmpWidth.width(tmp) <= Arg::Width32 ? 4 : 8, StackSlotKind::Spill);
             bool isNewTmp = stackSlots.add(tmp, stackSlot).isNewEntry;
             ASSERT_UNUSED(isNewTmp, isNewTmp);
         }
index 2ba3f9e..ac118aa 100644 (file)
@@ -31,6 +31,7 @@
 #include "AirBasicBlock.h"
 #include "AirCode.h"
 #include "AirInstInlines.h"
+#include "AirStackSlot.h"
 #include "AirTmpInlines.h"
 #include "B3IndexMap.h"
 #include "B3IndexSet.h"
index becd762..84f7bd7 100644 (file)
@@ -97,7 +97,7 @@ void lowerAfterRegAlloc(Code& code)
                 result[i] = Arg::stack(
                     code.addStackSlot(
                         Arg::bytes(Arg::conservativeWidth(type)),
-                        StackSlotKind::Anonymous));
+                        StackSlotKind::Spill));
             }
         }
         return result;
@@ -182,7 +182,7 @@ void lowerAfterRegAlloc(Code& code)
                         Arg arg(tmp);
                         Arg::Width width = Arg::conservativeWidth(arg.type());
                         StackSlot* stackSlot =
-                            code.addStackSlot(Arg::bytes(width), StackSlotKind::Anonymous);
+                            code.addStackSlot(Arg::bytes(width), StackSlotKind::Spill);
                         pairs.append(ShufflePair(arg, Arg::stack(stackSlot), width));
                         stackSlots.append(stackSlot);
                     });
index e60bb78..0c538aa 100644 (file)
@@ -36,8 +36,6 @@ namespace JSC { namespace B3 { namespace Air {
 const char* const Special::dumpPrefix = "&";
 
 Special::Special()
-    : m_index(UINT_MAX)
-    , m_code(nullptr)
 {
 }
 
index b944220..e5cb5f1 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(B3_JIT)
 
 #include "AirInst.h"
+#include "B3SparseCollection.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/ScopedLambda.h>
@@ -98,10 +99,10 @@ protected:
 
 private:
     friend class Code;
+    friend class SparseCollection<Special>;
 
-    const char* m_name;
-    unsigned m_index;
-    Code* m_code;
+    unsigned m_index { UINT_MAX };
+    Code* m_code { nullptr };
 };
 
 class DeepSpecialDump {
index 22d009c..18224e4 100644 (file)
@@ -88,7 +88,7 @@ void spillEverything(Code& code)
         Arg::Type type = static_cast<Arg::Type>(typeIndex);
         stackSlots.resize(code.numTmps(type));
         for (unsigned tmpIndex = code.numTmps(type); tmpIndex--;)
-            stackSlots[tmpIndex] = code.addStackSlot(8, StackSlotKind::Anonymous);
+            stackSlots[tmpIndex] = code.addStackSlot(8, StackSlotKind::Spill);
     }
 
     InsertionSet insertionSet(code);
index c23e77a..d0ab082 100644 (file)
@@ -41,7 +41,11 @@ void StackSlot::setOffsetFromFP(intptr_t value)
 
 void StackSlot::dump(PrintStream& out) const
 {
-    out.print("stack", m_index);
+    if (isSpill())
+        out.print("spill");
+    else
+        out.print("stack");
+    out.print(m_index);
 }
 
 void StackSlot::deepDump(PrintStream& out) const
@@ -51,9 +55,8 @@ void StackSlot::deepDump(PrintStream& out) const
         out.print(", b3Slot = ", *m_b3Slot, ": (", B3::deepDump(m_b3Slot), ")");
 }
 
-StackSlot::StackSlot(unsigned byteSize, unsigned index, StackSlotKind kind, B3::StackSlot* b3Slot)
+StackSlot::StackSlot(unsigned byteSize, StackSlotKind kind, B3::StackSlot* b3Slot)
     : m_byteSize(byteSize)
-    , m_index(index)
     , m_offsetFromFP(b3Slot ? b3Slot->offsetFromFP() : 0)
     , m_kind(kind)
     , m_b3Slot(b3Slot)
index 524f0d1..9a49c10 100644 (file)
@@ -28,7 +28,8 @@
 
 #if ENABLE(B3_JIT)
 
-#include "B3StackSlotKind.h"
+#include "AirStackSlotKind.h"
+#include "B3SparseCollection.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/PrintStream.h>
@@ -46,6 +47,7 @@ public:
     unsigned byteSize() const { return m_byteSize; }
     StackSlotKind kind() const { return m_kind; }
     bool isLocked() const { return m_kind == StackSlotKind::Locked; }
+    bool isSpill() const { return m_kind == StackSlotKind::Spill; }
     unsigned index() const { return m_index; }
 
     void ensureSize(unsigned requestedSize)
@@ -79,14 +81,15 @@ public:
 
 private:
     friend class Code;
+    friend class SparseCollection<StackSlot>;
 
-    StackSlot(unsigned byteSize, unsigned index, StackSlotKind, B3::StackSlot*);
+    StackSlot(unsigned byteSize, StackSlotKind, B3::StackSlot*);
     
-    unsigned m_byteSize;
-    unsigned m_index;
-    intptr_t m_offsetFromFP;
-    StackSlotKind m_kind;
-    B3::StackSlot* m_b3Slot;
+    unsigned m_byteSize { 0 };
+    unsigned m_index { UINT_MAX };
+    intptr_t m_offsetFromFP { 0 };
+    StackSlotKind m_kind { StackSlotKind::Locked };
+    B3::StackSlot* m_b3Slot { nullptr };
 };
 
 class DeepStackSlotDump {
@@ -24,7 +24,7 @@
  */
 
 #include "config.h"
-#include "B3StackSlotKind.h"
+#include "AirStackSlotKind.h"
 
 #if ENABLE(B3_JIT)
 
@@ -32,7 +32,7 @@
 
 namespace WTF {
 
-using namespace JSC::B3;
+using namespace JSC::B3::Air;
 
 void printInternal(PrintStream& out, StackSlotKind kind)
 {
@@ -40,8 +40,8 @@ void printInternal(PrintStream& out, StackSlotKind kind)
     case StackSlotKind::Locked:
         out.print("Locked");
         return;
-    case StackSlotKind::Anonymous:
-        out.print("Anonymous");
+    case StackSlotKind::Spill:
+        out.print("Spill");
         return;
     }
     RELEASE_ASSERT_NOT_REACHED();
similarity index 67%
rename from Source/JavaScriptCore/b3/B3StackSlotKind.h
rename to Source/JavaScriptCore/b3/air/AirStackSlotKind.h
index 3a89842..9236183 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef B3StackSlotKind_h
-#define B3StackSlotKind_h
+#ifndef AirStackSlotKind_h
+#define AirStackSlotKind_h
 
 #if ENABLE(B3_JIT)
 
-namespace JSC { namespace B3 {
+namespace JSC { namespace B3 { namespace Air {
 
 enum class StackSlotKind : uint8_t {
-    // A locked stack slot needs to be kept disjoint from all others because we intend to escape
-    // it even if that's not obvious. This happens when the runtime takes the address of a stack
-    // slot.
+    // A locked stack slot is an area of stack requested by the client. It cannot be killed. The
+    // client can get its FP offset and write to it from stack walking code, so we must assume
+    // that reads and writes to a locked stack slot can be clobbered the same way as reads and
+    // writes to any memory location.
     Locked,
 
-    // These stack slots behave like variables. Undefined behavior happens if you store less than
-    // the width of the slot.
-    Anonymous
+    // A spill slot. These have fundamentally different behavior than a typical memory location.
+    // They are lowered to from temporaries. This means for example that a 32-bit ZDef store to a
+    // 8 byte stack slot will zero the top 4 bytes, even though a 32-bit ZDef store to any other
+    // kind of memory location would do no such thing. UseAddr on a spill slot is not allowed, so
+    // they never escape.
+    Spill
 
     // FIXME: We should add a third mode, which means that the stack slot will be read asynchronously
     // as with Locked, but never written to asynchronously. Then, Air could optimize spilling and
@@ -47,13 +51,13 @@ enum class StackSlotKind : uint8_t {
     // https://bugs.webkit.org/show_bug.cgi?id=150587
 };
 
-} } // namespace JSC::B3
+} } } // namespace JSC::B3::Air
 
 namespace WTF {
 
 class PrintStream;
 
-void printInternal(PrintStream&, JSC::B3::StackSlotKind);
+void printInternal(PrintStream&, JSC::B3::Air::StackSlotKind);
 
 } // namespace WTF
 
index a824cc5..7b25c8e 100644 (file)
@@ -680,6 +680,18 @@ writeH("OpcodeUtils") {
                 filter = proc { false }
                 callback = proc {
                     | form |
+                    # This conservatively says that Stack is not a valid form for UseAddr,
+                    # because it's only valid if it's not a spill slot. This is consistent with
+                    # isValidForm() being conservative and it also happens to be practical since
+                    # we don't really use isValidForm for deciding when Stack is safe.
+                    overload.signature.length.times {
+                        | index |
+                        if overload.signature[index].role == "UA"
+                            outp.puts "if (opgenHiddenPtrIdentity(kinds)[#{index}] == Arg::Stack)"
+                            outp.puts "    return false;"
+                        end
+                    }
+                    
                     notCustom = (not form.kinds.detect { | kind | kind.custom })
                     if notCustom
                         beginArchs(outp, form.archs)
@@ -787,6 +799,11 @@ writeH("OpcodeGenerated") {
                     outp.puts "if (!Arg::isValidImmForm(args[#{index}].value()))"
                     outp.puts "OPGEN_RETURN(false);"
                 when "Addr"
+                    if arg.role == "UA"
+                        outp.puts "if (args[#{index}].isStack() && args[#{index}].stackSlot()->isSpill())"
+                        outp.puts "OPGEN_RETURN(false);"
+                    end
+                    
                     outp.puts "if (!Arg::isValidAddrForm(args[#{index}].offset()))"
                     outp.puts "OPGEN_RETURN(false);"
                 when "Index"
@@ -839,9 +856,11 @@ writeH("OpcodeGenerated") {
                 numNo = 0
                 opcode.overloads.each {
                     | overload |
+                    useAddr = (overload.signature[argIndex] and
+                               overload.signature[argIndex].role == "UA")
                     overload.forms.each {
                         | form |
-                        if form.kinds[argIndex] == "Addr"
+                        if form.kinds[argIndex] == "Addr" and not useAddr
                             numYes += 1
                         else
                             numNo += 1
@@ -864,12 +883,15 @@ writeH("OpcodeGenerated") {
                     opcode.overloads.each {
                         | overload |
 
+                        useAddr = (overload.signature[argIndex] and
+                                   overload.signature[argIndex].role == "UA")
+                        
                         # Again, check if all of them do what we want.
                         numYes = 0
                         numNo = 0
                         overload.forms.each {
                             | form |
-                            if form.kinds[argIndex] == "Addr"
+                            if form.kinds[argIndex] == "Addr" and not useAddr
                                 numYes += 1
                             else
                                 numNo += 1
index ab0728b..4316f88 100644 (file)
@@ -408,7 +408,7 @@ void testShuffleBroadcastAllRegs()
             shuffle.append(Tmp(GPRInfo::regT0), Tmp(reg), Arg::widthArg(Arg::Width32));
     }
 
-    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), B3::StackSlotKind::Locked);
+    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), StackSlotKind::Locked);
     for (unsigned i = 0; i < regs.size(); ++i)
         root->append(Move32, nullptr, Tmp(regs[i]), Arg::stack(slot, i * sizeof(int32_t)));
 
@@ -869,7 +869,7 @@ void testShuffleShiftAllRegs()
     for (unsigned i = 1; i < regs.size(); ++i)
         shuffle.append(Tmp(regs[i - 1]), Tmp(regs[i]), Arg::widthArg(Arg::Width32));
 
-    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), B3::StackSlotKind::Locked);
+    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), StackSlotKind::Locked);
     for (unsigned i = 0; i < regs.size(); ++i)
         root->append(Move32, nullptr, Tmp(regs[i]), Arg::stack(slot, i * sizeof(int32_t)));
 
@@ -906,7 +906,7 @@ void testShuffleRotateAllRegs()
         shuffle.append(Tmp(regs[i - 1]), Tmp(regs[i]), Arg::widthArg(Arg::Width32));
     shuffle.append(Tmp(regs.last()), Tmp(regs[0]), Arg::widthArg(Arg::Width32));
 
-    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), B3::StackSlotKind::Locked);
+    StackSlot* slot = code.addStackSlot(sizeof(int32_t) * regs.size(), StackSlotKind::Locked);
     for (unsigned i = 0; i < regs.size(); ++i)
         root->append(Move32, nullptr, Tmp(regs[i]), Arg::stack(slot, i * sizeof(int32_t)));
 
index c7a67dc..335dabe 100644 (file)
@@ -4990,7 +4990,7 @@ void testOverrideFramePointer()
         BasicBlock* root = proc.addBlock();
 
         // Add a stack slot to make the frame non trivial.
-        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8, StackSlotKind::Locked));
+        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8));
 
         // Sub on x86 UseDef the source. If FP is not protected correctly, it will be overridden since it is the last visible use.
         Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
@@ -5004,7 +5004,7 @@ void testOverrideFramePointer()
         Procedure proc;
         BasicBlock* root = proc.addBlock();
 
-        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8, StackSlotKind::Locked));
+        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(8));
 
         Value* offset = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
         Value* fp = root->appendNew<Value>(proc, FramePointer, Origin());
@@ -5024,8 +5024,7 @@ void testStackSlot()
     BasicBlock* root = proc.addBlock();
     root->appendNew<ControlValue>(
         proc, Return, Origin(),
-        root->appendNew<SlotBaseValue>(
-            proc, Origin(), proc.addStackSlot(1, StackSlotKind::Anonymous)));
+        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(1)));
 
     void* stackSlot = compileAndRun<void*>(proc);
     CHECK(stackSlot < &proc);
@@ -5053,8 +5052,8 @@ void testStoreLoadStackSlot(int value)
     Procedure proc;
     BasicBlock* root = proc.addBlock();
 
-    SlotBaseValue* stack = root->appendNew<SlotBaseValue>(
-        proc, Origin(), proc.addStackSlot(sizeof(int), StackSlotKind::Anonymous));
+    SlotBaseValue* stack =
+        root->appendNew<SlotBaseValue>(proc, Origin(), proc.addStackSlot(sizeof(int)));
 
     root->appendNew<MemoryValue>(
         proc, Store, Origin(),
index 9f958d6..8ae4d1e 100644 (file)
@@ -81,8 +81,7 @@ void Output::appendTo(LBasicBlock block)
 
 SlotBaseValue* Output::lockedStackSlot(size_t bytes)
 {
-    return m_block->appendNew<SlotBaseValue>(
-        m_proc, origin(), m_proc.addStackSlot(bytes, StackSlotKind::Locked));
+    return m_block->appendNew<SlotBaseValue>(m_proc, origin(), m_proc.addStackSlot(bytes));
 }
 
 LValue Output::neg(LValue value)
index 412026a..41d079e 100644 (file)
@@ -5961,8 +5961,8 @@ private:
 
         // Set some obvious things.
         patchpoint->effects.terminal = false;
-        patchpoint->effects.writesSSAState = false;
-        patchpoint->effects.readsSSAState = false;
+        patchpoint->effects.writesLocalState = false;
+        patchpoint->effects.readsLocalState = false;
         
         // This is how we tell B3 about the possibility of jump replacement.
         patchpoint->effects.exitsSideways = true;
index 84ac08e..8d32c6c 100644 (file)
@@ -1,3 +1,15 @@
+2016-02-02  Filip Pizlo  <fpizlo@apple.com>
+
+        Get rid of anonymous stack slots
+        https://bugs.webkit.org/show_bug.cgi?id=151128
+
+        Reviewed by Mark Lam.
+
+        This changes the documentation to account for the addition of Variables and the Get and Set
+        opcodes, and the removal of anonymous stack slots from B3 IR.
+
+        * docs/b3/intermediate-representation.html:
+
 2016-02-02  Dan Bernstein  <mitz@apple.com>
 
         Tagline in B3 docs should match the rest of the website
index 429e5fa..6a53c54 100644 (file)
@@ -85,7 +85,7 @@
       for the load.  We use the MemoryValue class for memory-accessing values, which all have
       such an offset.</p>
 
-    <h2>Stack Slot</h2>
+    <h2>Stack Slots</h2>
 
     <p>B3 exposes the concept of stack-allocated data and gives the client a lot of control.
       By default, stack slots get allocated wherever B3 chooses. It will try to pack them as
       for callee-saves.  Therefore, we recommend not using the frame pointer offset forcing
       feature unless you know a lot about the ABI and you have no other choice.</p>
 
-    <p>Stack slots are also used for creating non-SSA variables with the intention of having B3
-      convert them into SSA.  There are two kinds of stack slots.</p>
+    <h2>Variables</h2>
 
-    <dl>
-      <dt>Anonymous</dt>
-      <dd>Anonymous stack slots are used to represent local variables that aren't in SSA form.
-        B3 is allowed to assume that nobody will store to an anonymous stack slot except through
-        Store instructions in the B3 procedure.  B3 is allowed to assume that a Store that does
-        not write to the entire anonymous stack slot leaves the unwritten part in an undefined
-        state.  Usually, anonymous stack slots are allocated to have the same size as the type
-        of variable they are being used to represent.</dd>
-
-      <dt>Locked</dt>
-      <dd>These stack slots are assumed to operate "as if" they were in the heap, in the sense
-        that the may get read or written using operations not visible in B3 IR.</dd>
-    </dl>
+    <p>Sometimes, SSA is inconvenient. For example, it's hard to do path specialization over SSA.
+      B3 has the concept of Variables as a fall-back. The backend knows how to handle them and
+      will coalesce and copy-propagate them. Inside the B3 optimizer, there is a classic SSA
+      builder that eliminates variables and builds SSA in their place.</p>
+
+    <p>You can create Variables by using Procedure::addVariable(), and then you can access them
+      using the Get and Set opcodes.</p>
 
-    <p>The fixSSA() phase will convert anonymous stack slots to SSA.</p>
+    <p>The fixSSA() phase will convert variables to SSA. If you use a lot of variables in your
+      input to B3, it's a good idea to run fixSSA() manually before running the compiler. The
+      default optimizer only runs fixSSA() towards the middle of optimizations. Passing non-SSA code
+      as input to the optimizer may render the early phases ineffective. Fortunately, B3 phases
+      are super easy to run. The following runs SSA fix-up on a Procedure named "proc":</p>
+
+    <pre><code>fixSSA(proc);</code></pre>
 
     <h2>Control flow</h2>
 
       <dt>Double ConstDouble(constant)</dt>
       <dd>Double constant.  Must use the ConstDoubleValue class, which has space for the double constant.</dd>
 
+      <dt>Void Set(value, variable)</dt>
+      <dd>Assigns the given value to the given Variable. Must use the VariableValue class.</dd>
+
+      <dt>T Get(variable)</dt>
+      <dd>Returns the current value of the given Variable. Its return type depends on the
+        variable. Must use the VariableValue class.</dd>
+
       <dt>IntPtr SlotBase(stackSlot)</dt>
       <dd>Returns a pointer to the base of the given stack slot.  Must use the SlotBaseValue
         class.</dd>