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

Instead of watchpointing activation allocation, we should watchpoint entry...

Instead of watchpointing activation allocation, we should watchpoint entry into functions that have captured variables
https://bugs.webkit.org/show_bug.cgi?id=125052

Reviewed by Mark Hahnenberg.
        
This makes us watch function entry rather than activation creation. We only incur the
costs of doing so for functions that have captured variables, and only on the first two
entries into the function. This means that closure variable constant inference will
naturally work even for local uses of the captured variable, like:
        
    (function(){
        var blah = 42;
        ... // stuff
        function () { ... blah /* we can fold this to 42 */ }
        ... blah // we can also fold this to 42.
    })();
        
Previously, only the nested use would have been foldable.

* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecode/Watchpoint.h:
(JSC::WatchpointSet::touch):
(JSC::InlineWatchpointSet::touch):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasSymbolTable):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_touch_entry):
* llint/LowLevelInterpreter.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/JSActivation.h:
(JSC::JSActivation::create):
* runtime/SymbolTable.cpp:
(JSC::SymbolTable::SymbolTable):
* runtime/SymbolTable.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159942 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 4e2662cd
2013-12-02 Filip Pizlo <fpizlo@apple.com>
Instead of watchpointing activation allocation, we should watchpoint entry into functions that have captured variables
https://bugs.webkit.org/show_bug.cgi?id=125052
Reviewed by Mark Hahnenberg.
This makes us watch function entry rather than activation creation. We only incur the
costs of doing so for functions that have captured variables, and only on the first two
entries into the function. This means that closure variable constant inference will
naturally work even for local uses of the captured variable, like:
(function(){
var blah = 42;
... // stuff
function () { ... blah /* we can fold this to 42 */ }
... blah // we can also fold this to 42.
})();
Previously, only the nested use would have been foldable.
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecode/Watchpoint.h:
(JSC::WatchpointSet::touch):
(JSC::InlineWatchpointSet::touch):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasSymbolTable):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_touch_entry):
* llint/LowLevelInterpreter.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/JSActivation.h:
(JSC::JSActivation::create):
* runtime/SymbolTable.cpp:
(JSC::SymbolTable::SymbolTable):
* runtime/SymbolTable.h:
2013-12-02 Nick Diego Yamane <nick.yamane@openbossa.org>
[JSC] Get rid of some unused parameters in LLIntSlowPaths.cpp macros
......
......@@ -89,6 +89,7 @@ static void computeUsesForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecode
case op_init_lazy_reg:
case op_get_callee:
case op_enter:
case op_touch_entry:
case op_catch:
return;
// First argument.
......@@ -383,6 +384,7 @@ static void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecode
case op_put_by_index:
case op_del_by_id:
case op_del_by_val:
case op_touch_entry:
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
#undef LLINT_HELPER_OPCODES
......
......@@ -671,6 +671,10 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio
printLocationAndOp(out, exec, location, it, "enter");
break;
}
case op_touch_entry: {
printLocationAndOp(out, exec, location, it, "touch_entry");
break;
}
case op_create_activation: {
int r0 = (++it)->u.operand;
printLocationOpAndRegisterOperand(out, exec, location, it, "create_activation", r0);
......
......@@ -42,6 +42,7 @@ namespace JSC {
#define FOR_EACH_CORE_OPCODE_ID_WITH_EXTENSION(macro, extension__) \
macro(op_enter, 1) \
macro(op_create_activation, 2) \
macro(op_touch_entry, 1) \
macro(op_init_lazy_reg, 2) \
macro(op_create_arguments, 2) \
macro(op_create_this, 4) \
......
......@@ -108,7 +108,7 @@ public:
fireAllSlow();
}
void notifyWrite()
void touch()
{
if (state() == ClearWatchpoint)
startWatching();
......@@ -209,10 +209,10 @@ public:
WTF::storeStoreFence();
}
void notifyWrite()
void touch()
{
if (isFat()) {
fat()->notifyWrite();
fat()->touch();
return;
}
if (decodeState(m_data) == ClearWatchpoint)
......
......@@ -352,6 +352,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl
if (shouldCaptureAllTheThings)
m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset());
if (m_symbolTable->captureCount())
emitOpcode(op_touch_entry);
m_parameters.grow(parameters.size() + 1); // reserve space for "this"
// Add "this" as a parameter
......
......@@ -1146,7 +1146,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
m_state.setHaveStructures(true);
break;
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
break;
case CreateArguments:
......
......@@ -1881,7 +1881,12 @@ bool ByteCodeParser::parseBlock(unsigned limit)
for (int i = 0; i < m_inlineStackTop->m_codeBlock->m_numVars; ++i)
set(virtualRegisterForLocal(i), constantUndefined(), SetOnEntry);
NEXT_OPCODE(op_enter);
case op_touch_entry:
if (m_inlineStackTop->m_codeBlock->symbolTable()->m_functionEnteredOnce.isStillValid())
addToGraph(ForceOSRExit);
NEXT_OPCODE(op_touch_entry);
case op_to_this: {
Node* op1 = getThis();
if (op1->op() != ToThis) {
......@@ -3003,8 +3008,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
case ClosureVarWithVarInjectionChecks: {
JSActivation* activation = currentInstruction[5].u.activation.get();
if (activation
&& activation->symbolTable()->m_activationAllocatedOnce.isStillValid()) {
addToGraph(ActivationAllocationWatchpoint, OpInfo(activation->symbolTable()));
&& activation->symbolTable()->m_functionEnteredOnce.isStillValid()) {
addToGraph(FunctionReentryWatchpoint, OpInfo(activation->symbolTable()));
set(VirtualRegister(dst), cellConstant(activation));
break;
}
......
......@@ -81,6 +81,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
{
switch (opcodeID) {
case op_enter:
case op_touch_entry:
case op_to_this:
case op_create_this:
case op_get_callee:
......
......@@ -160,7 +160,7 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
write(GCState);
return;
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
read(Watchpoint_fire);
return;
......
......@@ -950,7 +950,7 @@ private:
case Unreachable:
case ExtractOSREntryLocal:
case LoopHint:
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
break;
#else
default:
......
......@@ -1063,7 +1063,7 @@ struct Node {
bool hasSymbolTable()
{
return op() == ActivationAllocationWatchpoint;
return op() == FunctionReentryWatchpoint;
}
SymbolTable* symbolTable()
......
......@@ -187,7 +187,7 @@ namespace JSC { namespace DFG {
macro(NotifyWrite, NodeMustGenerate) \
macro(VariableWatchpoint, NodeMustGenerate) \
macro(VarInjectionWatchpoint, NodeMustGenerate) \
macro(ActivationAllocationWatchpoint, NodeMustGenerate) \
macro(FunctionReentryWatchpoint, NodeMustGenerate) \
macro(CheckFunction, NodeMustGenerate) \
macro(AllocationProfileWatchpoint, NodeMustGenerate) \
\
......
......@@ -576,7 +576,7 @@ private:
case Unreachable:
case LoopHint:
case NotifyWrite:
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
break;
// This gets ignored because it already has a prediction.
......
......@@ -243,7 +243,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case Int52ToValue:
case InvalidationPoint:
case NotifyWrite:
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
return true;
case GetByVal:
......
......@@ -4336,7 +4336,7 @@ void SpeculativeJIT::compile(Node* node)
break;
}
case ActivationAllocationWatchpoint: {
case FunctionReentryWatchpoint: {
noResult(node);
break;
}
......
......@@ -4611,7 +4611,7 @@ void SpeculativeJIT::compile(Node* node)
break;
}
case ActivationAllocationWatchpoint: {
case FunctionReentryWatchpoint: {
noResult(node);
break;
}
......
......@@ -126,8 +126,8 @@ private:
addLazily(globalObject()->varInjectionWatchpoint());
break;
case ActivationAllocationWatchpoint:
addLazily(m_node->symbolTable()->m_activationAllocatedOnce);
case FunctionReentryWatchpoint:
addLazily(m_node->symbolTable()->m_functionEnteredOnce);
break;
default:
......
......@@ -102,7 +102,7 @@ inline CapabilityLevel canCompile(Node* node)
case CheckFunction:
case StringCharCodeAt:
case AllocatePropertyStorage:
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
case VariableWatchpoint:
case NotifyWrite:
// These are OK.
......
......@@ -399,7 +399,7 @@ private:
break;
case VariableWatchpoint:
break;
case ActivationAllocationWatchpoint:
case FunctionReentryWatchpoint:
break;
case GetMyScope:
compileGetMyScope();
......
......@@ -109,38 +109,13 @@ void JIT::emitEnterOptimizationCheck()
m_bytecodeOffset += OPCODE_LENGTH(name); \
break;
#if USE(JSVALUE32_64)
#define DEFINE_BINARY_OP(name) \
#define DEFINE_SLOW_OP(name) \
case op_##name: { \
JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_##name); \
slowPathCall.call(); \
NEXT_OPCODE(op_##name); \
}
#define DEFINE_UNARY_OP(name) \
case op_##name: { \
JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_##name); \
slowPathCall.call(); \
NEXT_OPCODE(op_##name); \
}
#else // USE(JSVALUE32_64)
#define DEFINE_BINARY_OP(name) \
case op_##name: { \
JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_##name); \
slowPathCall.call(); \
NEXT_OPCODE(op_##name); \
}
#define DEFINE_UNARY_OP(name) \
case op_##name: { \
JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_##name); \
slowPathCall.call(); \
NEXT_OPCODE(op_##name); \
}
#endif // USE(JSVALUE32_64)
#define DEFINE_OP(name) \
case name: { \
emit_##name(currentInstruction); \
......@@ -187,16 +162,17 @@ void JIT::privateCompileMainPass()
}
switch (opcodeID) {
DEFINE_BINARY_OP(del_by_val)
DEFINE_BINARY_OP(in)
DEFINE_BINARY_OP(less)
DEFINE_BINARY_OP(lesseq)
DEFINE_BINARY_OP(greater)
DEFINE_BINARY_OP(greatereq)
DEFINE_UNARY_OP(is_function)
DEFINE_UNARY_OP(is_object)
DEFINE_UNARY_OP(typeof)
DEFINE_SLOW_OP(del_by_val)
DEFINE_SLOW_OP(in)
DEFINE_SLOW_OP(less)
DEFINE_SLOW_OP(lesseq)
DEFINE_SLOW_OP(greater)
DEFINE_SLOW_OP(greatereq)
DEFINE_SLOW_OP(is_function)
DEFINE_SLOW_OP(is_object)
DEFINE_SLOW_OP(typeof)
DEFINE_OP(op_touch_entry)
DEFINE_OP(op_add)
DEFINE_OP(op_bitand)
DEFINE_OP(op_bitor)
......
......@@ -455,6 +455,7 @@ namespace JSC {
void emit_compareAndJump(OpcodeID, int op1, int op2, unsigned target, RelationalCondition);
void emit_compareAndJumpSlow(int op1, int op2, unsigned target, DoubleCondition, size_t (JIT_OPERATION *operation)(ExecState*, EncodedJSValue, EncodedJSValue), bool invert, Vector<SlowCaseEntry>::iterator&);
void emit_op_touch_entry(Instruction*);
void emit_op_add(Instruction*);
void emit_op_bitand(Instruction*);
void emit_op_bitor(Instruction*);
......
......@@ -1096,6 +1096,15 @@ void JIT::emitSlow_op_get_argument_by_val(Instruction* currentInstruction, Vecto
#endif // USE(JSVALUE64)
void JIT::emit_op_touch_entry(Instruction* currentInstruction)
{
if (m_codeBlock->symbolTable()->m_functionEnteredOnce.hasBeenInvalidated())
return;
JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_touch_entry);
slowPathCall.call();
}
void JIT::emit_op_loop_hint(Instruction*)
{
// Emit the JIT optimization check:
......
......@@ -476,6 +476,12 @@ end
# Value-representation-agnostic code.
_llint_op_touch_entry:
traceExecution()
callSlowPath(_slow_path_touch_entry)
dispatch(1)
_llint_op_new_array:
traceExecution()
callSlowPath(_llint_slow_path_new_array)
......
......@@ -193,6 +193,13 @@ SLOW_PATH_DECL(slow_path_construct_arityCheck)
RETURN_TWO(0, reinterpret_cast<ExecState*>(SlotsToAdd));
}
SLOW_PATH_DECL(slow_path_touch_entry)
{
BEGIN();
exec->codeBlock()->symbolTable()->m_functionEnteredOnce.touch();
END();
}
SLOW_PATH_DECL(slow_path_get_callee)
{
BEGIN();
......
......@@ -160,6 +160,7 @@ SLOW_PATH_DECL(name) WTF_INTERNAL
SLOW_PATH_HIDDEN_DECL(slow_path_call_arityCheck);
SLOW_PATH_HIDDEN_DECL(slow_path_construct_arityCheck);
SLOW_PATH_HIDDEN_DECL(slow_path_touch_entry);
SLOW_PATH_HIDDEN_DECL(slow_path_create_arguments);
SLOW_PATH_HIDDEN_DECL(slow_path_create_this);
SLOW_PATH_HIDDEN_DECL(slow_path_get_callee);
......
/*
* Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009, 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
......@@ -50,7 +50,6 @@ public:
{
SymbolTable* symbolTable = codeBlock->symbolTable();
ASSERT(codeBlock->codeType() == FunctionCode);
symbolTable->m_activationAllocatedOnce.notifyWrite();
JSActivation* activation = new (
NotNull,
allocateCell<JSActivation>(
......
......@@ -99,7 +99,7 @@ SymbolTable::SymbolTable(VM& vm)
, m_usesNonStrictEval(false)
, m_captureStart(0)
, m_captureEnd(0)
, m_activationAllocatedOnce(ClearWatchpoint)
, m_functionEnteredOnce(ClearWatchpoint)
{
}
......
......@@ -497,7 +497,7 @@ private:
std::unique_ptr<WatchpointCleanup> m_watchpointCleanup;
public:
InlineWatchpointSet m_activationAllocatedOnce;
InlineWatchpointSet m_functionEnteredOnce;
mutable ConcurrentJITLock m_lock;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment