blob: f8a41edb6a504d66a42271598f0e2a71d05df3d4 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <private/qv4bytecodegenerator_p.h>
#include <private/qv4compilercontext_p.h>
#include <private/qqmljsastfwd_p.h>
QT_USE_NAMESPACE
using namespace QV4;
using namespace Moth;
void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc)
{
currentLine = static_cast<int>(loc.startLine);
}
int BytecodeGenerator::newRegister()
{
int t = currentReg++;
if (regCount < currentReg)
regCount = currentReg;
return t;
}
int BytecodeGenerator::newRegisterArray(int n)
{
int t = currentReg;
currentReg += n;
if (regCount < currentReg)
regCount = currentReg;
return t;
}
void BytecodeGenerator::packInstruction(I &i)
{
Instr::Type type = Instr::unpack(i.packed);
Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
type = Instr::narrowInstructionType(type);
int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
uchar *code = i.packed + Instr::encodedLength(type);
for (int j = 0; j < nMembers; ++j) {
instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
}
enum {
Normal,
Wide
} width = Normal;
for (int n = 0; n < nMembers; ++n) {
if (width == Normal && (static_cast<qint8>(instructionsAsInts[n]) != instructionsAsInts[n])) {
width = Wide;
break;
}
}
code = i.packed;
switch (width) {
case Normal:
code = Instr::pack(code, type);
for (int n = 0; n < nMembers; ++n) {
qint8 v = static_cast<qint8>(instructionsAsInts[n]);
memcpy(code, &v, 1);
code += 1;
}
i.size = code - i.packed;
if (i.offsetForJump != -1)
i.offsetForJump = i.size - 1;
break;
case Wide:
// nothing to do
break;
}
}
void BytecodeGenerator::adjustJumpOffsets()
{
for (int index = 0; index < instructions.size(); ++index) {
auto &i = instructions[index];
if (i.offsetForJump == -1) // no jump
continue;
Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel));
qint8 *c = reinterpret_cast<qint8*>(i.packed + i.offsetForJump);
int jumpOffset = linkedInstruction.position - (i.position + i.size);
// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
Instr::Type type = Instr::unpack(i.packed);
if (Instr::isWide(type)) {
Q_ASSERT(i.offsetForJump == i.size - 4);
qToLittleEndian<qint32>(jumpOffset, c);
} else {
Q_ASSERT(i.offsetForJump == i.size - 1);
qint8 o = jumpOffset;
Q_ASSERT(o == jumpOffset);
*c = o;
}
}
}
void BytecodeGenerator::compressInstructions()
{
// first round: compress all non jump instructions
int position = 0;
for (auto &i : instructions) {
i.position = position;
if (i.offsetForJump == -1)
packInstruction(i);
position += i.size;
}
adjustJumpOffsets();
// compress all jumps
position = 0;
for (auto &i : instructions) {
i.position = position;
if (i.offsetForJump != -1)
packInstruction(i);
position += i.size;
}
// adjust once again, as the packing above could have changed offsets
adjustJumpOffsets();
}
void BytecodeGenerator::finalize(Compiler::Context *context)
{
compressInstructions();
// collect content and line numbers
QByteArray code;
QVector<CompiledData::CodeOffsetToLine> lineNumbers;
currentLine = -1;
Q_UNUSED(startLine);
for (const auto &i : qAsConst(instructions)) {
if (i.line != currentLine) {
currentLine = i.line;
CompiledData::CodeOffsetToLine entry;
entry.codeOffset = code.size();
entry.line = currentLine;
lineNumbers.append(entry);
}
code.append(reinterpret_cast<const char *>(i.packed), i.size);
}
context->code = code;
context->lineNumberMapping = lineNumbers;
for (const auto &li : _labelInfos) {
context->labelInfo.push_back(instructions.at(labels.at(li.labelIndex)).position);
}
}
int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
if (lastInstrType == int(Instr::Type::StoreReg)) {
if (type == Instr::Type::LoadReg) {
if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
// value is already in the accumulator
return -1;
}
}
if (type == Instr::Type::MoveReg) {
if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
Instruction::StoreReg store;
store.reg = i.MoveReg.destReg;
addInstruction(store);
return -1;
}
}
}
lastInstrType = int(type);
lastInstr = i;
if (debugMode && type != Instr::Type::Debug) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
addInstruction(Instruction::Debug());
} else if (type == Instr::Type::Ret) {
currentLine = -currentLine;
addInstruction(Instruction::Debug());
currentLine = -currentLine;
}
QT_WARNING_POP
}
const int pos = instructions.size();
const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
int s = argCount*sizeof(int);
if (offsetOfOffset != -1)
offsetOfOffset += Instr::encodedLength(type);
I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" };
uchar *code = instr.packed;
code = Instr::pack(code, Instr::wideInstructionType(type));
for (int j = 0; j < argCount; ++j) {
qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
code += sizeof(int);
}
instructions.append(instr);
return pos;
}