72
137
EvalISelFactory::~EvalISelFactory()
75
QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
140
QQmlRefPointer<QV4::CompiledData::CompilationUnit> EvalInstructionSelection::runAll(bool generateUnitData)
77
for (int i = 0; i < irModule->functions.size(); ++i)
80
QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep();
142
for (int i = 0; i < irModule->functions.size(); ++i) {
143
run(i); // Performs the actual compilation
146
QQmlRefPointer<QV4::CompiledData::CompilationUnit> result = backendCompileStep();
148
#ifdef ENABLE_UNIT_CACHE
149
result->isRestored = false;
81
152
if (generateUnitData)
153
result->data = jsGenerator->generateUnit();
158
QQmlRefPointer<QV4::CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
160
#ifndef ENABLE_UNIT_CACHE
161
return runAll(generateUnitData);
163
QQmlRefPointer<QV4::CompiledData::CompilationUnit> result(nullptr);
165
// Check if running JIT mode and if cache is enabled
166
if (!do_cache || !this->impl()) {
167
return runAll(generateUnitData);
170
QV4::CompiledData::CompilationUnit *unit;
174
QByteArray path(qgetenv("HOME") + QByteArray("/.cache/QML/Apps/") + (qgetenv("APP_ID").isEmpty() ? QCoreApplication::applicationName().toLatin1() : qgetenv("APP_ID")));
176
if (m_engine && m_engine->qmlCacheValid == CacheState::UNTESTED) {
179
cacheDir.setPath(QLatin1String(path));
180
QStringList files = cacheDir.entryList();
181
for (int i = 0; i < files.size(); i++) {
185
QFile cacheFile(path + QDir::separator() + files.at(i));
186
if (cacheFile.open(QIODevice::ReadOnly)) {
187
QDataStream stream(&cacheFile);
189
readData((char *)&strLen, sizeof(quint32), stream);
195
char *tmpStr = (char *) malloc(strLen);
196
readData((char *)tmpStr, strLen, stream);
198
readData((char *)&mtime, sizeof(quint32), stream);
202
if (QFile::exists(QLatin1String(tmpStr))) {
203
QByteArray time(ctime(&sb.st_mtime));
204
if (mtime != (quint32) sb.st_mtime) {
205
if (m_engine) m_engine->qmlCacheValid = CacheState::INVALID;
209
// Compilation unit of unresolvable type (inline), remove
218
m_engine->qmlCacheValid = CacheState::VALID;
220
for (int i = 0; i < files.size(); i++)
221
cacheDir.remove(files.at(i));
225
// Search for cache blob by mtime/app_id/file hash
227
stat(irModule->fileName.toLatin1().remove(0, 7).constData(), &sb);
228
QByteArray time(ctime(&sb.st_mtime));
229
QByteArray urlHash(QCryptographicHash::hash((irModule->fileName.toLatin1() + qgetenv("APP_ID") + time), QCryptographicHash::Md5).toHex());
231
dir.mkpath(QLatin1String(path));
232
QFile cacheFile(path + QDir::separator() + urlHash);
236
// TODO: Support inline compilation units
237
if (cacheFile.exists() && cacheFile.open(QIODevice::ReadOnly)) {
238
if (!irModule->fileName.isEmpty() && !irModule->fileName.contains(QStringLiteral("inline")) && m_engine) {
240
fileData.append(cacheFile.readAll());
244
return runAll(generateUnitData);
248
// Check file integrity
249
QString fileHash(QLatin1String(QCryptographicHash::hash(fileData.left(fileData.size()-32), QCryptographicHash::Md5).toHex()));
250
if (!fileHash.contains(QLatin1String(fileData.right(32)))) {
256
// This code has been inspired and influenced by Nomovok's QMLC compiler available at
257
// https://github.com/qmlc/qmlc. All original Copyrights are maintained for the
258
// basic code snippets.
260
// Retrieve unit skeleton from isel implementation
261
unit = mutableCompilationUnit();
262
QV4::JIT::CompilationUnit *tmpUnit = (QV4::JIT::CompilationUnit *) unit;
263
tmpUnit->codeRefs.resize(irModule->functions.size());
265
QDataStream stream(fileData);
268
readData((char *)&strLen, sizeof(quint32), stream);
269
char *tmpStr = (char *) malloc(strLen);
270
readData((char *)tmpStr, strLen, stream);
272
readData((char *)&mtime, sizeof(quint32), stream);
274
unit->lookupTable.reserve(tmpUnit->codeRefs.size());
276
for (int i = 0; i < tmpUnit->codeRefs.size(); i++) {
278
readData((char *)&strLen, sizeof(quint32), stream);
280
char *fStr = (char *) malloc(strLen);
281
readData(fStr, strLen, stream);
283
QString hashString(QLatin1String(irModule->functions.at(i)->name->toLatin1().constData()));
284
hashString.append(QString::number(irModule->functions.at(i)->line));
285
hashString.append(QString::number(irModule->functions.at(i)->column));
287
if (!hashString.contains(QLatin1String(fStr)))
288
return runAll(generateUnitData);
290
unit->lookupTable.append(i);
293
readData((char *)&len, sizeof(quint32), stream);
295
// Temporary unlinked code buffer
296
executableAllocator->allocate(len);
297
char *data = (char *) malloc(len);
298
readData(data, len, stream);
300
quint32 linkCallCount = 0;
301
readData((char *)&linkCallCount, sizeof(quint32), stream);
303
QVector<QV4::JIT::CachedLinkData> linkCalls;
304
linkCalls.resize(linkCallCount);
305
readData((char *)linkCalls.data(), linkCallCount * sizeof(QV4::JIT::CachedLinkData), stream);
307
quint32 constVectorLen = 0;
308
readData((char *)&constVectorLen, sizeof(quint32), stream);
310
QVector<QV4::Primitive > constantVector;
311
if (constVectorLen > 0) {
312
constantVector.resize(constVectorLen);
313
readData((char *)constantVector.data(), constVectorLen * sizeof(QV4::Primitive), stream);
316
// Pre-allocate link buffer to append code
317
QV4::ExecutableAllocator* executableAllocator = m_engine->v4engine()->executableAllocator;
319
QV4::IR::Function nullFunction(0, 0, QLatin1String(""));
321
QV4::JIT::Assembler* as = new QV4::JIT::Assembler(this->impl(), &nullFunction, executableAllocator);
323
QList<QV4::JIT::Assembler::CallToLink>& callsToLink = as->callsToLink();
324
for (int i = 0; i < linkCalls.size(); i++) {
325
QV4::JIT::CachedLinkData& call = linkCalls[i];
326
void *functionPtr = CACHED_LINK_TABLE[call.index].addr;
327
QV4::JIT::Assembler::CallToLink c;
328
JSC::AssemblerLabel label(call.offset);
329
c.call = QV4::JIT::Assembler::Call(label, QV4::JIT::Assembler::Call::Linkable);
330
c.externalFunction = JSC::FunctionPtr((quint32(*)(void))functionPtr);
331
c.functionName = CACHED_LINK_TABLE[call.index].name;
332
callsToLink.append(c);
335
QV4::JIT::Assembler::ConstantTable& constTable = as->constantTable();
336
foreach (const QV4::Primitive &p, constantVector)
339
as->appendData(data, len);
341
int dummySize = -1; // Pass known value to trigger use of code buffer
342
tmpUnit->codeRefs[i] = as->link(&dummySize);
343
Q_ASSERT(dummySize == (int)codeRefLen);
349
readData((char *)&size, sizeof(quint32), stream);
351
void *dataPtr = malloc(size);
352
QV4::CompiledData::Unit *finalUnit = reinterpret_cast<QV4::CompiledData::Unit*>(dataPtr);
354
readData((char *)dataPtr, size, stream);
356
result = backendCompileStep();
357
unit = result.data();
359
unit->data = nullptr;
360
if (irModule->functions.size() > 0)
361
unit->data = finalUnit;
362
unit->isRestored = true;
364
// Not loading from cache, run all instructions
365
result = runAll(false);
366
unit = result.data();
369
if ((unit->data == nullptr) && (do_save || generateUnitData))
82
370
unit->data = jsGenerator->generateUnit();
372
// Save compilation unit
373
QV4::JIT::CompilationUnit *jitUnit = (QV4::JIT::CompilationUnit *) unit;
375
if (cacheFile.open(QIODevice::WriteOnly)) {
376
// TODO: Support inline compilation units
377
if (!irModule->fileName.isEmpty() && !irModule->fileName.contains(QLatin1String("inline")) && m_engine) {
379
fillBuff.open(QIODevice::WriteOnly);
380
QDataStream stream(&fillBuff);
382
quint32 fileNameSize = irModule->fileName.size();
383
writeData(stream, (const char *)&fileNameSize, sizeof(quint32));
384
writeData(stream, (const char *)irModule->fileName.toLatin1().remove(0, 7).constData(), irModule->fileName.size());
387
stat(irModule->fileName.toLatin1().remove(0, 7).constData(), &sb);
388
writeData(stream, (const char *)&sb.st_mtime, sizeof(quint32));
390
for (int i = 0; i < jitUnit->codeRefs.size(); i++) {
391
const JSC::MacroAssemblerCodeRef &codeRef = jitUnit->codeRefs[i];
392
const QVector<QV4::Primitive> &constantValue = jitUnit->constantValues[i];
393
quint32 len = codeRef.size();
395
QString hashString(QLatin1String(irModule->functions.at(i)->name->toLatin1()));
396
hashString.append(QString::number(irModule->functions.at(i)->line));
397
hashString.append(QString::number(irModule->functions.at(i)->column));
399
quint32 strLen = hashString.size();
400
strLen += 1; // /0 char
401
writeData(stream, (const char *)&strLen, sizeof(quint32));
402
writeData(stream, (const char *)hashString.toLatin1().constData(), strLen);
403
writeData(stream, (const char *)&len, sizeof(quint32));
404
writeData(stream, (const char *)(((unsigned long)codeRef.code().executableAddress())&~1), len);
406
const QVector<QV4::JIT::CachedLinkData> &linkCalls = jitUnit->linkData[i];
407
quint32 linkCallCount = linkCalls.size();
409
writeData(stream, (const char *)&linkCallCount, sizeof(quint32));
410
if (linkCallCount > 0)
411
writeData(stream, (const char *)linkCalls.data(), linkCalls.size() * sizeof (QV4::JIT::CachedLinkData));
413
quint32 constTableCount = constantValue.size();
414
writeData(stream, (const char *)&constTableCount, sizeof(quint32));
416
if (constTableCount > 0)
417
writeData(stream, (const char*)constantValue.data(), sizeof(QV4::Primitive) * constantValue.size());
420
QV4::CompiledData::Unit *retUnit = unit->data;
421
quint32 size = retUnit->unitSize;
424
writeData(stream, (const char*)&size, sizeof(quint32));
425
writeData(stream, (const char*)retUnit, size);
428
// Write MD5 hash of stored data for consistency check
429
QByteArray fileHash(QCryptographicHash::hash(fillBuff.data(), QCryptographicHash::Md5).toHex());
430
fillBuff.write(fileHash);
431
cacheFile.write(fillBuff.data());
86
443
void IRDecoder::visitMove(IR::Move *s)