1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/qv4bytecodegenerator_p.h>
5#include <private/qv4compilercontext_p.h>
6#include <private/qqmljsastfwd_p.h>
7
8QT_USE_NAMESPACE
9using namespace QV4;
10using namespace Moth;
11
12void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc)
13{
14 currentLine = static_cast<int>(loc.startLine);
15 currentSourceLocation = loc;
16}
17
18void BytecodeGenerator::incrementStatement()
19{
20 ++currentStatement;
21}
22
23int BytecodeGenerator::newRegister()
24{
25 int t = currentReg++;
26 if (regCount < currentReg)
27 regCount = currentReg;
28 return t;
29}
30
31int BytecodeGenerator::newRegisterArray(int n)
32{
33 int t = currentReg;
34 currentReg += n;
35 if (regCount < currentReg)
36 regCount = currentReg;
37 return t;
38}
39
40void BytecodeGenerator::packInstruction(I &i)
41{
42 Instr::Type type = Instr::unpack(c: i.packed);
43 Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
44 type = Instr::narrowInstructionType(t: type);
45 int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
46 int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
47 uchar *code = i.packed + Instr::encodedLength(t: type);
48 for (int j = 0; j < nMembers; ++j) {
49 instructionsAsInts[j] = qFromLittleEndian<qint32>(src: code + j * sizeof(int));
50 }
51 enum {
52 Normal,
53 Wide
54 } width = Normal;
55 for (int n = 0; n < nMembers; ++n) {
56 if (width == Normal && (static_cast<qint8>(instructionsAsInts[n]) != instructionsAsInts[n])) {
57 width = Wide;
58 break;
59 }
60 }
61 code = i.packed;
62 switch (width) {
63 case Normal:
64 code = Instr::pack(c: code, t: type);
65 for (int n = 0; n < nMembers; ++n) {
66 qint8 v = static_cast<qint8>(instructionsAsInts[n]);
67 memcpy(dest: code, src: &v, n: 1);
68 code += 1;
69 }
70 i.size = code - i.packed;
71 if (i.offsetForJump != -1)
72 i.offsetForJump = i.size - 1;
73 break;
74 case Wide:
75 // nothing to do
76 break;
77 }
78}
79
80void BytecodeGenerator::adjustJumpOffsets()
81{
82 for (int index = 0; index < instructions.size(); ++index) {
83 auto &i = instructions[index];
84 if (i.offsetForJump == -1) // no jump
85 continue;
86 Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
87 const auto &linkedInstruction = instructions.at(i: labels.at(i: i.linkedLabel));
88 qint8 *c = reinterpret_cast<qint8*>(i.packed + i.offsetForJump);
89 int jumpOffset = linkedInstruction.position - (i.position + i.size);
90// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
91// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
92 Instr::Type type = Instr::unpack(c: i.packed);
93 if (Instr::isWide(t: type)) {
94 Q_ASSERT(i.offsetForJump == i.size - 4);
95 qToLittleEndian<qint32>(src: jumpOffset, dest: c);
96 } else {
97 Q_ASSERT(i.offsetForJump == i.size - 1);
98 qint8 o = jumpOffset;
99 Q_ASSERT(o == jumpOffset);
100 *c = o;
101 }
102 }
103}
104
105void BytecodeGenerator::compressInstructions()
106{
107 // first round: compress all non jump instructions
108 int position = 0;
109 for (auto &i : instructions) {
110 i.position = position;
111 if (i.offsetForJump == -1)
112 packInstruction(i);
113 position += i.size;
114 }
115
116 adjustJumpOffsets();
117
118 // compress all jumps
119 position = 0;
120 for (auto &i : instructions) {
121 i.position = position;
122 if (i.offsetForJump != -1)
123 packInstruction(i);
124 position += i.size;
125 }
126
127 // adjust once again, as the packing above could have changed offsets
128 adjustJumpOffsets();
129}
130
131void BytecodeGenerator::finalize(Compiler::Context *context)
132{
133 compressInstructions();
134
135 // collect content and line numbers
136 QByteArray code;
137 QVector<CompiledData::CodeOffsetToLineAndStatement> lineAndStatementNumbers;
138
139 currentLine = -1;
140 currentStatement = -1;
141
142 Q_UNUSED(startLine);
143 for (qsizetype i = 0; i < instructions.size(); i++) {
144 if (instructions[i].line != currentLine || instructions[i].statement != currentStatement) {
145 currentLine = instructions[i].line;
146 currentStatement = instructions[i].statement;
147 CompiledData::CodeOffsetToLineAndStatement entry;
148 entry.codeOffset = code.size();
149 entry.line = currentLine;
150 entry.statement = currentStatement;
151 lineAndStatementNumbers.append(t: entry);
152 }
153
154 if (m_sourceLocationTable)
155 m_sourceLocationTable->entries[i].offset = static_cast<quint32>(code.size());
156
157 code.append(s: reinterpret_cast<const char *>(instructions[i].packed), len: instructions[i].size);
158 }
159
160 context->code = code;
161 context->lineAndStatementNumberMapping = lineAndStatementNumbers;
162 context->sourceLocationTable = std::move(m_sourceLocationTable);
163
164 context->labelInfo.reserve(n: context->labelInfo.size() + _labelInfos.size());
165 for (const auto &li : _labelInfos)
166 context->labelInfo.push_back(x: instructions.at(i: labels.at(i: li.labelIndex)).position);
167}
168
169int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
170 if (lastInstrType == int(Instr::Type::StoreReg)) {
171 if (type == Instr::Type::LoadReg) {
172 if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
173 // value is already in the accumulator
174 return -1;
175 }
176 }
177 if (type == Instr::Type::MoveReg) {
178 if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
179 Instruction::StoreReg store;
180 store.reg = i.MoveReg.destReg;
181 addInstruction(data: store);
182 return -1;
183 }
184 }
185 }
186 lastInstrType = int(type);
187 lastInstr = i;
188
189 if (debugMode && type != Instr::Type::Debug) {
190QT_WARNING_PUSH
191QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
192 if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
193 addInstruction(data: Instruction::Debug());
194 } else if (type == Instr::Type::Ret) {
195 currentLine = -currentLine;
196 addInstruction(data: Instruction::Debug());
197 currentLine = -currentLine;
198 currentSourceLocation = QQmlJS::SourceLocation();
199 }
200QT_WARNING_POP
201 }
202
203 const int pos = instructions.size();
204
205 const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
206 int s = argCount*sizeof(int);
207 if (offsetOfOffset != -1)
208 offsetOfOffset += Instr::encodedLength(t: type);
209 I instr {
210 .type: type,
211 .size: static_cast<short>(s + Instr::encodedLength(t: type)),
212 .position: 0,
213 .line: currentLine,
214 .statement: currentStatement,
215 .offsetForJump: offsetOfOffset,
216 .linkedLabel: -1,
217 .packed: "\0\0"
218 };
219 uchar *code = instr.packed;
220 code = Instr::pack(c: code, t: Instr::wideInstructionType(t: type));
221
222 for (int j = 0; j < argCount; ++j) {
223 qToLittleEndian<qint32>(src: i.argumentsAsInts[j], dest: code);
224 code += sizeof(int);
225 }
226
227 instructions.append(t: instr);
228 if (m_sourceLocationTable)
229 m_sourceLocationTable->entries.append(t: { .offset: 0, .location: currentSourceLocation });
230
231 return pos;
232}
233

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