Merge "Merge remote-tracking branch 'origin/5.9' into dev" into refs/staging/dev

This commit is contained in:
Lars Knoll 2017-02-15 12:01:51 +00:00 committed by The Qt Project
commit 0abdf6db0e
42 changed files with 768 additions and 341 deletions

View File

@ -26,9 +26,10 @@
#ifndef ARM64Assembler_h
#define ARM64Assembler_h
#if ENABLE(ASSEMBLER) && CPU(ARM64)
#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP))
#include "AssemblerBuffer.h"
#include "AbstractMacroAssembler.h"
#include <limits.h>
#include <wtf/Assertions.h>
#include <wtf/Vector.h>
@ -520,8 +521,8 @@ typedef enum {
#undef DECLARE_REGISTER
} FPRegisterID;
static constexpr bool isSp(RegisterID reg) { return reg == sp; }
static constexpr bool isZr(RegisterID reg) { return reg == zr; }
static Q_DECL_CONSTEXPR bool isSp(RegisterID reg) { return reg == sp; }
static Q_DECL_CONSTEXPR bool isZr(RegisterID reg) { return reg == zr; }
} // namespace ARM64Registers
@ -530,15 +531,15 @@ public:
typedef ARM64Registers::RegisterID RegisterID;
typedef ARM64Registers::FPRegisterID FPRegisterID;
static constexpr RegisterID firstRegister() { return ARM64Registers::x0; }
static constexpr RegisterID lastRegister() { return ARM64Registers::sp; }
static Q_DECL_CONSTEXPR RegisterID firstRegister() { return ARM64Registers::x0; }
static Q_DECL_CONSTEXPR RegisterID lastRegister() { return ARM64Registers::sp; }
static constexpr FPRegisterID firstFPRegister() { return ARM64Registers::q0; }
static constexpr FPRegisterID lastFPRegister() { return ARM64Registers::q31; }
static Q_DECL_CONSTEXPR FPRegisterID firstFPRegister() { return ARM64Registers::q0; }
static Q_DECL_CONSTEXPR FPRegisterID lastFPRegister() { return ARM64Registers::q31; }
private:
static constexpr bool isSp(RegisterID reg) { return ARM64Registers::isSp(reg); }
static constexpr bool isZr(RegisterID reg) { return ARM64Registers::isZr(reg); }
static Q_DECL_CONSTEXPR bool isSp(RegisterID reg) { return ARM64Registers::isSp(reg); }
static Q_DECL_CONSTEXPR bool isZr(RegisterID reg) { return ARM64Registers::isZr(reg); }
public:
ARM64Assembler()
@ -546,7 +547,7 @@ public:
, m_indexOfTailOfLastWatchpoint(INT_MIN)
{
}
AssemblerBuffer& buffer() { return m_buffer; }
// (HS, LO, HI, LS) -> (AE, B, A, BE)
@ -653,9 +654,7 @@ public:
}
void operator=(const LinkRecord& other)
{
data.copyTypes.content[0] = other.data.copyTypes.content[0];
data.copyTypes.content[1] = other.data.copyTypes.content[1];
data.copyTypes.content[2] = other.data.copyTypes.content[2];
data.realTypes = other.data.realTypes;
}
intptr_t from() const { return data.realTypes.m_from; }
void setFrom(intptr_t from) { data.realTypes.m_from = from; }
@ -671,8 +670,8 @@ public:
private:
union {
struct RealTypes {
intptr_t m_from : 48;
intptr_t m_to : 48;
int64_t m_from : 48;
int64_t m_to : 48;
JumpType m_type : 8;
JumpLinkType m_linkType : 8;
Condition m_condition : 4;
@ -680,10 +679,6 @@ public:
RegisterID m_compareRegister : 6;
bool m_is64Bit : 1;
} realTypes;
struct CopyTypes {
uint64_t content[3];
} copyTypes;
COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct);
} data;
};
@ -743,6 +738,89 @@ public:
return isValidSignedImm9(offset);
}
// Jump:
//
// A jump object is a reference to a jump instruction that has been planted
// into the code buffer - it is typically used to link the jump, setting the
// relative offset such that when executed it will jump to the desired
// destination.
template <typename LabelType>
class Jump {
template<class TemplateAssemblerType>
friend class AbstractMacroAssembler;
friend class Call;
template <typename, template <typename> class> friend class LinkBufferBase;
public:
Jump()
{
}
Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type = ARM64Assembler::JumpNoCondition, ARM64Assembler::Condition condition = ARM64Assembler::ConditionInvalid)
: m_label(jmp)
, m_type(type)
, m_condition(condition)
{
}
Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type, ARM64Assembler::Condition condition, bool is64Bit, ARM64Assembler::RegisterID compareRegister)
: m_label(jmp)
, m_type(type)
, m_condition(condition)
, m_is64Bit(is64Bit)
, m_compareRegister(compareRegister)
{
ASSERT((type == ARM64Assembler::JumpCompareAndBranch) || (type == ARM64Assembler::JumpCompareAndBranchFixedSize));
}
Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type, ARM64Assembler::Condition condition, unsigned bitNumber, ARM64Assembler::RegisterID compareRegister)
: m_label(jmp)
, m_type(type)
, m_condition(condition)
, m_bitNumber(bitNumber)
, m_compareRegister(compareRegister)
{
ASSERT((type == ARM64Assembler::JumpTestBit) || (type == ARM64Assembler::JumpTestBitFixedSize));
}
LabelType label() const
{
LabelType result;
result.m_label = m_label;
return result;
}
void link(AbstractMacroAssembler<ARM64Assembler>* masm) const
{
if ((m_type == ARM64Assembler::JumpCompareAndBranch) || (m_type == ARM64Assembler::JumpCompareAndBranchFixedSize))
masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition, m_is64Bit, m_compareRegister);
else if ((m_type == ARM64Assembler::JumpTestBit) || (m_type == ARM64Assembler::JumpTestBitFixedSize))
masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition, m_bitNumber, m_compareRegister);
else
masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition);
}
void linkTo(LabelType label, AbstractMacroAssembler<ARM64Assembler>* masm) const
{
if ((m_type == ARM64Assembler::JumpCompareAndBranch) || (m_type == ARM64Assembler::JumpCompareAndBranchFixedSize))
masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition, m_is64Bit, m_compareRegister);
else if ((m_type == ARM64Assembler::JumpTestBit) || (m_type == ARM64Assembler::JumpTestBitFixedSize))
masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition, m_bitNumber, m_compareRegister);
else
masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition);
}
bool isSet() const { return m_label.isSet(); }
private:
AssemblerLabel m_label;
ARM64Assembler::JumpType m_type;
ARM64Assembler::Condition m_condition;
bool m_is64Bit;
unsigned m_bitNumber;
ARM64Assembler::RegisterID m_compareRegister;
};
private:
int encodeFPImm(double d)
{
@ -2857,11 +2935,11 @@ public:
expected = disassembleMoveWideImediate(address + 1, sf, opc, hw, imm16, rd);
ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 1 && rd == rdFirst);
result |= static_cast<uintptr_t>(imm16) << 16;
result |= static_cast<uint64_t>(imm16) << 16;
expected = disassembleMoveWideImediate(address + 2, sf, opc, hw, imm16, rd);
ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 2 && rd == rdFirst);
result |= static_cast<uintptr_t>(imm16) << 32;
result |= static_cast<uint64_t>(imm16) << 32;
return reinterpret_cast<void*>(result);
}
@ -2932,7 +3010,10 @@ public:
static void cacheFlush(void* code, size_t size)
{
#if OS(IOS)
#if defined(V4_BOOTSTRAP)
UNUSED_PARAM(code)
UNUSED_PARAM(size)
#elif OS(IOS)
sys_cache_control(kCacheFunctionPrepareForExecution, code, size);
#elif OS(LINUX)
size_t page = pageSize();
@ -2989,7 +3070,7 @@ public:
case JumpCondition: {
ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3));
intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
if (((relative << 43) >> 43) == relative)
return LinkJumpConditionDirect;
@ -2999,7 +3080,7 @@ public:
case JumpCompareAndBranch: {
ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3));
intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
if (((relative << 43) >> 43) == relative)
return LinkJumpCompareAndBranchDirect;
@ -3009,7 +3090,7 @@ public:
case JumpTestBit: {
ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3));
intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from));
if (((relative << 50) >> 50) == relative)
return LinkJumpTestBitDirect;
@ -3121,7 +3202,7 @@ private:
{
ASSERT(!(reinterpret_cast<intptr_t>(from) & 3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 3));
intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
ASSERT(((offset << 38) >> 38) == offset);
bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits
@ -3142,7 +3223,7 @@ private:
{
ASSERT(!(reinterpret_cast<intptr_t>(from) & 3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 3));
intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
ASSERT(((offset << 38) >> 38) == offset);
bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits
@ -3163,7 +3244,7 @@ private:
{
ASSERT(!(reinterpret_cast<intptr_t>(from) & 3));
ASSERT(!(reinterpret_cast<intptr_t>(to) & 3));
intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2;
ASSERT(static_cast<int>(offset) == offset);
ASSERT(((offset << 38) >> 38) == offset);
@ -3766,6 +3847,8 @@ private:
#undef DATASIZE
#undef MEMOPSIZE
#undef CHECK_FP_MEMOP_DATASIZE
#undef JUMP_ENUM_WITH_SIZE
#undef JUMP_ENUM_SIZE
#endif // ENABLE(ASSEMBLER) && CPU(ARM64)

View File

@ -2867,6 +2867,9 @@ private:
int m_indexOfTailOfLastWatchpoint;
};
#undef JUMP_ENUM_WITH_SIZE
#undef JUMP_ENUM_SIZE
} // namespace JSC
#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)

View File

@ -66,7 +66,7 @@ public:
typedef MacroAssemblerCodePtr CodePtr;
typedef MacroAssemblerCodeRef CodeRef;
#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP)
#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP)
class Jump;
#endif
@ -455,7 +455,7 @@ public:
AssemblerLabel m_label;
};
#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP)
using Jump = typename AssemblerType::template Jump<Label>;
friend Jump;
#endif
@ -510,7 +510,7 @@ public:
// into the code buffer - it is typically used to link the jump, setting the
// relative offset such that when executed it will jump to the desired
// destination.
#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP)
#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP)
class Jump {
template<class TemplateAssemblerType>
friend class AbstractMacroAssembler;

View File

@ -505,7 +505,7 @@ public:
};
#endif
#if CPU(ARM64)
#if CPU(ARM64) || defined(V4_BOOTSTRAP)
template <>
class LinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>> : public BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>>
{

View File

@ -31,12 +31,12 @@
#if ENABLE(ASSEMBLER)
#include "MacroAssemblerARMv7.h"
#include "MacroAssemblerARM64.h"
#if CPU(ARM_THUMB2)
namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; };
#elif CPU(ARM64)
#include "MacroAssemblerARM64.h"
namespace JSC { typedef MacroAssemblerARM64 MacroAssemblerBase; };
#elif CPU(ARM_TRADITIONAL)

View File

@ -26,7 +26,7 @@
#ifndef MacroAssemblerARM64_h
#define MacroAssemblerARM64_h
#if ENABLE(ASSEMBLER)
#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP))
#include "ARM64Assembler.h"
#include "AbstractMacroAssembler.h"
@ -42,7 +42,7 @@ class MacroAssemblerARM64 : public AbstractMacroAssembler<ARM64Assembler> {
friend class DataLabelPtr;
friend class DataLabel32;
friend class DataLabelCompact;
friend class Jump;
// template <typename> friend class Jump;
friend class Label;
public:
@ -119,9 +119,9 @@ public:
private:
static const ARM64Registers::FPRegisterID fpTempRegister = ARM64Registers::q31;
static const ARM64Assembler::SetFlags S = ARM64Assembler::S;
static const intptr_t maskHalfWord0 = 0xffffl;
static const intptr_t maskHalfWord1 = 0xffff0000l;
static const intptr_t maskUpperWord = 0xffffffff00000000l;
static const int64_t maskHalfWord0 = 0xffffl;
static const int64_t maskHalfWord1 = 0xffff0000l;
static const int64_t maskUpperWord = 0xffffffff00000000l;
// 4 instructions - 3 to load the function pointer, + blr.
static const ptrdiff_t REPATCH_OFFSET_CALL_TO_POINTER = -16;
@ -209,6 +209,33 @@ public:
static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; }
static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; }
#if defined(V4_BOOTSTRAP)
void loadPtr(ImplicitAddress address, RegisterID dest)
{
load32(address, dest);
}
void subPtr(TrustedImm32 imm, RegisterID dest)
{
sub32(imm, dest);
}
void addPtr(TrustedImm32 imm, RegisterID dest)
{
add32(imm, dest);
}
void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
{
add32(imm, src, dest);
}
void storePtr(RegisterID src, ImplicitAddress address)
{
store32(src, address);
}
#endif
// Integer operations:
void add32(RegisterID a, RegisterID b, RegisterID dest)
@ -2757,6 +2784,7 @@ public:
return branch32(cond, left, dataTempRegister);
}
#if !defined(V4_BOOTSTRAP)
PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right)
{
m_makeJumpPatchable = true;
@ -2764,6 +2792,7 @@ public:
m_makeJumpPatchable = false;
return PatchableJump(result);
}
#endif
PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
{
@ -3000,7 +3029,7 @@ private:
return m_cachedMemoryTempRegister.registerIDInvalidate();
}
ALWAYS_INLINE bool isInIntRange(intptr_t value)
ALWAYS_INLINE bool isInIntRange(int64_t value)
{
return value == ((value << 32) >> 32);
}
@ -3354,6 +3383,8 @@ private:
}
template <typename, template <typename> class> friend class LinkBufferBase;
template <typename> friend class BranchCompactingLinkBuffer;
template <typename> friend struct BranchCompactingExecutableOffsetCalculator;
void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); }
int executableOffsetFor(int location) { return m_assembler.executableOffsetFor(location); }

View File

@ -31,7 +31,17 @@ HEADERS += $$PWD/stubs/WTFStubs.h
SOURCES += $$PWD/stubs/Options.cpp
HEADERS += $$PWD/stubs/wtf/*.h
HEADERS += $$PWD/stubs/wtf/FastAllocBase.h \
$$PWD/stubs/wtf/FastMalloc.h \
$$PWD/stubs/wtf/Noncopyable.h \
$$PWD/stubs/wtf/OwnPtr.h \
$$PWD/stubs/wtf/PassOwnPtr.h \
$$PWD/stubs/wtf/PassRefPtr.h \
$$PWD/stubs/wtf/RefCounted.h \
$$PWD/stubs/wtf/RefPtr.h \
$$PWD/stubs/wtf/TypeTraits.h \
$$PWD/stubs/wtf/UnusedParam.h \
$$PWD/stubs/wtf/Vector.h
SOURCES += $$PWD/disassembler/Disassembler.cpp
SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp
@ -67,8 +77,21 @@ SOURCES += $$PWD/disassembler/ARM64Disassembler.cpp
SOURCES += $$PWD/disassembler/ARM64/A64DOpcode.cpp
HEADERS += $$PWD/disassembler/ARM64/A64DOpcode.h
SOURCES += $$PWD/yarr/*.cpp
HEADERS += $$PWD/yarr/*.h
!qmldevtools_build {
SOURCES += $$PWD/yarr/YarrCanonicalizeUCS2.cpp \
$$PWD/yarr/YarrInterpreter.cpp \
$$PWD/yarr/YarrJIT.cpp \
$$PWD/yarr/YarrPattern.cpp \
$$PWD/yarr/YarrSyntaxChecker.cpp
HEADERS += $$PWD/yarr/Yarr.h \
$$PWD/yarr/YarrCanonicalizeUCS2.h \
$$PWD/yarr/YarrInterpreter.h \
$$PWD/yarr/YarrJIT.h \
$$PWD/yarr/YarrParser.h \
$$PWD/yarr/YarrPattern.h \
$$PWD/yarr/YarrSyntaxChecker.h
}
#
# Generate RegExpJitTables.h

View File

@ -107,7 +107,7 @@ struct ExecutableAllocator {
size = size + (iaddr - roundAddr);
addr = reinterpret_cast<void*>(roundAddr);
#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
#if ENABLE(ASSEMBLER_WX_EXCLUSIVE) && !defined(V4_BOOTSTRAP)
# if OS(WINDOWS)
DWORD oldProtect;
# if !OS(WINRT)
@ -140,6 +140,7 @@ struct ExecutableAllocator {
size = size + (iaddr - roundAddr);
addr = reinterpret_cast<void*>(roundAddr);
#if !defined(V4_BOOTSTRAP)
#if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
# if OS(WINDOWS)
DWORD oldProtect;
@ -160,6 +161,10 @@ struct ExecutableAllocator {
# endif
#else
# error "Only W^X is supported"
#endif
#else
(void)addr; // suppress unused parameter warning
(void)size; // suppress unused parameter warning
#endif
}

View File

@ -219,24 +219,6 @@ QQmlSqlDatabaseData::~QQmlSqlDatabaseData()
{
}
static QString qmlsqldatabase_databasesPath(QV4::ExecutionEngine *engine)
{
return engine->qmlEngine()->offlineStoragePath() +
QDir::separator() + QLatin1String("Databases");
}
static void qmlsqldatabase_initDatabasesPath(QV4::ExecutionEngine *engine)
{
QString databasesPath = qmlsqldatabase_databasesPath(engine);
if (!QDir().mkpath(databasesPath))
qWarning() << "LocalStorage: can't create path - " << databasesPath;
}
static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV4::ExecutionEngine *engine)
{
return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName;
}
static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0)
{
Scope scope(v4);
@ -450,7 +432,8 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
if (ok) {
*w->d()->version = to_version;
#if QT_CONFIG(settings)
QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(), scope.engine) + QLatin1String(".ini"), QSettings::IniFormat);
const QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(scope.engine->qmlEngine());
QSettings ini(enginePrivate->offlineStorageDatabaseDirectory() + db.connectionName() + QLatin1String(".ini"), QSettings::IniFormat);
ini.setValue(QLatin1String("Version"), to_version);
#endif
}
@ -723,24 +706,23 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
if (scope.engine->qmlEngine()->offlineStoragePath().isEmpty())
V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, QQmlEngine::tr("SQL: can't create database, offline storage is disabled."));
qmlsqldatabase_initDatabasesPath(scope.engine);
QSqlDatabase database;
QV4::ScopedValue v(scope);
QString dbname = (v = (*args)[0])->toQStringNoThrow();
QString dbversion = (v = (*args)[1])->toQStringNoThrow();
QString dbdescription = (v = (*args)[2])->toQStringNoThrow();
int dbestimatedsize = (v = (*args)[3])->toInt32();
FunctionObject *dbcreationCallback = (v = (*args)[4])->as<FunctionObject>();
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(dbname.toUtf8());
QString dbid(QLatin1String(md5.result().toHex()));
QString basename = qmlsqldatabase_databaseFile(dbid, scope.engine);
QString basename = args->v4engine()->qmlEngine()->offlineStorageDatabaseFilePath(dbname);
QFileInfo dbFile(basename);
if (!QDir().mkpath(dbFile.dir().absolutePath())) {
const QString message = QQmlEngine::tr("LocalStorage: can't create path %1").
arg(QDir::toNativeSeparators(dbFile.dir().absolutePath()));
V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, message);
}
QString dbid = dbFile.fileName();
bool created = false;
QString version = dbversion;
QSqlDatabase database;
{
QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);

View File

@ -167,9 +167,9 @@ additional features. See the \l {qtqml-javascript-hostenvironment.html}
The QML engine provides automatic type conversion between QVariantList and
JavaScript arrays, and between QVariantMap and JavaScript objects.
For example, the function defined in QML below left expects two arguments, an
For example, the function defined in QML below expects two arguments, an
array and an object, and prints their contents using the standard JavaScript
syntax for array and object item access. The C++ code below right calls this
syntax for array and object item access. The C++ code below calls this
function, passing a QVariantList and a QVariantMap, which are automatically
converted to JavaScript array and object values, repectively:
@ -204,9 +204,9 @@ when it is passed to C++.
The QML engine provides automatic type conversion between QDateTime values and
JavaScript \c Date objects.
For example, the function defined in QML below left expects a JavaScript
For example, the function defined in QML below expects a JavaScript
\c Date object, and also returns a new \c Date object with the current date and
time. The C++ code below right calls this function, passing a QDateTime value
time. The C++ code below calls this function, passing a QDateTime value
that is automatically converted by the engine into a \c Date object when it is
passed to the \c readDate() function. In turn, the readDate() function returns
a \c Date object that is automatically converted into a QDateTime value when it
@ -215,7 +215,7 @@ is received in C++:
\table
\header
\row
\li QML
\li
\qml
// MyItem.qml
@ -227,6 +227,7 @@ Item {
}
\endqml
\row
\li C++
\li
\code
// C++

View File

@ -390,8 +390,8 @@ that is a public slot:
\endcode
If an instance of \c MessageBoard was set as the context data for a file \c
MyItem.qml, as shown below left, then \c MyItem.qml could invoke the two
methods, as shown below right:
MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the
examples below:
\table
\row

View File

@ -379,16 +379,7 @@ void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInfor
Q_ASSERT(slotAddr.offset == 0);
const int frameSize = _stackLayout->calculateStackFrameSize();
// Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
// work well for large immediates.
#if CPU(ARM_THUMB2)
move(TrustedImm32(frameSize), JSC::ARMRegisters::r3);
add32(JSC::ARMRegisters::r3, StackPointerRegister);
#else
addPtr(TrustedImm32(frameSize), StackPointerRegister);
#endif
platformLeaveStandardStackFrame(this);
platformLeaveStandardStackFrame(this, frameSize);
}
@ -709,8 +700,13 @@ JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize)
}
template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>;
#if defined(V4_BOOTSTRAP) && CPU(X86_64)
#if defined(V4_BOOTSTRAP)
#if !CPU(ARM_THUMB2)
template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
#endif
#if !CPU(ARM64)
template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
#endif
#endif
#endif

View File

@ -145,11 +145,13 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
using RegisterID = typename JITAssembler::RegisterID;
using FPRegisterID = typename JITAssembler::FPRegisterID;
using RelationalCondition = typename JITAssembler::RelationalCondition;
using ResultCondition = typename JITAssembler::ResultCondition;
using Address = typename JITAssembler::Address;
using Pointer = typename JITAssembler::Pointer;
using TrustedImm32 = typename JITAssembler::TrustedImm32;
using TrustedImm64 = typename JITAssembler::TrustedImm64;
using Jump = typename JITAssembler::Jump;
using Label = typename JITAssembler::Label;
static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest)
{
@ -352,6 +354,19 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister,
TrustedImm32(Value::NotDouble_Mask));
}
static void initializeLocalVariables(JITAssembler *as, int localsCount)
{
as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister);
as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister);
Label loop = as->label();
as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister);
as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister);
Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister);
jump.linkTo(loop, as);
}
};
template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform>
@ -364,8 +379,10 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
using TrustedImm64 = typename JITAssembler::TrustedImm64;
using Pointer = typename JITAssembler::Pointer;
using RelationalCondition = typename JITAssembler::RelationalCondition;
using ResultCondition = typename JITAssembler::ResultCondition;
using BranchTruncateType = typename JITAssembler::BranchTruncateType;
using Jump = typename JITAssembler::Jump;
using Label = typename JITAssembler::Label;
static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest)
{
@ -464,7 +481,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
{
// Use ReturnValueRegister as "scratch" register because loadArgument
// and storeArgument are functions that may need a scratch register themselves.
as->loadArgumentInRegister(source, TargetPlatform::ReturnValueRegister, 0);
loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0);
as->storeReturnValue(destination);
}
@ -477,6 +494,12 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
as->move64ToDouble(TargetPlatform::ReturnValueRegister, target);
}
static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber)
{
Q_UNUSED(argumentNumber);
as->load64(addressOfValue, dest);
}
static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber)
{
Q_UNUSED(argumentNumber);
@ -625,6 +648,17 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo
return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister,
TrustedImm32(0));
}
static void initializeLocalVariables(JITAssembler *as, int localsCount)
{
as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister);
as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister);
Label loop = as->label();
as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister);
Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister);
jump.linkTo(loop, as);
}
};
template <typename TargetConfiguration>
@ -1338,9 +1372,8 @@ public:
if (prepareCall(function))
loadArgumentOnStackOrRegister<0>(arg1);
#ifdef RESTORE_EBX_ON_CALL
load32(this->ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr
#endif
if (JITTargetPlatform::gotRegister != -1)
load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr
callAbsolute(functionName, function);
@ -1585,6 +1618,15 @@ public:
void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave);
const StackLayout &stackLayout() const { return *_stackLayout.data(); }
void initializeLocalVariables()
{
const int locals = _stackLayout->calculateJSStackFrameSize();
if (locals <= 0)
return;
loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister);
RegisterSizeDependentOps::initializeLocalVariables(this, locals);
storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
}
Label exceptionReturnLabel;
IR::BasicBlock * catchBlock;

View File

@ -41,8 +41,128 @@
#if ENABLE(ASSEMBLER)
using namespace QV4;
using namespace JIT;
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
template <typename JITAssembler>
struct ArchitectureSpecificBinaryOperation
{
using FPRegisterID = typename JITAssembler::FPRegisterID;
static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
Q_UNUSED(as);
Q_UNUSED(lhs);
Q_UNUSED(rhs);
Q_UNUSED(targetReg);
return false;
}
static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
Q_UNUSED(as);
Q_UNUSED(lhs);
Q_UNUSED(rhs);
Q_UNUSED(targetReg);
return false;
}
static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
Q_UNUSED(as);
Q_UNUSED(lhs);
Q_UNUSED(rhs);
Q_UNUSED(targetReg);
return false;
}
static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
Q_UNUSED(as);
Q_UNUSED(lhs);
Q_UNUSED(rhs);
Q_UNUSED(targetReg);
return false;
}
};
#if CPU(X86)
template <>
struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>>
{
using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>;
using FPRegisterID = JITAssembler::FPRegisterID;
using Address = JITAssembler::Address;
static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->addDouble(addr, targetReg);
return true;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->addDouble(as->loadTempAddress(t), targetReg);
return true;
}
}
return false;
}
static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->mulDouble(addr, targetReg);
return true;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->mulDouble(as->loadTempAddress(t), targetReg);
return true;
}
}
return false;
}
static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->subDouble(addr, targetReg);
return true;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->subDouble(as->loadTempAddress(t), targetReg);
return true;
}
}
return false;
}
static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
{
if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->divDouble(addr, targetReg);
return true;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->divDouble(as->loadTempAddress(t), targetReg);
return true;
}
}
return false;
}
};
#endif
#define OP(op) \
{ "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
@ -162,21 +282,9 @@ void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *ta
if (lhs->asConst())
std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->addDouble(addr, targetReg);
if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg))
break;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->addDouble(as->loadTempAddress(t), targetReg);
break;
}
}
#endif
as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
break;
@ -184,40 +292,16 @@ void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *ta
if (lhs->asConst())
std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->mulDouble(addr, targetReg);
if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg))
break;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->mulDouble(as->loadTempAddress(t), targetReg);
break;
}
}
#endif
as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
break;
case IR::OpSub:
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->subDouble(addr, targetReg);
if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg))
break;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->subDouble(as->loadTempAddress(t), targetReg);
break;
}
}
#endif
if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
&& targetTemp
&& targetTemp->kind == IR::Temp::PhysicalRegister
@ -231,21 +315,8 @@ void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *ta
break;
case IR::OpDiv:
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
as->divDouble(addr, targetReg);
if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg))
break;
}
if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address]
if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
as->divDouble(as->loadTempAddress(t), targetReg);
break;
}
}
#endif
if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
&& targetTemp
@ -577,8 +648,19 @@ typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSo
}
template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>;
#if defined(V4_BOOTSTRAP) && CPU(X86_64)
#if defined(V4_BOOTSTRAP)
#if !CPU(ARM_THUMB2)
template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>;
#endif
#if !CPU(ARM64)
template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>;
#endif
#endif
} // end of namespace JIT
} // end of namespace QV4
QT_END_NAMESPACE
#endif

View File

@ -114,37 +114,12 @@ void InstructionSelection<JITAssembler>::run(int functionIndex)
fpRegistersToSave.size());
_as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave);
#ifdef ARGUMENTS_IN_REGISTERS
_as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister);
#else
_as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister);
#endif
const int locals = _as->stackLayout().calculateJSStackFrameSize();
if (locals > 0) {
_as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister);
#ifdef VALUE_FITS_IN_REGISTER
_as->move(TrustedImm64(0), JITTargetPlatform::ReturnValueRegister);
_as->move(TrustedImm32(locals), JITTargetPlatform::ScratchRegister);
Label loop = _as->label();
_as->store64(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister));
_as->add64(TrustedImm32(8), JITTargetPlatform::LocalsRegister);
Jump jump = _as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), JITTargetPlatform::ScratchRegister);
jump.linkTo(loop, _as);
#else
_as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister);
_as->move(TrustedImm32(locals), JITTargetPlatform::ScratchRegister);
Label loop = _as->label();
_as->store32(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister));
_as->add32(TrustedImm32(4), JITTargetPlatform::LocalsRegister);
_as->store32(JITTargetPlatform::ReturnValueRegister, Address(JITTargetPlatform::LocalsRegister));
_as->add32(TrustedImm32(4), JITTargetPlatform::LocalsRegister);
Jump jump = _as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), JITTargetPlatform::ScratchRegister);
jump.linkTo(loop, _as);
#endif
_as->storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
}
if (JITTargetPlatform::RegisterArgumentCount > 0)
_as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister);
else
_as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister);
_as->initializeLocalVariables();
int lastLine = 0;
for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) {
@ -474,13 +449,7 @@ void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp)
{
_as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister);
_as->loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), JITTargetPlatform::ScratchRegister);
#if defined(VALUE_FITS_IN_REGISTER)
_as->load64(Pointer(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject)),
JITTargetPlatform::ReturnValueRegister);
_as->storeReturnValue(temp);
#else
_as->copyValue(temp, Pointer(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject)));
#endif
_as->copyValue(temp, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject)));
}
template <typename JITAssembler>
@ -1374,12 +1343,10 @@ void InstructionSelection<JITAssembler>::calculateRegistersToSave(const Register
fpRegistersToSave.clear();
for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) {
#if defined(RESTORE_EBX_ON_CALL)
if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) {
if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) {
regularRegistersToSave.append(ri);
continue;
}
#endif // RESTORE_EBX_ON_CALL
if (ri.isCallerSaved())
continue;
if (ri.isRegularRegister()) {
@ -1666,20 +1633,21 @@ QT_BEGIN_NAMESPACE
namespace QV4 { namespace JIT {
template class Q_QML_EXPORT InstructionSelection<>;
template class Q_QML_EXPORT ISelFactory<>;
#if defined(V4_BOOTSTRAP) && CPU(X86_64)
#if defined(V4_BOOTSTRAP)
Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture)
{
using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
if (architecture == QLatin1String("armv7"))
return new ISelFactory<ARMv7CrossAssembler>;
else if (architecture == QLatin1String("armv8"))
return new ISelFactory<ARM64CrossAssembler>;
QString hostArch;
#if CPU(ARM_THUMB2)
hostArch = QStringLiteral("armv7");
#elif CPU(ARM64)
hostArch = QStringLiteral("armv8");
#elif CPU(MIPS)
hostArch = QStringLiteral("mips");
#elif CPU(X86)

View File

@ -155,7 +155,6 @@ protected:
using JITTargetPlatform = typename JITAssembler::JITTargetPlatform;
#if !defined(ARGUMENTS_IN_REGISTERS)
Address addressForArgument(int index) const
{
// FramePointerRegister points to its old value on the stack, and above
@ -163,7 +162,6 @@ protected:
// values before reaching the first argument.
return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*));
}
#endif
Pointer baseAddressForCallArguments()
{

View File

@ -97,6 +97,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerX86;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
enum { RegAllocIsSupported = 1 };
@ -130,10 +131,8 @@ public:
}
# define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
# undef VALUE_FITS_IN_REGISTER
static const int RegisterSize = 4;
# undef ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 0;
static RegisterID registerForArgument(int) { Q_UNREACHABLE(); }
@ -141,15 +140,18 @@ public:
static const int StackShadowSpace = 0;
static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0)
as->add32(TrustedImm32(frameSize), StackPointerRegister);
as->pop(FramePointerRegister);
}
#if OS(WINDOWS) || OS(QNX) || \
((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__)))
#define RESTORE_EBX_ON_CALL
using Address = PlatformAssembler::Address;
static Address ebxAddressOnStack()
{
static const int gotRegister = JSC::X86Registers::ebx;
static int savedGOTRegisterSlotOnStack() {
static int ebxIdx = -1;
if (ebxIdx == -1) {
int calleeSaves = 0;
@ -165,8 +167,11 @@ public:
Q_ASSERT(ebxIdx >= 0);
ebxIdx += 1;
}
return Address(FramePointerRegister, ebxIdx * -int(sizeof(void*)));
return ebxIdx * -int(sizeof(void*));
}
#else
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
#endif
};
#endif // x86
@ -179,6 +184,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerX86_64;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
enum { RegAllocIsSupported = 1 };
@ -216,10 +222,8 @@ public:
}
#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
#define VALUE_FITS_IN_REGISTER
static const int RegisterSize = 8;
#define ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 6;
static RegisterID registerForArgument(int index)
{
@ -239,7 +243,15 @@ public:
static const int StackShadowSpace = 0;
static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0)
as->add64(TrustedImm32(frameSize), StackPointerRegister);
as->pop(FramePointerRegister);
}
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
};
#endif // Linux/MacOS on x86_64
@ -251,6 +263,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerX86_64;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
// Register allocation is not (yet) supported on win64, because the ABI related stack handling
// is not completely implemented. Specifically, the saving of xmm registers, and the saving of
@ -285,10 +298,8 @@ public:
}
#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
#define VALUE_FITS_IN_REGISTER
static const int RegisterSize = 8;
#define ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 4;
static RegisterID registerForArgument(int index)
{
@ -306,7 +317,15 @@ public:
static const int StackShadowSpace = 32;
static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as) { as->pop(FramePointerRegister); }
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0)
as->add64(TrustedImm32(frameSize), StackPointerRegister);
as->pop(FramePointerRegister);
}
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
};
#endif // Windows on x86_64
@ -318,6 +337,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerARMv7;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
enum { RegAllocIsSupported = 1 };
@ -389,10 +409,8 @@ public:
}
#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
#undef VALUE_FITS_IN_REGISTER
static const int RegisterSize = 4;
#define ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 4;
static RegisterID registerForArgument(int index)
{
@ -417,15 +435,24 @@ public:
as->push(FramePointerRegister);
}
static void platformLeaveStandardStackFrame(PlatformAssembler *as)
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0) {
// Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
// work well for large immediates.
as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3);
as->add32(JSC::ARMRegisters::r3, StackPointerRegister);
}
as->pop(FramePointerRegister);
as->pop(JSC::ARMRegisters::lr);
}
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
};
#endif // ARM (32 bit)
#if CPU(ARM64)
#if CPU(ARM64) || defined(V4_BOOTSTRAP)
template <>
class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>
{
@ -433,6 +460,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerARM64;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
enum { RegAllocIsSupported = 1 };
@ -510,10 +538,8 @@ public:
}
#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
#define VALUE_FITS_IN_REGISTER
static const int RegisterSize = 8;
#define ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 8;
static RegisterID registerForArgument(int index)
{
@ -541,10 +567,15 @@ public:
as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr);
}
static void platformLeaveStandardStackFrame(PlatformAssembler *as)
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0)
as->add64(TrustedImm32(frameSize), StackPointerRegister);
as->popPair(FramePointerRegister, JSC::ARM64Registers::lr);
}
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
};
#endif // ARM64
@ -556,6 +587,7 @@ public:
using PlatformAssembler = JSC::MacroAssemblerMIPS;
using RegisterID = PlatformAssembler::RegisterID;
using FPRegisterID = PlatformAssembler::FPRegisterID;
using TrustedImm32 = PlatformAssembler::TrustedImm32;
enum { RegAllocIsSupported = 1 };
static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp;
@ -598,10 +630,8 @@ public:
}
#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
#undef VALUE_FITS_IN_REGISTER
static const int RegisterSize = 4;
#define ARGUMENTS_IN_REGISTERS
static const int RegisterArgumentCount = 4;
static RegisterID registerForArgument(int index)
{
@ -626,11 +656,17 @@ public:
as->push(FramePointerRegister);
}
static void platformLeaveStandardStackFrame(PlatformAssembler *as)
static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
{
if (frameSize > 0)
as->add32(TrustedImm32(frameSize), StackPointerRegister);
as->pop(FramePointerRegister);
as->pop(JSC::MIPSRegisters::ra);
}
static const int gotRegister = -1;
static int savedGOTRegisterSlotOnStack() { return -1; }
};
#endif // Linux on MIPS (32 bit)

View File

@ -145,8 +145,13 @@ void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target)
}
template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>;
#if defined(V4_BOOTSTRAP) && CPU(X86_64)
#if defined(V4_BOOTSTRAP)
#if !CPU(ARM_THUMB2)
template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>;
#endif
#if !CPU(ARM64)
template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>;
#endif
#endif
#endif

View File

@ -61,7 +61,8 @@ DEFINE_OBJECT_VTABLE(Object);
void Object::setInternalClass(InternalClass *ic)
{
d()->internalClass = ic;
if ((!d()->memberData && ic->size) || (d()->memberData->size < ic->size))
bool hasMD = d()->memberData != nullptr;
if ((!hasMD && ic->size) || (hasMD && d()->memberData->size < ic->size))
d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData);
}

View File

@ -1264,12 +1264,16 @@ ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine)
*/
void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine)
{
engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine)));
QV4::Value *v = engine->jsAlloca(1);
Heap::Object *withObject = o.toObject(engine);
*v = withObject;
engine->pushContext(engine->currentContext->newWithContext(withObject));
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
}
void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex)
{
engine->jsAlloca(1); // keep this symmetric with pushWithScope
ExecutionContext *c = engine->currentContext;
engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0)));
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
@ -1279,7 +1283,7 @@ void Runtime::method_popScope(NoThrowEngine *engine)
{
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
engine->popContext();
engine->jsStackTop -= 2;
engine->jsStackTop -= 3;
}
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)

View File

@ -328,11 +328,16 @@ void Chunk::freeAll()
void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
{
// qDebug() << "sortIntoBins:";
HeapItem *base = realBase();
#if QT_POINTER_SIZE == 8
const int start = 0;
#else
const int start = 1;
#endif
#ifndef QT_NO_DEBUG
uint freeSlots = 0;
uint allocatedSlots = 0;
#endif
for (int i = start; i < EntriesInBitmap; ++i) {
quintptr usedSlots = (objectBitmap[i]|extendsBitmap[i]);
@ -340,28 +345,48 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
if (!i)
usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1;
#endif
uint index = qCountTrailingZeroBits(usedSlots + 1);
if (index == Bits)
continue;
uint freeStart = i*Bits + index;
usedSlots &= ~((static_cast<quintptr>(1) << index) - 1);
while (i < EntriesInBitmap && !usedSlots) {
++i;
usedSlots = (objectBitmap[i]|extendsBitmap[i]);
}
if (i == EntriesInBitmap)
usedSlots = 1;
HeapItem *freeItem = base + freeStart;
#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
while (1) {
uint index = qCountTrailingZeroBits(usedSlots + 1);
if (index == Bits)
break;
uint freeStart = i*Bits + index;
usedSlots &= ~((static_cast<quintptr>(1) << index) - 1);
while (!usedSlots) {
++i;
if (i == EntriesInBitmap) {
usedSlots = (quintptr)-1;
break;
}
usedSlots = (objectBitmap[i]|extendsBitmap[i]);
#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
}
HeapItem *freeItem = base + freeStart;
uint freeEnd = i*Bits + qCountTrailingZeroBits(usedSlots);
uint nSlots = freeEnd - freeStart;
Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots);
freeItem->freeData.availableSlots = nSlots;
uint bin = qMin(nBins - 1, nSlots);
freeItem->freeData.next = bins[bin];
bins[bin] = freeItem;
// DEBUG << "binnig item" << freeItem << nSlots << bin << freeItem->freeData.availableSlots;
index = qCountTrailingZeroBits(usedSlots);
usedSlots |= (quintptr(1) << index) - 1;
uint freeEnd = i*Bits + index;
uint nSlots = freeEnd - freeStart;
#ifndef QT_NO_DEBUG
// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots;
freeSlots += nSlots;
#endif
Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots);
freeItem->freeData.availableSlots = nSlots;
uint bin = qMin(nBins - 1, nSlots);
freeItem->freeData.next = bins[bin];
bins[bin] = freeItem;
}
}
#ifndef QT_NO_DEBUG
Q_ASSERT(freeSlots + allocatedSlots == EntriesInBitmap*8*sizeof(quintptr));
#endif
}
@ -427,28 +452,7 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) {
goto done;
}
}
#if 0
for (uint b = bin + 1; b < NumBins - 1; ++b) {
if ((m = freeBins[b])) {
Q_ASSERT(binForSlots(m->freeData.availableSlots) == b);
freeBins[b] = m->freeData.next;
// DEBUG << "looking for empty bin" << bin << "size" << size << "found" << b;
uint remainingSlots = m->freeData.availableSlots - slotsRequired;
// DEBUG << "found free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots;
if (remainingSlots < 2) {
// avoid too much fragmentation and rather mark the memory as used
size += remainingSlots*Chunk::SlotSize;
goto done;
}
HeapItem *remainder = m + slotsRequired;
remainder->freeData.availableSlots = remainingSlots;
uint binForRemainder = binForSlots(remainingSlots);
remainder->freeData.next = freeBins[binForRemainder];
freeBins[binForRemainder] = remainder;
goto done;
}
}
#endif
if (nFree >= slotsRequired) {
// use bump allocation
Q_ASSERT(nextFree);
@ -467,13 +471,11 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) {
size_t remainingSlots = m->freeData.availableSlots - slotsRequired;
// DEBUG << "found large free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots;
if (remainingSlots < 2) {
// avoid too much fragmentation and rather mark the memory as used
size += remainingSlots*Chunk::SlotSize;
if (remainingSlots == 0)
goto done;
}
HeapItem *remainder = m + slotsRequired;
if (remainingSlots >= 2*NumBins) {
if (remainingSlots > nFree) {
if (nFree) {
size_t bin = binForSlots(nFree);
nextFree->freeData.next = freeBins[bin];
@ -493,6 +495,24 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) {
last = &m->freeData.next;
}
if (slotsRequired < NumBins - 1) {
// check if we can split up another slot
for (size_t i = slotsRequired + 1; i < NumBins - 1; ++i) {
m = freeBins[i];
if (m) {
freeBins[i] = m->freeData.next; // take it out of the list
// qDebug() << "got item" << slotsRequired << "from slot" << i;
size_t remainingSlots = i - slotsRequired;
Q_ASSERT(remainingSlots < NumBins - 1);
HeapItem *remainder = m + slotsRequired;
remainder->freeData.availableSlots = remainingSlots;
remainder->freeData.next = freeBins[remainingSlots];
freeBins[remainingSlots] = remainder;
goto done;
}
}
}
if (!m) {
if (!forceAllocation)
return 0;
@ -622,14 +642,24 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
#endif
}
#ifndef QT_NO_DEBUG
static int lastAllocRequestedSlots = 0;
#endif
Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
{
if (aggressiveGC)
runGC();
const size_t stringSize = align(sizeof(Heap::String));
unmanagedHeapSize += unmanagedSize;
#ifndef QT_NO_DEBUG
lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift;
#endif
bool didGCRun = false;
if (aggressiveGC) {
runGC();
didGCRun = true;
}
unmanagedHeapSize += unmanagedSize;
if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
runGC();
@ -655,8 +685,15 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
Heap::Base *MemoryManager::allocData(std::size_t size)
{
if (aggressiveGC)
#ifndef QT_NO_DEBUG
lastAllocRequestedSlots = size >> Chunk::SlotSizeShift;
#endif
bool didRunGC = false;
if (aggressiveGC) {
runGC();
didRunGC = true;
}
#ifdef DETAILED_MM_STATS
willAllocate(size);
#endif // DETAILED_MM_STATS
@ -671,7 +708,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size)
HeapItem *m = blockAllocator.allocate(size);
if (!m) {
if (shouldRunGC())
if (!didRunGC && shouldRunGC())
runGC();
m = blockAllocator.allocate(size, true);
}
@ -817,6 +854,26 @@ bool MemoryManager::shouldRunGC() const
return false;
}
size_t dumpBins(BlockAllocator *b, bool printOutput = true)
{
size_t totalFragmentedSlots = 0;
if (printOutput)
qDebug() << "Fragmentation map:";
for (uint i = 0; i < BlockAllocator::NumBins; ++i) {
uint nEntries = 0;
HeapItem *h = b->freeBins[i];
while (h) {
++nEntries;
totalFragmentedSlots += h->freeData.availableSlots;
h = h->freeData.next;
}
if (printOutput)
qDebug() << " number of entries in slot" << i << ":" << nEntries;
}
if (printOutput)
qDebug() << " total mem in bins" << totalFragmentedSlots*Chunk::SlotSize;
return totalFragmentedSlots*Chunk::SlotSize;
}
void MemoryManager::runGC()
{
@ -833,31 +890,56 @@ void MemoryManager::runGC()
sweep();
// DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem();
} else {
bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit);
size_t oldUnmanagedSize = unmanagedHeapSize;
const size_t totalMem = getAllocatedMem();
const size_t usedBefore = getUsedMem();
const size_t largeItemsBefore = getLargeItemsMem();
qDebug() << "========== GC ==========";
#ifndef QT_NO_DEBUG
qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots.";
#endif
qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks";
qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore);
dumpBins(&blockAllocator);
QElapsedTimer t;
t.start();
mark();
qint64 markTime = t.restart();
const size_t usedBefore = getUsedMem();
const size_t largeItemsBefore = getLargeItemsMem();
sweep();
const size_t usedAfter = getUsedMem();
const size_t largeItemsAfter = getLargeItemsMem();
qint64 sweepTime = t.elapsed();
qDebug() << "========== GC ==========";
if (triggeredByUnmanagedHeap) {
qDebug() << "triggered by unmanaged heap:";
qDebug() << " old unmanaged heap size:" << oldUnmanagedSize;
qDebug() << " new unmanaged heap:" << unmanagedHeapSize;
qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit;
}
size_t memInBins = dumpBins(&blockAllocator);
qDebug() << "Marked object in" << markTime << "ms.";
qDebug() << "Sweeped object in" << sweepTime << "ms.";
qDebug() << "Allocated" << totalMem << "bytes";
qDebug() << "Used memory before GC:" << usedBefore;
qDebug() << "Used memory after GC:" << usedAfter;
qDebug() << "Freed up bytes:" << (usedBefore - usedAfter);
qDebug() << "Large item memory before GC:" << largeItemsBefore;
qDebug() << "Large item memory after GC:" << largeItemsAfter;
qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter);
size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter;
if (lost)
qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
if (largeItemsBefore || largeItemsAfter) {
qDebug() << "Large item memory before GC:" << largeItemsBefore;
qDebug() << "Large item memory after GC:" << largeItemsAfter;
qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter);
}
qDebug() << "======== End GC ========";
}
if (aggressiveGC) {
// ensure we don't 'loose' any memory
Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false));
}
}
size_t MemoryManager::getUsedMem() const

View File

@ -65,6 +65,7 @@
#include <QtCore/qmetaobject.h>
#include <QDebug>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdir.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
@ -564,7 +565,7 @@ The following functions are also on the Qt object.
\l{Screen} attached object. In practice the array corresponds to the screen
list returned by QGuiApplication::screens(). In addition to examining
properties like name, width, height, etc., the array elements can also be
assigned to the targetScreen property of Window items, thus serving as an
assigned to the screen property of Window items, thus serving as an
alternative to the C++ side's QWindow::setScreen(). This property has been
added in Qt 5.9.
@ -585,7 +586,7 @@ The following functions are also on the Qt object.
\li application.font
\endlist
\sa Screen, Window, Window.targetScreen
\sa Screen, Window, Window.screen
*/
/*!
@ -2178,6 +2179,27 @@ QString QQmlEngine::offlineStoragePath() const
return d->offlineStoragePath;
}
/*!
Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage}
database with the identifier \a databaseName is (or would be) located.
\sa LocalStorage.openDatabaseSync()
\since 5.9
*/
QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const
{
Q_D(const QQmlEngine);
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(databaseName.toUtf8());
return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
}
QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
{
Q_Q(const QQmlEngine);
return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
}
QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion)
{
QList<QQmlType *> types;

View File

@ -136,6 +136,7 @@ public:
void setOfflineStoragePath(const QString& dir);
QString offlineStoragePath() const;
QString offlineStorageDatabaseFilePath(const QString &databaseName) const;
QUrl baseUrl() const;
void setBaseUrl(const QUrl &);

View File

@ -205,6 +205,7 @@ public:
inline void deleteInEngineThread(T *);
template<typename T>
inline static void deleteInEngineThread(QQmlEngine *, T *);
QString offlineStorageDatabaseDirectory() const;
// These methods may be called from the loader thread
inline QQmlPropertyCache *cache(QQmlType *, int);

View File

@ -58,5 +58,12 @@ QT_BEGIN_NAMESPACE
#define Q_QML_EXPORT
#define Q_QML_PRIVATE_EXPORT
/* Some classes built into QtQmlDevTools are marked Q_AUTOTEST_EXPORT but we
have nothing to export in this static library */
#if defined(Q_AUTOTEST_EXPORT)
#undef Q_AUTOTEST_EXPORT
#endif
#define Q_AUTOTEST_EXPORT
QT_END_NAMESPACE
#endif // QTQMLGLOBAL_P_H

View File

@ -640,6 +640,17 @@ void QQuickCanvasItem::releaseResources()
}
}
bool QQuickCanvasItem::event(QEvent *event)
{
switch (event->type()) {
case QEvent::PolishRequest:
polish();
return true;
default:
return QQuickItem::event(event);
}
}
void QQuickCanvasItem::invalidateSceneGraph()
{
Q_D(QQuickCanvasItem);
@ -651,6 +662,12 @@ void QQuickCanvasItem::invalidateSceneGraph()
d->textureProvider = 0;
}
void QQuickCanvasItem::schedulePolish()
{
auto polishRequestEvent = new QEvent(QEvent::PolishRequest);
QCoreApplication::postEvent(this, polishRequestEvent);
}
void QQuickCanvasItem::componentComplete()
{
QQuickItem::componentComplete();
@ -892,8 +909,9 @@ void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args)
d->animationCallbacks.insert(++id, QV4::PersistentValue(scope.engine, f->asReturnedValue()));
// QTBUG-55778: Calling polish directly here can lead to a polish loop
if (isVisible())
polish();
schedulePolish();
args->setReturnValue(QV4::Encode(id));
}

View File

@ -182,6 +182,7 @@ private Q_SLOTS:
void sceneGraphInitialized();
void checkAnimationCallbacks();
void invalidateSceneGraph();
void schedulePolish();
protected:
void componentComplete() Q_DECL_OVERRIDE;
@ -190,6 +191,7 @@ protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
void releaseResources() Q_DECL_OVERRIDE;
bool event(QEvent *event) Q_DECL_OVERRIDE;
private:
Q_DECLARE_PRIVATE(QQuickCanvasItem)
Q_INVOKABLE void delayedCreate();

View File

@ -1879,7 +1879,7 @@ void QQuickJSContext2D::method_get_lineWidth(const QV4::BuiltinFunction *, QV4::
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(r->d()->context->state.lineWidth);
RETURN_RESULT(QV4::Encode(r->d()->context->state.lineWidth));
}
void QQuickJSContext2D::method_set_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
@ -1906,7 +1906,7 @@ void QQuickJSContext2D::method_get_miterLimit(const QV4::BuiltinFunction *, QV4:
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(r->d()->context->state.miterLimit);
RETURN_RESULT(QV4::Encode(r->d()->context->state.miterLimit));
}
void QQuickJSContext2D::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
@ -1933,7 +1933,7 @@ void QQuickJSContext2D::method_get_shadowBlur(const QV4::BuiltinFunction *, QV4:
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(r->d()->context->state.shadowBlur);
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowBlur));
}
void QQuickJSContext2D::method_set_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
@ -1990,7 +1990,7 @@ void QQuickJSContext2D::method_get_shadowOffsetX(const QV4::BuiltinFunction *, Q
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(r->d()->context->state.shadowOffsetX);
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetX));
}
void QQuickJSContext2D::method_set_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
@ -2016,7 +2016,7 @@ void QQuickJSContext2D::method_get_shadowOffsetY(const QV4::BuiltinFunction *, Q
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(r->d()->context->state.shadowOffsetY);
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetY));
}
void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
@ -3043,7 +3043,7 @@ void QQuickJSContext2DPixelData::proto_get_length(const QV4::BuiltinFunction *,
if (!r || r->d()->image->isNull())
RETURN_UNDEFINED();
RETURN_RESULT(r->d()->image->width() * r->d()->image->height() * 4);
RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
}
QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)

View File

@ -67,7 +67,7 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine
.arg(current));
}
if (!requestedUrl.isEmpty()) {
if (QQuickPixmap::isCached(requestedUrl, QSize()))
if (QQuickPixmap::isCached(requestedUrl, QSize(), QQuickImageProviderOptions()))
pixmap = new QQuickPixmap(engine, requestedUrl);
else
pixmap = new QQuickPixmap(requestedUrl, _movie->currentImage());

View File

@ -207,9 +207,9 @@ QT_BEGIN_NAMESPACE
By default it is set to the value of the QScreen that the window uses.
*/
QQuickScreenInfo::QQuickScreenInfo(QObject *parent)
: QObject(parent),
m_screen(nullptr)
QQuickScreenInfo::QQuickScreenInfo(QObject *parent, QScreen *wrappedScreen)
: QObject(parent)
, m_screen(wrappedScreen)
{
}

View File

@ -84,7 +84,7 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject
Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION 1)
public:
QQuickScreenInfo(QObject *parent = nullptr);
QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr);
QString name() const;
int width() const;

View File

@ -4172,25 +4172,25 @@ void QQuickWindow::resetOpenGLState()
*/
/*!
\qmlproperty variant Window::targetScreen
\qmlproperty variant Window::screen
Specifies the screen the window should be placed on. Equivalent to
QWindow::setScreen().
The screen with which the window is associated.
The value must be an element from the Qt.application.screens array.
If specified before showing a window, will result in the window being shown
on that screen, unless an explicit window position has been set. The value
must be an element from the Qt.application.screens array.
By default the value is null which leads to using the primary screen.
\note To ensure that the window is associated with the desired screen right
upon the underlying native window's initial creation, make sure this
property is set as early as possible and that the setting of its value is
not deferred. This can be particularly important on embedded platforms
without a windowing system, where only one window per screen is allowed at a
time.
\note To ensure that the window is associated with the desired screen when
the underlying native window is created, make sure this property is set as
early as possible and that the setting of its value is not deferred. This
can be particularly important on embedded platforms without a windowing system,
where only one window per screen is allowed at a time. Setting the screen after
a window has been created does not move the window if the new screen is part of
the same virtual desktop as the old screen.
\since 5.9
\sa QWindow::setScreen(), QScreen, Qt.application
\sa QWindow::setScreen(), QWindow::screen(), QScreen, Qt.application
*/
/*!

View File

@ -59,7 +59,6 @@ public:
: complete(false)
, visible(false)
, visibility(QQuickWindow::AutomaticVisibility)
, targetScreen(nullptr)
{
}
@ -67,7 +66,6 @@ public:
bool visible;
QQuickWindow::Visibility visibility;
QV4::PersistentValue rootItemMarker;
QObject *targetScreen;
};
QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent)
@ -75,6 +73,7 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent)
{
connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged);
connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged);
connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
}
void QQuickWindowQmlImpl::setVisible(bool visible)
@ -175,24 +174,15 @@ void QQuickWindowQmlImpl::setWindowVisibility()
}
}
QObject *QQuickWindowQmlImpl::targetScreen() const
QObject *QQuickWindowQmlImpl::screen() const
{
Q_D(const QQuickWindowQmlImpl);
return d->targetScreen;
return new QQuickScreenInfo(const_cast<QQuickWindowQmlImpl *>(this), QWindow::screen());
}
void QQuickWindowQmlImpl::setTargetScreen(QObject *screen)
void QQuickWindowQmlImpl::setScreen(QObject *screen)
{
Q_D(QQuickWindowQmlImpl);
if (d->targetScreen != screen) {
d->targetScreen = screen;
emit targetScreenChanged();
QQuickScreenInfo *screenWrapper = qobject_cast<QQuickScreenInfo *>(screen);
if (screenWrapper)
setScreen(screenWrapper->wrappedScreen());
else
setScreen(nullptr);
}
QQuickScreenInfo *screenWrapper = qobject_cast<QQuickScreenInfo *>(screen);
QWindow::setScreen(screenWrapper ? screenWrapper->wrappedScreen() : nullptr);
}
void QQuickWindowModule::defineModule()

View File

@ -67,7 +67,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged)
Q_PROPERTY(QObject *targetScreen READ targetScreen WRITE setTargetScreen NOTIFY targetScreenChanged REVISION 2)
Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION 2)
public:
QQuickWindowQmlImpl(QWindow *parent = Q_NULLPTR);
@ -75,15 +75,15 @@ public:
void setVisible(bool visible);
void setVisibility(Visibility visibility);
QObject *targetScreen() const;
void setTargetScreen(QObject *screen);
QObject *screen() const;
void setScreen(QObject *screen);
static QQuickWindowAttached *qmlAttachedProperties(QObject *object);
Q_SIGNALS:
void visibleChanged(bool arg);
void visibilityChanged(QWindow::Visibility visibility);
Q_REVISION(2) void targetScreenChanged();
Q_REVISION(2) void screenChanged();
protected:
void classBegin() Q_DECL_OVERRIDE;

View File

@ -127,7 +127,7 @@ QSGRendererInterface::~QSGRendererInterface()
*/
/*!
Queries a graphics \a resource. Returns null when the resource in question is
Queries a graphics \a resource in \a window. Returns null when the resource in question is
not supported or not available.
When successful, the returned pointer is either a direct pointer to an

View File

@ -1541,9 +1541,9 @@ void QQuickPixmap::clear(QObject *obj)
}
}
bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize)
bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options)
{
QQuickPixmapKey key = { &url, &requestSize, QQuickImageProviderOptions() };
QQuickPixmapKey key = { &url, &requestSize, options };
QQuickPixmapStore *store = pixmapStore();
return store->m_cache.contains(key);

View File

@ -175,7 +175,7 @@ public:
bool connectDownloadProgress(QObject *, int);
static void purgeCache();
static bool isCached(const QUrl &url, const QSize &requestSize);
static bool isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options);
static const QLatin1String itemGrabberScheme;

View File

@ -337,6 +337,7 @@ private slots:
void constkw_data();
void constkw();
void redefineGlobalProp();
void freeze_empty_object();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@ -8289,6 +8290,18 @@ void tst_qqmlecmascript::redefineGlobalProp()
}
}
void tst_qqmlecmascript::freeze_empty_object()
{
// this shouldn't crash
QJSEngine engine;
QJSValue v = engine.evaluate(QString::fromLatin1(
"var obj = {};\n"
"Object.freeze(obj);\n"
));
QVERIFY(!v.isError());
QCOMPARE(v.toBool(), true);
}
QTEST_MAIN(tst_qqmlecmascript)

View File

@ -56,6 +56,7 @@ private slots:
void baseUrl();
void contextForObject();
void offlineStoragePath();
void offlineDatabaseStoragePath();
void clearComponentCache();
void trimComponentCache();
void trimComponentCache_data();
@ -252,6 +253,34 @@ void tst_qqmlengine::offlineStoragePath()
QCOMPARE(engine.offlineStoragePath(), QDir::homePath());
}
void tst_qqmlengine::offlineDatabaseStoragePath()
{
// Without these set, QDesktopServices::storageLocation returns
// strings with extra "//" at the end. We set them to ignore this problem.
qApp->setApplicationName("tst_qqmlengine");
qApp->setOrganizationName("QtProject");
qApp->setOrganizationDomain("www.qt-project.org");
QQmlEngine engine;
QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
const QString databaseName = QLatin1String("foo");
QString databaseLocation = engine.offlineStorageDatabaseFilePath(databaseName);
QCOMPARE(dataLocation.isEmpty(), databaseLocation.isEmpty());
QDir dir(dataLocation);
dir.mkpath("QML");
dir.cd("QML");
dir.mkpath("OfflineStorage");
dir.cd("OfflineStorage");
dir.mkpath("Databases");
dir.cd("Databases");
QCOMPARE(QFileInfo(databaseLocation).dir().path(), dir.path());
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(databaseName.toUtf8());
QCOMPARE(databaseLocation, QDir::toNativeSeparators(dir.filePath(QLatin1String(md5.result().toHex()))));
}
void tst_qqmlengine::clearComponentCache()
{
QQmlEngine engine;

View File

@ -3,7 +3,7 @@ import QtQuick.Window 2.3 as Window
Window.Window {
color: "#00FF00"
targetScreen: Qt.application.screens[0]
screen: Qt.application.screens[0]
Item {
objectName: "item"
}

View File

@ -141,7 +141,8 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i
// ### translation binding simplification
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, /*executable allocator*/nullptr, &irDocument.jsModule, &irDocument.jsGenerator));
QV4::ExecutableAllocator allocator;
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
// Disable lookups in non-standalone (aka QML) mode
isel->setUseFastLookups(false);
irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false);

View File

@ -2,7 +2,8 @@ TEMPLATE = subdirs
QT_FOR_CONFIG += qml-private
SUBDIRS += \
qmlmin \
qmlimportscanner
qmlimportscanner \
qmlcachegen
!android|android_app {
SUBDIRS += \
@ -27,7 +28,7 @@ SUBDIRS += \
qml.depends = qmlimportscanner
qmleasing.depends = qmlimportscanner
# qmlmin, qmlimportscanner & qmlbundle are build tools.
# qmlmin, qmlimportscanner & qmlcachegen are build tools.
# qmlscene is needed by the autotests.
# qmltestrunner may be useful for manual testing.
# qmlplugindump cannot be a build tool, because it loads target plugins.