V4: Generate labels for backward jumps
When analyzing the bytecode from top-to-bottom in a single pass, we don't know when a jump back to previously seen code occurs. For example, in the baseline JIT we would already have generated code for some bytecode when we see a jump back (like at the end of a loop body), and we can't go back and insert a label to jump to. As JavaScript has no goto's, the only backward jumps are at the end of loops, so there are very few cases where we need to actually generate labels. This was previously handled by analyzing the bytecode twice: once to collect all jump targets, and then second pass over the bytecode to do the actual JITting (which would use the jump targets to insert labels). We can now do that with one single pass. So the trade-off is to store 4 bytes more per function plus 4 bytes for each loop, instead of having to analyze all functions only to find where all jumps are each time that function is JITted. Change-Id: I3abfcb69f65851a397dbd4a9762ea5e9e57495f6 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
965f49bb4f
commit
3f2efbd1b9
|
@ -180,6 +180,10 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
|
|||
|
||||
context->code = code;
|
||||
context->lineNumberMapping = lineNumbers;
|
||||
|
||||
for (const auto &li : _labelInfos) {
|
||||
context->labelInfo.push_back(instructions.at(labels.at(li.labelIndex)).position);
|
||||
}
|
||||
}
|
||||
|
||||
int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
|
||||
|
|
|
@ -306,6 +306,11 @@ QT_WARNING_POP
|
|||
return nTraceInfos;
|
||||
}
|
||||
|
||||
void addLoopStart(const Label &start)
|
||||
{
|
||||
_labelInfos.push_back({ start.index });
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct Jump;
|
||||
friend struct Label;
|
||||
|
@ -342,6 +347,11 @@ private:
|
|||
Moth::Instr lastInstr;
|
||||
|
||||
TraceInfoCount nTraceInfos = TraceInfoCount(0);
|
||||
|
||||
struct LabelInfo {
|
||||
int labelIndex;
|
||||
};
|
||||
std::vector<LabelInfo> _labelInfos;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -79,485 +79,3 @@ void ByteCodeHandler::decode(const char *code, uint len)
|
|||
|
||||
#undef DECODE_AND_DISPATCH
|
||||
#undef DISPATCH_INSTRUCTION
|
||||
|
||||
#define MOTH_UNUSED_ARGS0()
|
||||
#define MOTH_UNUSED_ARGS1(arg) \
|
||||
Q_UNUSED(arg);
|
||||
#define MOTH_UNUSED_ARGS2(arg1, arg2) \
|
||||
Q_UNUSED(arg1); \
|
||||
Q_UNUSED(arg2);
|
||||
#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
|
||||
Q_UNUSED(arg1); \
|
||||
Q_UNUSED(arg2); \
|
||||
Q_UNUSED(arg3);
|
||||
#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
|
||||
Q_UNUSED(arg1); \
|
||||
Q_UNUSED(arg2); \
|
||||
Q_UNUSED(arg3); \
|
||||
Q_UNUSED(arg4);
|
||||
#define MOTH_UNUSED_ARGS5(arg1, arg2, arg3, arg4, arg5) \
|
||||
Q_UNUSED(arg1); \
|
||||
Q_UNUSED(arg2); \
|
||||
Q_UNUSED(arg3); \
|
||||
Q_UNUSED(arg4); \
|
||||
Q_UNUSED(arg5);
|
||||
|
||||
#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
|
||||
MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
|
||||
|
||||
#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
|
||||
MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
|
||||
|
||||
#define COLLECTOR_BEGIN_INSTR(instr) \
|
||||
{ \
|
||||
INSTR_##instr(MOTH_DECODE_WITH_BASE) \
|
||||
INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
|
||||
Q_UNUSED(base_ptr);
|
||||
|
||||
#define COLLECTOR_END_INSTR(instr) \
|
||||
continue; \
|
||||
}
|
||||
|
||||
std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len)
|
||||
{
|
||||
MOTH_JUMP_TABLE;
|
||||
|
||||
std::vector<int> labels;
|
||||
|
||||
const auto addLabel = [&labels,len](int offset) {
|
||||
Q_ASSERT(offset >= 0 && offset < static_cast<int>(len));
|
||||
labels.push_back(offset);
|
||||
};
|
||||
|
||||
const char *start = code;
|
||||
const char *end = code + len;
|
||||
while (code < end) {
|
||||
MOTH_DISPATCH()
|
||||
Q_UNREACHABLE();
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadReg)
|
||||
COLLECTOR_END_INSTR(LoadReg)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreReg)
|
||||
COLLECTOR_END_INSTR(StoreReg)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(MoveReg)
|
||||
COLLECTOR_END_INSTR(MoveReg)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadConst)
|
||||
COLLECTOR_END_INSTR(LoadConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadNull)
|
||||
COLLECTOR_END_INSTR(LoadNull)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadZero)
|
||||
COLLECTOR_END_INSTR(LoadZero)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadTrue)
|
||||
COLLECTOR_END_INSTR(LoadTrue)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadFalse)
|
||||
COLLECTOR_END_INSTR(LoadFalse)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadUndefined)
|
||||
COLLECTOR_END_INSTR(LoadUndefined)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadInt)
|
||||
COLLECTOR_END_INSTR(LoadInt)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(MoveConst)
|
||||
COLLECTOR_END_INSTR(MoveConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadImport)
|
||||
COLLECTOR_END_INSTR(LoadImport)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadLocal)
|
||||
COLLECTOR_END_INSTR(LoadLocal)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreLocal)
|
||||
COLLECTOR_END_INSTR(StoreLocal)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadScopedLocal)
|
||||
COLLECTOR_END_INSTR(LoadScopedLocal)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreScopedLocal)
|
||||
COLLECTOR_END_INSTR(StoreScopedLocal)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadRuntimeString)
|
||||
COLLECTOR_END_INSTR(LoadRuntimeString)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(MoveRegExp)
|
||||
COLLECTOR_END_INSTR(MoveRegExp)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadClosure)
|
||||
COLLECTOR_END_INSTR(LoadClosure)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadName)
|
||||
COLLECTOR_END_INSTR(LoadName)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadGlobalLookup)
|
||||
COLLECTOR_END_INSTR(LoadGlobalLookup)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreNameSloppy)
|
||||
COLLECTOR_END_INSTR(StoreNameSloppy)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreNameStrict)
|
||||
COLLECTOR_END_INSTR(StoreNameStrict)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadElement)
|
||||
COLLECTOR_END_INSTR(LoadElement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreElement)
|
||||
COLLECTOR_END_INSTR(StoreElement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadProperty)
|
||||
COLLECTOR_END_INSTR(LoadProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(GetLookup)
|
||||
COLLECTOR_END_INSTR(GetLookup)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreProperty)
|
||||
COLLECTOR_END_INSTR(StoreProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(SetLookup)
|
||||
COLLECTOR_END_INSTR(SetLookup)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadSuperProperty)
|
||||
COLLECTOR_END_INSTR(LoadSuperProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreSuperProperty)
|
||||
COLLECTOR_END_INSTR(StoreSuperProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty)
|
||||
COLLECTOR_END_INSTR(StoreScopeObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty)
|
||||
COLLECTOR_END_INSTR(LoadScopeObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty)
|
||||
COLLECTOR_END_INSTR(StoreContextObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty)
|
||||
COLLECTOR_END_INSTR(LoadContextObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadIdObject)
|
||||
COLLECTOR_END_INSTR(LoadIdObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Yield)
|
||||
COLLECTOR_END_INSTR(Yield)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(YieldStar)
|
||||
COLLECTOR_END_INSTR(YieldStar)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Resume)
|
||||
COLLECTOR_END_INSTR(Resume)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallValue)
|
||||
COLLECTOR_END_INSTR(CallValue)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallWithReceiver)
|
||||
COLLECTOR_END_INSTR(CallWithReceiver)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallProperty)
|
||||
COLLECTOR_END_INSTR(CallProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallPropertyLookup)
|
||||
COLLECTOR_END_INSTR(CallPropertyLookup)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallElement)
|
||||
COLLECTOR_END_INSTR(CallElement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallName)
|
||||
COLLECTOR_END_INSTR(CallName)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval)
|
||||
COLLECTOR_END_INSTR(CallPossiblyDirectEval)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallGlobalLookup)
|
||||
COLLECTOR_END_INSTR(CallGlobalLookup)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty)
|
||||
COLLECTOR_END_INSTR(CallScopeObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallContextObjectProperty)
|
||||
COLLECTOR_END_INSTR(CallContextObjectProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CallWithSpread)
|
||||
COLLECTOR_END_INSTR(CallWithSpread)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Construct)
|
||||
COLLECTOR_END_INSTR(Construct)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ConstructWithSpread)
|
||||
COLLECTOR_END_INSTR(ConstructWithSpread)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(SetUnwindHandler)
|
||||
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(DeadTemporalZoneCheck)
|
||||
COLLECTOR_END_INSTR(DeadTemporalZoneCheck)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ThrowException)
|
||||
COLLECTOR_END_INSTR(ThrowException)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(GetException)
|
||||
COLLECTOR_END_INSTR(HasException)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(SetException)
|
||||
COLLECTOR_END_INSTR(SetExceptionFlag)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CreateCallContext)
|
||||
COLLECTOR_END_INSTR(CreateCallContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PushCatchContext)
|
||||
COLLECTOR_END_INSTR(PushCatchContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PushWithContext)
|
||||
COLLECTOR_END_INSTR(PushWithContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PushBlockContext)
|
||||
COLLECTOR_END_INSTR(PushBlockContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CloneBlockContext)
|
||||
COLLECTOR_END_INSTR(CloneBlockContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PushScriptContext)
|
||||
COLLECTOR_END_INSTR(PushScriptContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PopScriptContext)
|
||||
COLLECTOR_END_INSTR(PopScriptContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(PopContext)
|
||||
COLLECTOR_END_INSTR(PopContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(GetIterator)
|
||||
COLLECTOR_END_INSTR(GetIterator)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(IteratorNext)
|
||||
COLLECTOR_END_INSTR(IteratorNext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(IteratorNextForYieldStar)
|
||||
COLLECTOR_END_INSTR(IteratorNextForYieldStar)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(IteratorClose)
|
||||
COLLECTOR_END_INSTR(IteratorClose)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DestructureRestElement)
|
||||
COLLECTOR_END_INSTR(DestructureRestElement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DeleteProperty)
|
||||
COLLECTOR_END_INSTR(DeleteProperty)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DeleteName)
|
||||
COLLECTOR_END_INSTR(DeleteName)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(TypeofName)
|
||||
COLLECTOR_END_INSTR(TypeofName)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(TypeofValue)
|
||||
COLLECTOR_END_INSTR(TypeofValue)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DeclareVar)
|
||||
COLLECTOR_END_INSTR(DeclareVar)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DefineArray)
|
||||
COLLECTOR_END_INSTR(DefineArray)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(DefineObjectLiteral)
|
||||
COLLECTOR_END_INSTR(DefineObjectLiteral)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CreateClass)
|
||||
COLLECTOR_END_INSTR(CreateClass)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject)
|
||||
COLLECTOR_END_INSTR(CreateMappedArgumentsObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject)
|
||||
COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CreateRestParameter)
|
||||
COLLECTOR_END_INSTR(CreateRestParameter)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ConvertThisToObject)
|
||||
COLLECTOR_END_INSTR(ConvertThisToObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadSuperConstructor)
|
||||
COLLECTOR_END_INSTR(LoadSuperConstructor)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ToObject)
|
||||
COLLECTOR_END_INSTR(ToObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Jump)
|
||||
addLabel(code - start + offset);
|
||||
COLLECTOR_END_INSTR(Jump)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(JumpTrue)
|
||||
addLabel(code - start + offset);
|
||||
COLLECTOR_END_INSTR(JumpTrue)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(JumpFalse)
|
||||
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)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpEqNull)
|
||||
COLLECTOR_END_INSTR(CmpEqNull)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpNeNull)
|
||||
COLLECTOR_END_INSTR(CmpNeNull)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpEqInt)
|
||||
COLLECTOR_END_INSTR(CmpEq)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpNeInt)
|
||||
COLLECTOR_END_INSTR(CmpNeInt)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpEq)
|
||||
COLLECTOR_END_INSTR(CmpEq)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpNe)
|
||||
COLLECTOR_END_INSTR(CmpNe)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpGt)
|
||||
COLLECTOR_END_INSTR(CmpGt)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpGe)
|
||||
COLLECTOR_END_INSTR(CmpGe)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpLt)
|
||||
COLLECTOR_END_INSTR(CmpLt)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpLe)
|
||||
COLLECTOR_END_INSTR(CmpLe)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpStrictEqual)
|
||||
COLLECTOR_END_INSTR(CmpStrictEqual)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual)
|
||||
COLLECTOR_END_INSTR(CmpStrictNotEqual)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpIn)
|
||||
COLLECTOR_END_INSTR(CmpIn)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
|
||||
COLLECTOR_END_INSTR(CmpInstanceOf)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UNot)
|
||||
COLLECTOR_END_INSTR(UNot)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UPlus)
|
||||
COLLECTOR_END_INSTR(UPlus)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UMinus)
|
||||
COLLECTOR_END_INSTR(UMinus)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UCompl)
|
||||
COLLECTOR_END_INSTR(UCompl)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Increment)
|
||||
COLLECTOR_END_INSTR(PreIncrement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Decrement)
|
||||
COLLECTOR_END_INSTR(PreDecrement)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Add)
|
||||
COLLECTOR_END_INSTR(Add)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitAnd)
|
||||
COLLECTOR_END_INSTR(BitAnd)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitOr)
|
||||
COLLECTOR_END_INSTR(BitOr)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitXor)
|
||||
COLLECTOR_END_INSTR(BitXor)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UShr)
|
||||
COLLECTOR_END_INSTR(UShr)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Shr)
|
||||
COLLECTOR_END_INSTR(Shr)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Shl)
|
||||
COLLECTOR_END_INSTR(Shl)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitAndConst)
|
||||
COLLECTOR_END_INSTR(BitAndConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitOrConst)
|
||||
COLLECTOR_END_INSTR(BitOr)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(BitXorConst)
|
||||
COLLECTOR_END_INSTR(BitXor)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(UShrConst)
|
||||
COLLECTOR_END_INSTR(UShrConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ShrConst)
|
||||
COLLECTOR_END_INSTR(ShrConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ShlConst)
|
||||
COLLECTOR_END_INSTR(ShlConst)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Exp)
|
||||
COLLECTOR_END_INSTR(Exp)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Mul)
|
||||
COLLECTOR_END_INSTR(Mul)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Div)
|
||||
COLLECTOR_END_INSTR(Div)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Mod)
|
||||
COLLECTOR_END_INSTR(Mod)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Sub)
|
||||
COLLECTOR_END_INSTR(Sub)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(Ret)
|
||||
COLLECTOR_END_INSTR(Ret)
|
||||
|
||||
#ifndef QT_NO_QML_DEBUGGER
|
||||
COLLECTOR_BEGIN_INSTR(Debug)
|
||||
COLLECTOR_END_INSTR(Debug)
|
||||
#endif // QT_NO_QML_DEBUGGER
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
|
||||
COLLECTOR_END_INSTR(InitializeBlockDeadTemporalZone)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(ThrowOnNullOrUndefined)
|
||||
COLLECTOR_END_INSTR(ThrowOnNullOrUndefined)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(GetTemplateObject)
|
||||
COLLECTOR_END_INSTR(GetTemplateObject)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadQmlContext)
|
||||
COLLECTOR_END_INSTR(LoadQmlContext)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts)
|
||||
COLLECTOR_END_INSTR(LoadQmlImportedScripts)
|
||||
|
||||
COLLECTOR_BEGIN_INSTR(TailCall)
|
||||
COLLECTOR_END_INSTR(TailCall)
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
#undef COLLECTOR_BEGIN_INSTR
|
||||
#undef COLLECTOR_END_INSTR
|
||||
|
|
|
@ -100,8 +100,6 @@ public:
|
|||
int currentInstructionOffset() const { return _currentOffset; }
|
||||
int nextInstructionOffset() const { return _nextOffset; }
|
||||
|
||||
static std::vector<int> collectLabelsInBytecode(const char *code, uint len);
|
||||
|
||||
protected:
|
||||
FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
|
||||
|
||||
|
|
|
@ -3260,24 +3260,27 @@ bool Codegen::visit(DoWhileStatement *ast)
|
|||
|
||||
RegisterScope scope(this);
|
||||
|
||||
BytecodeGenerator::Label body = bytecodeGenerator->label();
|
||||
BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
|
||||
BytecodeGenerator::Label cond = bytecodeGenerator->newLabel();
|
||||
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
|
||||
|
||||
ControlFlowLoop flow(this, &end, &cond);
|
||||
bytecodeGenerator->jump().link(body);
|
||||
|
||||
cond.link();
|
||||
bytecodeGenerator->addLoopStart(cond);
|
||||
|
||||
if (!AST::cast<TrueLiteral *>(ast->expression)) {
|
||||
TailCallBlocker blockTailCalls(this);
|
||||
condition(ast->expression, &body, &end, true);
|
||||
}
|
||||
|
||||
body.link();
|
||||
statement(ast->statement);
|
||||
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken);
|
||||
|
||||
cond.link();
|
||||
|
||||
TailCallBlocker blockTailCalls(this);
|
||||
if (!AST::cast<FalseLiteral *>(ast->expression)) {
|
||||
if (AST::cast<TrueLiteral *>(ast->expression))
|
||||
bytecodeGenerator->jump().link(body);
|
||||
else
|
||||
condition(ast->expression, &body, &end, false);
|
||||
}
|
||||
if (!AST::cast<FalseLiteral *>(ast->expression))
|
||||
bytecodeGenerator->jump().link(cond);
|
||||
|
||||
end.link();
|
||||
|
||||
|
@ -3350,9 +3353,14 @@ bool Codegen::visit(ForEachStatement *ast)
|
|||
}
|
||||
};
|
||||
ControlFlowLoop flow(this, &end, &in, cleanup);
|
||||
bytecodeGenerator->jump().link(in);
|
||||
|
||||
BytecodeGenerator::Label body = bytecodeGenerator->label();
|
||||
bytecodeGenerator->addLoopStart(in);
|
||||
in.link();
|
||||
iterator.loadInAccumulator();
|
||||
Instruction::IteratorNext next;
|
||||
next.value = lhsValue.stackSlot();
|
||||
next.done = iteratorDone.stackSlot();
|
||||
bytecodeGenerator->addInstruction(next);
|
||||
bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(done);
|
||||
|
||||
// each iteration gets it's own context, as per spec
|
||||
{
|
||||
|
@ -3383,22 +3391,17 @@ bool Codegen::visit(ForEachStatement *ast)
|
|||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
blockTailCalls.unblock();
|
||||
statement(ast->statement);
|
||||
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
|
||||
|
||||
}
|
||||
|
||||
error:
|
||||
in.link();
|
||||
iterator.loadInAccumulator();
|
||||
Instruction::IteratorNext next;
|
||||
next.value = lhsValue.stackSlot();
|
||||
next.done = iteratorDone.stackSlot();
|
||||
bytecodeGenerator->addInstruction(next);
|
||||
bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpFalse()).link(body);
|
||||
bytecodeGenerator->jump().link(done);
|
||||
bytecodeGenerator->jump().link(in);
|
||||
|
||||
error:
|
||||
end.link();
|
||||
|
||||
// ~ControlFlowLoop will be called here, which will generate unwind code when needed
|
||||
}
|
||||
|
||||
done.link();
|
||||
|
@ -3427,7 +3430,7 @@ bool Codegen::visit(ForStatement *ast)
|
|||
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
|
||||
|
||||
ControlFlowLoop flow(this, &end, &step);
|
||||
|
||||
bytecodeGenerator->addLoopStart(cond);
|
||||
condition(ast->condition, &body, &end, true);
|
||||
|
||||
body.link();
|
||||
|
@ -3720,16 +3723,17 @@ bool Codegen::visit(WhileStatement *ast)
|
|||
return false;
|
||||
|
||||
RegisterScope scope(this);
|
||||
TailCallBlocker blockTailCalls(this);
|
||||
|
||||
BytecodeGenerator::Label start = bytecodeGenerator->newLabel();
|
||||
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
|
||||
BytecodeGenerator::Label cond = bytecodeGenerator->label();
|
||||
ControlFlowLoop flow(this, &end, &cond);
|
||||
bytecodeGenerator->addLoopStart(cond);
|
||||
|
||||
if (!AST::cast<TrueLiteral *>(ast->expression))
|
||||
if (!AST::cast<TrueLiteral *>(ast->expression)) {
|
||||
TailCallBlocker blockTailCalls(this);
|
||||
condition(ast->expression, &start, &end, true);
|
||||
blockTailCalls.unblock();
|
||||
}
|
||||
|
||||
start.link();
|
||||
statement(ast->statement);
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// Bump this whenever the compiler data structures change in an incompatible way.
|
||||
#define QV4_DATA_STRUCTURE_VERSION 0x1a
|
||||
#define QV4_DATA_STRUCTURE_VERSION 0x1b
|
||||
|
||||
class QIODevice;
|
||||
class QQmlPropertyCache;
|
||||
|
@ -302,9 +302,14 @@ struct Function
|
|||
typedef quint16_le TraceInfoCount;
|
||||
TraceInfoCount nTraceInfos;
|
||||
static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
|
||||
|
||||
quint32_le nLabelInfos;
|
||||
size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; }
|
||||
|
||||
// Keep all unaligned data at the end
|
||||
quint8 flags;
|
||||
quint8 padding1;
|
||||
quint16 padding2;
|
||||
|
||||
// quint32 formalsIndex[nFormals]
|
||||
// quint32 localsIndex[nLocals]
|
||||
|
@ -321,12 +326,14 @@ struct Function
|
|||
const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
|
||||
// ---
|
||||
|
||||
const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); }
|
||||
|
||||
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
|
||||
|
||||
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
|
||||
|
||||
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int codeSize) {
|
||||
int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies +
|
||||
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) {
|
||||
int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize +
|
||||
2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
|
||||
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
|
||||
Q_ASSERT(size < INT_MAX);
|
||||
|
@ -337,7 +344,7 @@ struct Function
|
|||
return (a + 7) & ~size_t(7);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
|
||||
static_assert(sizeof(Function) == 60, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
|
||||
|
||||
struct Method {
|
||||
enum Type {
|
||||
|
|
|
@ -445,6 +445,12 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
|
|||
currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
|
||||
}
|
||||
|
||||
if (!irFunction->labelInfo.empty()) {
|
||||
function->nLabelInfos = quint32(irFunction->labelInfo.size());
|
||||
Q_ASSERT(function->labelInfosOffset() == currentOffset);
|
||||
currentOffset += function->nLabelInfos * sizeof(quint32);
|
||||
}
|
||||
|
||||
function->location.line = irFunction->line;
|
||||
function->location.column = irFunction->column;
|
||||
|
||||
|
@ -483,6 +489,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
|
|||
*writtenDeps++ = property.value(); // notify index
|
||||
}
|
||||
|
||||
quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
|
||||
for (unsigned u : irFunction->labelInfo) {
|
||||
*labels++ = u;
|
||||
}
|
||||
|
||||
// write byte code
|
||||
memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
|
||||
}
|
||||
|
@ -682,7 +693,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
|
|||
const int qmlIdDepsCount = f->idObjectDependencies.count();
|
||||
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
|
||||
quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
|
||||
qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
|
||||
qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size());
|
||||
functionSize += size - f->code.size();
|
||||
nextOffset += size;
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ struct Context {
|
|||
ControlFlow *controlFlow = nullptr;
|
||||
QByteArray code;
|
||||
QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
|
||||
std::vector<unsigned> labelInfo;
|
||||
|
||||
int nRegisters = 0;
|
||||
int registerOffset = -1;
|
||||
|
|
|
@ -670,7 +670,8 @@ public:
|
|||
|
||||
void addLabelForOffset(int offset)
|
||||
{
|
||||
labelForOffset.insert(offset, label());
|
||||
if (!labelForOffset.contains(offset))
|
||||
labelForOffset.insert(offset, label());
|
||||
}
|
||||
|
||||
void addJumpToOffset(const Jump &jump, int offset)
|
||||
|
|
|
@ -1382,28 +1382,31 @@ void BaselineAssembler::cmpStrictNotEqual(int lhs)
|
|||
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
|
||||
}
|
||||
|
||||
void BaselineAssembler::jump(int offset)
|
||||
int BaselineAssembler::jump(int offset)
|
||||
{
|
||||
pasm()->addJumpToOffset(pasm()->jump(), offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::jumpTrue(int offset)
|
||||
int BaselineAssembler::jumpTrue(int offset)
|
||||
{
|
||||
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
|
||||
auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
|
||||
pasm()->addJumpToOffset(jump, offset);
|
||||
});
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::jumpFalse(int offset)
|
||||
int BaselineAssembler::jumpFalse(int offset)
|
||||
{
|
||||
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
|
||||
auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
|
||||
pasm()->addJumpToOffset(jump, offset);
|
||||
});
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::jumpNoException(int offset)
|
||||
int BaselineAssembler::jumpNoException(int offset)
|
||||
{
|
||||
auto jump = pasm()->branch32(
|
||||
PlatformAssembler::Equal,
|
||||
|
@ -1411,11 +1414,13 @@ void BaselineAssembler::jumpNoException(int offset)
|
|||
offsetof(EngineBase, hasException)),
|
||||
TrustedImm32(0));
|
||||
pasm()->addJumpToOffset(jump, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::jumpNotUndefined(int offset)
|
||||
int BaselineAssembler::jumpNotUndefined(int offset)
|
||||
{
|
||||
pasm()->jumpNotUndefined(offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::prepareCallWithArgCount(int argc)
|
||||
|
@ -1539,10 +1544,11 @@ void BaselineAssembler::setException()
|
|||
noException.link(pasm());
|
||||
}
|
||||
|
||||
void BaselineAssembler::setUnwindHandler(int offset)
|
||||
int BaselineAssembler::setUnwindHandler(int offset)
|
||||
{
|
||||
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
|
||||
pasm()->addEHTarget(l, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1568,12 +1574,13 @@ void JIT::BaselineAssembler::unwindDispatch()
|
|||
noUnwind.link(pasm());
|
||||
}
|
||||
|
||||
void JIT::BaselineAssembler::unwindToLabel(int level, int offset)
|
||||
int JIT::BaselineAssembler::unwindToLabel(int level, int offset)
|
||||
{
|
||||
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
|
||||
pasm()->addEHTarget(l, offset);
|
||||
pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
|
||||
gotoCatchException();
|
||||
return offset;
|
||||
}
|
||||
|
||||
void BaselineAssembler::pushCatchContext(int index, int name)
|
||||
|
|
|
@ -135,11 +135,11 @@ public:
|
|||
void cmpStrictNotEqual(int lhs);
|
||||
|
||||
// jumps
|
||||
void jump(int offset);
|
||||
void jumpTrue(int offset);
|
||||
void jumpFalse(int offset);
|
||||
void jumpNoException(int offset);
|
||||
void jumpNotUndefined(int offset);
|
||||
Q_REQUIRED_RESULT int jump(int offset);
|
||||
Q_REQUIRED_RESULT int jumpTrue(int offset);
|
||||
Q_REQUIRED_RESULT int jumpFalse(int offset);
|
||||
Q_REQUIRED_RESULT int jumpNoException(int offset);
|
||||
Q_REQUIRED_RESULT int jumpNotUndefined(int offset);
|
||||
|
||||
// stuff for runtime calls
|
||||
void prepareCallWithArgCount(int argc);
|
||||
|
@ -160,10 +160,10 @@ public:
|
|||
void gotoCatchException();
|
||||
void getException();
|
||||
void setException();
|
||||
void setUnwindHandler(int offset);
|
||||
Q_REQUIRED_RESULT int setUnwindHandler(int offset);
|
||||
void clearUnwindHandler();
|
||||
void unwindDispatch();
|
||||
void unwindToLabel(int level, int offset);
|
||||
Q_REQUIRED_RESULT int unwindToLabel(int level, int offset);
|
||||
void pushCatchContext(int index, int name);
|
||||
void popContext();
|
||||
void deadTemporalZoneCheck(int offsetForSavedIP, int variableName);
|
||||
|
|
|
@ -63,7 +63,9 @@ void BaselineJIT::generate()
|
|||
// qDebug()<<"jitting" << function->name()->toQString();
|
||||
const char *code = function->codeData;
|
||||
uint len = function->compiledFunction->codeSize;
|
||||
labels = collectLabelsInBytecode(code, len);
|
||||
|
||||
for (unsigned i = 0, ei = function->compiledFunction->nLabelInfos; i != ei; ++i)
|
||||
labels.insert(int(function->compiledFunction->labelInfoTable()[i]));
|
||||
|
||||
as->generatePrologue();
|
||||
decode(code, len);
|
||||
|
@ -591,7 +593,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
|
|||
void BaselineJIT::generate_SetUnwindHandler(int offset)
|
||||
{
|
||||
if (offset)
|
||||
as->setUnwindHandler(absoluteOffsetForJump(offset));
|
||||
labels.insert(as->setUnwindHandler(absoluteOffsetForJump(offset)));
|
||||
else
|
||||
as->clearUnwindHandler();
|
||||
}
|
||||
|
@ -603,7 +605,7 @@ void BaselineJIT::generate_UnwindDispatch()
|
|||
|
||||
void BaselineJIT::generate_UnwindToLabel(int level, int offset)
|
||||
{
|
||||
as->unwindToLabel(level, absoluteOffsetForJump(offset));
|
||||
labels.insert(as->unwindToLabel(level, absoluteOffsetForJump(offset)));
|
||||
}
|
||||
|
||||
void BaselineJIT::generate_DeadTemporalZoneCheck(int name)
|
||||
|
@ -870,11 +872,11 @@ void BaselineJIT::generate_ToObject()
|
|||
|
||||
}
|
||||
|
||||
void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); }
|
||||
void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); }
|
||||
void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); }
|
||||
void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); }
|
||||
void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); }
|
||||
void BaselineJIT::generate_Jump(int offset) { labels.insert(as->jump(absoluteOffsetForJump(offset))); }
|
||||
void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) { labels.insert(as->jumpTrue(absoluteOffsetForJump(offset))); }
|
||||
void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) { labels.insert(as->jumpFalse(absoluteOffsetForJump(offset))); }
|
||||
void BaselineJIT::generate_JumpNoException(int offset) { labels.insert(as->jumpNoException(absoluteOffsetForJump(offset))); }
|
||||
void BaselineJIT::generate_JumpNotUndefined(int offset) { labels.insert(as->jumpNotUndefined(absoluteOffsetForJump(offset))); }
|
||||
|
||||
void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
|
||||
void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
|
||||
|
@ -1004,7 +1006,7 @@ void BaselineJIT::generate_GetTemplateObject(int index)
|
|||
|
||||
void BaselineJIT::startInstruction(Instr::Type /*instr*/)
|
||||
{
|
||||
if (hasLabel())
|
||||
if (labels.contains(currentInstructionOffset()))
|
||||
as->addLabel(currentInstructionOffset());
|
||||
}
|
||||
|
||||
|
|
|
@ -221,16 +221,13 @@ public:
|
|||
void endInstruction(Moth::Instr::Type instr) override;
|
||||
|
||||
protected:
|
||||
bool hasLabel() const
|
||||
{ return std::find(labels.cbegin(), labels.cend(), currentInstructionOffset()) != labels.cend(); }
|
||||
|
||||
int absoluteOffsetForJump(int relativeOffset) const
|
||||
{ return nextInstructionOffset() + relativeOffset; }
|
||||
|
||||
private:
|
||||
QV4::Function *function;
|
||||
QScopedPointer<BaselineAssembler> as;
|
||||
std::vector<int> labels;
|
||||
QSet<int> labels;
|
||||
};
|
||||
#endif // V4_ENABLE_JIT
|
||||
|
||||
|
|
Loading…
Reference in New Issue