Groundwork for reimplementing the slow script dialog
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jun 2008 21:19:56 +0000 (21:19 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jun 2008 21:19:56 +0000 (21:19 +0000)
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.

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

JavaScriptCore/ChangeLog
JavaScriptCore/VM/CodeGenerator.cpp
JavaScriptCore/VM/LabelID.h
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/VM/Machine.h
JavaScriptCore/VM/Opcode.h

index d47e5a8..c5a07db 100644 (file)
@@ -1,3 +1,22 @@
+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.
index b5f3e5a..bfab3ee 100644 (file)
@@ -420,6 +420,7 @@ void CodeGenerator::rewindBinaryOp()
 
 PassRefPtr<LabelID> CodeGenerator::emitJump(LabelID* target)
 {
+    ASSERT(target->isForwardLabel());
     emitOpcode(op_jmp);
     instructions().append(target->offsetFrom(instructions().size()));
     return target;
@@ -436,7 +437,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrueMayCombine(RegisterID* cond, La
         
         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()));
@@ -449,7 +450,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrueMayCombine(RegisterID* cond, La
 
 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;
@@ -457,6 +458,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrue(RegisterID* cond, LabelID* tar
 
 PassRefPtr<LabelID> CodeGenerator::emitJumpIfFalse(RegisterID* cond, LabelID* target)
 {
+    ASSERT(target->isForwardLabel());
     emitOpcode(op_jfalse);
     instructions().append(cond->index());
     instructions().append(target->offsetFrom(instructions().size()));
@@ -1031,6 +1033,7 @@ PassRefPtr<LabelID> CodeGenerator::emitComplexJumpScopes(LabelID* target, Contro
 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());
index 6edb5e2..7153bb2 100644 (file)
@@ -98,6 +98,7 @@ namespace KJS {
             return m_refCount;
         }
 
+        bool isForwardLabel() { return m_location == invalidLocation; }
     private:
         typedef Vector<int, 8> JumpVector;
 
index 6c9640b..8f132bd 100644 (file)
@@ -895,7 +895,7 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
     Instruction* vPC = codeBlock->instructions.begin();
     JSValue** k = codeBlock->jsValues.data();
     Profiler** enabledProfilerReference = Profiler::enabledProfilerReference();
-
+    
     registerFile->setSafeForReentry(false);
 #define VM_CHECK_EXCEPTION() \
      do { \
@@ -908,7 +908,9 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
 #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
@@ -1887,6 +1889,26 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         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)
 
@@ -1919,6 +1941,33 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         ++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)
 
index 47cb033..0039e78 100644 (file)
@@ -127,6 +127,7 @@ namespace KJS {
         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
index a913222..3f459cc 100644 (file)
@@ -97,6 +97,8 @@ namespace KJS {
         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) \