1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <private/qv4bytecodegenerator_p.h>
41#include <private/qv4compilercontext_p.h>
42#include <private/qqmljsastfwd_p.h>
43
44QT_USE_NAMESPACE
45using namespace QV4;
46using namespace Moth;
47
48void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc)
49{
50 currentLine = static_cast<int>(loc.startLine);
51}
52
53int BytecodeGenerator::newRegister()
54{
55 int t = currentReg++;
56 if (regCount < currentReg)
57 regCount = currentReg;
58 return t;
59}
60
61int BytecodeGenerator::newRegisterArray(int n)
62{
63 int t = currentReg;
64 currentReg += n;
65 if (regCount < currentReg)
66 regCount = currentReg;
67 return t;
68}
69
70void BytecodeGenerator::packInstruction(I &i)
71{
72 Instr::Type type = Instr::unpack(c: i.packed);
73 Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
74 type = Instr::narrowInstructionType(t: type);
75 int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
76 int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
77 uchar *code = i.packed + Instr::encodedLength(t: type);
78 for (int j = 0; j < nMembers; ++j) {
79 instructionsAsInts[j] = qFromLittleEndian<qint32>(src: code + j * sizeof(int));
80 }
81 enum {
82 Normal,
83 Wide
84 } width = Normal;
85 for (int n = 0; n < nMembers; ++n) {
86 if (width == Normal && (static_cast<qint8>(instructionsAsInts[n]) != instructionsAsInts[n])) {
87 width = Wide;
88 break;
89 }
90 }
91 code = i.packed;
92 switch (width) {
93 case Normal:
94 code = Instr::pack(c: code, t: type);
95 for (int n = 0; n < nMembers; ++n) {
96 qint8 v = static_cast<qint8>(instructionsAsInts[n]);
97 memcpy(dest: code, src: &v, n: 1);
98 code += 1;
99 }
100 i.size = code - i.packed;
101 if (i.offsetForJump != -1)
102 i.offsetForJump = i.size - 1;
103 break;
104 case Wide:
105 // nothing to do
106 break;
107 }
108}
109
110void BytecodeGenerator::adjustJumpOffsets()
111{
112 for (int index = 0; index < instructions.size(); ++index) {
113 auto &i = instructions[index];
114 if (i.offsetForJump == -1) // no jump
115 continue;
116 Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
117 const auto &linkedInstruction = instructions.at(i: labels.at(i: i.linkedLabel));
118 qint8 *c = reinterpret_cast<qint8*>(i.packed + i.offsetForJump);
119 int jumpOffset = linkedInstruction.position - (i.position + i.size);
120// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
121// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
122 Instr::Type type = Instr::unpack(c: i.packed);
123 if (Instr::isWide(t: type)) {
124 Q_ASSERT(i.offsetForJump == i.size - 4);
125 qToLittleEndian<qint32>(src: jumpOffset, dest: c);
126 } else {
127 Q_ASSERT(i.offsetForJump == i.size - 1);
128 qint8 o = jumpOffset;
129 Q_ASSERT(o == jumpOffset);
130 *c = o;
131 }
132 }
133}
134
135void BytecodeGenerator::compressInstructions()
136{
137 // first round: compress all non jump instructions
138 int position = 0;
139 for (auto &i : instructions) {
140 i.position = position;
141 if (i.offsetForJump == -1)
142 packInstruction(i);
143 position += i.size;
144 }
145
146 adjustJumpOffsets();
147
148 // compress all jumps
149 position = 0;
150 for (auto &i : instructions) {
151 i.position = position;
152 if (i.offsetForJump != -1)
153 packInstruction(i);
154 position += i.size;
155 }
156
157 // adjust once again, as the packing above could have changed offsets
158 adjustJumpOffsets();
159}
160
161void BytecodeGenerator::finalize(Compiler::Context *context)
162{
163 compressInstructions();
164
165 // collect content and line numbers
166 QByteArray code;
167 QVector<CompiledData::CodeOffsetToLine> lineNumbers;
168 currentLine = -1;
169 Q_UNUSED(startLine);
170 for (const auto &i : qAsConst(t&: instructions)) {
171 if (i.line != currentLine) {
172 currentLine = i.line;
173 CompiledData::CodeOffsetToLine entry;
174 entry.codeOffset = code.size();
175 entry.line = currentLine;
176 lineNumbers.append(t: entry);
177 }
178 code.append(s: reinterpret_cast<const char *>(i.packed), len: i.size);
179 }
180
181 context->code = code;
182 context->lineNumberMapping = lineNumbers;
183
184 for (const auto &li : _labelInfos) {
185 context->labelInfo.push_back(x: instructions.at(i: labels.at(i: li.labelIndex)).position);
186 }
187}
188
189int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
190 if (lastInstrType == int(Instr::Type::StoreReg)) {
191 if (type == Instr::Type::LoadReg) {
192 if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
193 // value is already in the accumulator
194 return -1;
195 }
196 }
197 if (type == Instr::Type::MoveReg) {
198 if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
199 Instruction::StoreReg store;
200 store.reg = i.MoveReg.destReg;
201 addInstruction(data: store);
202 return -1;
203 }
204 }
205 }
206 lastInstrType = int(type);
207 lastInstr = i;
208
209 if (debugMode && type != Instr::Type::Debug) {
210QT_WARNING_PUSH
211QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
212 if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
213 addInstruction(data: Instruction::Debug());
214 } else if (type == Instr::Type::Ret) {
215 currentLine = -currentLine;
216 addInstruction(data: Instruction::Debug());
217 currentLine = -currentLine;
218 }
219QT_WARNING_POP
220 }
221
222 const int pos = instructions.size();
223
224 const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
225 int s = argCount*sizeof(int);
226 if (offsetOfOffset != -1)
227 offsetOfOffset += Instr::encodedLength(t: type);
228 I instr{.type: type, .size: static_cast<short>(s + Instr::encodedLength(t: type)), .position: 0, .line: currentLine, .offsetForJump: offsetOfOffset, .linkedLabel: -1, .packed: "\0\0" };
229 uchar *code = instr.packed;
230 code = Instr::pack(c: code, t: Instr::wideInstructionType(t: type));
231
232 for (int j = 0; j < argCount; ++j) {
233 qToLittleEndian<qint32>(src: i.argumentsAsInts[j], dest: code);
234 code += sizeof(int);
235 }
236
237 instructions.append(t: instr);
238
239 return pos;
240}
241

source code of qtdeclarative/src/qml/compiler/qv4bytecodegenerator.cpp