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