+2008-06-24 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Cameron.
+
+ Add special loop opcodes as groundwork for slow script
+ termination. Also added a few assertions to prevent us
+ from accidentally coalescing conditional jump operands
+ in a way that might bypass the slow script opcodes.
+
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * VM/CodeGenerator.cpp:
+ (KJS::CodeGenerator::emitJumpIfTrueMayCombine):
+ (KJS::CodeGenerator::emitJumpScopes):
+ * VM/LabelID.h:
+ * VM/Machine.cpp:
+ (KJS::Machine::privateExecute):
+ * VM/Machine.h:
+ * VM/Opcode.h:
+
2008-06-24 Darin Adler <darin@apple.com>
Reviewed by Cameron.
PassRefPtr<LabelID> CodeGenerator::emitJump(LabelID* target)
{
+ ASSERT(target->isForwardLabel());
emitOpcode(op_jmp);
instructions().append(target->offsetFrom(instructions().size()));
return target;
if (cond->index() == dstIndex) {
rewindBinaryOp();
- emitOpcode(op_jless);
+ emitOpcode(target->isForwardLabel() ? op_jless : op_loop_if_less);
instructions().append(src1Index);
instructions().append(src2Index);
instructions().append(target->offsetFrom(instructions().size()));
PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrue(RegisterID* cond, LabelID* target)
{
- emitOpcode(op_jtrue);
+ emitOpcode(target->isForwardLabel() ? op_jtrue : op_loop_if_true);
instructions().append(cond->index());
instructions().append(target->offsetFrom(instructions().size()));
return target;
PassRefPtr<LabelID> CodeGenerator::emitJumpIfFalse(RegisterID* cond, LabelID* target)
{
+ ASSERT(target->isForwardLabel());
emitOpcode(op_jfalse);
instructions().append(cond->index());
instructions().append(target->offsetFrom(instructions().size()));
PassRefPtr<LabelID> CodeGenerator::emitJumpScopes(LabelID* target, int targetScopeDepth)
{
ASSERT(scopeDepth() - targetScopeDepth >= 0);
+ ASSERT(target->isForwardLabel());
size_t scopeDelta = scopeDepth() - targetScopeDepth;
ASSERT(scopeDelta <= m_scopeContextStack.size());
return m_refCount;
}
+ bool isForwardLabel() { return m_location == invalidLocation; }
private:
typedef Vector<int, 8> JumpVector;
Instruction* vPC = codeBlock->instructions.begin();
JSValue** k = codeBlock->jsValues.data();
Profiler** enabledProfilerReference = Profiler::enabledProfilerReference();
-
+
registerFile->setSafeForReentry(false);
#define VM_CHECK_EXCEPTION() \
do { \
#if DUMP_OPCODE_STATS
OpcodeStats::resetLastInstruction();
#endif
-
+
+#define CHECK_FOR_TIMEOUT()
+
#if HAVE(COMPUTED_GOTO)
#define NEXT_OPCODE goto *vPC->u.opcode
#if DUMP_OPCODE_STATS
vPC += target;
NEXT_OPCODE;
}
+ BEGIN_OPCODE(op_loop_if_true) {
+ /* loop_if_true cond(r) target(offset)
+
+ Jumps to offset target from the current instruction, if and
+ only if register cond converts to boolean as true.
+
+ Additionally this loop instruction may terminate JS execution is
+ the JS timeout is reached.
+ */
+ int cond = (++vPC)->u.operand;
+ int target = (++vPC)->u.operand;
+ if (r[cond].u.jsValue->toBoolean(exec)) {
+ vPC += target;
+ CHECK_FOR_TIMEOUT();
+ NEXT_OPCODE;
+ }
+
+ ++vPC;
+ NEXT_OPCODE;
+ }
BEGIN_OPCODE(op_jtrue) {
/* jtrue cond(r) target(offset)
++vPC;
NEXT_OPCODE;
}
+ BEGIN_OPCODE(op_loop_if_less) {
+ /* loop_if_less src1(r) src2(r) target(offset)
+
+ Checks whether register src1 is less than register src2, as
+ with the ECMAScript '<' operator, and then jumps to offset
+ target from the current instruction, if and only if the
+ result of the comparison is true.
+
+ Additionally this loop instruction may terminate JS execution is
+ the JS timeout is reached.
+ */
+ JSValue* src1 = r[(++vPC)->u.operand].u.jsValue;
+ JSValue* src2 = r[(++vPC)->u.operand].u.jsValue;
+ int target = (++vPC)->u.operand;
+
+ bool result = jsLess(exec, src1, src2);
+ VM_CHECK_EXCEPTION();
+
+ if (result) {
+ vPC += target;
+ CHECK_FOR_TIMEOUT();
+ NEXT_OPCODE;
+ }
+
+ ++vPC;
+ NEXT_OPCODE;
+ }
BEGIN_OPCODE(op_jless) {
/* jless src1(r) src2(r) target(offset)
bool isGlobalCallFrame(Register** registerBase, const Register* r) const { return (*registerBase) == r; }
int m_reentryDepth;
+
#if HAVE(COMPUTED_GOTO)
Opcode m_opcodeTable[numOpcodeIDs]; // Maps OpcodeID => Opcode for compiling
HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling
macro(op_jfalse) \
macro(op_jless) \
macro(op_jmp_scopes) \
+ macro(op_loop_if_true) \
+ macro(op_loop_if_less) \
\
macro(op_new_func) \
macro(op_new_func_exp) \