Commit 0309686b authored by fpizlo@apple.com's avatar fpizlo@apple.com

Stores to local captured variables should be intercepted

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

Source/JavaScriptCore: 

Reviewed by Mark Hahnenberg.
        
Previously, in bytecode, you could assign to a captured variable just as you would
assign to any other kind of variable. This complicates closure variable constant
inference because we don't have any place where we can intercept stores to captured
variables in the LLInt.
        
This patch institutes a policy that only certain instructions can store to captured
variables. If you interpret those instructions and you are required to notifyWrite()
then you need to check if the relevant variable is captured. Those instructions are
tracked in CodeBlock.cpp's VerifyCapturedDef. The main one is simply op_captured_mov.
In the future, we'll probably modify those instructions to have a pointer directly to
the VariableWatchpointSet; but for now we just introduce the captured instructions as
placeholders.
        
In order to validate that the placeholders are inserted correctly, this patch improves
the CodeBlock validation to be able to inspect every def in the bytecode. To do that,
this patch refactors the liveness analysis' use/def calculator to be reusable; it now
takes a functor for each use or def.
        
In the process of refactoring the liveness analysis, I noticed that op_enter was
claiming to def all callee registers. That's wrong; it only defs the non-temporary
variables. Making that change revealed preexisting bugs in the liveness analysis, since
now the validator would pick up cases where the bytecode claimed to use a temporary and
the def calculator never noticed the definition (or the converse - where the bytecode
was actually not using a temporary but the liveness analysis thought that it was a
use). This patch fixes a few of those bugs.

* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::stepOverInstruction):
* bytecode/BytecodeUseDef.h: Added.
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::validate):
* bytecode/CodeBlock.h:
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::emitMove):
(JSC::BytecodeGenerator::isCaptured):
(JSC::BytecodeGenerator::local):
(JSC::BytecodeGenerator::constLocal):
(JSC::BytecodeGenerator::emitNewFunction):
(JSC::BytecodeGenerator::emitLazyNewFunction):
(JSC::BytecodeGenerator::emitNewFunctionInternal):
* bytecompiler/BytecodeGenerator.h:
(JSC::Local::Local):
(JSC::Local::isCaptured):
(JSC::Local::captureMode):
(JSC::BytecodeGenerator::captureMode):
(JSC::BytecodeGenerator::emitNode):
(JSC::BytecodeGenerator::pushOptimisedForIn):
* bytecompiler/NodesCodegen.cpp:
(JSC::PostfixNode::emitResolve):
(JSC::PrefixNode::emitResolve):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
(JSC::ForInNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/SymbolTable.h:
(JSC::SymbolTable::isCaptured):

LayoutTests: 

Reviewed by Mark Hahnenberg.

* js/regress/captured-assignments-expected.txt: Added.
* js/regress/captured-assignments.html: Added.
* js/regress/script-tests/captured-assignments.js: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159943 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a4ea0663
2013-12-01 Filip Pizlo <fpizlo@apple.com>
Stores to local captured variables should be intercepted
https://bugs.webkit.org/show_bug.cgi?id=124883
Reviewed by Mark Hahnenberg.
* js/regress/captured-assignments-expected.txt: Added.
* js/regress/captured-assignments.html: Added.
* js/regress/script-tests/captured-assignments.js: Added.
2013-12-02 Lauro Neto <lauro.neto@openbossa.org>
[MediaStream] Update layout tests to newer spec.
......
JSRegress/captured-assignments
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/captured-assignments.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
function foo() {
var x = 0;
++x;
for (var x in [1, 2, 3]) {
}
x = 1;
x++;
var y = x++;
var z = y++;
z = x++;
x += 2;
z -= 1;
x *= 2;
z <<= 1;
x |= 5;
return function() { return x++ + z; }
}
for (var i = 0; i < 100; ++i) {
if (foo()() != 17)
throw "Error";
}
2013-12-01 Filip Pizlo <fpizlo@apple.com>
Stores to local captured variables should be intercepted
https://bugs.webkit.org/show_bug.cgi?id=124883
Reviewed by Mark Hahnenberg.
Previously, in bytecode, you could assign to a captured variable just as you would
assign to any other kind of variable. This complicates closure variable constant
inference because we don't have any place where we can intercept stores to captured
variables in the LLInt.
This patch institutes a policy that only certain instructions can store to captured
variables. If you interpret those instructions and you are required to notifyWrite()
then you need to check if the relevant variable is captured. Those instructions are
tracked in CodeBlock.cpp's VerifyCapturedDef. The main one is simply op_captured_mov.
In the future, we'll probably modify those instructions to have a pointer directly to
the VariableWatchpointSet; but for now we just introduce the captured instructions as
placeholders.
In order to validate that the placeholders are inserted correctly, this patch improves
the CodeBlock validation to be able to inspect every def in the bytecode. To do that,
this patch refactors the liveness analysis' use/def calculator to be reusable; it now
takes a functor for each use or def.
In the process of refactoring the liveness analysis, I noticed that op_enter was
claiming to def all callee registers. That's wrong; it only defs the non-temporary
variables. Making that change revealed preexisting bugs in the liveness analysis, since
now the validator would pick up cases where the bytecode claimed to use a temporary and
the def calculator never noticed the definition (or the converse - where the bytecode
was actually not using a temporary but the liveness analysis thought that it was a
use). This patch fixes a few of those bugs.
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::stepOverInstruction):
* bytecode/BytecodeUseDef.h: Added.
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::validate):
* bytecode/CodeBlock.h:
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::emitMove):
(JSC::BytecodeGenerator::isCaptured):
(JSC::BytecodeGenerator::local):
(JSC::BytecodeGenerator::constLocal):
(JSC::BytecodeGenerator::emitNewFunction):
(JSC::BytecodeGenerator::emitLazyNewFunction):
(JSC::BytecodeGenerator::emitNewFunctionInternal):
* bytecompiler/BytecodeGenerator.h:
(JSC::Local::Local):
(JSC::Local::isCaptured):
(JSC::Local::captureMode):
(JSC::BytecodeGenerator::captureMode):
(JSC::BytecodeGenerator::emitNode):
(JSC::BytecodeGenerator::pushOptimisedForIn):
* bytecompiler/NodesCodegen.cpp:
(JSC::PostfixNode::emitResolve):
(JSC::PrefixNode::emitResolve):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
(JSC::ForInNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/SymbolTable.h:
(JSC::SymbolTable::isCaptured):
2013-12-02 Filip Pizlo <fpizlo@apple.com>
Instead of watchpointing activation allocation, we should watchpoint entry into functions that have captured variables
......
......@@ -102,10 +102,11 @@ javascriptcore_sources += \
Source/JavaScriptCore/bytecode/ByValInfo.h \
Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp \
Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h \
Source/JavaScriptCore/bytecode/BytecodeConventions.h \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h \
Source/JavaScriptCore/bytecode/BytecodeConventions.h \
Source/JavaScriptCore/bytecode/BytecodeUseDef.h \
Source/JavaScriptCore/bytecode/CallLinkInfo.cpp \
Source/JavaScriptCore/bytecode/CallLinkInfo.h \
Source/JavaScriptCore/bytecode/CallLinkStatus.cpp \
......
......@@ -751,6 +751,7 @@
<ClInclude Include="..\bytecode\BytecodeBasicBlock.h" />
<ClInclude Include="..\bytecode\BytecodeLivenessAnalysis.h" />
<ClInclude Include="..\bytecode\BytecodeLivenessAnalysisInline.h" />
<ClInclude Include="..\bytecode\BytecodeUseDef.h" />
<ClInclude Include="..\bytecode\CallLinkInfo.h" />
<ClInclude Include="..\bytecode\CallLinkStatus.h" />
<ClInclude Include="..\bytecode\CallReturnOffsetToBytecodeOffset.h" />
......
......@@ -289,6 +289,7 @@
0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; };
0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */; };
0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8F2B94172E049E007DBDA5 /* FTLLink.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */; };
......@@ -1583,6 +1584,7 @@
0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; };
0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; };
0F85A31E16AB76AE0077571E /* DFGVariadicFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariadicFunction.h; path = dfg/DFGVariadicFunction.h; sourceTree = "<group>"; };
0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeUseDef.h; sourceTree = "<group>"; };
0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLink.cpp; path = ftl/FTLLink.cpp; sourceTree = "<group>"; };
0F8F2B94172E049E007DBDA5 /* FTLLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FTLLink.h; path = ftl/FTLLink.h; sourceTree = "<group>"; };
0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredIdentifiers.cpp; path = dfg/DFGDesiredIdentifiers.cpp; sourceTree = "<group>"; };
......@@ -4058,6 +4060,7 @@
C2FCAE0E17A9C24E0034C735 /* BytecodeLivenessAnalysis.cpp */,
C2FCAE0F17A9C24E0034C735 /* BytecodeLivenessAnalysis.h */,
0F666EBE183566F900D017F1 /* BytecodeLivenessAnalysisInlines.h */,
0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */,
0F8023E91613832300A0BA45 /* ByValInfo.h */,
0F0B83AE14BCF71400885B4F /* CallLinkInfo.cpp */,
0F0B83AF14BCF71400885B4F /* CallLinkInfo.h */,
......@@ -4269,6 +4272,7 @@
BC3135640F302FA3003DFD3A /* DebuggerActivation.h in Headers */,
BC18C3FB0E16F5CD00B34460 /* DebuggerCallFrame.h in Headers */,
0F136D4D174AD69E0075B354 /* DeferGC.h in Headers */,
0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */,
0FC712DF17CD877C008CC93C /* DeferredCompilationCallback.h in Headers */,
A77A423E17A0BBFD00A8DB81 /* DFGAbstractHeap.h in Headers */,
A704D90317A0BAA8006BA554 /* DFGAbstractInterpreter.h in Headers */,
......
......@@ -27,6 +27,7 @@
#include "BytecodeLivenessAnalysis.h"
#include "BytecodeLivenessAnalysisInlines.h"
#include "BytecodeUseDef.h"
#include "CodeBlock.h"
#include "FullBytecodeLiveness.h"
#include "PreciseJumpTargets.h"
......@@ -67,426 +68,26 @@ static void setForOperand(CodeBlock* codeBlock, FastBitVector& bits, int operand
bits.set(virtualReg.toLocal() - codeBlock->captureCount());
}
static void computeUsesForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, FastBitVector& uses)
{
Interpreter* interpreter = codeBlock->vm()->interpreter;
Instruction* instructionsBegin = codeBlock->instructions().begin();
Instruction* instruction = &instructionsBegin[bytecodeOffset];
OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
switch (opcodeID) {
// No uses.
case op_new_regexp:
case op_new_array_buffer:
case op_throw_static_error:
case op_debug:
case op_resolve_scope:
case op_pop_scope:
case op_jneq_ptr:
case op_new_func_exp:
case op_loop_hint:
case op_jmp:
case op_new_object:
case op_init_lazy_reg:
case op_get_callee:
case op_enter:
case op_touch_entry:
case op_catch:
return;
// First argument.
case op_new_func:
case op_create_activation:
case op_create_arguments:
case op_to_this:
case op_tear_off_activation:
case op_profile_will_call:
case op_profile_did_call:
case op_throw:
case op_push_with_scope:
case op_end:
case op_ret:
case op_jtrue:
case op_jfalse:
case op_jeq_null:
case op_jneq_null:
case op_dec:
case op_inc: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
return;
}
// First and second arguments.
case op_del_by_id:
case op_ret_object_or_this:
case op_jlesseq:
case op_jgreater:
case op_jgreatereq:
case op_jnless:
case op_jnlesseq:
case op_jngreater:
case op_jngreatereq:
case op_jless: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
return;
}
// First, second, and third arguments.
case op_del_by_val:
case op_put_by_val_direct:
case op_put_by_val: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
return;
}
// First and third arguments.
case op_put_by_index:
case op_put_by_id_replace:
case op_put_by_id_transition:
case op_put_by_id_transition_direct:
case op_put_by_id_transition_direct_out_of_line:
case op_put_by_id_transition_normal:
case op_put_by_id_transition_normal_out_of_line:
case op_put_by_id_generic:
case op_put_by_id_out_of_line:
case op_put_by_id:
case op_put_to_scope: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
return;
}
// First, third, and fourth arguments.
case op_put_getter_setter: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
setForOperand(codeBlock, uses, instruction[4].u.operand);
return;
}
// Second argument.
case op_init_global_const_nop:
case op_init_global_const:
case op_push_name_scope:
case op_get_from_scope:
case op_to_primitive:
case op_get_by_id:
case op_get_by_id_out_of_line:
case op_get_by_id_self:
case op_get_by_id_proto:
case op_get_by_id_chain:
case op_get_by_id_getter_self:
case op_get_by_id_getter_proto:
case op_get_by_id_getter_chain:
case op_get_by_id_custom_self:
case op_get_by_id_custom_proto:
case op_get_by_id_custom_chain:
case op_get_by_id_generic:
case op_get_array_length:
case op_get_string_length:
case op_get_arguments_length:
case op_typeof:
case op_is_undefined:
case op_is_boolean:
case op_is_number:
case op_is_string:
case op_is_object:
case op_is_function:
case op_to_number:
case op_negate:
case op_neq_null:
case op_eq_null:
case op_not:
case op_mov:
case op_new_array_with_size:
case op_create_this: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
return;
}
// Second and third arguments.
case op_get_by_val:
case op_get_argument_by_val:
case op_in:
case op_instanceof:
case op_check_has_instance:
case op_add:
case op_mul:
case op_div:
case op_mod:
case op_sub:
case op_lshift:
case op_rshift:
case op_urshift:
case op_bitand:
case op_bitxor:
case op_bitor:
case op_less:
case op_lesseq:
case op_greater:
case op_greatereq:
case op_nstricteq:
case op_stricteq:
case op_neq:
case op_eq: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
return;
}
// Second, third, and fourth arguments.
case op_call_varargs:
case op_get_pnames: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
setForOperand(codeBlock, uses, instruction[4].u.operand);
return;
}
// Second, third, fourth, and fifth arguments.
case op_next_pname: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
setForOperand(codeBlock, uses, instruction[4].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[5].u.operand))
setForOperand(codeBlock, uses, instruction[5].u.operand);
return;
}
// Second, third, fourth, fifth, and sixth arguments.
case op_get_by_pname: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
setForOperand(codeBlock, uses, instruction[4].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[5].u.operand))
setForOperand(codeBlock, uses, instruction[5].u.operand);
if (isValidRegisterForLiveness(codeBlock, instruction[6].u.operand))
setForOperand(codeBlock, uses, instruction[6].u.operand);
return;
}
// Third argument.
case op_switch_string:
case op_switch_char:
case op_switch_imm: {
if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
setForOperand(codeBlock, uses, instruction[3].u.operand);
return;
}
// Variable number of arguments.
case op_new_array:
case op_strcat: {
int base = instruction[2].u.operand;
int count = instruction[3].u.operand;
for (int i = 0; i < count; i++) {
if (isValidRegisterForLiveness(codeBlock, base - i))
setForOperand(codeBlock, uses, base - i);
}
return;
}
// Crazy stuff for calls.
case op_construct:
case op_call_eval:
case op_call: {
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
int argCount = instruction[3].u.operand;
int registerOffset = -instruction[4].u.operand;
int lastArg = registerOffset + CallFrame::thisArgumentOffset();
for (int i = opcodeID == op_construct ? 1 : 0; i < argCount; i++) {
if (isValidRegisterForLiveness(codeBlock, lastArg + i))
setForOperand(codeBlock, uses, lastArg + i);
}
return;
}
// Special stuff for tear off arguments.
case op_tear_off_arguments: {
if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
setForOperand(codeBlock, uses, instruction[1].u.operand);
if (isValidRegisterForLiveness(codeBlock, unmodifiedArgumentsRegister(VirtualRegister(instruction[1].u.operand)).offset()))
setForOperand(codeBlock, uses, unmodifiedArgumentsRegister(VirtualRegister(instruction[1].u.operand)).offset());
if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
setForOperand(codeBlock, uses, instruction[2].u.operand);
return;
}
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
}
namespace {
static void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, FastBitVector& defs)
{
Interpreter* interpreter = codeBlock->vm()->interpreter;
Instruction* instructionsBegin = codeBlock->instructions().begin();
Instruction* instruction = &instructionsBegin[bytecodeOffset];
OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
switch (opcodeID) {
// These don't define anything.
case op_init_global_const:
case op_init_global_const_nop:
case op_push_name_scope:
case op_push_with_scope:
case op_put_to_scope:
case op_pop_scope:
case op_end:
case op_profile_will_call:
case op_profile_did_call:
case op_throw:
case op_throw_static_error:
case op_debug:
case op_ret:
case op_ret_object_or_this:
case op_jmp:
case op_jtrue:
case op_jfalse:
case op_jeq_null:
case op_jneq_null:
case op_jneq_ptr:
case op_jless:
case op_jlesseq:
case op_jgreater:
case op_jgreatereq:
case op_jnless:
case op_jnlesseq:
case op_jngreater:
case op_jngreatereq:
case op_loop_hint:
case op_switch_imm:
case op_switch_char:
case op_switch_string:
case op_put_by_id:
case op_put_by_id_out_of_line:
case op_put_by_id_replace:
case op_put_by_id_transition:
case op_put_by_id_transition_direct:
case op_put_by_id_transition_direct_out_of_line:
case op_put_by_id_transition_normal:
case op_put_by_id_transition_normal_out_of_line:
case op_put_by_id_generic:
case op_put_getter_setter:
case op_put_by_val:
case op_put_by_val_direct:
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
return;
// These all have a single destination for the first argument.
case op_next_pname:
case op_get_pnames:
case op_resolve_scope:
case op_strcat:
case op_tear_off_activation:
case op_to_primitive:
case op_catch:
case op_create_this:
case op_new_array:
case op_new_array_buffer:
case op_new_array_with_size:
case op_new_regexp:
case op_new_func:
case op_new_func_exp:
case op_call_varargs:
case op_get_from_scope:
case op_call:
case op_call_eval:
case op_construct:
case op_get_by_id:
case op_get_by_id_out_of_line:
case op_get_by_id_self:
case op_get_by_id_proto:
case op_get_by_id_chain:
case op_get_by_id_getter_self:
case op_get_by_id_getter_proto:
case op_get_by_id_getter_chain:
case op_get_by_id_custom_self:
case op_get_by_id_custom_proto:
case op_get_by_id_custom_chain:
case op_get_by_id_generic:
case op_get_array_length:
case op_get_string_length:
case op_check_has_instance:
case op_instanceof:
case op_get_by_val:
case op_get_argument_by_val:
case op_get_by_pname:
case op_get_arguments_length:
case op_typeof:
case op_is_undefined:
case op_is_boolean:
case op_is_number:
case op_is_string:
case op_is_object:
case op_is_function:
case op_in:
case op_to_number:
case op_negate:
case op_add:
case op_mul:
case op_div:
case op_mod:
case op_sub:
case op_lshift:
case op_rshift:
case op_urshift:
case op_bitand:
case op_bitxor:
case op_bitor:
case op_inc:
case op_dec:
case op_eq:
case op_neq:
case op_stricteq:
case op_nstricteq:
case op_less:
case op_lesseq:
case op_greater:
case op_greatereq: