Commit 9b7647ba authored by oliver@apple.com's avatar oliver@apple.com

fourthTier: DFG should support op_switch_imm

https://bugs.webkit.org/show_bug.cgi?id=117559

Reviewed by Oliver Hunt.

Implement integer (i.e. immediate) switches in the DFG. Reduce the minimum
threshold for using op_switch.

Also get rid of edge code support, since we haven't used it in the year since
I introduced it. It was supposed to allow us to break critical edges late in
the backend, thus enabling global register allocation from an SSA-form graph.
But we aren't doing that so I figure we should just kill the code for now. It
would have made implementing switch harder.

* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::timesPtr):
* assembler/MacroAssemblerCodeRef.h:
(JSC::MacroAssemblerCodePtr::dumpWithName):
(MacroAssemblerCodePtr):
(JSC::MacroAssemblerCodePtr::dump):
(MacroAssemblerCodeRef):
(JSC::MacroAssemblerCodeRef::dump):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::shrinkToFit):
* bytecode/JumpTable.h:
(SimpleJumpTable):
(JSC::SimpleJumpTable::clear):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
(JSC::DFG::AbstractState::mergeToSuccessors):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(InlineStackEntry):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::linkBlock):
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGCommon.h:
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::determineReachability):
* dfg/DFGGraph.h:
(Graph):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::JITCompiler):
(JSC::DFG::JITCompiler::link):
* dfg/DFGJITCompiler.h:
(JITCompiler):
(JSC::DFG::JITCompiler::blockHeads):
* dfg/DFGNode.h:
(DFG):
(JSC::DFG::SwitchCase::SwitchCase):
(SwitchCase):
(SwitchData):
(JSC::DFG::SwitchData::SwitchData):
(Node):
(JSC::DFG::Node::isSwitch):
(JSC::DFG::Node::isTerminal):
(JSC::DFG::Node::switchData):
(JSC::DFG::Node::numSuccessors):
(JSC::DFG::Node::successor):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::compile):
(JSC::DFG::SpeculativeJIT::createOSREntries):
(JSC::DFG::SpeculativeJIT::emitSwitchImmIntJump):
(DFG):
(JSC::DFG::SpeculativeJIT::emitSwitchImm):
(JSC::DFG::SpeculativeJIT::emitSwitch):
(JSC::DFG::SpeculativeJIT::linkBranches):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::branchDouble):
(JSC::DFG::SpeculativeJIT::branchDoubleNonZero):
(JSC::DFG::SpeculativeJIT::branch32):
(JSC::DFG::SpeculativeJIT::branchTest32):
(JSC::DFG::SpeculativeJIT::branch64):
(JSC::DFG::SpeculativeJIT::branchPtr):
(JSC::DFG::SpeculativeJIT::branchTestPtr):
(JSC::DFG::SpeculativeJIT::branchTest8):
(JSC::DFG::SpeculativeJIT::jump):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* parser/Nodes.h:
(CaseBlockNode):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@153228 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent acdde49f
2013-06-13 Filip Pizlo <fpizlo@apple.com>
fourthTier: DFG should support op_switch_imm
https://bugs.webkit.org/show_bug.cgi?id=117559
Reviewed by Oliver Hunt.
Implement integer (i.e. immediate) switches in the DFG. Reduce the minimum
threshold for using op_switch.
Also get rid of edge code support, since we haven't used it in the year since
I introduced it. It was supposed to allow us to break critical edges late in
the backend, thus enabling global register allocation from an SSA-form graph.
But we aren't doing that so I figure we should just kill the code for now. It
would have made implementing switch harder.
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::timesPtr):
* assembler/MacroAssemblerCodeRef.h:
(JSC::MacroAssemblerCodePtr::dumpWithName):
(MacroAssemblerCodePtr):
(JSC::MacroAssemblerCodePtr::dump):
(MacroAssemblerCodeRef):
(JSC::MacroAssemblerCodeRef::dump):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::shrinkToFit):
* bytecode/JumpTable.h:
(SimpleJumpTable):
(JSC::SimpleJumpTable::clear):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
(JSC::DFG::AbstractState::mergeToSuccessors):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(InlineStackEntry):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::linkBlock):
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGCommon.h:
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::determineReachability):
* dfg/DFGGraph.h:
(Graph):
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::JITCompiler):
(JSC::DFG::JITCompiler::link):
* dfg/DFGJITCompiler.h:
(JITCompiler):
(JSC::DFG::JITCompiler::blockHeads):
* dfg/DFGNode.h:
(DFG):
(JSC::DFG::SwitchCase::SwitchCase):
(SwitchCase):
(SwitchData):
(JSC::DFG::SwitchData::SwitchData):
(Node):
(JSC::DFG::Node::isSwitch):
(JSC::DFG::Node::isTerminal):
(JSC::DFG::Node::switchData):
(JSC::DFG::Node::numSuccessors):
(JSC::DFG::Node::successor):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::compile):
(JSC::DFG::SpeculativeJIT::createOSREntries):
(JSC::DFG::SpeculativeJIT::emitSwitchImmIntJump):
(DFG):
(JSC::DFG::SpeculativeJIT::emitSwitchImm):
(JSC::DFG::SpeculativeJIT::emitSwitch):
(JSC::DFG::SpeculativeJIT::linkBranches):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::branchDouble):
(JSC::DFG::SpeculativeJIT::branchDoubleNonZero):
(JSC::DFG::SpeculativeJIT::branch32):
(JSC::DFG::SpeculativeJIT::branchTest32):
(JSC::DFG::SpeculativeJIT::branch64):
(JSC::DFG::SpeculativeJIT::branchPtr):
(JSC::DFG::SpeculativeJIT::branchTestPtr):
(JSC::DFG::SpeculativeJIT::branchTest8):
(JSC::DFG::SpeculativeJIT::jump):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* parser/Nodes.h:
(CaseBlockNode):
2013-06-15 Filip Pizlo <fpizlo@apple.com>
Concurrent JIT shouldn't try to recompute the CodeBlockHash as part of debug dumps, since doing so may fail if dealing with a CachedScript that doesn't have its script string handy
......
......@@ -95,6 +95,13 @@ public:
TimesFour,
TimesEight,
};
static Scale timesPtr()
{
if (sizeof(void*) == 4)
return TimesFour;
return TimesEight;
}
// Address:
//
......
......@@ -31,6 +31,7 @@
#include "LLIntData.h"
#include <wtf/DataLog.h>
#include <wtf/PassRefPtr.h>
#include <wtf/PrintStream.h>
#include <wtf/RefPtr.h>
// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid
......@@ -320,6 +321,20 @@ public:
return m_value == other.m_value;
}
void dumpWithName(const char* name, PrintStream& out) const
{
if (executableAddress() == dataLocation()) {
out.print(name, "(", RawPointer(executableAddress()), ")");
return;
}
out.print(name, "(executable = ", RawPointer(executableAddress()), ", dataLocation = ", RawPointer(dataLocation()), ")");
}
void dump(PrintStream& out) const
{
dumpWithName("CodePtr", out);
}
private:
void* m_value;
};
......@@ -392,6 +407,11 @@ public:
}
bool operator!() const { return !m_codePtr; }
void dump(PrintStream& out) const
{
m_codePtr.dumpWithName("CodeRef", out);
}
private:
MacroAssemblerCodePtr m_codePtr;
......
......@@ -2458,13 +2458,16 @@ void CodeBlock::shrinkToFit(ShrinkMode shrinkMode)
m_functionDecls.shrinkToFit();
m_functionExprs.shrinkToFit();
m_constantRegisters.shrinkToFit();
if (m_rareData) {
m_rareData->m_immediateSwitchJumpTables.shrinkToFit();
m_rareData->m_characterSwitchJumpTables.shrinkToFit();
m_rareData->m_stringSwitchJumpTables.shrinkToFit();
}
} // else don't shrink these, because we would have already pointed pointers into these tables.
if (m_rareData) {
m_rareData->m_exceptionHandlers.shrinkToFit();
m_rareData->m_immediateSwitchJumpTables.shrinkToFit();
m_rareData->m_characterSwitchJumpTables.shrinkToFit();
m_rareData->m_stringSwitchJumpTables.shrinkToFit();
#if ENABLE(JIT)
m_rareData->m_callReturnIndexVector.shrinkToFit();
#endif
......
......@@ -96,6 +96,12 @@ namespace JSC {
return ctiDefault;
}
#endif
void clear()
{
branchOffsets.clear();
ctiOffsets.clear();
}
};
} // namespace JSC
......
......@@ -1038,6 +1038,12 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
m_branchDirection = TakeBoth;
break;
}
case Switch: {
// Nothing to do for now.
// FIXME: Do sparse conditional things.
break;
}
case Return:
m_isValid = false;
......@@ -1809,6 +1815,17 @@ inline bool AbstractState::mergeToSuccessors(Graph& graph, BasicBlock* basicBloc
return changed;
}
case Switch: {
// FIXME: It would be cool to be sparse conditional for Switch's. Currently
// we're not. However I somehow doubt that this will ever be a big deal.
ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
SwitchData* data = terminal->switchData();
bool changed = merge(basicBlock, graph.m_blocks[data->fallThrough].get());
for (unsigned i = data->cases.size(); i--;)
changed |= merge(basicBlock, graph.m_blocks[data->cases[i].target].get());
return changed;
}
case Return:
case Throw:
case ThrowReferenceError:
......
......@@ -347,6 +347,20 @@ private:
break;
}
case Switch: {
SwitchData* data = node->switchData();
switch (data->kind) {
case SwitchImm: {
// We don't need NodeNeedsNegZero because if the cases are all integers
// then -0 and 0 are treated the same. We don't need NodeUsedAsOther
// because if all of the cases are integers then NaN and undefined are
// treated the same.
node->child1()->mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
break;
} }
break;
}
default:
mergeDefaultFlags(node);
break;
......
......@@ -1009,6 +1009,7 @@ private:
Vector<unsigned> m_identifierRemap;
Vector<unsigned> m_constantRemap;
Vector<unsigned> m_constantBufferRemap;
Vector<unsigned> m_switchImmRemap;
// Blocks introduced by this code block, which need successor linking.
// May include up to one basic block that includes the continuation after
......@@ -2756,6 +2757,25 @@ bool ByteCodeParser::parseBlock(unsigned limit)
addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jngreatereq)), OpInfo(m_currentIndex + relativeOffset), condition);
LAST_OPCODE(op_jngreatereq);
}
case op_switch_imm: {
SwitchData data;
data.kind = SwitchImm;
data.switchTableIndex = m_inlineStackTop->m_switchImmRemap[currentInstruction[1].u.operand];
data.fallThrough = m_currentIndex + currentInstruction[2].u.operand;
SimpleJumpTable& table = m_codeBlock->immediateSwitchJumpTable(data.switchTableIndex);
for (unsigned i = 0; i < table.branchOffsets.size(); ++i) {
if (!table.branchOffsets[i])
continue;
unsigned target = m_currentIndex + table.branchOffsets[i];
if (target == data.fallThrough)
continue;
data.cases.append(SwitchCase(jsNumber(table.min + i), target));
}
m_graph.m_switchData.append(data);
addToGraph(Switch, OpInfo(&m_graph.m_switchData.last()), get(currentInstruction[3].u.operand));
LAST_OPCODE(op_switch_imm);
}
case op_ret:
flushArgumentsAndCapturedVariables();
......@@ -3122,6 +3142,12 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa
#endif
break;
case Switch:
for (unsigned i = node->switchData()->cases.size(); i--;)
node->switchData()->cases[i].target = m_graph.blockIndexForBytecodeOffset(possibleTargets, node->switchData()->cases[i].target);
node->switchData()->fallThrough = m_graph.blockIndexForBytecodeOffset(possibleTargets, node->switchData()->fallThrough);
break;
default:
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLogF("Marking basic block %p as linked.\n", block);
......@@ -3247,6 +3273,7 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
m_identifierRemap.resize(codeBlock->numberOfIdentifiers());
m_constantRemap.resize(codeBlock->numberOfConstantRegisters());
m_constantBufferRemap.resize(codeBlock->numberOfConstantBuffers());
m_switchImmRemap.resize(codeBlock->numberOfImmediateSwitchJumpTables());
for (size_t i = 0; i < codeBlock->numberOfIdentifiers(); ++i) {
StringImpl* rep = codeBlock->identifier(i).impl();
......@@ -3287,6 +3314,10 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
m_constantBufferRemap[i] = newIndex;
byteCodeParser->m_constantBufferCache.add(ConstantBufferKey(codeBlock, i), newIndex);
}
for (unsigned i = 0; i < codeBlock->numberOfImmediateSwitchJumpTables(); ++i) {
m_switchImmRemap[i] = byteCodeParser->m_codeBlock->numberOfImmediateSwitchJumpTables();
byteCodeParser->m_codeBlock->addImmediateSwitchJumpTable() = codeBlock->immediateSwitchJumpTable(i);
}
m_callsiteBlockHeadNeedsLinking = true;
} else {
// Machine code block case.
......@@ -3301,12 +3332,15 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
m_identifierRemap.resize(codeBlock->numberOfIdentifiers());
m_constantRemap.resize(codeBlock->numberOfConstantRegisters());
m_constantBufferRemap.resize(codeBlock->numberOfConstantBuffers());
m_switchImmRemap.resize(codeBlock->numberOfImmediateSwitchJumpTables());
for (size_t i = 0; i < codeBlock->numberOfIdentifiers(); ++i)
m_identifierRemap[i] = i;
for (size_t i = 0; i < codeBlock->numberOfConstantRegisters(); ++i)
m_constantRemap[i] = i + FirstConstantRegisterIndex;
for (size_t i = 0; i < codeBlock->numberOfConstantBuffers(); ++i)
m_constantBufferRemap[i] = i;
for (size_t i = 0; i < codeBlock->numberOfImmediateSwitchJumpTables(); ++i)
m_switchImmRemap[i] = i;
m_callsiteBlockHeadNeedsLinking = false;
}
......
......@@ -166,6 +166,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
case op_jneq_ptr:
case op_typeof:
case op_to_number:
case op_switch_imm:
case op_in:
case op_get_from_scope:
case op_put_to_scope:
......
......@@ -68,8 +68,6 @@
#define DFG_ENABLE_OSR_ENTRY ENABLE(DFG_JIT)
// Generate stats on how successful we were in making use of the DFG jit, and remaining on the hot path.
#define DFG_ENABLE_SUCCESS_STATS 0
// Enable verification that the DFG is able to insert code for control flow edges.
#define DFG_ENABLE_EDGE_CODE_VERIFICATION 0
namespace JSC { namespace DFG {
......
......@@ -578,6 +578,17 @@ private:
break;
}
case Switch: {
SwitchData* data = node->switchData();
switch (data->kind) {
case SwitchImm:
if (node->child1()->shouldSpeculateInteger())
setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
break;
}
break;
}
case ToPrimitive: {
fixupToPrimitive(node);
break;
......
......@@ -258,6 +258,12 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node)
out.print(comma, "T:#", node->takenBlockIndex());
if (node->isBranch())
out.print(comma, "F:#", node->notTakenBlockIndex());
if (node->isSwitch()) {
SwitchData* data = node->switchData();
for (unsigned i = 0; i < data->cases.size(); ++i)
out.print(comma, data->cases[i].value, ":#", data->cases[i].target);
out.print(comma, "default:#", data->fallThrough);
}
out.print(comma, "bc#", node->codeOrigin.bytecodeIndex);
out.print(")");
......@@ -404,17 +410,8 @@ void Graph::determineReachability()
worklist.removeLast();
BasicBlock* block = m_blocks[index].get();
ASSERT(block->isLinked);
Node* node = block->last();
ASSERT(node->isTerminal());
if (node->isJump())
handleSuccessor(worklist, index, node->takenBlockIndex());
else if (node->isBranch()) {
handleSuccessor(worklist, index, node->takenBlockIndex());
handleSuccessor(worklist, index, node->notTakenBlockIndex());
}
for (unsigned i = numSuccessors(block); i--;)
handleSuccessor(worklist, index, successor(block, i));
}
}
......
......@@ -65,7 +65,6 @@ enum AddSpeculationMode {
SpeculateInteger
};
//
// === Graph ===
//
......@@ -704,6 +703,7 @@ public:
SegmentedVector<StructureSet, 16> m_structureSet;
SegmentedVector<StructureTransitionData, 8> m_structureTransitionData;
SegmentedVector<NewArrayBufferData, 4> m_newArrayBufferData;
SegmentedVector<SwitchData, 4> m_switchData;
bool m_hasArguments;
HashSet<ExecutableBase*> m_executablesWhoseArgumentsEscaped;
BitVector m_preservedVars;
......
......@@ -48,6 +48,7 @@ JITCompiler::JITCompiler(Graph& dfg)
: CCallHelpers(&dfg.m_vm, dfg.m_codeBlock)
, m_graph(dfg)
, m_jitCode(adoptRef(new JITCode()))
, m_blockHeads(dfg.m_blocks.size())
, m_currentCodeOriginIndex(0)
{
if (shouldShowDisassembly() || m_graph.m_vm.m_perBytecodeProfiler)
......@@ -155,6 +156,34 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLogF("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize());
#endif
BitVector usedJumpTables;
for (unsigned i = m_graph.m_switchData.size(); i--;) {
SwitchData& data = m_graph.m_switchData[i];
if (!data.didUseJumpTable)
continue;
// Cast to int to avoid tautological comparison warnings.
RELEASE_ASSERT(static_cast<int>(data.kind) == static_cast<int>(SwitchImm));
usedJumpTables.set(data.switchTableIndex);
SimpleJumpTable& table =
m_codeBlock->immediateSwitchJumpTable(data.switchTableIndex);
table.ctiDefault = linkBuffer.locationOf(m_blockHeads[data.fallThrough]);
for (unsigned j = table.ctiOffsets.size(); j--;)
table.ctiOffsets[j] = table.ctiDefault;
for (unsigned j = data.cases.size(); j--;) {
SwitchCase& myCase = data.cases[j];
table.ctiOffsets[myCase.value.asInt32() - table.min] =
linkBuffer.locationOf(m_blockHeads[myCase.target]);
}
}
for (unsigned i = m_codeBlock->numberOfImmediateSwitchJumpTables(); i--;) {
if (usedJumpTables.get(i))
continue;
m_codeBlock->immediateSwitchJumpTable(i).clear();
}
// Link all calls out from the JIT code to their respective functions.
for (unsigned i = 0; i < m_calls.size(); ++i)
......
......@@ -462,6 +462,8 @@ public:
}
PassRefPtr<JITCode> jitCode() { return m_jitCode; }
Vector<Label>& blockHeads() { return m_blockHeads; }
private:
friend class OSRExitJumpPlaceholder;
......@@ -488,6 +490,8 @@ private:
Vector<CallLinkRecord> m_calls;
Vector<CallExceptionRecord> m_exceptionChecks;
Vector<Label> m_blockHeads;
struct JSCallRecord {
JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin)
: m_fastCall(fastCall)
......
......@@ -66,6 +66,50 @@ struct NewArrayBufferData {
IndexingType indexingType;
};
// The SwitchData and associated data structures duplicate the information in
// JumpTable. The DFG may ultimately end up using the JumpTable, though it may
// instead decide to do something different - this is entirely up to the DFG.
// These data structures give the DFG a higher-level semantic description of
// what is going on, which will allow it to make the right decision.
struct SwitchCase {
SwitchCase()
: target(NoBlock)
{
}
SwitchCase(JSValue value, BlockIndex target)
: value(value)
, target(target)
{
}
JSValue value;
BlockIndex target;
};
enum SwitchKind {
SwitchImm
};
struct SwitchData {
// Initializes most fields to obviously invalid values. Anyone
// constructing this should make sure to initialize everything they
// care about manually.
SwitchData()
: fallThrough(NoBlock)
, kind(static_cast<SwitchKind>(-1))
, switchTableIndex(UINT_MAX)
, didUseJumpTable(false)
{
}
Vector<SwitchCase> cases;
BlockIndex fallThrough;
SwitchKind kind;
unsigned switchTableIndex;
bool didUseJumpTable;
};
// This type used in passing an immediate argument to Node constructor;
// distinguishes an immediate value (typically an index into a CodeBlock data structure -
// a constant index, argument, or identifier) from a Node*.
......@@ -659,12 +703,18 @@ struct Node {
{
return op() == Branch;
}
bool isSwitch()
{
return op() == Switch;
}
bool isTerminal()
{
switch (op()) {
case Jump:
case Branch:
case Switch:
case Return:
case Throw:
case ThrowReferenceError:
......@@ -710,6 +760,12 @@ struct Node {
return m_opInfo2;
}
SwitchData* switchData()
{
ASSERT(isSwitch());
return bitwise_cast<SwitchData*>(m_opInfo);
}
unsigned numSuccessors()
{
switch (op()) {
......@@ -717,6 +773,8 @@ struct Node {
return 1;
case Branch:
return 2;
case Switch:
return switchData()->cases.size() + 1;
default:
return 0;
}
......@@ -724,6 +782,12 @@ struct Node {
BlockIndex successor(unsigned index)
{
if (isSwitch()) {
if (index < switchData()->cases.size())
return switchData()->cases[index].target;
RELEASE_ASSERT(index == switchData()->cases.size());
return switchData()->fallThrough;
}
switch (index) {
case 0:
return takenBlockIndex();
......
/*
* Copyright (C) 2012 Apple Inc. All rights reserved.
* Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
......@@ -249,6 +249,7 @@ namespace JSC { namespace DFG {
/* Block terminals. */\
macro(Jump, NodeMustGenerate) \
macro(Branch, NodeMustGenerate) \
macro(Switch, NodeMustGenerate) \
macro(Return, NodeMustGenerate) \
macro(Throw, NodeMustGenerate) \
macro(ThrowReferenceError, NodeMustGenerate) \
......
......@@ -1612,6 +1612,20 @@ JSCell* DFG_OPERATION operationMakeRope3(ExecState* exec, JSString* a, JSString*
return JSRopeString::create(vm, a, b, c);
}
char* DFG_OPERATION operationFindSwitchImmTargetForDouble(
ExecState* exec, EncodedJSValue encodedValue, size_t tableIndex)
{
CodeBlock* codeBlock = exec->codeBlock();
SimpleJumpTable& table = codeBlock->immediateSwitchJumpTable(tableIndex);
JSValue value = JSValue::decode(encodedValue);
ASSERT(value.isDouble());
double asDouble = value.asDouble();
int32_t asInt32 = static_cast<int32_t>(asDouble);
if (asDouble == asInt32)
return static_cast<char*>(table.ctiForValue(asInt32).executableAddress());
return static_cast<char*>(table.ctiDefault.executableAddress());
}
double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b)
{
return fmod(a, b);
......
......@@ -113,6 +113,7 @@ typedef void DFG_OPERATION (*V_DFGOperation_EPZJ)(ExecState*, void*, int32_t, En
typedef void DFG_OPERATION (*V_DFGOperation_W)(WatchpointSet*);
typedef char* DFG_OPERATION (*P_DFGOperation_E)(ExecState*);
typedef char* DFG_OPERATION (*P_DFGOperation_EC)(ExecState*, JSCell*);
typedef char* DFG_OPERATION (*P_DFGOperation_EJS)(ExecState*, EncodedJSValue, size_t);
typedef char* DFG_OPERATION (*P_DFGOperation_EO)(ExecState*, JSObject*);
typedef char* DFG_OPERATION (*P_DFGOperation_EOS)(ExecState*, JSObject*, size_t);
typedef char* DFG_OPERATION (*P_DFGOperation_EOZ)(ExecState*, JSObject*, int32_t);
......@@ -223,6 +224,7 @@ JSCell* DFG_OPERATION operationToStringOnCell(ExecState*, JSCell*);
JSCell* DFG_OPERATION operationToString(ExecState*, EncodedJSValue);
JSCell* DFG_OPERATION operationMakeRope2(ExecState*, JSString*, JSString*);
JSCell* DFG_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
char* DFG_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex);
// This method is used to lookup an exception hander, keyed by faultLocation, which is
// the return location from one of the calls out to one of the helper operations above.
......
......@@ -518,6 +518,7 @@ private:
case SetMyScope:
case DFG::Jump:
case Branch:
case Switch:
case Breakpoint:
case CheckHasInstance:
case ThrowReferenceError:
......
......@@ -43,7 +43,6 @@ SpeculativeJIT::SpeculativeJIT(JITCompiler& jit)
, m_currentNode(0)
, m_indexInBlock(0)
, m_generationInfo(m_jit.codeBlock()->m_numCalleeRegisters)
, m_blockHeads(jit.graph().m_blocks.size())
, m_arguments(jit.codeBlock()->numParameters())
, m_variables(jit.graph().m_localVars)
, m_lastSetOperand(std::numeric_limits<int>::max())
......@@ -1672,7 +1671,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
return;
}
m_blockHeads[m_block] = m_jit.label();
m_jit.blockHeads()[m_block] = m_jit.label();
#if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK)
m_jit.breakpoint();
#endif
......@@ -1719,13 +1718,6 @@ void SpeculativeJIT::compile(BasicBlock& block)
m_lastSetOperand = std::numeric_limits<int>::max();
m_codeOriginForOSR = CodeOrigin();