| /**************************************************************************** |
| ** |
| ** 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; |
| } |