Optimize exception throwing
authorSimon Hausmann <simon.hausmann@digia.com>
Wed, 6 Mar 2013 19:04:21 +0000 (20:04 +0100)
committerLars Knoll <lars.knoll@digia.com>
Wed, 6 Mar 2013 19:28:56 +0000 (20:28 +0100)
Introduce a try statement in the IR that allows the back-end to jump to the try
and catch blocks directly instead of having a conditional jump for each entry.

For the case where we have no catch but only try { ... } finally { ... } we
create a synthetic catch block that sets hasException to true, to ensure that
when an exception is thrown during try we execute finally and then rethrow the
exception.

Change-Id: If4be5421f9731522beab80e843283b517d4aa41c
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
12 files changed:
src/v4/moth/qv4instr_moth_p.h
src/v4/moth/qv4isel_moth.cpp
src/v4/moth/qv4isel_moth_p.h
src/v4/moth/qv4vme_moth.cpp
src/v4/qv4codegen.cpp
src/v4/qv4isel_llvm.cpp
src/v4/qv4isel_masm.cpp
src/v4/qv4isel_masm_p.h
src/v4/qv4isel_p.cpp
src/v4/qv4isel_p.h
src/v4/qv4jsir.cpp
src/v4/qv4jsir_p.h

index 8810a92..7a9b59c 100644 (file)
     F(LoadProperty, loadProperty) \
     F(StoreProperty, storeProperty) \
     F(Push, push) \
+    F(EnterTry, enterTry) \
     F(CallValue, callValue) \
     F(CallProperty, callProperty) \
     F(CallElement, callElement) \
     F(CallActivationProperty, callActivationProperty) \
     F(CallBuiltinThrow, callBuiltinThrow) \
-    F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \
     F(CallBuiltinFinishTry, callBuiltinFinishTry) \
     F(CallBuiltinGetException, callBuiltinGetException) \
     F(CallBuiltinPushScope, callBuiltinPushScope) \
@@ -210,6 +210,11 @@ union Instr
         MOTH_INSTR_HEADER
         quint32 value;
     };
+    struct instr_enterTry {
+        MOTH_INSTR_HEADER
+        ptrdiff_t tryOffset;
+        ptrdiff_t catchOffset;
+    };
     struct instr_callValue {
         MOTH_INSTR_HEADER
         quint32 argc;
@@ -244,10 +249,6 @@ union Instr
         MOTH_INSTR_HEADER
         Param arg;
     };
-    struct instr_callBuiltinCreateExceptionHandler {
-        MOTH_INSTR_HEADER
-        Param result;
-    };
     struct instr_callBuiltinFinishTry {
         MOTH_INSTR_HEADER
     };
@@ -464,12 +465,12 @@ union Instr
     instr_loadProperty loadProperty;
     instr_storeProperty storeProperty;
     instr_push push;
+    instr_enterTry enterTry;
     instr_callValue callValue;
     instr_callProperty callProperty;
     instr_callElement callElement;
     instr_callActivationProperty callActivationProperty;
     instr_callBuiltinThrow callBuiltinThrow;
-    instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler;
     instr_callBuiltinFinishTry callBuiltinFinishTry;
     instr_callBuiltinGetException callBuiltinGetException;
     instr_callBuiltinPushScope callBuiltinPushScope;
index c416839..46a40d0 100644 (file)
@@ -677,6 +677,20 @@ void InstructionSelection::visitRet(IR::Ret *s)
     addInstruction(ret);
 }
 
+void InstructionSelection::visitTry(IR::Try *t)
+{
+    Instruction::EnterTry enterTry;
+    enterTry.tryOffset = 0;
+    enterTry.catchOffset = 0;
+    ptrdiff_t enterTryLoc = addInstruction(enterTry);
+
+    ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry));
+    _patches[t->tryBlock].append(tryLoc);
+
+    ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry));
+    _patches[t->catchBlock].append(catchLoc);
+}
+
 void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result)
 {
     Instruction::CallActivationProperty call;
@@ -829,13 +843,6 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg)
     addInstruction(call);
 }
 
-void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
-{
-    Instruction::CallBuiltinCreateExceptionHandler call;
-    call.result = getResultParam(result);
-    addInstruction(call);
-}
-
 void InstructionSelection::callBuiltinFinishTry()
 {
     Instruction::CallBuiltinFinishTry call;
index 161fbf3..04bb72d 100644 (file)
@@ -24,6 +24,7 @@ protected:
     virtual void visitJump(IR::Jump *);
     virtual void visitCJump(IR::CJump *);
     virtual void visitRet(IR::Ret *);
+    virtual void visitTry(IR::Try *);
 
     virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result);
     virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result);
@@ -43,7 +44,6 @@ protected:
     virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result);
     virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result);
     virtual void callBuiltinThrow(IR::Temp *arg);
-    virtual void callBuiltinCreateExceptionHandler(IR::Temp *result);
     virtual void callBuiltinFinishTry();
     virtual void callBuiltinGetException(IR::Temp *result);
     virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result);
index 0d2f2bc..e47dae2 100644 (file)
@@ -258,34 +258,26 @@ VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code,
         __qmljs_builtin_throw(context, VALUE(instr.arg));
     MOTH_END_INSTR(CallBuiltinThrow)
 
-    MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler)
+    MOTH_BEGIN_INSTR(EnterTry)
         __qmljs_create_exception_handler(context);
-        VM::Value *result = getValueRef(context, stack, instr.result
-#if !defined(QT_NO_DEBUG)
-                                        , stackSize
-#endif
-                                        );
-         try {
-            *result = VM::Value::fromInt32(0);
-            const uchar *tryCode = code;
+        try {
+            const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset;
             run(context, tryCode, stack, stackSize);
             code = tryCode;
         } catch (VM::Exception &ex) {
             ex.accept(context);
             try {
-                *result = VM::Value::fromInt32(1);
-                const uchar *catchCode = code;
+                const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset;
                 run(context, catchCode, stack, stackSize);
                 code = catchCode;
             } catch (VM::Exception &ex) {
                 ex.accept(context);
-                *result = VM::Value::fromInt32(1);
-                const uchar *catchCode = code;
+                const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset;
                 run(context, catchCode, stack, stackSize);
                 code = catchCode;
             }
         }
-    MOTH_END_INSTR(CallBuiltinCreateExceptionHandler)
+    MOTH_END_INSTR(EnterTry)
 
     MOTH_BEGIN_INSTR(CallBuiltinFinishTry)
         return VM::Value();
index e6c6f34..8946041 100644 (file)
@@ -111,6 +111,7 @@ struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor
     virtual void visitJump(IR::Jump *) {}
     virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
     virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
+    virtual void visitTry(IR::Try *) {}
 
     virtual void visitTemp(IR::Temp *e) {
         if (e->index < 0 || e->scope != 0)
@@ -1769,6 +1770,9 @@ void Codegen::linearize(IR::Function *function)
                         trace(cj->iffalse, V, output);
                     else
                         trace(cj->iftrue, V, output);
+                } else if (IR::Try *t = term->asTry()) {
+                    trace(t->tryBlock, V, output);
+                    trace(t->catchBlock, V, output);
                 }
             }
 
@@ -2448,7 +2452,7 @@ bool Codegen::visit(TryStatement *ast)
         throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode"));
 
     IR::BasicBlock *tryBody = _function->newBasicBlock();
-    IR::BasicBlock *catchBody = ast->catchExpression ?  _function->newBasicBlock() : 0;
+    IR::BasicBlock *catchBody =  _function->newBasicBlock();
     // We always need a finally body to clean up the exception handler
     IR::BasicBlock *finallyBody = _function->newBasicBlock();
 
@@ -2456,52 +2460,40 @@ bool Codegen::visit(TryStatement *ast)
     IR::ExprList *throwArgs = _function->New<IR::ExprList>();
     throwArgs->expr = throwBlock->TEMP(_returnAddress);
     throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
-    throwBlock->JUMP(catchBody ? catchBody : finallyBody);
+    throwBlock->JUMP(catchBody);
     qSwap(_throwBlock, throwBlock);
 
-    int inCatch = 0;
-    if (catchBody) {
-        inCatch = _block->newTemp();
-        move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false));
-    }
-
     int hasException = _block->newTemp();
-    move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0));
+    move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
 
-    // Pass the hidden "inCatch" and "hasException" TEMPs to the
+    // Pass the hidden "needRethrow" TEMP to the
     // builtin_delete_exception_handler, in order to have those TEMPs alive for
     // the duration of the exception handling block.
     IR::ExprList *finishTryArgs = _function->New<IR::ExprList>();
     finishTryArgs->init(_block->TEMP(hasException));
-    if (inCatch) {
-        finishTryArgs->next = _function->New<IR::ExprList>();
-        finishTryArgs->next->init(_block->TEMP(inCatch));
-    }
 
     ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs);
     _scopeAndFinally = &tcf;
 
-    _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody);
+    _block->TRY(tryBody, catchBody);
 
     _block = tryBody;
     statement(ast->statement);
     _block->JUMP(finallyBody);
 
-    // regular flow does not go into the catch statement
-    if (catchBody) {
-        _block = catchBody;
+    _block = catchBody;
 
-        if (inCatch != 0) {
-            // check if an exception got thrown within catch. Go to finally
-            // and then rethrow
-            IR::BasicBlock *b = _function->newBasicBlock();
-            _block->CJUMP(_block->TEMP(inCatch), finallyBody, b);
-            _block = b;
-        }
-        // if we have finally we need to clear the exception here, so we don't rethrow
-        move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true));
-        move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
+    if (ast->catchExpression) {
+        // check if an exception got thrown within catch. Go to finally
+        // and then rethrow
+        IR::BasicBlock *b = _function->newBasicBlock();
+        _block->CJUMP(_block->TEMP(hasException), finallyBody, b);
+        _block = b;
+    }
+
+    move(_block->TEMP(hasException), _block->CONST(IR::BoolType, true));
 
+    if (ast->catchExpression) {
         IR::ExprList *catchScopeArgs = _function->New<IR::ExprList>();
         catchScopeArgs->init(_block->NAME(ast->catchExpression->name.toString(), ast->catchExpression->identifierToken.startLine, ast->catchExpression->identifierToken.startColumn));
         _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchScopeArgs));
@@ -2515,9 +2507,10 @@ bool Codegen::visit(TryStatement *ast)
         }
         --_function->insideWithOrCatch;
 
+        move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
         _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0)));
-        _block->JUMP(finallyBody);
     }
+    _block->JUMP(finallyBody);
 
     _scopeAndFinally = tcf.parent;
 
index 2dee4a8..b79001c 100644 (file)
@@ -1140,12 +1140,6 @@ void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result)
             _llvmValue = llvm::UndefValue::get(_valueTy);
             return;
 
-        case IR::Name::builtin_create_exception_handler:
-            CreateCall2(getRuntimeFunction("__qmljs_llvm_create_exception_handler"),
-                        _llvmFunction->arg_begin(), result);
-            _llvmValue = CreateLoad(result);
-            return;
-
         case IR::Name::builtin_finish_try:
             // ### FIXME.
             return;
index fc39f4c..69e6d21 100644 (file)
@@ -131,6 +131,11 @@ void Assembler::addPatch(DataLabelPtr patch, Label target)
     _dataLabelPatches.append(p);
 }
 
+void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target)
+{
+    _labelPatches[target].append(patch);
+}
+
 Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t)
 {
     int32_t offset = 0;
@@ -410,14 +415,16 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi
 
 void Assembler::link(VM::Function *vmFunc)
 {
-    QHashIterator<IR::BasicBlock *, QVector<Jump> > it(_patches);
-    while (it.hasNext()) {
-        it.next();
-        IR::BasicBlock *block = it.key();
-        Label target = _addrs.value(block);
-        assert(target.isSet());
-        foreach (Jump jump, it.value())
-            jump.linkTo(target, this);
+    {
+        QHashIterator<IR::BasicBlock *, QVector<Jump> > it(_patches);
+        while (it.hasNext()) {
+            it.next();
+            IR::BasicBlock *block = it.key();
+            Label target = _addrs.value(block);
+            assert(target.isSet());
+            foreach (Jump jump, it.value())
+                jump.linkTo(target, this);
+        }
     }
 
     JSC::JSGlobalData dummy;
@@ -433,6 +440,18 @@ void Assembler::link(VM::Function *vmFunc)
     foreach (const DataLabelPatch &p, _dataLabelPatches)
         linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
 
+    {
+        QHashIterator<IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches);
+        while (it.hasNext()) {
+            it.next();
+            IR::BasicBlock *block = it.key();
+            Label target = _addrs.value(block);
+            assert(target.isSet());
+            foreach (DataLabelPtr label, it.value())
+                linkBuffer.patch(label, linkBuffer.locationOf(target));
+        }
+    }
+
     static bool showCode = !qgetenv("SHOW_CODE").isNull();
     if (showCode) {
 #if OS(LINUX)
@@ -478,9 +497,11 @@ InstructionSelection::~InstructionSelection()
 void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
 {
     QVector<Lookup> lookups;
+    QSet<IR::BasicBlock*> reentryBlocks;
     qSwap(_function, function);
     qSwap(_vmFunction, vmFunction);
     qSwap(_lookups, lookups);
+    qSwap(_reentryBlocks, reentryBlocks);
     Assembler* oldAssembler = _as;
     _as = new Assembler(_function, _vmFunction);
 
@@ -505,6 +526,18 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
     foreach (IR::BasicBlock *block, _function->basicBlocks) {
         _block = block;
         _as->registerBlock(_block);
+
+        if (_reentryBlocks.contains(_block)) {
+            _as->enterStandardStackFrame(/*locals*/0);
+#ifdef ARGUMENTS_IN_REGISTERS
+            _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister);
+            _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister);
+#else
+            _as->loadPtr(addressForArgument(0), Assembler::ContextRegister);
+            _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister);
+#endif
+        }
+
         foreach (IR::Stmt *s, block->statements) {
             s->accept(this);
         }
@@ -533,6 +566,7 @@ void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
     qSwap(_vmFunction, vmFunction);
     qSwap(_function, function);
     qSwap(_lookups, lookups);
+    qSwap(_reentryBlocks, reentryBlocks);
     delete _as;
     _as = oldAssembler;
 }
@@ -642,55 +676,38 @@ void InstructionSelection::callBuiltinThrow(IR::Temp *arg)
     generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg));
 }
 
-static void *tryWrapper(ExecutionContext *context, void *localsPtr, void *(*exceptionEntryPointInCallingFunction)(ExecutionContext*, void*, int))
+typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr));
+static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody)
 {
     void *addressToContinueAt = 0;
     try {
-        addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 0);
+        addressToContinueAt = tryBody(context, localsPtr);
     } catch (Exception& ex) {
         ex.accept(context);
         try {
-            addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1);
+            addressToContinueAt = catchBody(context, localsPtr);
         } catch (Exception& ex) {
             ex.accept(context);
-            addressToContinueAt = exceptionEntryPointInCallingFunction(context, localsPtr, 1);
+            addressToContinueAt = catchBody(context, localsPtr);
         }
     }
     return addressToContinueAt;
 }
 
-void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
+void InstructionSelection::visitTry(IR::Try *t)
 {
     generateFunctionCall(Assembler::Void, __qmljs_create_exception_handler, Assembler::ContextRegister);
 
-    // Call tryWrapper, which is going to re-enter the same function again below.
-    // When tryWrapper returns, it returns the with address of where to continue.
-    Assembler::DataLabelPtr movePatch = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ScratchRegister);
-    generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, Assembler::ScratchRegister);
-    _as->jump(Assembler::ReturnValueRegister);
+    // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end
+    // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper
+    // returns and to which we jump to.
 
-    // tryWrapper calls us at this place with arg3 == 0 if we're supposed to execute the try block
-    // and arg == 1 if we caught an exception. The generated IR takes care of returning from this
-    // call when deleteExceptionHandler is called.
-    _as->addPatch(movePatch, _as->label());
-    _as->enterStandardStackFrame(/*locals*/0);
-#ifdef ARGUMENTS_IN_REGISTERS
-    _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister);
-    _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister);
-#else
-    _as->loadPtr(addressForArgument(0), Assembler::ContextRegister);
-    _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister);
-#endif
+    _reentryBlocks.insert(t->tryBlock);
+    _reentryBlocks.insert(t->catchBlock);
 
-    Address addr = _as->loadTempAddress(Assembler::ScratchRegister, result);
-#ifdef ARGUMENTS_IN_REGISTERS
-    _as->store32(Assembler::registerForArgument(2), addr);
-#else
-    _as->load32(addressForArgument(2), Assembler::ReturnValueRegister);
-    _as->store32(Assembler::ReturnValueRegister, addr);
-#endif
-    addr.offset += 4;
-    _as->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr);
+    generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister,
+                         Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock));
+    _as->jump(Assembler::ReturnValueRegister);
 }
 
 void InstructionSelection::callBuiltinFinishTry()
index 0d3a5a1..92b30ab 100644 (file)
@@ -205,6 +205,11 @@ public:
         IR::Temp *value;
     };
 
+    struct ReentryBlock {
+        ReentryBlock(IR::BasicBlock *b) : block(b) {}
+        IR::BasicBlock *block;
+    };
+
     void callAbsolute(const char* functionName, FunctionPtr function) {
         CallToLink ctl;
         ctl.call = call();
@@ -217,6 +222,7 @@ public:
     void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target);
     void addPatch(IR::BasicBlock* targetBlock, Jump targetJump);
     void addPatch(DataLabelPtr patch, Label target);
+    void addPatch(DataLabelPtr patch, IR::BasicBlock *target);
 
     Pointer loadTempAddress(RegisterID reg, IR::Temp *t);
 
@@ -252,6 +258,13 @@ public:
         loadArgument(addr, dest);
     }
 
+    void loadArgument(ReentryBlock block, RegisterID dest)
+    {
+        assert(block.block);
+        DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest);
+        addPatch(patch, block.block);
+    }
+
 #ifdef VALUE_FITS_IN_REGISTER
     void loadArgument(IR::Temp* temp, RegisterID dest)
     {
@@ -372,6 +385,14 @@ public:
         push(ptr);
     }
 
+    void push(ReentryBlock block)
+    {
+        assert(block.block);
+        DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister);
+        push(ScratchRegister);
+        addPatch(patch, block.block);
+    }
+
     void push(IR::Temp* temp)
     {
         if (temp) {
@@ -468,6 +489,8 @@ public:
     { return sizeof(void *); }
     static inline int sizeOfArgument(const Reference &)
     { return sizeof(void *); }
+    static inline int sizeOfArgument(const ReentryBlock &)
+    { return sizeof(void *); }
     static inline int sizeOfArgument(TrustedImmPtr)
     { return sizeof(void*); }
     static inline int sizeOfArgument(TrustedImm32)
@@ -731,6 +754,8 @@ private:
         Label target;
     };
     QList<DataLabelPatch> _dataLabelPatches;
+
+    QHash<IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
 };
 
 class Q_V4_EXPORT InstructionSelection:
@@ -762,7 +787,6 @@ protected:
     virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result);
     virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result);
     virtual void callBuiltinThrow(IR::Temp *arg);
-    virtual void callBuiltinCreateExceptionHandler(IR::Temp *result);
     virtual void callBuiltinFinishTry();
     virtual void callBuiltinGetException(IR::Temp *result);
     virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result);
@@ -829,6 +853,7 @@ protected:
     virtual void visitJump(IR::Jump *);
     virtual void visitCJump(IR::CJump *);
     virtual void visitRet(IR::Ret *);
+    virtual void visitTry(IR::Try *);
 
 private:
     #define isel_stringIfyx(s) #s
@@ -851,6 +876,7 @@ private:
     VM::Function* _vmFunction;
     QVector<VM::Lookup> _lookups;
     Assembler* _as;
+    QSet<IR::BasicBlock*> _reentryBlocks;
 };
 
 class Q_V4_EXPORT ISelFactory: public EvalISelFactory
index 49cc3b3..85758df 100644 (file)
@@ -310,11 +310,6 @@ void InstructionSelection::callBuiltin(IR::Call *call, IR::Temp *result)
         callBuiltinThrow(arg);
     } return;
 
-    case IR::Name::builtin_create_exception_handler: {
-        callBuiltinCreateExceptionHandler(result);
-        return;
-    }
-
     case IR::Name::builtin_finish_try:
         callBuiltinFinishTry();
         return;
index 7fabf3a..9226f06 100644 (file)
@@ -103,7 +103,6 @@ public: // to implement by subclasses:
     virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0;
     virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0;
     virtual void callBuiltinThrow(IR::Temp *arg) = 0;
-    virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0;
     virtual void callBuiltinFinishTry() = 0;
     virtual void callBuiltinGetException(IR::Temp *result) = 0;
     virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0;
index fbac69e..c4953a8 100644 (file)
@@ -216,6 +216,10 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
         s->expr = cleanup(s->expr);
     }
 
+    virtual void visitTry(Try *)
+    {
+        // nothing to do for Try statements
+    }
 
     // expressions
     virtual void visitConst(Const *) {}
@@ -352,8 +356,6 @@ static const char *builtin_to_string(Name::Builtin b)
         return "builtin_postdecrement";
     case Name::builtin_throw:
         return "builtin_throw";
-    case Name::builtin_create_exception_handler:
-        return "builtin_create_exception_handler";
     case Name::builtin_finish_try:
         return "builtin_finish_try";
     case Name::builtin_get_exception:
@@ -522,6 +524,11 @@ void Ret::dump(QTextStream &out, Mode)
     out << ';';
 }
 
+void Try::dump(QTextStream &out, Stmt::Mode mode)
+{
+    out << "try L" << tryBlock->index << "; catch L" << catchBlock->index << ';';
+}
+
 Function *Module::newFunction(const QString &name, Function *outer)
 {
     Function *f = new Function(this, outer, name);
@@ -786,6 +793,30 @@ Stmt *BasicBlock::RET(Temp *expr)
     return s;
 }
 
+Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock)
+{
+    if (isTerminated())
+        return 0;
+
+    Try *t = function->New<Try>();
+    t->init(tryBlock, catchBlock);
+    statements.append(t);
+
+    assert(! out.contains(tryBlock));
+    out.append(tryBlock);
+
+    assert(! out.contains(catchBlock));
+    out.append(catchBlock);
+
+    assert(! tryBlock->in.contains(this));
+    tryBlock->in.append(this);
+
+    assert(! catchBlock->in.contains(this));
+    catchBlock->in.append(this);
+
+    return t;
+}
+
 void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
 {
     out << 'L' << index << ':' << endl;
index 50a5247..570b0db 100644 (file)
@@ -116,6 +116,7 @@ struct Move;
 struct Jump;
 struct CJump;
 struct Ret;
+struct Try;
 
 enum AluOp {
     OpInvalid = 0,
@@ -195,6 +196,7 @@ struct StmtVisitor {
     virtual void visitJump(Jump *) = 0;
     virtual void visitCJump(CJump *) = 0;
     virtual void visitRet(Ret *) = 0;
+    virtual void visitTry(Try *) = 0;
 };
 
 struct Expr {
@@ -289,7 +291,6 @@ struct Name: Expr {
         builtin_postincrement,
         builtin_postdecrement,
         builtin_throw,
-        builtin_create_exception_handler,
         builtin_finish_try,
         builtin_get_exception,
         builtin_foreach_iterator_object,
@@ -488,6 +489,7 @@ struct Stmt {
     virtual Jump *asJump() { return 0; }
     virtual CJump *asCJump() { return 0; }
     virtual Ret *asRet() { return 0; }
+    virtual Try *asTry() { return 0; }
     virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
 
     void destroyData() {
@@ -603,6 +605,24 @@ struct Ret: Stmt {
     virtual void dump(QTextStream &out, Mode);
 };
 
+struct Try: Stmt {
+    BasicBlock *tryBlock;
+    BasicBlock *catchBlock;
+
+    void init(BasicBlock *tryBlock, BasicBlock *catchBlock)
+    {
+        this->tryBlock = tryBlock;
+        this->catchBlock = catchBlock;
+    }
+
+    virtual Stmt *asTerminator() { return this; }
+
+    virtual void accept(StmtVisitor *v) { v->visitTry(this); }
+    virtual Try *asTry() { return this; }
+
+    virtual void dump(QTextStream &out, Mode mode);
+};
+
 struct Q_V4_EXPORT Module {
     MemoryPool pool;
     QVector<Function *> functions;
@@ -729,6 +749,7 @@ struct BasicBlock {
     Stmt *JUMP(BasicBlock *target);
     Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
     Stmt *RET(Temp *expr);
+    Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock);
 
     void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
 };