/**************************************************************************** ** ** 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 #include #include QT_USE_NAMESPACE using namespace QV4; using namespace Moth; void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc) { currentLine = static_cast(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(i.type)]; uchar *code = i.packed + Instr::encodedLength(type); for (int j = 0; j < nMembers; ++j) { instructionsAsInts[j] = qFromLittleEndian(code + j * sizeof(int)); } enum { Normal, Wide } width = Normal; for (int n = 0; n < nMembers; ++n) { if (width == Normal && (static_cast(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(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(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(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 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(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(type)]; int s = argCount*sizeof(int); if (offsetOfOffset != -1) offsetOfOffset += Instr::encodedLength(type); I instr{type, static_cast(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(i.argumentsAsInts[j], code); code += sizeof(int); } instructions.append(instr); return pos; }