aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-06-01 12:21:08 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-06-21 12:27:21 +0000
commit267513e6d955cb93abb879c495dd079c254d3493 (patch)
tree8cd3bd26756142b80b9e12f77e8b3e7e868be35b /src/qml
parent93a70a051b21ae253331f922027bd9bb7a3e457a (diff)
Rework unwind handling
The old code was rather convoluted and expanded to quite a bit of bytecode. It was also very hard to fix some of the remaining issues with unwinding in there. The new code handles unwinding a bit differently. Basically, we now have three instructions to do what the spec requires. SetUnwindHandler is the same as the old SetExceptionHandler instruction. It basically tells the runtime where to jump to to handle any abrupt completion (ie. throw/break/continue/return) that requires unwinding. UnwindToLabel is a new instruction that is used for unwinding break/continue/return statements. It takes two arguments, one telling the runtime how many levels to unwind and the second a target label to jump to when unwinding is done. UnwindDispatch is the third instruction and is invoked at the end of each unwind block to dispatch the the parent unwind handler if required and thus implement the support for the levelled unwinding. Change-Id: I079a39d0d897b3ecc2f0dc631ca29b25eae05250 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h44
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp19
-rw-r--r--src/qml/compiler/qv4codegen.cpp135
-rw-r--r--src/qml/compiler/qv4codegen_p.h7
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h334
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp19
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h10
-rw-r--r--src/qml/jit/qv4assembler.cpp105
-rw-r--r--src/qml/jit/qv4assembler_p.h5
-rw-r--r--src/qml/jit/qv4baselinejit.cpp22
-rw-r--r--src/qml/jit/qv4baselinejit_p.h7
-rw-r--r--src/qml/jsruntime/qv4engine_p.h4
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp68
13 files changed, 349 insertions, 430 deletions
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index b12584d41f..78ce3624ea 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -81,14 +81,6 @@ public:
if (mode == LinkNow)
link();
}
- static Label returnLabel() {
- Label l;
- l.index = INT_MAX;
- return l;
- }
- bool isReturn() const {
- return index == INT_MAX;
- }
void link() {
Q_ASSERT(index >= 0);
@@ -96,6 +88,7 @@ public:
generator->labels[index] = generator->instructions.size();
generator->clearLastInstruction();
}
+ bool isValid() const { return generator != nullptr; }
BytecodeGenerator *generator = nullptr;
int index = -1;
@@ -192,6 +185,12 @@ public:
return addJumpInstruction(data);
}
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
void jumpStrictEqual(const StackSlot &lhs, const Label &target)
{
Instruction::CmpStrictEqual cmp;
@@ -208,22 +207,6 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(target);
}
- Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictNotEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
void setUnwindHandler(ExceptionHandler *handler)
{
currentExceptionHandler = handler;
@@ -235,6 +218,19 @@ public:
addJumpInstruction(data).link(*handler);
}
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
void setLocation(const QQmlJS::AST::SourceLocation &loc);
ExceptionHandler *exceptionHandler() const {
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index 27182dcc72..ccd54c7444 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -273,6 +273,13 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(SetUnwindHandler)
+ COLLECTOR_BEGIN_INSTR(UnwindDispatch)
+ COLLECTOR_END_INSTR(UnwindDispatch)
+
+ COLLECTOR_BEGIN_INSTR(UnwindToLabel)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(UnwindToLabel)
+
COLLECTOR_BEGIN_INSTR(ThrowException)
COLLECTOR_END_INSTR(ThrowException)
@@ -372,6 +379,10 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(JumpFalse)
+ COLLECTOR_BEGIN_INSTR(JumpNoException)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNoException)
+
COLLECTOR_BEGIN_INSTR(JumpNotUndefined)
addLabel(code - start + offset);
COLLECTOR_END_INSTR(JumpNotUndefined)
@@ -422,14 +433,6 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
COLLECTOR_END_INSTR(CmpInstanceOf)
- COLLECTOR_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- addLabel(code - start + offset);
- COLLECTOR_END_INSTR(JumpStrictEqualStackSlotInt)
-
- COLLECTOR_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- addLabel(code - start + offset);
- COLLECTOR_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
COLLECTOR_BEGIN_INSTR(UNot)
COLLECTOR_END_INSTR(UNot)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index ec2c0dad3e..9337f6c625 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -90,7 +90,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(nullptr)
- , _returnAddress(0)
+ , _returnAddress(-1)
, _context(nullptr)
, _labelledStatement(nullptr)
, jsUnitGenerator(jsUnitGenerator)
@@ -2376,6 +2376,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
savedBytecodeGenerator = bytecodeGenerator;
bytecodeGenerator = &bytecode;
bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
// reserve the js stack frame (Context & js Function & accumulator)
bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
@@ -2386,8 +2388,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
int returnAddress = -1;
bool _requiresReturnValue = _context->requiresImplicitReturnValue();
qSwap(requiresReturnValue, _requiresReturnValue);
- if (requiresReturnValue)
- returnAddress = bytecodeGenerator->newRegister();
+ returnAddress = bytecodeGenerator->newRegister();
qSwap(_returnAddress, returnAddress);
// register the lexical scope for global code
@@ -2438,19 +2439,21 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
statementList(body);
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
_context->emitBlockFooter(this);
- if (hasError || !endsWithReturn(_module, body)) {
- bytecodeGenerator->setLocation(ast->lastSourceLocation());
- if (requiresReturnValue) {
- if (_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(_returnAddress);
- bytecodeGenerator->addInstruction(load);
- }
+ if (_returnLabel || hasError || !endsWithReturn(_module, body)) {
+ if (_returnLabel)
+ _returnLabel->link();
+
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
} else {
Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
}
+
bytecodeGenerator->addInstruction(Instruction::Ret());
}
@@ -2470,6 +2473,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(requiresReturnValue, _requiresReturnValue);
qSwap(_inFormalParameterList, inFormalParameterList);
bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
controlFlow = savedControlFlow;
_functionContext = savedFunctionContext;
@@ -2498,8 +2503,8 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
else
@@ -2507,7 +2512,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2524,8 +2529,8 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
else
@@ -2533,7 +2538,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2628,51 +2633,53 @@ bool Codegen::visit(ForEachStatement *ast)
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
- bytecodeGenerator->jump().link(in);
-
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- // each iteration gets it's own context, as per spec
{
- RegisterScope innerScope(this);
- ControlFlowBlock controlFlow(this, ast);
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
- if (ExpressionNode *e = ast->lhs->expressionCast()) {
- if (AST::Pattern *p = e->patternCast()) {
- RegisterScope scope(this);
- destructurePattern(p, lhsValue);
- } else {
- Reference lhs = expression(e);
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue);
if (hasError)
goto error;
- lhs = lhs.asLValue();
- lhsValue.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ } else {
+ Q_UNREACHABLE();
}
- } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
- initializeAndDestructureBindingElement(p, lhsValue);
- if (hasError)
- goto error;
- } else {
- Q_UNREACHABLE();
- }
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- }
+ }
- error:
- in.link();
- iterator.loadInAccumulator();
- Instruction::IteratorNext next;
- next.value = lhsValue.stackSlot();
- bytecodeGenerator->addInstruction(next);
- bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
- BytecodeGenerator::Jump done = bytecodeGenerator->jump();
+ error:
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
end.link();
@@ -2796,13 +2803,11 @@ bool Codegen::visit(LabelledStatement *ast)
void Codegen::emitReturn(const Reference &expr)
{
- if (controlFlow && controlFlow->returnRequiresUnwind()) {
- if (_returnAddress >= 0)
- (void) expr.storeOnStack(_returnAddress);
- else
- expr.loadInAccumulator();
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Return);
- controlFlow->jumpToHandler(h);
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
} else {
expr.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Ret());
@@ -2923,13 +2928,9 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return false;
- if (controlFlow) {
- controlFlow->handleThrow(expr);
- } else {
- expr.loadInAccumulator();
- Instruction::ThrowException instr;
- bytecodeGenerator->addInstruction(instr);
- }
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
return false;
}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 80ef8edfcc..697da71480 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -660,6 +660,12 @@ public:
Module *module() const { return _module; }
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
+
protected:
friend class ScanFunctions;
friend struct ControlFlow;
@@ -674,6 +680,7 @@ protected:
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index b6c207e68c..9ab581f015 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -53,6 +53,7 @@
#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
@@ -73,20 +74,15 @@ struct ControlFlow {
Catch
};
- enum HandlerType {
- Invalid,
+ enum UnwindType {
Break,
Continue,
- Return,
- Throw
+ Return
};
- struct Handler {
- HandlerType type;
- QString label;
+ struct UnwindTarget {
BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
+ int unwindLevel;
};
Codegen *cg;
@@ -103,82 +99,44 @@ struct ControlFlow {
cg->controlFlow = parent;
}
- void emitReturnStatement() const {
- if (cg->_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
- generator()->addInstruction(load);
- }
- Instruction::Ret ret;
- cg->bytecodeGenerator->addInstruction(ret);
- }
-
- void jumpToHandler(const Handler &h) {
- if (h.linkLabel.isReturn()) {
- emitReturnStatement();
- } else {
- if (h.tempIndex >= 0)
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
- }
-
- bool returnRequiresUnwind() const {
- const ControlFlow *f = this;
- while (f) {
- if (f->type == Finally)
- return true;
- f = f->parent;
+ UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
+ {
+ Q_ASSERT(type == Break || type == Continue || type == Return);
+ ControlFlow *flow = this;
+ int level = 0;
+ while (flow) {
+ BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
+ if (l.isValid())
+ return UnwindTarget{l, level};
+ if (flow->requiresUnwind())
+ ++level;
+ flow = flow->parent;
}
- return false;
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
}
virtual QString label() const { return QString(); }
- bool isSimple() const {
- return type == Loop;
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
}
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
+ virtual bool requiresUnwind() {
+ return false;
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
-
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : nullptr;
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parentExceptionHandler();
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
}
- virtual void handleThrow(const Reference &expr) {
- Reference e = expr;
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- e = e.storeOnStack();
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- }
- e.loadInAccumulator();
- Instruction::ThrowException instr;
- generator()->addInstruction(instr);
- }
-
protected:
QString loopLabel() const {
QString label;
@@ -193,119 +151,87 @@ protected:
}
};
-struct ControlFlowLoop : public ControlFlow
-{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = nullptr;
- BytecodeGenerator::Label *continueLabel = nullptr;
-
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
- {
- }
-
- virtual QString label() const { return loopLabel; }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return getParentHandler(type, label);
- }
-
-};
-
struct ControlFlowUnwind : public ControlFlow
{
BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
ControlFlowUnwind(Codegen *cg, Type type)
: ControlFlow(cg, type)
{
- Q_ASSERT(type != Loop);
}
- void setupExceptionHandler()
+ void setupUnwindHandler()
{
unwindLabel = generator()->newExceptionHandler();
- controlFlowTemp = static_cast<int>(generator()->newRegister());
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- // we'll need at least a handler for throw
- getHandler(Throw);
}
void emitUnwindHandler()
{
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Handler parentHandler = getParentHandler(h.type, h.label);
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- jumpToHandler(parentHandler);
- skip.link();
- } else {
- if (parentHandler.linkLabel.isReturn()) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- emitReturnStatement();
- skip.link();
- } else {
- generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
- }
- }
+ Q_ASSERT(requiresUnwind());
+
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
+};
+
+struct ControlFlowLoop : public ControlFlowUnwind
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
+ bool _requiresUnwind;
+
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, bool requiresUnwind = false)
+ : ControlFlowUnwind(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel), _requiresUnwind(requiresUnwind)
+ {
+ if (_requiresUnwind) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
}
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
+ ~ControlFlowLoop() {
+ if (_requiresUnwind) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ emitUnwindHandler();
}
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return unwindLabel.isValid() ? &unwindLabel : parentExceptionHandler();
+ bool requiresUnwind() override {
+ return _requiresUnwind;
}
- virtual void emitForThrowHandling() { }
+ BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return *breakLabel;
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return *continueLabel;
+ break;
+ default:
+ break;
+ }
+ return BytecodeGenerator::Label();
+ }
+
+ QString label() const override { return loopLabel; }
};
+
struct ControlFlowWith : public ControlFlowUnwind
{
ControlFlowWith(Codegen *cg)
: ControlFlowUnwind(cg, With)
{
- setupExceptionHandler();
+ setupUnwindHandler();
// assumes the with object is in the accumulator
Instruction::PushWithContext pushScope;
@@ -313,16 +239,22 @@ struct ControlFlowWith : public ControlFlowUnwind
generator()->setUnwindHandler(&unwindLabel);
}
- virtual ~ControlFlowWith() {
+ ~ControlFlowWith() {
// emit code for unwinding
unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
Instruction::PopContext pop;
generator()->addInstruction(pop);
emitUnwindHandler();
}
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
};
struct ControlFlowBlock : public ControlFlowUnwind
@@ -334,16 +266,16 @@ struct ControlFlowBlock : public ControlFlowUnwind
block->emitBlockHeader(cg);
if (block->requiresExecutionContext) {
- setupExceptionHandler();
+ setupUnwindHandler();
generator()->setUnwindHandler(&unwindLabel);
}
}
virtual ~ControlFlowBlock() {
// emit code for unwinding
- if (block->requiresExecutionContext ) {
+ if (block->requiresExecutionContext) {
unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
}
block->emitBlockFooter(cg);
@@ -352,10 +284,9 @@ struct ControlFlowBlock : public ControlFlowUnwind
emitUnwindHandler();
cg->leaveBlock();
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- if (!block->requiresExecutionContext )
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
}
Context *block;
@@ -366,54 +297,39 @@ struct ControlFlowCatch : public ControlFlowUnwind
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
bool oldLookupByName;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
+ exceptionLabel(generator()->newExceptionHandler())
{
- setupExceptionHandler();
generator()->setUnwindHandler(&exceptionLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = getParentHandler(type, label);
- if (h.type == Invalid)
- return h;
- h = ControlFlowUnwind::getHandler(type, label);
- if (insideCatch)
- // if we're inside the catch block, we need to jump to the pop scope
- // instruction at the end of the catch block, not the unwind handler
- h.linkLabel = catchUnwindLabel;
- else if (type == Throw)
- // if we're inside the try block, we need to jump to the catch block,
- // not the unwind handler
- h.linkLabel = exceptionLabel;
- return h;
+ virtual bool requiresUnwind() override {
+ return true;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideCatch ? &unwindLabel : &exceptionLabel;
}
~ControlFlowCatch() {
// emit code for unwinding
insideCatch = true;
+ setupUnwindHandler();
Codegen::RegisterScope scope(cg);
// exceptions inside the try block go here
exceptionLabel.link();
+ BytecodeGenerator::Jump noException = generator()->jumpNoException();
Context *block = cg->enterBlock(catchExpression);
block->emitBlockHeader(cg);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- generator()->setUnwindHandler(&catchUnwindLabel);
+ generator()->setUnwindHandler(&unwindLabel);
if (catchExpression->patternElement->bindingIdentifier.isEmpty())
// destructuring pattern
@@ -421,19 +337,17 @@ struct ControlFlowCatch : public ControlFlowUnwind
// skip the additional block
cg->statementList(catchExpression->statement->statements);
- insideCatch = false;
-
// exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
+ unwindLabel.link();
block->emitBlockFooter(cg);
cg->leaveBlock();
- // break/continue/return statements in try go here
- unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
+ insideCatch = false;
}
};
@@ -447,20 +361,16 @@ struct ControlFlowFinally : public ControlFlowUnwind
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
- setupExceptionHandler();
+ setupUnwindHandler();
generator()->setUnwindHandler(&unwindLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- // if we're inside the finally block, any exceptions etc. should
- // go directly to the parent handler
- if (insideFinally)
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
}
~ControlFlowFinally() {
@@ -469,34 +379,22 @@ struct ControlFlowFinally : public ControlFlowUnwind
Codegen::RegisterScope scope(cg);
- Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::StoreReg storeRetVal;
- storeRetVal.reg = retVal;
- generator()->addInstruction(storeRetVal);
-
insideFinally = true;
exceptionTemp = generator()->newRegister();
Instruction::GetException instr;
generator()->addInstruction(instr);
Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
cg->statement(finally->statement);
insideFinally = false;
- Instruction::LoadReg loadRetVal;
- loadRetVal.reg = retVal;
- generator()->addInstruction(loadRetVal);
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
+
Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
generator()->addInstruction(setException);
+
+ emitUnwindHandler();
}
};
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index ec9f3fc972..4a11f46dc5 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -391,6 +391,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "<null>";
MOTH_END_INSTR(SetUnwindHandler)
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
+
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -511,6 +518,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNotUndefined)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(JumpEmpty)
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpEmpty)
@@ -561,14 +572,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
MOTH_END_INSTR(UNot)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index ea72be12fe..3adb9c2e0f 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -112,6 +112,8 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv)
#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
@@ -145,6 +147,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
#define INSTR_JumpEmpty(op) INSTRUCTION(op, JumpEmpty, 1, offset)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
@@ -160,8 +163,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
-#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
-#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
@@ -241,6 +242,8 @@ QT_BEGIN_NAMESPACE
F(CallScopeObjectProperty) \
F(CallContextObjectProperty) \
F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
F(ThrowException) \
F(GetException) \
F(SetException) \
@@ -273,6 +276,7 @@ QT_BEGIN_NAMESPACE
F(Jump) \
F(JumpTrue) \
F(JumpFalse) \
+ F(JumpNoException) \
F(JumpNotUndefined) \
F(JumpEmpty) \
F(CmpEqNull) \
@@ -289,8 +293,6 @@ QT_BEGIN_NAMESPACE
F(CmpStrictNotEqual) \
F(CmpIn) \
F(CmpInstanceOf) \
- F(JumpStrictEqualStackSlotInt) \
- F(JumpStrictNotEqualStackSlotInt) \
F(UNot) \
F(UPlus) \
F(UMinus) \
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index a32039dc96..5702774cd7 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -712,6 +712,11 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
patches.push_back({ jump, offset });
}
+ Jump jumpEmpty()
+ {
+ return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -815,26 +820,6 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
return branch32(Equal, TrustedImm32(3), ScratchRegister);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ equal, offset });
- isUndef.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- patches.push_back({ isUndef, offset });
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg == NoRegister)
@@ -1163,6 +1148,11 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
patches.push_back({ jump, offset });
}
+ Jump jumpEmpty()
+ {
+ return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1202,34 +1192,6 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
done.link(this);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
- Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefVal, offset });
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefTag, offset });
- notEqInt.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
- patches.push_back({ equalUndef, offset });
- notUndefValue.link(this);
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg != NoRegister)
@@ -2009,6 +1971,16 @@ void Assembler::jumpFalse(int offset)
});
}
+void Assembler::jumpNoException(int offset)
+{
+ auto jump = pasm()->branch32(
+ PlatformAssembler::Equal,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0));
+ pasm()->patches.push_back({ jump, offset });
+}
+
void Assembler::jumpNotUndefined(int offset)
{
pasm()->jumpNotUndefined(offset);
@@ -2019,16 +1991,6 @@ void JIT::Assembler::jumpEmpty(int offset)
pasm()->jumpEmpty(offset);
}
-void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
-}
-
-void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
-}
-
void Assembler::prepareCallWithArgCount(int argc)
{
#ifndef QT_NO_DEBUG
@@ -2220,12 +2182,14 @@ void Assembler::getException()
void Assembler::setException()
{
+ auto noException = pasm()->jumpEmpty();
Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
addr.offset = offsetof(EngineBase, hasException);
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
pasm()->store8(TrustedImm32(1), addr);
+ noException.link(pasm());
}
void Assembler::setUnwindHandler(int offset)
@@ -2240,6 +2204,31 @@ void Assembler::clearUnwindHandler()
pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
}
+void JIT::Assembler::unwindDispatch()
+{
+ checkException();
+ pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
+ auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
+ pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ gotoCatchException();
+ jump.link(pasm());
+
+ pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
+ pasm()->jump(PlatformAssembler::ScratchRegister);
+
+ noUnwind.link(pasm());
+}
+
+void JIT::Assembler::unwindToLabel(int level, int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
+ pasm()->ehTargets.push_back({ l, offset });
+ pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ gotoCatchException();
+}
+
void Assembler::pushCatchContext(int index, int name)
{
prepareCallWithArgCount(3);
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 73faf7b69f..d41b79f562 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -140,10 +140,9 @@ public:
void jump(int offset);
void jumpTrue(int offset);
void jumpFalse(int offset);
+ void jumpNoException(int offset);
void jumpNotUndefined(int offset);
void jumpEmpty(int offset);
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset);
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset);
// stuff for runtime calls
void prepareCallWithArgCount(int argc);
@@ -164,6 +163,8 @@ public:
void setException();
void setUnwindHandler(int offset);
void clearUnwindHandler();
+ void unwindDispatch();
+ void unwindToLabel(int level, int offset);
void pushCatchContext(int index, int name);
void popContext();
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index 031c6e620c..6c298d9331 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -561,6 +561,17 @@ void BaselineJIT::generate_SetUnwindHandler(int offset)
as->clearUnwindHandler();
}
+void BaselineJIT::generate_UnwindDispatch()
+{
+ as->unwindDispatch();
+}
+
+void BaselineJIT::generate_UnwindToLabel(int level, int offset)
+{
+ as->unwindToLabel(level, instructionOffset() + offset);
+}
+
+
void BaselineJIT::generate_ThrowException()
{
STORE_IP();
@@ -891,6 +902,7 @@ void BaselineJIT::generate_Construct(int func, int argc, int argv)
void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); }
void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); }
void BaselineJIT::generate_JumpEmpty(int offset) { as->jumpEmpty(instructionOffset() + offset); }
@@ -929,16 +941,6 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
as->checkException();
}
-void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
-void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
void BaselineJIT::generate_UNot() { as->unot(); }
void BaselineJIT::generate_UPlus() { as->toNumber(); }
void BaselineJIT::generate_UMinus() { as->uminus(); }
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 96fd8348b8..5441ea974b 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -129,6 +129,8 @@ public:
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_SetUnwindHandler(int offset) override;
+ void generate_UnwindDispatch() override;
+ void generate_UnwindToLabel(int level, int offset) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
@@ -161,6 +163,7 @@ public:
void generate_Jump(int offset) override;
void generate_JumpTrue(int offset) override;
void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
void generate_JumpNotUndefined(int offset) override;
void generate_JumpEmpty(int offset) override;
void generate_CmpEqNull() override;
@@ -177,10 +180,6 @@ public:
void generate_CmpStrictNotEqual(int lhs) override;
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
- void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
- void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
void generate_UNot() override;
void generate_UPlus() override;
void generate_UMinus() override;
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index ecc110a16f..b007e65c4b 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -97,7 +97,9 @@ struct Q_QML_EXPORT CppStackFrame {
int originalArgumentsCount;
int instructionPointer;
const char *yield;
- const char *exceptionHandler;
+ const char *unwindHandler;
+ const char *unwindLabel;
+ int unwindLevel;
QString source() const;
QString function() const;
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index e180298789..165902ed36 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -344,7 +344,7 @@ static struct InstrCount {
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
- goto catchException
+ goto handleUnwind
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
@@ -510,7 +510,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
frame.originalArguments = argv;
frame.originalArgumentsCount = argc;
frame.yield = nullptr;
- frame.exceptionHandler = nullptr;
+ frame.unwindHandler = nullptr;
+ frame.unwindLabel = nullptr;
+ frame.unwindLevel = 0;
+
Function *function;
{
@@ -810,7 +813,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (engine->hasException) {
// an empty value indicates that the generator was called with return()
if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue())
- goto catchException;
+ goto handleUnwind;
engine->hasException = false;
*engine->exceptionValue = Primitive::undefinedValue();
} else {
@@ -823,7 +826,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
Value func = STACK_VALUE(name);
if (Q_UNLIKELY(!func.isFunctionObject())) {
acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
CHECK_EXCEPTION;
@@ -843,7 +846,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
@@ -887,14 +890,30 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
MOTH_END_INSTR(CallContextObjectProperty)
MOTH_BEGIN_INSTR(SetUnwindHandler)
- frame.exceptionHandler = offset ? code + offset : nullptr;
+ frame.unwindHandler = offset ? code + offset : nullptr;
MOTH_END_INSTR(SetUnwindHandler)
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ CHECK_EXCEPTION;
+ if (frame.unwindLevel) {
+ --frame.unwindLevel;
+ if (frame.unwindLevel)
+ goto handleUnwind;
+ code = frame.unwindLabel;
+ }
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ frame.unwindLevel = level;
+ frame.unwindLabel = code + offset;
+ goto handleUnwind;
+ MOTH_END_INSTR(UnwindToLabel)
+
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
Runtime::method_throwException(engine, accumulator);
- goto catchException;
+ goto handleUnwind;
MOTH_END_INSTR(ThrowException)
MOTH_BEGIN_INSTR(GetException)
@@ -904,8 +923,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
MOTH_END_INSTR(HasException)
MOTH_BEGIN_INSTR(SetException)
- *engine->exceptionValue = acc;
- engine->hasException = true;
+ if (acc != Primitive::emptyValue().asReturnedValue()) {
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ }
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
@@ -979,7 +1000,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -992,7 +1013,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -1006,7 +1027,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
STORE_IP();
QString n = function->compilationUnit->runtimeStrings[name]->toQString();
engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -1096,6 +1117,11 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
}
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ if (!engine->hasException)
+ code += offset;
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(JumpNotUndefined)
if (Q_LIKELY(acc != QV4::Encode::undefined()))
code += offset;
@@ -1242,16 +1268,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
if (ACC.integerCompatible()) {
acc = Encode(!static_cast<bool>(ACC.int_32()));
@@ -1456,12 +1472,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
- catchException:
- Q_ASSERT(engine->hasException);
- if (!frame.exceptionHandler) {
+ handleUnwind:
+ Q_ASSERT(engine->hasException || frame.unwindLevel);
+ if (!frame.unwindHandler) {
acc = Encode::undefined();
return acc;
}
- code = frame.exceptionHandler;
+ code = frame.unwindHandler;
}
}