~ubuntu-branches/ubuntu/vivid/qtdeclarative-opensource-src-gles/vivid

« back to all changes in this revision

Viewing changes to src/qml/compiler/qv4isel_p.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2015-02-26 09:12:59 UTC
  • Revision ID: package-import@ubuntu.com-20150226091259-krq068zh9bwi20or
Tags: 5.4.0-0ubuntu2
Sync package with qtdeclarative-opensource-src - 5.4.0-4ubuntu2

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4
4
** Contact: http://www.qt-project.org/legal
5
5
**
 
6
** Copyright (C) 2015 Nomovok Ltd. All rights reserved.
 
7
** Contact: info@nomovok.com
 
8
**
 
9
** Copyright (C) 2015 Canonical Limited and/or its subsidiary(-ies).
 
10
** Contact: ricardo.mendoza@canonical.com
 
11
**
6
12
** This file is part of the QtQml module of the Qt Toolkit.
7
13
**
8
14
** $QT_BEGIN_LICENSE:LGPL21$
41
47
 
42
48
#include <QString>
43
49
 
 
50
#ifndef V4_UNIT_CACHE
 
51
#undef ENABLE_UNIT_CACHE
 
52
#endif
 
53
 
 
54
#ifdef ENABLE_UNIT_CACHE
 
55
#include <private/qqmltypenamecache_p.h>
 
56
#include <private/qqmlcompiler_p.h>
 
57
#include <private/qqmltypeloader_p.h>
 
58
#include <private/qv4compileddata_p.h>
 
59
#include <private/qv4assembler_p.h>
 
60
#include "../jit/qv4cachedlinkdata_p.h"
 
61
#include "../jit/qv4assembler_p.h"
 
62
#include <sys/stat.h>
 
63
#include <QCryptographicHash>
 
64
#include <QStandardPaths>
 
65
#include <QDir>
 
66
#include <QFile>
 
67
#include <QDataStream>
 
68
#include <QBuffer>
 
69
#endif
 
70
 
 
71
bool writeData(QDataStream& stream, const char* data, int len)
 
72
{
 
73
    if (stream.writeRawData(data, len) != len)
 
74
        return false;
 
75
    else
 
76
        return true;
 
77
}
 
78
 
 
79
bool writeDataWithLen(QDataStream& stream, const char* data, int len)
 
80
{
 
81
    quint32 l = len;
 
82
    if (!writeData(stream, (const char *)&l, sizeof(quint32)))
 
83
        return false;
 
84
    if (!writeData(stream, data, len))
 
85
        return false;
 
86
    return true;
 
87
}
 
88
 
 
89
bool readData(char *data, int len, QDataStream &stream)
 
90
{
 
91
    if (stream.readRawData(data, len) != len) {
 
92
        return false;
 
93
    } else {
 
94
        return true;
 
95
    }
 
96
}
 
97
 
44
98
namespace {
45
99
Q_GLOBAL_STATIC_WITH_ARGS(QTextStream, qout, (stderr, QIODevice::WriteOnly));
46
100
#define qout *qout()
49
103
using namespace QV4;
50
104
using namespace QV4::IR;
51
105
 
 
106
static bool do_cache = false;
 
107
 
 
108
enum CacheState {
 
109
    UNTESTED = 0,
 
110
    VALID = 1,
 
111
    INVALID = 2
 
112
};
 
113
 
52
114
EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
53
115
    : useFastLookups(true)
54
116
    , useTypeInference(true)
64
126
    Q_ASSERT(execAllocator);
65
127
#endif
66
128
    Q_ASSERT(module);
 
129
 
 
130
    // Enable JIT cache only when explicitly requested and only cache files-on-disk (no qrc or inlines)
 
131
    do_cache = !qgetenv("QV4_ENABLE_JIT_CACHE").isEmpty() && irModule->fileName.startsWith(QStringLiteral("file://"));
67
132
}
68
133
 
69
134
EvalInstructionSelection::~EvalInstructionSelection()
72
137
EvalISelFactory::~EvalISelFactory()
73
138
{}
74
139
 
75
 
QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
 
140
QQmlRefPointer<QV4::CompiledData::CompilationUnit> EvalInstructionSelection::runAll(bool generateUnitData)
76
141
{
77
 
    for (int i = 0; i < irModule->functions.size(); ++i)
78
 
        run(i);
79
 
 
80
 
    QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep();
 
142
    for (int i = 0; i < irModule->functions.size(); ++i) {
 
143
        run(i); // Performs the actual compilation
 
144
    }
 
145
 
 
146
    QQmlRefPointer<QV4::CompiledData::CompilationUnit> result = backendCompileStep();
 
147
 
 
148
#ifdef ENABLE_UNIT_CACHE
 
149
    result->isRestored = false;
 
150
#endif
 
151
 
81
152
    if (generateUnitData)
 
153
        result->data = jsGenerator->generateUnit();
 
154
 
 
155
    return result;
 
156
}
 
157
 
 
158
QQmlRefPointer<QV4::CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
 
159
{
 
160
#ifndef ENABLE_UNIT_CACHE
 
161
    return runAll(generateUnitData);
 
162
#else
 
163
    QQmlRefPointer<QV4::CompiledData::CompilationUnit> result(nullptr);
 
164
 
 
165
    // Check if running JIT mode and if cache is enabled
 
166
    if (!do_cache || !this->impl()) {
 
167
        return runAll(generateUnitData);
 
168
    }
 
169
 
 
170
    QV4::CompiledData::CompilationUnit *unit;
 
171
    bool loaded = false;
 
172
    bool do_save = true;
 
173
 
 
174
    QByteArray path(qgetenv("HOME") + QByteArray("/.cache/QML/Apps/") + (qgetenv("APP_ID").isEmpty() ? QCoreApplication::applicationName().toLatin1() : qgetenv("APP_ID")));
 
175
 
 
176
    if (m_engine && m_engine->qmlCacheValid == CacheState::UNTESTED) {
 
177
        bool valid = true;
 
178
        QDir cacheDir;
 
179
        cacheDir.setPath(QLatin1String(path));
 
180
        QStringList files = cacheDir.entryList();
 
181
        for (int i = 0; i < files.size(); i++) {
 
182
            if (valid == false)
 
183
                break;
 
184
 
 
185
            QFile cacheFile(path + QDir::separator() + files.at(i));
 
186
            if (cacheFile.open(QIODevice::ReadOnly)) {
 
187
                QDataStream stream(&cacheFile);
 
188
                quint32 strLen = 0;
 
189
                readData((char *)&strLen, sizeof(quint32), stream);
 
190
                if (strLen == 0) {
 
191
                    cacheFile.close();
 
192
                    continue;
 
193
                }
 
194
 
 
195
                char *tmpStr = (char *) malloc(strLen);
 
196
                readData((char *)tmpStr, strLen, stream);
 
197
                quint32 mtime = 0;
 
198
                readData((char *)&mtime, sizeof(quint32), stream);
 
199
 
 
200
                struct stat sb;
 
201
                stat(tmpStr, &sb);
 
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;
 
206
                        valid = false;
 
207
                    }
 
208
                } else {
 
209
                    // Compilation unit of unresolvable type (inline), remove
 
210
                    cacheFile.remove();
 
211
                }
 
212
 
 
213
                free(tmpStr);
 
214
                cacheFile.close();
 
215
            }
 
216
        }
 
217
        if (valid) {
 
218
            m_engine->qmlCacheValid = CacheState::VALID;
 
219
        } else {
 
220
            for (int i = 0; i < files.size(); i++)
 
221
                cacheDir.remove(files.at(i));
 
222
        }
 
223
    }
 
224
 
 
225
    // Search for cache blob by mtime/app_id/file hash
 
226
    struct stat sb;
 
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());
 
230
    QDir dir;
 
231
    dir.mkpath(QLatin1String(path));
 
232
    QFile cacheFile(path + QDir::separator() + urlHash);
 
233
 
 
234
    QByteArray fileData;
 
235
 
 
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) {
 
239
            loaded = true;
 
240
            fileData.append(cacheFile.readAll());
 
241
        } else {
 
242
            loaded = false;
 
243
            cacheFile.close();
 
244
            return runAll(generateUnitData);
 
245
        }
 
246
    }
 
247
 
 
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)))) {
 
251
        cacheFile.close();
 
252
        cacheFile.remove();
 
253
        loaded = false;
 
254
    }
 
255
 
 
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.
 
259
    if (loaded) {
 
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());
 
264
 
 
265
        QDataStream stream(fileData);
 
266
 
 
267
        quint32 strLen = 0;
 
268
        readData((char *)&strLen, sizeof(quint32), stream);
 
269
        char *tmpStr = (char *) malloc(strLen);
 
270
        readData((char *)tmpStr, strLen, stream);
 
271
        quint32 mtime = 0;
 
272
        readData((char *)&mtime, sizeof(quint32), stream);
 
273
 
 
274
        unit->lookupTable.reserve(tmpUnit->codeRefs.size());
 
275
        quint32 len;
 
276
        for (int i = 0; i < tmpUnit->codeRefs.size(); i++) {
 
277
            quint32 strLen;
 
278
            readData((char *)&strLen, sizeof(quint32), stream);
 
279
 
 
280
            char *fStr = (char *) malloc(strLen);
 
281
            readData(fStr, strLen, stream);
 
282
 
 
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));
 
286
 
 
287
            if (!hashString.contains(QLatin1String(fStr)))
 
288
                return runAll(generateUnitData);
 
289
 
 
290
            unit->lookupTable.append(i);
 
291
 
 
292
            len = 0;
 
293
            readData((char *)&len, sizeof(quint32), stream);
 
294
 
 
295
            // Temporary unlinked code buffer
 
296
            executableAllocator->allocate(len);
 
297
            char *data = (char *) malloc(len);
 
298
            readData(data, len, stream);
 
299
 
 
300
            quint32 linkCallCount = 0;
 
301
            readData((char *)&linkCallCount, sizeof(quint32), stream);
 
302
 
 
303
            QVector<QV4::JIT::CachedLinkData> linkCalls;
 
304
            linkCalls.resize(linkCallCount);
 
305
            readData((char *)linkCalls.data(), linkCallCount * sizeof(QV4::JIT::CachedLinkData), stream);
 
306
 
 
307
            quint32 constVectorLen = 0;
 
308
            readData((char *)&constVectorLen, sizeof(quint32), stream);
 
309
 
 
310
            QVector<QV4::Primitive > constantVector;
 
311
            if (constVectorLen > 0) {
 
312
                constantVector.resize(constVectorLen);
 
313
                readData((char *)constantVector.data(), constVectorLen * sizeof(QV4::Primitive), stream);
 
314
            }
 
315
 
 
316
            // Pre-allocate link buffer to append code
 
317
            QV4::ExecutableAllocator* executableAllocator = m_engine->v4engine()->executableAllocator;
 
318
 
 
319
            QV4::IR::Function nullFunction(0, 0, QLatin1String(""));
 
320
 
 
321
            QV4::JIT::Assembler* as = new QV4::JIT::Assembler(this->impl(), &nullFunction, executableAllocator);
 
322
 
 
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);
 
333
            }
 
334
 
 
335
            QV4::JIT::Assembler::ConstantTable& constTable = as->constantTable();
 
336
            foreach (const QV4::Primitive &p, constantVector)
 
337
               constTable.add(p);
 
338
 
 
339
            as->appendData(data, len);
 
340
 
 
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);
 
344
 
 
345
            delete as;
 
346
        }
 
347
 
 
348
        quint32 size = 0;
 
349
        readData((char *)&size, sizeof(quint32), stream);
 
350
 
 
351
        void *dataPtr = malloc(size);
 
352
        QV4::CompiledData::Unit *finalUnit = reinterpret_cast<QV4::CompiledData::Unit*>(dataPtr);
 
353
        if (size > 0)
 
354
            readData((char *)dataPtr, size, stream);
 
355
 
 
356
        result = backendCompileStep();
 
357
        unit = result.data();
 
358
 
 
359
        unit->data = nullptr;
 
360
        if (irModule->functions.size() > 0)
 
361
            unit->data = finalUnit;
 
362
        unit->isRestored = true;
 
363
    } else {
 
364
        // Not loading from cache, run all instructions
 
365
        result = runAll(false);
 
366
        unit = result.data();
 
367
    }
 
368
 
 
369
    if ((unit->data == nullptr) && (do_save || generateUnitData))
82
370
        unit->data = jsGenerator->generateUnit();
83
 
    return unit;
 
371
 
 
372
    // Save compilation unit
 
373
    QV4::JIT::CompilationUnit *jitUnit = (QV4::JIT::CompilationUnit *) unit;
 
374
    if (!loaded) {
 
375
        if (cacheFile.open(QIODevice::WriteOnly)) {
 
376
            // TODO: Support inline compilation units
 
377
            if (!irModule->fileName.isEmpty() && !irModule->fileName.contains(QLatin1String("inline")) && m_engine) {
 
378
                QBuffer fillBuff;
 
379
                fillBuff.open(QIODevice::WriteOnly);
 
380
                QDataStream stream(&fillBuff);
 
381
 
 
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());
 
385
 
 
386
                struct stat sb;
 
387
                stat(irModule->fileName.toLatin1().remove(0, 7).constData(), &sb);
 
388
                writeData(stream, (const char *)&sb.st_mtime, sizeof(quint32));
 
389
 
 
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();
 
394
 
 
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));
 
398
 
 
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);
 
405
 
 
406
                    const QVector<QV4::JIT::CachedLinkData> &linkCalls = jitUnit->linkData[i];
 
407
                    quint32 linkCallCount = linkCalls.size();
 
408
 
 
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));
 
412
 
 
413
                    quint32 constTableCount = constantValue.size();
 
414
                    writeData(stream, (const char *)&constTableCount, sizeof(quint32));
 
415
 
 
416
                    if (constTableCount > 0)
 
417
                        writeData(stream, (const char*)constantValue.data(), sizeof(QV4::Primitive) * constantValue.size());
 
418
                }
 
419
 
 
420
                QV4::CompiledData::Unit *retUnit = unit->data;
 
421
                quint32 size = retUnit->unitSize;
 
422
 
 
423
                if (size > 0) {
 
424
                    writeData(stream, (const char*)&size, sizeof(quint32));
 
425
                    writeData(stream, (const char*)retUnit, size);
 
426
                }
 
427
 
 
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());
 
432
            }
 
433
            cacheFile.close();
 
434
        } else {
 
435
            cacheFile.close();
 
436
        }
 
437
    }
 
438
 
 
439
    return result;
 
440
#endif
84
441
}
85
442
 
86
443
void IRDecoder::visitMove(IR::Move *s)