aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler/qv4isel_masm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler/qv4isel_masm.cpp')
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp397
1 files changed, 201 insertions, 196 deletions
diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp
index d5e67d91c3..ed57852cd6 100644
--- a/src/qml/compiler/qv4isel_masm.cpp
+++ b/src/qml/compiler/qv4isel_masm.cpp
@@ -44,7 +44,6 @@
#include "qv4object_p.h"
#include "qv4functionobject_p.h"
#include "qv4regexpobject_p.h"
-#include "qv4unwindhelper_p.h"
#include "qv4lookup_p.h"
#include "qv4function_p.h"
#include "qv4ssa_p.h"
@@ -70,7 +69,6 @@ CompilationUnit::~CompilationUnit()
{
foreach (Function *f, runtimeFunctions)
engine->allFunctions.remove(reinterpret_cast<quintptr>(f->codePtr));
- UnwindHelper::deregisterFunctions(runtimeFunctions);
}
void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
@@ -86,8 +84,6 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
runtimeFunctions[i] = runtimeFunction;
}
- UnwindHelper::registerFunctions(runtimeFunctions);
-
foreach (Function *f, runtimeFunctions)
engine->allFunctions.insert(reinterpret_cast<quintptr>(f->codePtr), f);
}
@@ -103,73 +99,6 @@ QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int fu
}
namespace {
-class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor
-{
- int _nextFreeStackSlot;
- QHash<V4IR::Temp, int> _stackSlotForTemp;
-
- void renumber(V4IR::Temp *t)
- {
- if (t->kind != V4IR::Temp::VirtualRegister)
- return;
-
- int stackSlot = _stackSlotForTemp.value(*t, -1);
- if (stackSlot == -1) {
- stackSlot = _nextFreeStackSlot++;
- _stackSlotForTemp[*t] = stackSlot;
- }
-
- t->kind = V4IR::Temp::StackSlot;
- t->index = stackSlot;
- }
-
-public:
- ConvertTemps()
- : _nextFreeStackSlot(0)
- {}
-
- void toStackSlots(V4IR::Function *function)
- {
- _stackSlotForTemp.reserve(function->tempCount);
-
- foreach (V4IR::BasicBlock *bb, function->basicBlocks)
- foreach (V4IR::Stmt *s, bb->statements)
- s->accept(this);
-
- function->tempCount = _nextFreeStackSlot;
- }
-
-protected:
- virtual void visitConst(V4IR::Const *) {}
- virtual void visitString(V4IR::String *) {}
- virtual void visitRegExp(V4IR::RegExp *) {}
- virtual void visitName(V4IR::Name *) {}
- virtual void visitTemp(V4IR::Temp *e) { renumber(e); }
- virtual void visitClosure(V4IR::Closure *) {}
- virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(V4IR::Call *e) {
- e->base->accept(this);
- for (V4IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitNew(V4IR::New *e) {
- e->base->accept(this);
- for (V4IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(V4IR::Member *e) { e->base->accept(this); }
- virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); }
- virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); }
- virtual void visitJump(V4IR::Jump *) {}
- virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
- virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
- virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); }
- virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); }
-};
-
inline bool isPregOrConst(V4IR::Expr *e)
{
if (V4IR::Temp *t = e->asTemp())
@@ -202,8 +131,6 @@ static const Assembler::RegisterID calleeSavedRegisters[] = {
#if CPU(ARM)
static const Assembler::RegisterID calleeSavedRegisters[] = {
// ### FIXME: remove unused registers.
- // Keep these in reverse order and make sure to also edit the unwind program in
- // qv4unwindhelper_arm_p.h when changing this list.
JSC::ARMRegisters::r12,
JSC::ARMRegisters::r10,
JSC::ARMRegisters::r9,
@@ -236,11 +163,14 @@ Assembler::Assembler(InstructionSelection *isel, V4IR::Function* function, QV4::
void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock)
{
_addrs[block] = label();
+ catchBlock = block->catchBlock;
_nextBlock = nextBlock;
}
void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target)
{
+ Q_UNUSED(current);
+
if (target != _nextBlock)
_patches[target].append(jump());
}
@@ -314,7 +244,7 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, V4IR::Temp *t)
switch (t->kind) {
case V4IR::Temp::Formal:
case V4IR::Temp::ScopedFormal: {
- loadPtr(Address(context, qOffsetOf(CallContext, callData)), baseReg);
+ loadPtr(Address(context, qOffsetOf(ExecutionContext, callData)), baseReg);
offset = sizeof(CallData) + (t->index - 1) * sizeof(SafeValue);
} break;
case V4IR::Temp::Local:
@@ -377,6 +307,8 @@ void Assembler::copyValue(Result result, V4IR::Expr* source)
storeDouble(toDoubleRegister(source), result);
} else if (V4IR::Temp *temp = source->asTemp()) {
#ifdef VALUE_FITS_IN_REGISTER
+ Q_UNUSED(temp);
+
// Use ReturnValueRegister as "scratch" register because loadArgument
// and storeArgument are functions that may need a scratch register themselves.
loadArgumentInRegister(source, ReturnValueRegister, 0);
@@ -507,23 +439,9 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi
}
#endif
-void Assembler::recordLineNumber(int lineNumber)
-{
- CodeLineNumerMapping mapping;
- mapping.location = label();
- mapping.lineNumber = lineNumber;
- codeLineNumberMappings << mapping;
-}
-
-
JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
{
Label endOfCode = label();
-#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS)
- // Let the ARM exception table follow right after that
- for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i)
- nop();
-#endif
{
QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches);
@@ -540,14 +458,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
JSC::JSGlobalData dummy(_executableAllocator);
JSC::LinkBuffer linkBuffer(dummy, this, 0);
- QVector<uint> lineNumberMapping(codeLineNumberMappings.count() * 2);
-
- for (int i = 0; i < codeLineNumberMappings.count(); ++i) {
- lineNumberMapping[i * 2] = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location);
- lineNumberMapping[i * 2 + 1] = codeLineNumberMappings.at(i).lineNumber;
- }
- _isel->jsUnitGenerator()->registerLineNumberMapping(_function, lineNumberMapping);
-
QHash<void*, const char*> functions;
foreach (CallToLink ctl, _callsToLink) {
linkBuffer.link(ctl.call, ctl.externalFunction);
@@ -557,6 +467,10 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
foreach (const DataLabelPatch &p, _dataLabelPatches)
linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
+ // link exception handlers
+ foreach(Jump jump, exceptionPropagationJumps)
+ linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel));
+
{
QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches);
while (it.hasNext()) {
@@ -571,9 +485,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
_constTable.finalize(linkBuffer, _isel);
*codeSize = linkBuffer.offsetOf(endOfCode);
-#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS)
- UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), *codeSize);
-#endif
JSC::MacroAssemblerCodeRef codeRef;
@@ -628,11 +539,11 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
return codeRef;
}
-InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator)
+InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator)
: EvalInstructionSelection(execAllocator, module, jsGenerator)
, _block(0)
- , _function(0)
, _as(0)
+ , qmlEngine(qmlEngine)
{
compilationUnit = new CompilationUnit;
compilationUnit->codeRefs.resize(module->functions.size());
@@ -648,12 +559,10 @@ void InstructionSelection::run(int functionIndex)
{
V4IR::Function *function = irModule->functions[functionIndex];
QVector<Lookup> lookups;
- QSet<V4IR::BasicBlock*> reentryBlocks;
qSwap(_function, function);
- qSwap(_reentryBlocks, reentryBlocks);
V4IR::Optimizer opt(_function);
- opt.run();
+ opt.run(qmlEngine);
#if (CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX))) || (CPU(X86) && OS(LINUX))
static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty();
@@ -709,34 +618,31 @@ void InstructionSelection::run(int functionIndex)
_as->addPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister);
_as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
+ int lastLine = -1;
for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) {
V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0;
_block = _function->basicBlocks[i];
_as->registerBlock(_block, nextBlock);
- if (_reentryBlocks.contains(_block)) {
- _as->enterStandardStackFrame();
-#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 (V4IR::Stmt *s, _block->statements) {
- if (s->location.isValid())
- _as->recordLineNumber(s->location.startLine);
+ if (s->location.isValid()) {
+ if (int(s->location.startLine) != lastLine) {
+ Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lineNumber));
+ _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr);
+ lastLine = s->location.startLine;
+ }
+ }
s->accept(this);
}
}
+ if (!_as->exceptionReturnLabel.isSet())
+ visitRet(0);
+
JSC::MacroAssemblerCodeRef codeRef =_as->link(&compilationUnit->codeSizes[functionIndex]);
compilationUnit->codeRefs[functionIndex] = codeRef;
qSwap(_function, function);
- qSwap(_reentryBlocks, reentryBlocks);
delete _as;
_as = oldAssembler;
qSwap(_removableJumps, removableJumps);
@@ -759,7 +665,7 @@ QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep()
void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
{
- int argc = prepareCallData(args, 0);
+ prepareCallData(args, 0);
if (useFastLookups && func->global) {
uint index = registerGlobalGetterLookup(*func->id);
@@ -828,64 +734,25 @@ void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
void InstructionSelection::callBuiltinThrow(V4IR::Expr *arg)
{
- generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister,
+ generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_throw, Assembler::ContextRegister,
Assembler::PointerToValue(arg));
}
-typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr));
-static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody,
- QV4::StringRef exceptionVarName, ValueRef exceptionVar)
-{
- exceptionVar = Primitive::undefinedValue();
- void *addressToContinueAt = 0;
- SafeValue *jsStackTop = context->engine->jsStackTop;
- bool caughtException = false;
- try {
- addressToContinueAt = tryBody(context, localsPtr);
- } catch (...) {
- context->engine->jsStackTop = jsStackTop;
- exceptionVar = context->catchException();
- caughtException = true;
- }
- // Can't nest try { ... } catch (...) {} due to inability of nesting foreign exceptions
- // with common CXX ABI.
- if (caughtException) {
- try {
- ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, exceptionVar, context);
- addressToContinueAt = catchBody(catchContext, localsPtr);
- context = __qmljs_builtin_pop_scope(catchContext);
- } catch (...) {
- context->engine->jsStackTop = jsStackTop;
- exceptionVar = context->catchException();
- addressToContinueAt = catchBody(context, localsPtr);
- }
- }
- return addressToContinueAt;
+void InstructionSelection::callBuiltinReThrow()
+{
+ _as->jumpToExceptionHandler();
}
-void InstructionSelection::visitTry(V4IR::Try *t)
+void InstructionSelection::callBuiltinUnwindException(V4IR::Temp *result)
{
- // 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.
-
- _reentryBlocks.insert(t->tryBlock);
- _reentryBlocks.insert(t->catchBlock);
+ generateFunctionCall(result, __qmljs_builtin_unwind_exception, Assembler::ContextRegister);
- generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister,
- Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock),
- Assembler::PointerToString(*t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar));
- _as->jump(Assembler::ReturnValueRegister);
}
-void InstructionSelection::callBuiltinFinishTry()
+void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
{
- // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper
- // with the address that we'd like to continue at, which is right after the ret below.
- Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister);
- _as->leaveStandardStackFrame();
- _as->ret();
- _as->addPatch(continuation, _as->label());
+ Assembler::Pointer s = _as->loadStringAddress(Assembler::ScratchRegister, exceptionName);
+ generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_catch_scope, Assembler::ContextRegister, s);
}
void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result)
@@ -986,11 +853,16 @@ void InstructionSelection::callBuiltinSetupArgumentObject(V4IR::Temp *result)
generateFunctionCall(result, __qmljs_builtin_setup_arguments_object, Assembler::ContextRegister);
}
+void InstructionSelection::callBuiltinConvertThisToObject()
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_convert_this_to_object, Assembler::ContextRegister);
+}
+
void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
{
Q_ASSERT(value);
- int argc = prepareCallData(args, 0);
+ prepareCallData(args, 0);
generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister,
Assembler::Reference(value),
baseAddressForCallData());
@@ -998,7 +870,7 @@ void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4
void InstructionSelection::loadThisObject(V4IR::Temp *temp)
{
- _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(CallContext, callData)), Assembler::ScratchRegister);
+ _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, callData)), Assembler::ScratchRegister);
#if defined(VALUE_FITS_IN_REGISTER)
_as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)),
Assembler::ReturnValueRegister);
@@ -1008,6 +880,31 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp)
#endif
}
+void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_id_array, Assembler::ContextRegister);
+}
+
+void InstructionSelection::loadQmlImportedScripts(V4IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_imported_scripts, Assembler::ContextRegister);
+}
+
+void InstructionSelection::loadQmlContextObject(V4IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_context_object, Assembler::ContextRegister);
+}
+
+void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_scope_object, Assembler::ContextRegister);
+}
+
+void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_qml_singleton, Assembler::ContextRegister, Assembler::PointerToString(name));
+}
+
void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp)
{
if (targetTemp->kind == V4IR::Temp::PhysicalRegister) {
@@ -1086,6 +983,12 @@ void InstructionSelection::getProperty(V4IR::Expr *base, const QString &name, V4
}
}
+void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *target)
+{
+ generateFunctionCall(target, __qmljs_get_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
+ Assembler::TrustedImm32(captureRequired));
+}
+
void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBase,
const QString &targetName)
{
@@ -1101,6 +1004,12 @@ void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBas
}
}
+void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *targetBase, int propertyIndex)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_set_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(targetBase),
+ Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source));
+}
+
void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
{
#if QT_POINTER_SIZE == 8
@@ -1119,13 +1028,16 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
_as->and32(Assembler::TrustedImm32(QV4::Managed::SimpleArray), Assembler::ReturnValueRegister);
Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
- Assembler::Jump fallback;
+ bool needNegativeCheck = false;
+ Assembler::Jump fallback, fallback2;
if (tindex->kind == V4IR::Temp::PhysicalRegister) {
if (tindex->type == V4IR::SInt32Type) {
+ fallback = _as->branch32(Assembler::LessThan, (Assembler::RegisterID)tindex->index, Assembler::TrustedImm32(0));
_as->move((Assembler::RegisterID) tindex->index, Assembler::ScratchRegister);
+ needNegativeCheck = true;
} else {
// double, convert and check if it's a int
- _as->truncateDoubleToUint32((Assembler::FPRegisterID) tindex->index, Assembler::ScratchRegister);
+ fallback2 = _as->branchTruncateDoubleToUint32((Assembler::FPRegisterID) tindex->index, Assembler::ScratchRegister);
_as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr0);
fallback = _as->branchDouble(Assembler::DoubleNotEqual, Assembler::FPGpr0, (Assembler::FPRegisterID) tindex->index);
}
@@ -1142,19 +1054,23 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
_as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ReturnValueRegister);
_as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
_as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0);
- _as->truncateDoubleToUint32(Assembler::FPGpr0, Assembler::ScratchRegister);
+ fallback2 = _as->branchTruncateDoubleToUint32(Assembler::FPGpr0, Assembler::ScratchRegister);
_as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr1);
fallback = _as->branchDouble(Assembler::DoubleNotEqualOrUnordered, Assembler::FPGpr0, Assembler::FPGpr1);
isInteger.link(_as);
_as->or32(Assembler::TrustedImm32(0), Assembler::ScratchRegister);
+ needNegativeCheck = true;
}
// get data, ScratchRegister holds index
addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
_as->load64(addr, Assembler::ReturnValueRegister);
Address arrayDataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayDataLen));
- Assembler::Jump outOfRange = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, arrayDataLen);
+ Assembler::Jump outOfRange;
+ if (needNegativeCheck)
+ outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
+ Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, arrayDataLen);
Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData));
_as->load64(arrayData, Assembler::ReturnValueRegister);
Q_ASSERT(sizeof(Property) == (1<<4));
@@ -1172,9 +1088,13 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
Assembler::Jump done = _as->jump();
emptyValue.link(_as);
- outOfRange.link(_as);
+ if (outOfRange.isSet())
+ outOfRange.link(_as);
+ outOfRange2.link(_as);
if (fallback.isSet())
fallback.link(_as);
+ if (fallback2.isSet())
+ fallback2.link(_as);
notSimple.link(_as);
notManaged.link(_as);
@@ -1373,7 +1293,7 @@ void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::
}
}
-static inline Assembler::FPRegisterID getFreeFPReg(V4IR::Expr *shouldNotOverlap, int hint)
+static inline Assembler::FPRegisterID getFreeFPReg(V4IR::Expr *shouldNotOverlap, unsigned hint)
{
if (V4IR::Temp *t = shouldNotOverlap->asTemp())
if (t->type == V4IR::DoubleType)
@@ -1465,7 +1385,7 @@ Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Exp
void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
{
- if (oper != V4IR:: OpMod
+ if (oper != V4IR::OpMod
&& leftSource->type == V4IR::DoubleType && rightSource->type == V4IR::DoubleType
&& isPregOrConst(leftSource) && isPregOrConst(rightSource)) {
doubleBinop(oper, leftSource, rightSource, target);
@@ -1481,7 +1401,14 @@ void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR:
done = genInlineBinop(oper, leftSource, rightSource, target);
// TODO: inline var===null and var!==null
- const Assembler::BinaryOperationInfo& info = Assembler::binaryOperation(oper);
+ Assembler::BinaryOperationInfo info = Assembler::binaryOperation(oper);
+
+ if (oper == V4IR::OpAdd &&
+ (leftSource->type == V4IR::StringType || rightSource->type == V4IR::StringType)) {
+ const Assembler::BinaryOperationInfo stringAdd = OPCONTEXT(__qmljs_add_string);
+ info = stringAdd;
+ }
+
if (info.fallbackImplementation) {
_as->generateFunctionCallImp(target, info.name, info.fallbackImplementation,
Assembler::PointerToValue(leftSource),
@@ -1504,7 +1431,7 @@ void InstructionSelection::callProperty(V4IR::Expr *base, const QString &name, V
{
assert(base != 0);
- int argc = prepareCallData(args, base);
+ prepareCallData(args, base);
if (useFastLookups) {
uint index = registerGetterLookup(name);
@@ -1525,7 +1452,7 @@ void InstructionSelection::callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4
{
assert(base != 0);
- int argc = prepareCallData(args, base);
+ prepareCallData(args, base);
generateFunctionCall(result, __qmljs_call_element, Assembler::ContextRegister,
Assembler::PointerToValue(index),
baseAddressForCallData());
@@ -1829,7 +1756,7 @@ void InstructionSelection::convertTypeToUInt32(V4IR::Temp *source, V4IR::Temp *t
void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
{
assert(func != 0);
- int argc = prepareCallData(args, 0);
+ prepareCallData(args, 0);
if (useFastLookups && func->global) {
uint index = registerGlobalGetterLookup(*func->id);
@@ -1848,9 +1775,18 @@ void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::E
void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
{
- int argc = prepareCallData(args, 0);
+ prepareCallData(args, base);
+ if (useFastLookups) {
+ uint index = registerGetterLookup(name);
+ generateFunctionCall(result, __qmljs_construct_property_lookup,
+ Assembler::ContextRegister,
+ Assembler::TrustedImm32(index),
+ baseAddressForCallData());
+ return;
+ }
+
generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister,
- Assembler::Reference(base), Assembler::PointerToString(name),
+ Assembler::PointerToString(name),
baseAddressForCallData());
}
@@ -1858,7 +1794,7 @@ void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *arg
{
assert(value != 0);
- int argc = prepareCallData(args, 0);
+ prepareCallData(args, 0);
generateFunctionCall(result, __qmljs_construct_value,
Assembler::ContextRegister,
Assembler::Reference(value),
@@ -1917,6 +1853,10 @@ void InstructionSelection::visitCJump(V4IR::CJump *s)
visitCJumpStrict(b, s->iftrue, s->iffalse);
return;
}
+ if (b->op == V4IR::OpEqual || b->op == V4IR::OpNotEqual) {
+ visitCJumpEqual(b, s->iftrue, s->iffalse);
+ return;
+ }
CmpOp op = 0;
CmpOpContext opContext = 0;
@@ -1958,7 +1898,10 @@ void InstructionSelection::visitCJump(V4IR::CJump *s)
void InstructionSelection::visitRet(V4IR::Ret *s)
{
- if (V4IR::Temp *t = s->expr->asTemp()) {
+ if (!s) {
+ // this only happens if the method doesn't have a return statement and can
+ // only exit through an exception
+ } else if (V4IR::Temp *t = s->expr->asTemp()) {
#if CPU(X86) || CPU(ARM)
# if CPU(X86)
@@ -2052,6 +1995,8 @@ void InstructionSelection::visitRet(V4IR::Ret *s)
Q_UNUSED(s);
}
+ _as->exceptionReturnLabel = _as->label();
+
const int locals = _as->stackLayout().calculateJSStackFrameSize();
_as->subPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister);
_as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister);
@@ -2341,6 +2286,8 @@ bool InstructionSelection::visitCJumpDouble(V4IR::AluOp op, V4IR::Expr *left, V4
void InstructionSelection::visitCJumpStrict(V4IR::Binop *binop, V4IR::BasicBlock *trueBlock,
V4IR::BasicBlock *falseBlock)
{
+ Q_ASSERT(binop->op == V4IR::OpStrictEqual || binop->op == V4IR::OpStrictNotEqual);
+
if (visitCJumpStrictNullUndefined(V4IR::NullType, binop, trueBlock, falseBlock))
return;
if (visitCJumpStrictNullUndefined(V4IR::UndefinedType, binop, trueBlock, falseBlock))
@@ -2348,22 +2295,14 @@ void InstructionSelection::visitCJumpStrict(V4IR::Binop *binop, V4IR::BasicBlock
if (visitCJumpStrictBool(binop, trueBlock, falseBlock))
return;
- QV4::BinOp op;
- const char *opName;
- if (binop->op == V4IR::OpStrictEqual) {
- op = __qmljs_se;
- opName = "__qmljs_se";
- } else {
- op = __qmljs_sne;
- opName = "__qmljs_sne";
- }
-
V4IR::Expr *left = binop->left;
V4IR::Expr *right = binop->right;
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op,
+ _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "__qmljs_cmp_se", __qmljs_cmp_se,
Assembler::PointerToValue(left), Assembler::PointerToValue(right));
- _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, trueBlock, falseBlock);
+ _as->generateCJumpOnCompare(binop->op == V4IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal,
+ Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
+ _block, trueBlock, falseBlock);
}
// Only load the non-null temp.
@@ -2465,6 +2404,72 @@ bool InstructionSelection::visitCJumpStrictBool(V4IR::Binop *binop, V4IR::BasicB
return true;
}
+bool InstructionSelection::visitCJumpNullUndefined(V4IR::Type nullOrUndef, V4IR::Binop *binop,
+ V4IR::BasicBlock *trueBlock,
+ V4IR::BasicBlock *falseBlock)
+{
+ Q_ASSERT(nullOrUndef == V4IR::NullType || nullOrUndef == V4IR::UndefinedType);
+
+ V4IR::Expr *varSrc = 0;
+ if (binop->left->type == V4IR::VarType && binop->right->type == nullOrUndef)
+ varSrc = binop->left;
+ else if (binop->left->type == nullOrUndef && binop->right->type == V4IR::VarType)
+ varSrc = binop->right;
+ if (!varSrc)
+ return false;
+
+ if (varSrc->asTemp() && varSrc->asTemp()->kind == V4IR::Temp::PhysicalRegister) {
+ _as->jumpToBlock(_block, falseBlock);
+ return true;
+ }
+
+ if (V4IR::Const *c = varSrc->asConst()) {
+ if (c->type == nullOrUndef)
+ _as->jumpToBlock(_block, trueBlock);
+ else
+ _as->jumpToBlock(_block, falseBlock);
+ return true;
+ }
+
+ V4IR::Temp *t = varSrc->asTemp();
+ Q_ASSERT(t);
+
+ Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, t);
+ tagAddr.offset += 4;
+ const Assembler::RegisterID tagReg = Assembler::ScratchRegister;
+ _as->load32(tagAddr, tagReg);
+
+ if (binop->op == V4IR::OpNotEqual)
+ qSwap(trueBlock, falseBlock);
+ Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::_Null_Type)));
+ Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type)));
+ _as->addPatch(trueBlock, isNull);
+ _as->addPatch(trueBlock, isUndefined);
+ _as->jumpToBlock(_block, falseBlock);
+
+ return true;
+}
+
+
+void InstructionSelection::visitCJumpEqual(V4IR::Binop *binop, V4IR::BasicBlock *trueBlock,
+ V4IR::BasicBlock *falseBlock)
+{
+ Q_ASSERT(binop->op == V4IR::OpEqual || binop->op == V4IR::OpNotEqual);
+
+ if (visitCJumpNullUndefined(V4IR::NullType, binop, trueBlock, falseBlock))
+ return;
+
+ V4IR::Expr *left = binop->left;
+ V4IR::Expr *right = binop->right;
+
+ _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "__qmljs_cmp_eq", __qmljs_cmp_eq,
+ Assembler::PointerToValue(left), Assembler::PointerToValue(right));
+ _as->generateCJumpOnCompare(binop->op == V4IR::OpEqual ? Assembler::NotEqual : Assembler::Equal,
+ Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
+ _block, trueBlock, falseBlock);
+}
+
+
bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource,
V4IR::Expr *rightSource, V4IR::Temp *target)
{