1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef QQMLJSCOMPILEPASS_P_H
5#define QQMLJSCOMPILEPASS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16
17
18#include <private/qqmljslogger_p.h>
19#include <private/qqmljsregistercontent_p.h>
20#include <private/qqmljsscope_p.h>
21#include <private/qqmljstyperesolver_p.h>
22#include <private/qv4bytecodehandler_p.h>
23#include <private/qv4compiler_p.h>
24#include <private/qflatmap_p.h>
25
26QT_BEGIN_NAMESPACE
27
28class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
29{
30 Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
31public:
32 enum RegisterShortcuts {
33 InvalidRegister = -1,
34 Accumulator = QV4::CallData::Accumulator,
35 This = QV4::CallData::This,
36 FirstArgument = QV4::CallData::OffsetCount
37 };
38
39 using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable;
40
41 struct VirtualRegister
42 {
43 QQmlJSRegisterContent content;
44 bool canMove = false;
45 bool affectedBySideEffects = false;
46
47 private:
48 friend bool operator==(const VirtualRegister &a, const VirtualRegister &b)
49 {
50 return a.content == b.content && a.canMove == b.canMove
51 && a.affectedBySideEffects == b.affectedBySideEffects;
52 }
53 };
54
55 // map from register index to expected type
56 using VirtualRegisters = QFlatMap<int, VirtualRegister>;
57
58 struct BasicBlock
59 {
60 QList<int> jumpOrigins;
61 QList<int> readRegisters;
62 QList<QQmlJSScope::ConstPtr> readTypes;
63 int jumpTarget = -1;
64 bool jumpIsUnconditional = false;
65 bool isReturnBlock = false;
66 bool isThrowBlock = false;
67 };
68
69 using BasicBlocks = QFlatMap<int, BasicBlock>;
70
71 struct InstructionAnnotation
72 {
73 // Registers explicit read as part of the instruction.
74 VirtualRegisters readRegisters;
75
76 // Registers that have to be converted for future instructions after a jump.
77 VirtualRegisters typeConversions;
78
79 QQmlJSRegisterContent changedRegister;
80 int changedRegisterIndex = InvalidRegister;
81 bool hasSideEffects = false;
82 bool isRename = false;
83 };
84
85 using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
86 struct BlocksAndAnnotations
87 {
88 BasicBlocks basicBlocks;
89 InstructionAnnotations annotations;
90 };
91
92 struct Function
93 {
94 QQmlJSScopesById addressableScopes;
95 QList<QQmlJSRegisterContent> argumentTypes;
96 QList<QQmlJSRegisterContent> registerTypes;
97 QQmlJSRegisterContent returnType;
98 QQmlJSScope::ConstPtr qmlScope;
99 QByteArray code;
100 const SourceLocationTable *sourceLocations = nullptr;
101 bool isSignalHandler = false;
102 bool isQPropertyBinding = false;
103 bool isProperty = false;
104 bool isFullyTyped = false;
105 };
106
107 struct ObjectOrArrayDefinition
108 {
109 enum {
110 ArrayClassId = -1,
111 ArrayConstruct1ArgId = -2,
112 };
113
114 int instructionOffset = -1;
115 int internalClassId = ArrayClassId;
116 int argc = 0;
117 int argv = -1;
118 };
119
120 struct State
121 {
122 VirtualRegisters registers;
123 VirtualRegisters lookups;
124
125 /*!
126 \internal
127 \brief The accumulatorIn is the input register of the current instruction.
128
129 It holds a content, a type that content is acctually stored in, and an enclosing type
130 of the stored type called the scope. Note that passes after the original type
131 propagation may change the type of this register to a different type that the original
132 one can be coerced to. Therefore, when analyzing the same instruction in a later pass,
133 the type may differ from what was seen or requested ealier. See \l {readAccumulator()}.
134 The input type may then need to be converted to the expected type.
135 */
136 const QQmlJSRegisterContent &accumulatorIn() const
137 {
138 auto it = registers.find(key: Accumulator);
139 Q_ASSERT(it != registers.end());
140 return it.value().content;
141 };
142
143 /*!
144 \internal
145 \brief The accumulatorOut is the output register of the current instruction.
146 */
147 const QQmlJSRegisterContent &accumulatorOut() const
148 {
149 Q_ASSERT(m_changedRegisterIndex == Accumulator);
150 return m_changedRegister;
151 };
152
153 void setRegister(int registerIndex, QQmlJSRegisterContent content)
154 {
155 const int lookupIndex = content.resultLookupIndex();
156 if (lookupIndex != QQmlJSRegisterContent::InvalidLookupIndex)
157 lookups[lookupIndex] = { .content: content, .canMove: false, .affectedBySideEffects: false };
158
159 m_changedRegister = std::move(content);
160 m_changedRegisterIndex = registerIndex;
161 }
162
163 void clearChangedRegister()
164 {
165 m_changedRegisterIndex = InvalidRegister;
166 m_changedRegister = QQmlJSRegisterContent();
167 }
168
169 int changedRegisterIndex() const { return m_changedRegisterIndex; }
170 const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; }
171
172 void addReadRegister(int registerIndex, const QQmlJSRegisterContent &reg)
173 {
174 Q_ASSERT(isRename() || reg.isConversion());
175 const VirtualRegister &source = registers[registerIndex];
176 VirtualRegister &target = m_readRegisters[registerIndex];
177 target.content = reg;
178 target.canMove = source.canMove;
179 target.affectedBySideEffects = source.affectedBySideEffects;
180 }
181
182 void addReadAccumulator(const QQmlJSRegisterContent &reg)
183 {
184 addReadRegister(registerIndex: Accumulator, reg);
185 }
186
187 VirtualRegisters takeReadRegisters() const { return std::move(m_readRegisters); }
188 void setReadRegisters(VirtualRegisters readReagisters)
189 {
190 m_readRegisters = std::move(readReagisters);
191 }
192
193 QQmlJSRegisterContent readRegister(int registerIndex) const
194 {
195 Q_ASSERT(m_readRegisters.contains(registerIndex));
196 return m_readRegisters[registerIndex].content;
197 }
198
199 bool canMoveReadRegister(int registerIndex) const
200 {
201 auto it = m_readRegisters.find(key: registerIndex);
202 return it != m_readRegisters.end() && it->second.canMove;
203 }
204
205 bool isRegisterAffectedBySideEffects(int registerIndex) const
206 {
207 auto it = m_readRegisters.find(key: registerIndex);
208 return it != m_readRegisters.end() && it->second.affectedBySideEffects;
209 }
210
211 /*!
212 \internal
213 \brief The readAccumulator is the register content expected by the current instruction.
214
215 It may differ from the actual input type of the accumulatorIn register and usage of the
216 value may require a conversion.
217 */
218 QQmlJSRegisterContent readAccumulator() const
219 {
220 return readRegister(registerIndex: Accumulator);
221 }
222
223 bool readsRegister(int registerIndex) const
224 {
225 return m_readRegisters.contains(key: registerIndex);
226 }
227
228 bool hasSideEffects() const { return m_hasSideEffects; }
229
230 void markSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; }
231 void applySideEffects(bool hasSideEffects)
232 {
233 if (!hasSideEffects)
234 return;
235
236 for (auto it = registers.begin(), end = registers.end(); it != end; ++it)
237 it.value().affectedBySideEffects = true;
238
239 for (auto it = lookups.begin(), end = lookups.end(); it != end; ++it)
240 it.value().affectedBySideEffects = true;
241 }
242
243 void setHasSideEffects(bool hasSideEffects) {
244 markSideEffects(hasSideEffects);
245 applySideEffects(hasSideEffects);
246 }
247
248 bool isRename() const { return m_isRename; }
249 void setIsRename(bool isRename) { m_isRename = isRename; }
250
251 int renameSourceRegisterIndex() const
252 {
253 Q_ASSERT(m_isRename);
254 Q_ASSERT(m_readRegisters.size() == 1);
255 return m_readRegisters.begin().key();
256 }
257
258 private:
259 VirtualRegisters m_readRegisters;
260 QQmlJSRegisterContent m_changedRegister;
261 int m_changedRegisterIndex = InvalidRegister;
262 bool m_hasSideEffects = false;
263 bool m_isRename = false;
264 };
265
266 QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
267 const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger,
268 BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {})
269 : m_jsUnitGenerator(jsUnitGenerator)
270 , m_typeResolver(typeResolver)
271 , m_logger(logger)
272 , m_basicBlocks(basicBlocks)
273 , m_annotations(annotations)
274 {}
275
276protected:
277 const QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr;
278 const QQmlJSTypeResolver *m_typeResolver = nullptr;
279 QQmlJSLogger *m_logger = nullptr;
280
281 const Function *m_function = nullptr;
282 BasicBlocks m_basicBlocks;
283 InstructionAnnotations m_annotations;
284 QQmlJS::DiagnosticMessage *m_error = nullptr;
285
286 int firstRegisterIndex() const
287 {
288 return FirstArgument + m_function->argumentTypes.size();
289 }
290
291 bool isArgument(int registerIndex) const
292 {
293 return registerIndex >= FirstArgument && registerIndex < firstRegisterIndex();
294 }
295
296 QQmlJSRegisterContent argumentType(int registerIndex) const
297 {
298 Q_ASSERT(isArgument(registerIndex));
299 return m_function->argumentTypes[registerIndex - FirstArgument];
300 }
301
302
303 State initialState(const Function *function)
304 {
305 State state;
306 for (int i = 0, end = function->argumentTypes.size(); i < end; ++i) {
307 state.registers[FirstArgument + i].content = function->argumentTypes.at(i);
308 Q_ASSERT(state.registers[FirstArgument + i].content.isValid());
309 }
310 for (int i = 0, end = function->registerTypes.size(); i != end; ++i)
311 state.registers[firstRegisterIndex() + i].content = function->registerTypes[i];
312 return state;
313 }
314
315 State nextStateFromAnnotations(
316 const State &oldState, const InstructionAnnotations &annotations)
317 {
318 State newState;
319
320 const auto instruction = annotations.find(key: currentInstructionOffset());
321 newState.registers = oldState.registers;
322 newState.lookups = oldState.lookups;
323
324 // Usually the initial accumulator type is the output of the previous instruction, but ...
325 if (oldState.changedRegisterIndex() != InvalidRegister) {
326 newState.registers[oldState.changedRegisterIndex()].affectedBySideEffects = false;
327 newState.registers[oldState.changedRegisterIndex()].content
328 = oldState.changedRegister();
329 }
330
331 // Side effects are applied at the end of an instruction: An instruction with side
332 // effects can still read its registers before the side effects happen.
333 newState.applySideEffects(hasSideEffects: oldState.hasSideEffects());
334
335 if (instruction == annotations.constEnd())
336 return newState;
337
338 newState.markSideEffects(hasSideEffects: instruction->second.hasSideEffects);
339 newState.setReadRegisters(instruction->second.readRegisters);
340 newState.setIsRename(instruction->second.isRename);
341
342 for (auto it = instruction->second.typeConversions.begin(),
343 end = instruction->second.typeConversions.end(); it != end; ++it) {
344 Q_ASSERT(it.key() != InvalidRegister);
345 newState.registers[it.key()] = it.value();
346 }
347
348 if (instruction->second.changedRegisterIndex != InvalidRegister) {
349 newState.setRegister(registerIndex: instruction->second.changedRegisterIndex,
350 content: instruction->second.changedRegister);
351 }
352
353 return newState;
354 }
355
356 QQmlJS::SourceLocation sourceLocation(int instructionOffset) const
357 {
358 Q_ASSERT(m_function);
359 Q_ASSERT(m_function->sourceLocations);
360 const auto &entries = m_function->sourceLocations->entries;
361 auto item = std::lower_bound(first: entries.begin(), last: entries.end(), val: instructionOffset,
362 comp: [](auto entry, uint offset) { return entry.offset < offset; });
363
364 Q_ASSERT(item != entries.end());
365 return item->location;
366 }
367
368 QQmlJS::SourceLocation currentSourceLocation() const
369 {
370 return sourceLocation(instructionOffset: currentInstructionOffset());
371 }
372
373 void setError(const QString &message, int instructionOffset)
374 {
375 Q_ASSERT(m_error);
376 if (m_error->isValid())
377 return;
378 m_error->message = message;
379 m_error->loc = sourceLocation(instructionOffset);
380 }
381
382 void setError(const QString &message)
383 {
384 setError(message, instructionOffset: currentInstructionOffset());
385 }
386
387 static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
388 {
389 using Type = QV4::Moth::Instr::Type;
390 switch (type) {
391 case Type::PopContext:
392 case Type::PopScriptContext:
393 case Type::CreateCallContext:
394 case Type::CreateCallContext_Wide:
395 case Type::PushCatchContext:
396 case Type::PushCatchContext_Wide:
397 case Type::PushWithContext:
398 case Type::PushWithContext_Wide:
399 case Type::PushBlockContext:
400 case Type::PushBlockContext_Wide:
401 case Type::CloneBlockContext:
402 case Type::CloneBlockContext_Wide:
403 case Type::PushScriptContext:
404 case Type::PushScriptContext_Wide:
405 return true;
406 default:
407 break;
408 }
409 return false;
410 }
411
412 // Stub out all the methods so that passes can choose to only implement part of them.
413 void generate_Add(int) override {}
414 void generate_As(int) override {}
415 void generate_BitAnd(int) override {}
416 void generate_BitAndConst(int) override {}
417 void generate_BitOr(int) override {}
418 void generate_BitOrConst(int) override {}
419 void generate_BitXor(int) override {}
420 void generate_BitXorConst(int) override {}
421 void generate_CallGlobalLookup(int, int, int) override {}
422 void generate_CallName(int, int, int) override {}
423 void generate_CallPossiblyDirectEval(int, int) override {}
424 void generate_CallProperty(int, int, int, int) override {}
425 void generate_CallPropertyLookup(int, int, int, int) override {}
426 void generate_CallQmlContextPropertyLookup(int, int, int) override {}
427 void generate_CallValue(int, int, int) override {}
428 void generate_CallWithReceiver(int, int, int, int) override {}
429 void generate_CallWithSpread(int, int, int, int) override {}
430 void generate_CheckException() override {}
431 void generate_CloneBlockContext() override {}
432 void generate_CmpEq(int) override {}
433 void generate_CmpEqInt(int) override {}
434 void generate_CmpEqNull() override {}
435 void generate_CmpGe(int) override {}
436 void generate_CmpGt(int) override {}
437 void generate_CmpIn(int) override {}
438 void generate_CmpInstanceOf(int) override {}
439 void generate_CmpLe(int) override {}
440 void generate_CmpLt(int) override {}
441 void generate_CmpNe(int) override {}
442 void generate_CmpNeInt(int) override {}
443 void generate_CmpNeNull() override {}
444 void generate_CmpStrictEqual(int) override {}
445 void generate_CmpStrictNotEqual(int) override {}
446 void generate_Construct(int, int, int) override {}
447 void generate_ConstructWithSpread(int, int, int) override {}
448 void generate_ConvertThisToObject() override {}
449 void generate_CreateCallContext() override {}
450 void generate_CreateClass(int, int, int) override {}
451 void generate_CreateMappedArgumentsObject() override {}
452 void generate_CreateRestParameter(int) override {}
453 void generate_CreateUnmappedArgumentsObject() override {}
454 void generate_DeadTemporalZoneCheck(int) override {}
455 void generate_Debug() override {}
456 void generate_DeclareVar(int, int) override {}
457 void generate_Decrement() override {}
458 void generate_DefineArray(int, int) override {}
459 void generate_DefineObjectLiteral(int, int, int) override {}
460 void generate_DeleteName(int) override {}
461 void generate_DeleteProperty(int, int) override {}
462 void generate_DestructureRestElement() override {}
463 void generate_Div(int) override {}
464 void generate_Exp(int) override {}
465 void generate_GetException() override {}
466 void generate_GetIterator(int) override {}
467 void generate_GetLookup(int) override {}
468 void generate_GetOptionalLookup(int, int) override {}
469 void generate_GetTemplateObject(int) override {}
470 void generate_Increment() override {}
471 void generate_InitializeBlockDeadTemporalZone(int, int) override {}
472 void generate_IteratorClose() override {}
473 void generate_IteratorNext(int, int) override {}
474 void generate_IteratorNextForYieldStar(int, int, int) override {}
475 void generate_Jump(int) override {}
476 void generate_JumpFalse(int) override {}
477 void generate_JumpNoException(int) override {}
478 void generate_JumpNotUndefined(int) override {}
479 void generate_JumpTrue(int) override {}
480 void generate_LoadClosure(int) override {}
481 void generate_LoadConst(int) override {}
482 void generate_LoadElement(int) override {}
483 void generate_LoadFalse() override {}
484 void generate_LoadGlobalLookup(int) override {}
485 void generate_LoadImport(int) override {}
486 void generate_LoadInt(int) override {}
487 void generate_LoadLocal(int) override {}
488 void generate_LoadName(int) override {}
489 void generate_LoadNull() override {}
490 void generate_LoadOptionalProperty(int, int) override {}
491 void generate_LoadProperty(int) override {}
492 void generate_LoadQmlContextPropertyLookup(int) override {}
493 void generate_LoadReg(int) override {}
494 void generate_LoadRuntimeString(int) override {}
495 void generate_LoadScopedLocal(int, int) override {}
496 void generate_LoadSuperConstructor() override {}
497 void generate_LoadSuperProperty(int) override {}
498 void generate_LoadTrue() override {}
499 void generate_LoadUndefined() override {}
500 void generate_LoadZero() override {}
501 void generate_Mod(int) override {}
502 void generate_MoveConst(int, int) override {}
503 void generate_MoveReg(int, int) override {}
504 void generate_MoveRegExp(int, int) override {}
505 void generate_Mul(int) override {}
506 void generate_PopContext() override {}
507 void generate_PopScriptContext() override {}
508 void generate_PushBlockContext(int) override {}
509 void generate_PushCatchContext(int, int) override {}
510 void generate_PushScriptContext(int) override {}
511 void generate_PushWithContext() override {}
512 void generate_Resume(int) override {}
513 void generate_Ret() override {}
514 void generate_SetException() override {}
515 void generate_SetLookup(int, int) override {}
516 void generate_SetUnwindHandler(int) override {}
517 void generate_Shl(int) override {}
518 void generate_ShlConst(int) override {}
519 void generate_Shr(int) override {}
520 void generate_ShrConst(int) override {}
521 void generate_StoreElement(int, int) override {}
522 void generate_StoreLocal(int) override {}
523 void generate_StoreNameSloppy(int) override {}
524 void generate_StoreNameStrict(int) override {}
525 void generate_StoreProperty(int, int) override {}
526 void generate_StoreReg(int) override {}
527 void generate_StoreScopedLocal(int, int) override {}
528 void generate_StoreSuperProperty(int) override {}
529 void generate_Sub(int) override {}
530 void generate_TailCall(int, int, int, int) override {}
531 void generate_ThrowException() override {}
532 void generate_ThrowOnNullOrUndefined() override {}
533 void generate_ToObject() override {}
534 void generate_TypeofName(int) override {}
535 void generate_TypeofValue() override {}
536 void generate_UCompl() override {}
537 void generate_UMinus() override {}
538 void generate_UNot() override {}
539 void generate_UPlus() override {}
540 void generate_UShr(int) override {}
541 void generate_UShrConst(int) override {}
542 void generate_UnwindDispatch() override {}
543 void generate_UnwindToLabel(int, int) override {}
544 void generate_Yield() override {}
545 void generate_YieldStar() override {}
546};
547
548QT_END_NAMESPACE
549
550#endif // QQMLJSCOMPILEPASS_P_H
551

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/qmlcompiler/qqmljscompilepass_p.h