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) \
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;
MOTH_INSTR_HEADER
Param arg;
};
- struct instr_callBuiltinCreateExceptionHandler {
- MOTH_INSTR_HEADER
- Param result;
- };
struct instr_callBuiltinFinishTry {
MOTH_INSTR_HEADER
};
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;
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;
addInstruction(call);
}
-void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
-{
- Instruction::CallBuiltinCreateExceptionHandler call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
void InstructionSelection::callBuiltinFinishTry()
{
Instruction::CallBuiltinFinishTry call;
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);
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);
__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();
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)
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);
}
}
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();
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));
}
--_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;
_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;
_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;
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;
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)
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);
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);
}
qSwap(_vmFunction, vmFunction);
qSwap(_function, function);
qSwap(_lookups, lookups);
+ qSwap(_reentryBlocks, reentryBlocks);
delete _as;
_as = oldAssembler;
}
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()
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();
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);
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)
{
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) {
{ 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)
Label target;
};
QList<DataLabelPatch> _dataLabelPatches;
+
+ QHash<IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
};
class Q_V4_EXPORT InstructionSelection:
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);
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
VM::Function* _vmFunction;
QVector<VM::Lookup> _lookups;
Assembler* _as;
+ QSet<IR::BasicBlock*> _reentryBlocks;
};
class Q_V4_EXPORT ISelFactory: public EvalISelFactory
callBuiltinThrow(arg);
} return;
- case IR::Name::builtin_create_exception_handler: {
- callBuiltinCreateExceptionHandler(result);
- return;
- }
-
case IR::Name::builtin_finish_try:
callBuiltinFinishTry();
return;
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;
s->expr = cleanup(s->expr);
}
+ virtual void visitTry(Try *)
+ {
+ // nothing to do for Try statements
+ }
// expressions
virtual void visitConst(Const *) {}
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:
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);
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;
struct Jump;
struct CJump;
struct Ret;
+struct Try;
enum AluOp {
OpInvalid = 0,
virtual void visitJump(Jump *) = 0;
virtual void visitCJump(CJump *) = 0;
virtual void visitRet(Ret *) = 0;
+ virtual void visitTry(Try *) = 0;
};
struct Expr {
builtin_postincrement,
builtin_postdecrement,
builtin_throw,
- builtin_create_exception_handler,
builtin_finish_try,
builtin_get_exception,
builtin_foreach_iterator_object,
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() {
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;
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);
};