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
qv4baselineassembler.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:critical reason:jit
4
5#include <QBuffer>
6#include <QFile>
7
8#include "qv4engine_p.h"
11#include <private/qv4function_p.h>
12#include <private/qv4runtime_p.h>
13#include <private/qv4stackframe_p.h>
14
15#include <wtf/Vector.h>
16#include <assembler/MacroAssembler.h>
17#include <assembler/MacroAssemblerCodeRef.h>
18#include <assembler/LinkBuffer.h>
19#include <WTFStubs.h>
20
21#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
22
23#if QT_CONFIG(qml_jit)
24
25QT_BEGIN_NAMESPACE
26namespace QV4 {
27namespace JIT {
28
29#define ASM_GENERATE_RUNTIME_CALL(function, destination)
30 pasm()->GENERATE_RUNTIME_CALL(function, destination)
31#define callHelper(x)
32 PlatformAssemblerCommon::callRuntimeUnchecked(reinterpret_cast<void *>(&x), #x)
33
34const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
35
36static ReturnedValue toNumberHelper(ReturnedValue v)
37{
38 return Encode(Value::fromReturnedValue(v).toNumber());
39}
40
41static ReturnedValue toInt32Helper(ReturnedValue v)
42{
43 return Encode(Value::fromReturnedValue(v).toInt32());
44}
45
46#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
47class PlatformAssembler64 : public PlatformAssemblerCommon
48{
49public:
50 PlatformAssembler64(const Value *constantTable)
51 : PlatformAssemblerCommon(constantTable)
52 {}
53
54 void callRuntime(const void *funcPtr, CallResultDestination dest)
55 {
56 PlatformAssemblerCommon::callRuntime(funcPtr);
57 if (dest == CallResultDestination::InAccumulator)
58 saveReturnValueInAccumulator();
59 else if (AccumulatorRegister == ReturnValueRegister)
60 loadUndefined();
61 }
62
63 void saveReturnValueInAccumulator()
64 {
65 move(ReturnValueRegister, AccumulatorRegister);
66 }
67
68 void loadUndefined(RegisterID dest = AccumulatorRegister)
69 {
70 move(TrustedImm64(0), dest);
71 }
72
73 void copyConst(int constIndex, Address dest)
74 {
75 //###
76 if (constant(constIndex).isUndefined()) {
77 loadUndefined(ScratchRegister);
78 } else {
79 load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
80 }
81 store64(ScratchRegister, dest);
82 }
83
84 void copyReg(Address src, Address dst)
85 {
86 load64(src, ScratchRegister);
87 store64(ScratchRegister, dst);
88 }
89
90 void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegister)
91 {
92 load64(addr, dest);
93 }
94
95 void loadAccumulator(Address addr)
96 {
97 load64(addr, AccumulatorRegister);
98 }
99
100 void storeAccumulator(Address addr)
101 {
102 store64(AccumulatorRegister, addr);
103 }
104
105 void moveReg(Address sourceRegAddress, Address destRegAddress)
106 {
107 load64(sourceRegAddress, ScratchRegister);
108 store64(ScratchRegister, destRegAddress);
109 }
110
111 void loadString(int stringId)
112 {
113 loadAccumulator(loadStringAddress(stringId));
114 }
115
116 void loadValue(ReturnedValue value)
117 {
118 move(TrustedImm64(value), AccumulatorRegister);
119 }
120
121 void storeHeapObject(RegisterID source, Address addr)
122 {
123 store64(source, addr);
124 }
125
126 void generateCatchTrampoline()
127 {
128 PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
129 }
130
131 void jumpNotUndefined(int offset)
132 {
133 auto jump = branch64(NotEqual, AccumulatorRegister, TrustedImm64(0));
134 addJumpToOffset(jump, offset);
135 }
136
137 Jump jumpEmpty()
138 {
139 return branch64(Equal, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
140 }
141
142 Jump jumpNotEmpty()
143 {
144 return branch64(NotEqual, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
145 }
146
147 void toBoolean(std::function<void(RegisterID)> continuation)
148 {
149 urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
150 auto needsConversion = branch32(
151 NotEqual, TrustedImm32(Value::IsIntegerConvertible_Value), ScratchRegister);
152
153 continuation(AccumulatorRegister);
154 Jump done = jump();
155
156 // slow path:
157 needsConversion.link(this);
158 push(AccumulatorRegister);
159 move(AccumulatorRegister, registerForArg(0));
160 callHelper(Value::toBooleanImpl);
161 and32(TrustedImm32(1), ReturnValueRegister, ScratchRegister);
162 pop(AccumulatorRegister);
163 continuation(ScratchRegister);
164
165 done.link(this);
166 }
167
168 void toNumber()
169 {
170 move(TrustedImm64(Value::NumberMask), ScratchRegister);
171 and64(AccumulatorRegister, ScratchRegister);
172 move(TrustedImm64(Value::NumberDiscriminator), ScratchRegister2);
173 auto isNumber = branch64(GreaterThanOrEqual, ScratchRegister, ScratchRegister2);
174
175 move(AccumulatorRegister, registerForArg(0));
176 callHelper(toNumberHelper);
177 saveReturnValueInAccumulator();
178
179 isNumber.link(this);
180 }
181
182 // this converts both the lhs and the accumulator to int32
183 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
184 {
185 load64(lhs, lhsTarget);
186 urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
187 auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
188
189 const Address accumulatorStackAddress(JSStackFrameRegister,
190 offsetof(CallData, accumulator));
191 storeAccumulator(accumulatorStackAddress);
192 move(lhsTarget, registerForArg(0));
193 callHelper(toInt32Helper);
194 move(ReturnValueRegister, lhsTarget);
195 loadAccumulator(accumulatorStackAddress);
196
197 lhsIsInt.link(this);
198 urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
199 auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
200
201 pushAligned(lhsTarget);
202 move(AccumulatorRegister, registerForArg(0));
203 callHelper(toInt32Helper);
204 saveReturnValueInAccumulator();
205 popAligned(lhsTarget);
206
207 isInt.link(this);
208 }
209
210 void toInt32()
211 {
212 urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
213 auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
214
215 move(AccumulatorRegister, registerForArg(0));
216 callHelper(toInt32Helper);
217 saveReturnValueInAccumulator();
218
219 isInt.link(this);
220 }
221
222 void regToInt32(Address srcReg, RegisterID targetReg)
223 {
224 load64(srcReg, targetReg);
225 urshift64(targetReg, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
226 auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
227
228 pushAligned(AccumulatorRegister);
229 move(targetReg, registerForArg(0));
230 callHelper(toInt32Helper);
231 move(ReturnValueRegister, targetReg);
232 popAligned(AccumulatorRegister);
233
234 isInt.link(this);
235 }
236
237 void isNullOrUndefined()
238 {
239 move(AccumulatorRegister, ScratchRegister);
240 compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister);
241 Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister);
242
243 // not undefined
244 rshift64(TrustedImm32(32), ScratchRegister);
245 compare32(Equal, ScratchRegister, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
246 AccumulatorRegister);
247
248 isUndef.link(this);
249 }
250
251 Jump isIntOrBool()
252 {
253 urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerOrBool_Shift), ScratchRegister);
254 return branch32(Equal, TrustedImm32(Value::IsIntegerOrBool_Value), ScratchRegister);
255 }
256
257 void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
258 {
259 Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
260 load64(lhsAddr, ScratchRegister);
261 Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
262 Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
263 addJumpToOffset(equal, offset);
264 isUndef.link(this);
265 }
266
267 void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
268 {
269 Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
270 load64(lhsAddr, ScratchRegister);
271 Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
272 addJumpToOffset(isUndef, offset);
273 Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
274 addJumpToOffset(notEqual, offset);
275 }
276
277 void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
278 {
279 if (sourceReg == NoRegister)
280 or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister);
281 else
282 or64(TrustedImm64(int64_t(tag) << 32), sourceReg, AccumulatorRegister);
283 }
284
285 void encodeDoubleIntoAccumulator(FPRegisterID src)
286 {
287 moveDoubleTo64(src, AccumulatorRegister);
288 move(TrustedImm64(Value::EncodeMask), ScratchRegister);
289 xor64(ScratchRegister, AccumulatorRegister);
290 }
291
292 void pushValueAligned(ReturnedValue v)
293 {
294 loadValue(v);
295 pushAligned(AccumulatorRegister);
296 }
297
298 void popValueAligned()
299 {
300 addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
301 }
302
303 Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
304 {
305 urshift64(AccumulatorRegister, TrustedImm32(32), ScratchRegister);
306 Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
307 load64(lhsAddr, ScratchRegister);
308 urshift64(ScratchRegister, TrustedImm32(32), ScratchRegister2);
309 Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister2);
310
311 // both integer
312 Jump failure = fastPath();
313 Jump done = jump();
314
315 // all other cases
316 if (failure.isSet())
317 failure.link(this);
318 accNotInt.link(this);
319 lhsNotInt.link(this);
320
321 return done;
322 }
323
324 Jump unopIntPath(std::function<Jump(void)> fastPath)
325 {
326 urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
327 Jump accNotIntConvertible = branch32(
328 NotEqual, TrustedImm32(Value::IsIntegerConvertible_Value), ScratchRegister);
329
330 // both integer
331 Jump failure = fastPath();
332 Jump done = jump();
333
334 // all other cases
335 if (failure.isSet())
336 failure.link(this);
337 accNotIntConvertible.link(this);
338
339 return done;
340 }
341
342 void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall)
343 {
344 passAsArg(AccumulatorRegister, 0);
345 doCall();
346 }
347};
348
349typedef PlatformAssembler64 PlatformAssembler;
350#endif
351
352#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
353class PlatformAssembler32 : public PlatformAssemblerCommon
354{
355public:
356 PlatformAssembler32(const Value *constantTable)
357 : PlatformAssemblerCommon(constantTable)
358 {}
359
360 void callRuntime(const void *funcPtr, CallResultDestination dest)
361 {
362 PlatformAssemblerCommon::callRuntime(funcPtr);
363 if (dest == CallResultDestination::InAccumulator)
364 saveReturnValueInAccumulator();
365 else if (AccumulatorRegisterValue == ReturnValueRegisterValue)
366 loadUndefined();
367 }
368
369 void saveReturnValueInAccumulator()
370 {
371 move(ReturnValueRegisterValue, AccumulatorRegisterValue);
372 move(ReturnValueRegisterTag, AccumulatorRegisterTag);
373 }
374
375 void loadUndefined()
376 {
377 move(TrustedImm32(0), AccumulatorRegisterValue);
378 move(TrustedImm32(0), AccumulatorRegisterTag);
379 }
380
381 void copyConst(int constIndex, Address destRegAddr)
382 {
383 //###
384 if (constant(constIndex).isUndefined()) {
385 move(TrustedImm32(0), ScratchRegister);
386 store32(ScratchRegister, destRegAddr);
387 destRegAddr.offset += 4;
388 store32(ScratchRegister, destRegAddr);
389 } else {
390 Address src = loadConstAddress(constIndex);
391 loadDouble(src, FPScratchRegister);
392 storeDouble(FPScratchRegister, destRegAddr);
393 }
394 }
395
396 void copyReg(Address src, Address dest)
397 {
398 loadDouble(src, FPScratchRegister);
399 storeDouble(FPScratchRegister, dest);
400 }
401
402 void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegisterValue)
403 {
404 load32(addr, dest);
405 }
406
407 void loadAccumulator(Address src)
408 {
409 load32(src, AccumulatorRegisterValue);
410 src.offset += 4;
411 load32(src, AccumulatorRegisterTag);
412 }
413
414 void storeAccumulator(Address addr)
415 {
416 store32(AccumulatorRegisterValue, addr);
417 addr.offset += 4;
418 store32(AccumulatorRegisterTag, addr);
419 }
420
421 void moveReg(Address sourceRegAddress, Address destRegAddress)
422 {
423 load32(sourceRegAddress, ScratchRegister);
424 store32(ScratchRegister, destRegAddress);
425 sourceRegAddress.offset += 4;
426 destRegAddress.offset += 4;
427 load32(sourceRegAddress, ScratchRegister);
428 store32(ScratchRegister, destRegAddress);
429 }
430
431 void loadString(int stringId)
432 {
433 load32(loadStringAddress(stringId), AccumulatorRegisterValue);
434 move(TrustedImm32(0), AccumulatorRegisterTag);
435 }
436
437 void loadValue(ReturnedValue value)
438 {
439 move(TrustedImm32(Value::fromReturnedValue(value).value()), AccumulatorRegisterValue);
440 move(TrustedImm32(Value::fromReturnedValue(value).tag()), AccumulatorRegisterTag);
441 }
442
443 void storeHeapObject(RegisterID source, Address addr)
444 {
445 store32(source, addr);
446 addr.offset += 4;
447 store32(TrustedImm32(0), addr);
448 }
449
450
451 void generateCatchTrampoline()
452 {
453 PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
454 }
455
456 void toNumber()
457 {
458 and32(TrustedImm32(Value::NumberMask >> Value::Tag_Shift),
459 AccumulatorRegisterTag, ScratchRegister);
460 auto isNumber = branch32(
461 GreaterThanOrEqual, ScratchRegister,
462 TrustedImm32(Value::NumberDiscriminator >> Value::Tag_Shift));
463
464
465 if (ArgInRegCount < 2) {
466 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // stack alignment
467 push(AccumulatorRegisterTag);
468 push(AccumulatorRegisterValue);
469 } else {
470 move(AccumulatorRegisterValue, registerForArg(0));
471 move(AccumulatorRegisterTag, registerForArg(1));
472 }
473 callHelper(toNumberHelper);
474 saveReturnValueInAccumulator();
475 if (ArgInRegCount < 2)
476 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
477
478 isNumber.link(this);
479 }
480
481 // this converts both the lhs and the accumulator to int32
482 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
483 {
484 bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
485 || AccumulatorRegisterTag == ReturnValueRegisterTag;
486 lhs.offset += 4;
487 load32(lhs, lhsTarget);
488 lhs.offset -= 4;
489 auto lhsIsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), lhsTarget);
490 load32(lhs, lhsTarget);
491 auto lhsIsInt = jump();
492
493 lhsIsNotInt.link(this);
494
495 // Save accumulator from being garbage collected, no matter if we will reuse the register.
496 const Address accumulatorStackAddress(JSStackFrameRegister,
497 offsetof(CallData, accumulator));
498 storeAccumulator(accumulatorStackAddress);
499
500 if (ArgInRegCount < 2) {
501 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
502 push(lhsTarget);
503 load32(lhs, lhsTarget);
504 push(lhsTarget);
505 } else {
506 move(lhsTarget, registerForArg(1));
507 load32(lhs, registerForArg(0));
508 }
509 callHelper(toInt32Helper);
510 move(ReturnValueRegisterValue, lhsTarget);
511 if (ArgInRegCount < 2)
512 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
513
514 if (accumulatorNeedsSaving) // otherwise it's still the same
515 loadAccumulator(accumulatorStackAddress);
516
517 lhsIsInt.link(this);
518
519 auto rhsIsInt = branch32(Equal, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
520
521 pushAligned(lhsTarget);
522 if (ArgInRegCount < 2) {
523 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
524 push(AccumulatorRegisterTag);
525 push(AccumulatorRegisterValue);
526 } else {
527 move(AccumulatorRegisterValue, registerForArg(0));
528 move(AccumulatorRegisterTag, registerForArg(1));
529 }
530 callHelper(toInt32Helper);
531 saveReturnValueInAccumulator();
532 if (ArgInRegCount < 2)
533 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
534 popAligned(lhsTarget);
535
536 rhsIsInt.link(this);
537 }
538
539 void toInt32()
540 {
541 urshift32(AccumulatorRegisterTag, TrustedImm32(Value::QuickType_Shift - 32), ScratchRegister);
542 auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister);
543
544 if (ArgInRegCount < 2) {
545 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // align the stack on a 16-byte boundary
546 push(AccumulatorRegisterTag);
547 push(AccumulatorRegisterValue);
548 } else {
549 move(AccumulatorRegisterValue, registerForArg(0));
550 move(AccumulatorRegisterTag, registerForArg(1));
551 }
552 callHelper(toInt32Helper);
553 saveReturnValueInAccumulator();
554 if (ArgInRegCount < 2)
555 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
556
557 isInt.link(this);
558 }
559
560 void regToInt32(Address srcReg, RegisterID targetReg)
561 {
562 bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
563 || AccumulatorRegisterTag == ReturnValueRegisterTag;
564 if (accumulatorNeedsSaving) {
565 push(AccumulatorRegisterTag);
566 push(AccumulatorRegisterValue);
567 }
568 if (ArgInRegCount < 2) {
569 if (!accumulatorNeedsSaving)
570 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
571 srcReg.offset += 4;
572 load32(srcReg, targetReg);
573 push(targetReg);
574 srcReg.offset -= 4;
575 load32(srcReg, targetReg);
576 push(targetReg);
577 } else {
578 if (accumulatorNeedsSaving)
579 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
580 load32(srcReg, registerForArg(0));
581 srcReg.offset += 4;
582 load32(srcReg, registerForArg(1));
583 }
584 callHelper(toInt32Helper);
585 move(ReturnValueRegisterValue, targetReg);
586 if (accumulatorNeedsSaving) {
587 addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
588 pop(AccumulatorRegisterValue);
589 pop(AccumulatorRegisterTag);
590 } else if (ArgInRegCount < 2) {
591 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
592 }
593 }
594
595 void isNullOrUndefined()
596 {
597 Jump notUndefOrPtr = branch32(NotEqual, TrustedImm32(0), AccumulatorRegisterTag);
598 compare32(Equal, AccumulatorRegisterValue, TrustedImm32(0), AccumulatorRegisterValue);
599 auto done = jump();
600
601 // not undefined or managed
602 notUndefOrPtr.link(this);
603 compare32(Equal, AccumulatorRegisterTag, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
604 AccumulatorRegisterValue);
605
606 done.link(this);
607 }
608
609 Jump isIntOrBool()
610 {
611 urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerOrBool_Shift - 32), ScratchRegister);
612 return branch32(Equal, TrustedImm32(Value::IsIntegerOrBool_Value), ScratchRegister);
613 }
614
615 void pushValue(ReturnedValue v)
616 {
617 push(TrustedImm32(v >> 32));
618 push(TrustedImm32(v));
619 }
620
621 void jumpNotUndefined(int offset)
622 {
623 move(AccumulatorRegisterTag, ScratchRegister);
624 or32(AccumulatorRegisterValue, ScratchRegister);
625 auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
626 addJumpToOffset(jump, offset);
627 }
628
629 Jump jumpEmpty()
630 {
631 return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
632 }
633
634 Jump jumpNotEmpty()
635 {
636 return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
637 }
638
639 void toBoolean(std::function<void(RegisterID)> continuation)
640 {
641 urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
642 ScratchRegister);
643 auto needsConversion = branch32(
644 NotEqual, TrustedImm32(Value::IsIntegerConvertible_Value), ScratchRegister);
645 continuation(AccumulatorRegisterValue);
646 Jump done = jump();
647
648 // slow path:
649 needsConversion.link(this);
650
651 bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
652 || AccumulatorRegisterTag == ReturnValueRegisterTag;
653 if (accumulatorNeedsSaving) {
654 push(AccumulatorRegisterTag);
655 push(AccumulatorRegisterValue);
656 }
657
658 if (ArgInRegCount < 2) {
659 if (!accumulatorNeedsSaving)
660 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
661 push(AccumulatorRegisterTag);
662 push(AccumulatorRegisterValue);
663 } else {
664 if (accumulatorNeedsSaving)
665 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
666 move(AccumulatorRegisterValue, registerForArg(0));
667 move(AccumulatorRegisterTag, registerForArg(1));
668 }
669 callHelper(Value::toBooleanImpl);
670 and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister);
671 if (accumulatorNeedsSaving) {
672 addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
673 pop(AccumulatorRegisterValue);
674 pop(AccumulatorRegisterTag);
675 } else if (ArgInRegCount < 2) {
676 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
677 }
678 continuation(ScratchRegister);
679
680 done.link(this);
681 }
682
683 void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
684 {
685 Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
686 load32(lhsAddr, ScratchRegister);
687 Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
688 Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
689 addJumpToOffset(notEqUndefVal, offset);
690 lhsAddr.offset += 4;
691 load32(lhsAddr, ScratchRegister);
692 Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
693 addJumpToOffset(notEqUndefTag, offset);
694 notEqInt.link(this);
695 }
696
697 void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
698 {
699 Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
700 load32(lhsAddr, ScratchRegister);
701 Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
702 addJumpToOffset(notEqual, offset);
703 Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
704 lhsAddr.offset += 4;
705 load32(lhsAddr, ScratchRegister);
706 Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
707 addJumpToOffset(equalUndef, offset);
708 notUndefValue.link(this);
709 }
710
711 void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
712 {
713 if (sourceReg != NoRegister)
714 move(sourceReg, AccumulatorRegisterValue);
715 move(TrustedImm32(int(tag)), AccumulatorRegisterTag);
716 }
717
718 void encodeDoubleIntoAccumulator(FPRegisterID src)
719 {
720 moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag);
721 xor32(TrustedImm32(Value::EncodeMask >> 32), AccumulatorRegisterTag);
722 }
723
724 void pushValueAligned(ReturnedValue v)
725 {
726 pushValue(v);
727 }
728
729 void popValueAligned()
730 {
731 popValue();
732 }
733
734 Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
735 {
736 Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
737 Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset();
738 load32(lhsAddrTag, ScratchRegister);
739 Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
740
741 // both integer
742 Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset();
743 load32(lhsAddrValue, ScratchRegister);
744 Jump failure = fastPath();
745 Jump done = jump();
746
747 // all other cases
748 if (failure.isSet())
749 failure.link(this);
750 accNotInt.link(this);
751 lhsNotInt.link(this);
752
753 return done;
754 }
755
756 Jump unopIntPath(std::function<Jump(void)> fastPath)
757 {
758 Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
759
760 // both integer
761 Jump failure = fastPath();
762 Jump done = jump();
763
764 // all other cases
765 if (failure.isSet())
766 failure.link(this);
767 accNotInt.link(this);
768
769 return done;
770 }
771
772 void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall)
773 {
774 if (ArgInRegCount < 2) {
775 subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
776 push(AccumulatorRegisterTag);
777 push(AccumulatorRegisterValue);
778 } else {
779 move(AccumulatorRegisterValue, registerForArg(0));
780 move(AccumulatorRegisterTag, registerForArg(1));
781 }
782 doCall();
783 if (ArgInRegCount < 2)
784 addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
785 }
786};
787
788typedef PlatformAssembler32 PlatformAssembler;
789#endif
790
791#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
792
793typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr;
794typedef PlatformAssembler::TrustedImm32 TrustedImm32;
795typedef PlatformAssembler::TrustedImm64 TrustedImm64;
796typedef PlatformAssembler::Address Address;
797typedef PlatformAssembler::RegisterID RegisterID;
798typedef PlatformAssembler::FPRegisterID FPRegisterID;
799
800static Address regAddr(int reg)
801{
802 return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
803}
804
805BaselineAssembler::BaselineAssembler(const Value *constantTable)
806 : d(new PlatformAssembler(constantTable))
807{
808}
809
810BaselineAssembler::~BaselineAssembler()
811{
812 delete pasm();
813}
814
815void BaselineAssembler::generatePrologue()
816{
817 pasm()->generateFunctionEntry();
818}
819
820void BaselineAssembler::generateEpilogue()
821{
822 pasm()->generateCatchTrampoline();
823}
824
825void BaselineAssembler::link(Function *function)
826{
827 pasm()->link(function, "BaselineJIT");
828}
829
830void BaselineAssembler::addLabel(int offset)
831{
832 pasm()->addLabelForOffset(offset);
833}
834
835void BaselineAssembler::loadConst(int constIndex)
836{
837 //###
838 if (pasm()->constant(constIndex).isUndefined()) {
839 pasm()->loadUndefined();
840 } else {
841 pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
842 }
843}
844
845void BaselineAssembler::copyConst(int constIndex, int destReg)
846{
847 pasm()->copyConst(constIndex, regAddr(destReg));
848}
849
850void BaselineAssembler::loadReg(int reg)
851{
852 pasm()->loadAccumulator(regAddr(reg));
853}
854
855void JIT::BaselineAssembler::moveReg(int sourceReg, int destReg)
856{
857 pasm()->moveReg(regAddr(sourceReg), regAddr(destReg));
858}
859
860void BaselineAssembler::storeReg(int reg)
861{
862 pasm()->storeAccumulator(regAddr(reg));
863}
864
865void BaselineAssembler::loadLocal(int index, int level)
866{
867 Heap::CallContext ctx;
868 Q_UNUSED(ctx);
869 pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
870 while (level) {
871 pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
872 --level;
873 }
874 pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
875}
876
877void BaselineAssembler::storeLocal(int index, int level)
878{
879 Heap::CallContext ctx;
880 Q_UNUSED(ctx);
881 pasm()->loadPtr(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
882 while (level) {
883 pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
884 --level;
885 }
886 pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
887 // check if we need a write barrier
888 auto skipBarrier = pasm()->branch8(
889 PlatformAssembler::Equal,
890 PlatformAssembler::Address(PlatformAssembler::EngineRegister,
891 offsetof(EngineBase, isGCOngoing)),
892 TrustedImm32(0));
893 saveAccumulatorInFrame();
894 // if so, do a runtime call
895 pasm()->prepareCallWithArgCount(1);
896 pasm()->passAccumulatorAsArg(0);
897 pasm()->callRuntime((void*)Runtime::MarkCustom::call, CallResultDestination::Ignore);
898 loadAccumulatorFromFrame();
899 skipBarrier.link(pasm());
900
901}
902
903void BaselineAssembler::loadString(int stringId)
904{
905 pasm()->loadString(stringId);
906}
907
908void BaselineAssembler::loadValue(ReturnedValue value)
909{
910 pasm()->loadValue(value);
911}
912
913void BaselineAssembler::storeHeapObject(int reg)
914{
915 pasm()->storeHeapObject(PlatformAssembler::ReturnValueRegisterValue, regAddr(reg));
916}
917
918void BaselineAssembler::loadImport(int index)
919{
920 Address addr = pasm()->loadCompilationUnitPtr(PlatformAssembler::ScratchRegister);
921 addr.offset = offsetof(QV4::CompilationUnitRuntimeData, imports);
922 pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
923 addr.offset = index * int(sizeof(QV4::Value*));
924 pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
925 pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
926}
927
928void BaselineAssembler::toNumber()
929{
930 pasm()->toNumber();
931}
932
933void BaselineAssembler::uminus()
934{
935 saveAccumulatorInFrame();
936 pasm()->prepareCallWithArgCount(1);
937 pasm()->passAccumulatorAsArg(0);
938 ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator);
939 checkException();
940}
941
942void BaselineAssembler::ucompl()
943{
944 pasm()->toInt32();
945 pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
946 pasm()->setAccumulatorTag(IntegerTag);
947}
948
949static ReturnedValue incHelper(const Value v)
950{
951 double d;
952 if (Q_LIKELY(v.isDouble()))
953 d = v.doubleValue();
954 else
955 d = v.toNumberImpl();
956 return Encode(d + 1.);
957}
958
959void BaselineAssembler::inc()
960{
961 auto done = pasm()->unopIntPath([this](){
962 auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
963 PlatformAssembler::AccumulatorRegisterValue,
964 TrustedImm32(1),
965 PlatformAssembler::ScratchRegister);
966 pasm()->setAccumulatorTag(IntegerTag, PlatformAssembler::ScratchRegister);
967 return overflowed;
968 });
969
970 // slow path:
971 pasm()->callWithAccumulatorByValueAsFirstArgument([this]() {
972 pasm()->callHelper(incHelper);
973 pasm()->saveReturnValueInAccumulator();
974 });
975 checkException();
976
977 // done.
978 done.link(pasm());
979}
980
981static ReturnedValue decHelper(const Value v)
982{
983 double d;
984 if (Q_LIKELY(v.isDouble()))
985 d = v.doubleValue();
986 else
987 d = v.toNumberImpl();
988 return Encode(d - 1.);
989}
990
991void BaselineAssembler::dec()
992{
993 auto done = pasm()->unopIntPath([this](){
994 auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
995 PlatformAssembler::AccumulatorRegisterValue,
996 TrustedImm32(1),
997 PlatformAssembler::ScratchRegister);
998 pasm()->setAccumulatorTag(IntegerTag, PlatformAssembler::ScratchRegister);
999 return overflowed;
1000 });
1001
1002 // slow path:
1003 pasm()->callWithAccumulatorByValueAsFirstArgument([this]() {
1004 pasm()->callHelper(decHelper);
1005 pasm()->saveReturnValueInAccumulator();
1006 });
1007 checkException();
1008
1009 // done.
1010 done.link(pasm());
1011}
1012
1013void BaselineAssembler::unot()
1014{
1015 pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){
1016 pasm()->compare32(PlatformAssembler::Equal, resultReg,
1017 TrustedImm32(0), PlatformAssembler::AccumulatorRegisterValue);
1018 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1019 });
1020}
1021
1022void BaselineAssembler::add(int lhs)
1023{
1024 auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
1025 auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
1026 PlatformAssembler::AccumulatorRegisterValue,
1027 PlatformAssembler::ScratchRegister);
1028 pasm()->setAccumulatorTag(IntegerTag,
1029 PlatformAssembler::ScratchRegister);
1030 return overflowed;
1031 });
1032
1033 // slow path:
1034 saveAccumulatorInFrame();
1035 pasm()->prepareCallWithArgCount(3);
1036 pasm()->passAccumulatorAsArg(2);
1037 pasm()->passJSSlotAsArg(lhs, 1);
1038 pasm()->passEngineAsArg(0);
1039 ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator);
1040 checkException();
1041
1042 // done.
1043 done.link(pasm());
1044}
1045
1046void BaselineAssembler::bitAnd(int lhs)
1047{
1048 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1049 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1050 pasm()->and32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1051 pasm()->setAccumulatorTag(IntegerTag);
1052}
1053
1054void BaselineAssembler::bitOr(int lhs)
1055{
1056 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1057 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1058 pasm()->or32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1059 pasm()->setAccumulatorTag(IntegerTag);
1060}
1061
1062void BaselineAssembler::bitXor(int lhs)
1063{
1064 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1065 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1066 pasm()->xor32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1067 pasm()->setAccumulatorTag(IntegerTag);
1068}
1069
1070void BaselineAssembler::ushr(int lhs)
1071{
1072 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1073 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1074 pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
1075 pasm()->urshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
1076 pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1077 auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
1078 PlatformAssembler::AccumulatorRegisterValue,
1079 TrustedImm32(0));
1080 pasm()->setAccumulatorTag(IntegerTag);
1081 auto done = pasm()->jump();
1082
1083 doubleEncode.link(pasm());
1084 pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
1085 PlatformAssembler::FPScratchRegister,
1086 PlatformAssembler::ScratchRegister);
1087 pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
1088 done.link(pasm());
1089}
1090
1091void BaselineAssembler::shr(int lhs)
1092{
1093 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1094 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1095 pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
1096 pasm()->rshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
1097 pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1098 pasm()->setAccumulatorTag(IntegerTag);
1099}
1100
1101void BaselineAssembler::shl(int lhs)
1102{
1103 PlatformAssembler::Address lhsAddr = regAddr(lhs);
1104 pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
1105 pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
1106 pasm()->lshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
1107 pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
1108 pasm()->setAccumulatorTag(IntegerTag);
1109}
1110
1111void BaselineAssembler::bitAndConst(int rhs)
1112{
1113 pasm()->toInt32();
1114 pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1115 pasm()->setAccumulatorTag(IntegerTag);
1116}
1117
1118void BaselineAssembler::bitOrConst(int rhs)
1119{
1120 pasm()->toInt32();
1121 pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1122 pasm()->setAccumulatorTag(IntegerTag);
1123}
1124
1125void BaselineAssembler::bitXorConst(int rhs)
1126{
1127 pasm()->toInt32();
1128 pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1129 pasm()->setAccumulatorTag(IntegerTag);
1130}
1131
1132void BaselineAssembler::ushrConst(int rhs)
1133{
1134 rhs &= 0x1f;
1135 pasm()->toInt32();
1136 if (rhs) {
1137 // a non zero shift will always give a number encodable as an int
1138 pasm()->urshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1139 pasm()->setAccumulatorTag(IntegerTag);
1140 } else {
1141 // shift with 0 can lead to a negative result
1142 auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
1143 PlatformAssembler::AccumulatorRegisterValue,
1144 TrustedImm32(0));
1145 pasm()->setAccumulatorTag(IntegerTag);
1146 auto done = pasm()->jump();
1147
1148 doubleEncode.link(pasm());
1149 pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
1150 PlatformAssembler::FPScratchRegister,
1151 PlatformAssembler::ScratchRegister);
1152 pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
1153 done.link(pasm());
1154 }
1155}
1156
1157void BaselineAssembler::shrConst(int rhs)
1158{
1159 rhs &= 0x1f;
1160 pasm()->toInt32();
1161 if (rhs)
1162 pasm()->rshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1163 pasm()->setAccumulatorTag(IntegerTag);
1164}
1165
1166void BaselineAssembler::shlConst(int rhs)
1167{
1168 rhs &= 0x1f;
1169 pasm()->toInt32();
1170 if (rhs)
1171 pasm()->lshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
1172 pasm()->setAccumulatorTag(IntegerTag);
1173}
1174
1175void BaselineAssembler::mul(int lhs)
1176{
1177 auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
1178 auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
1179 PlatformAssembler::AccumulatorRegisterValue,
1180 PlatformAssembler::ScratchRegister);
1181 pasm()->setAccumulatorTag(IntegerTag,
1182 PlatformAssembler::ScratchRegister);
1183 return overflowed;
1184 });
1185
1186 // slow path:
1187 saveAccumulatorInFrame();
1188 pasm()->prepareCallWithArgCount(2);
1189 pasm()->passAccumulatorAsArg(1);
1190 pasm()->passJSSlotAsArg(lhs, 0);
1191 ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator);
1192 checkException();
1193
1194 // done.
1195 done.link(pasm());
1196}
1197
1198void BaselineAssembler::div(int lhs)
1199{
1200 saveAccumulatorInFrame();
1201 pasm()->prepareCallWithArgCount(2);
1202 pasm()->passAccumulatorAsArg(1);
1203 pasm()->passJSSlotAsArg(lhs, 0);
1204 ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator);
1205 checkException();
1206}
1207
1208void BaselineAssembler::mod(int lhs)
1209{
1210 saveAccumulatorInFrame();
1211 pasm()->prepareCallWithArgCount(2);
1212 pasm()->passAccumulatorAsArg(1);
1213 pasm()->passJSSlotAsArg(lhs, 0);
1214 ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator);
1215 checkException();
1216}
1217
1218void BaselineAssembler::sub(int lhs)
1219{
1220 auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
1221 auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
1222 PlatformAssembler::AccumulatorRegisterValue,
1223 PlatformAssembler::ScratchRegister);
1224 pasm()->setAccumulatorTag(IntegerTag,
1225 PlatformAssembler::ScratchRegister);
1226 return overflowed;
1227 });
1228
1229 // slow path:
1230 saveAccumulatorInFrame();
1231 pasm()->prepareCallWithArgCount(2);
1232 pasm()->passAccumulatorAsArg(1);
1233 pasm()->passJSSlotAsArg(lhs, 0);
1234 ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator);
1235 checkException();
1236
1237 // done.
1238 done.link(pasm());
1239}
1240
1241void BaselineAssembler::cmpeqNull()
1242{
1243 pasm()->isNullOrUndefined();
1244 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1245}
1246
1247void BaselineAssembler::cmpneNull()
1248{
1249 pasm()->isNullOrUndefined();
1250 pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
1251 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1252}
1253
1254void BaselineAssembler::cmpeqInt(int lhs)
1255{
1256 auto isIntOrBool = pasm()->isIntOrBool();
1257 saveAccumulatorInFrame();
1258 pasm()->pushValueAligned(Encode(lhs));
1259 if (PlatformAssembler::ArgInRegCount < 2)
1260 pasm()->push(PlatformAssembler::StackPointerRegister);
1261 else
1262 pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
1263 pasm()->pushAccumulatorAsArg(0);
1264 pasm()->callRuntimeUnchecked((void*)Runtime::Equal::call);
1265 pasm()->saveReturnValueInAccumulator();
1266 if (PlatformAssembler::ArgInRegCount < 2)
1267 pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
1268 pasm()->popValueAligned();
1269 auto done = pasm()->jump();
1270 isIntOrBool.link(pasm());
1271 pasm()->compare32(PlatformAssembler::Equal, PlatformAssembler::AccumulatorRegisterValue,
1272 TrustedImm32(lhs),
1273 PlatformAssembler::AccumulatorRegisterValue);
1274 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1275 done.link(pasm());
1276}
1277
1278void BaselineAssembler::cmpneInt(int lhs)
1279{
1280 auto isIntOrBool = pasm()->isIntOrBool();
1281 saveAccumulatorInFrame();
1282 pasm()->pushValueAligned(Encode(lhs));
1283 if (PlatformAssembler::ArgInRegCount < 2)
1284 pasm()->push(PlatformAssembler::StackPointerRegister);
1285 else
1286 pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
1287 pasm()->pushAccumulatorAsArg(0);
1288 pasm()->callRuntimeUnchecked((void*)Runtime::NotEqual::call);
1289 pasm()->saveReturnValueInAccumulator();
1290 if (PlatformAssembler::ArgInRegCount < 2)
1291 pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
1292 pasm()->popValueAligned();
1293 auto done = pasm()->jump();
1294 isIntOrBool.link(pasm());
1295 pasm()->compare32(PlatformAssembler::NotEqual, PlatformAssembler::AccumulatorRegisterValue,
1296 TrustedImm32(lhs),
1297 PlatformAssembler::AccumulatorRegisterValue);
1298 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1299 done.link(pasm());
1300}
1301
1302void BaselineAssembler::cmp(int cond, CmpFunc function, int lhs)
1303{
1304 auto c = static_cast<PlatformAssembler::RelationalCondition>(cond);
1305 auto done = pasm()->binopBothIntPath(regAddr(lhs), [this, c](){
1306 pasm()->compare32(c, PlatformAssembler::ScratchRegister,
1307 PlatformAssembler::AccumulatorRegisterValue,
1308 PlatformAssembler::AccumulatorRegisterValue);
1309 return PlatformAssembler::Jump();
1310 });
1311
1312 // slow path:
1313 saveAccumulatorInFrame();
1314 pasm()->prepareCallWithArgCount(2);
1315 pasm()->passAccumulatorAsArg(1);
1316 pasm()->passJSSlotAsArg(lhs, 0);
1317
1318 callRuntime(reinterpret_cast<void*>(function), CallResultDestination::InAccumulator);
1319 checkException();
1320
1321 // done.
1322 done.link(pasm());
1323 pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
1324}
1325
1326void BaselineAssembler::cmpeq(int lhs)
1327{
1328 cmp(PlatformAssembler::Equal, &Runtime::CompareEqual::call, lhs);
1329}
1330
1331void BaselineAssembler::cmpne(int lhs)
1332{
1333 cmp(PlatformAssembler::NotEqual, &Runtime::CompareNotEqual::call, lhs);
1334}
1335
1336void BaselineAssembler::cmpgt(int lhs)
1337{
1338 cmp(PlatformAssembler::GreaterThan, &Runtime::CompareGreaterThan::call, lhs);
1339}
1340
1341void BaselineAssembler::cmpge(int lhs)
1342{
1343 cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::CompareGreaterEqual::call, lhs);
1344}
1345
1346void BaselineAssembler::cmplt(int lhs)
1347{
1348 cmp(PlatformAssembler::LessThan, &Runtime::CompareLessThan::call, lhs);
1349}
1350
1351void BaselineAssembler::cmple(int lhs)
1352{
1353 cmp(PlatformAssembler::LessThanOrEqual, &Runtime::CompareLessEqual::call, lhs);
1354}
1355
1356void BaselineAssembler::cmpStrictEqual(int lhs)
1357{
1358 cmp(PlatformAssembler::Equal, &Runtime::CompareStrictEqual::call, lhs);
1359}
1360
1361void BaselineAssembler::cmpStrictNotEqual(int lhs)
1362{
1363 cmp(PlatformAssembler::NotEqual, &Runtime::CompareStrictNotEqual::call, lhs);
1364}
1365
1366int BaselineAssembler::jump(int offset)
1367{
1368 pasm()->addJumpToOffset(pasm()->jump(), offset);
1369 return offset;
1370}
1371
1372int BaselineAssembler::jumpTrue(int offset)
1373{
1374 pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
1375 auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
1376 pasm()->addJumpToOffset(jump, offset);
1377 });
1378 return offset;
1379}
1380
1381int BaselineAssembler::jumpFalse(int offset)
1382{
1383 pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
1384 auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
1385 pasm()->addJumpToOffset(jump, offset);
1386 });
1387 return offset;
1388}
1389
1390int BaselineAssembler::jumpNoException(int offset)
1391{
1392 auto jump = pasm()->branch32(
1393 PlatformAssembler::Equal,
1394 PlatformAssembler::Address(PlatformAssembler::EngineRegister,
1395 offsetof(EngineBase, hasException)),
1396 TrustedImm32(0));
1397 pasm()->addJumpToOffset(jump, offset);
1398 return offset;
1399}
1400
1401int BaselineAssembler::jumpNotUndefined(int offset)
1402{
1403 pasm()->jumpNotUndefined(offset);
1404 return offset;
1405}
1406
1407int BaselineAssembler::jumpEqNull(int offset)
1408{
1409 saveAccumulatorInFrame();
1410 cmpeqNull();
1411
1412 pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
1413 auto isFalse = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
1414 loadValue(Encode::undefined());
1415 pasm()->addJumpToOffset(pasm()->jump(), offset);
1416 isFalse.link(pasm());
1417 loadAccumulatorFromFrame();
1418 });
1419
1420 return offset;
1421}
1422
1423
1424void BaselineAssembler::prepareCallWithArgCount(int argc)
1425{
1426 pasm()->prepareCallWithArgCount(argc);
1427}
1428
1429void BaselineAssembler::storeInstructionPointer(int instructionOffset)
1430{
1431 pasm()->storeInstructionPointer(instructionOffset);
1432}
1433
1434void BaselineAssembler::passAccumulatorAsArg(int arg)
1435{
1436 pasm()->passAccumulatorAsArg(arg);
1437}
1438
1439void BaselineAssembler::passFunctionAsArg(int arg)
1440{
1441 pasm()->passFunctionAsArg(arg);
1442}
1443
1444void BaselineAssembler::passEngineAsArg(int arg)
1445{
1446 pasm()->passEngineAsArg(arg);
1447}
1448
1449void BaselineAssembler::passJSSlotAsArg(int reg, int arg)
1450{
1451 pasm()->passJSSlotAsArg(reg, arg);
1452}
1453
1454void BaselineAssembler::passCppFrameAsArg(int arg)
1455{
1456 pasm()->passCppFrameAsArg(arg);
1457}
1458
1459void BaselineAssembler::passInt32AsArg(int value, int arg)
1460{
1461 pasm()->passInt32AsArg(value, arg);
1462}
1463
1464void BaselineAssembler::passPointerAsArg(void *ptr, int arg)
1465{
1466 pasm()->passPointerAsArg(ptr, arg);
1467}
1468
1469void BaselineAssembler::callRuntime(const void *funcPtr, CallResultDestination dest)
1470{
1471 pasm()->callRuntime(funcPtr, dest);
1472}
1473
1474void BaselineAssembler::saveAccumulatorInFrame()
1475{
1476 pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
1477 offsetof(CallData, accumulator)));
1478}
1479
1480void BaselineAssembler::loadAccumulatorFromFrame()
1481{
1482 pasm()->loadAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
1483 offsetof(CallData, accumulator)));
1484}
1485
1486static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(JSTypesStackFrame *frame, ExecutionEngine *engine)
1487{
1488 return Runtime::TailCall::call(frame, engine);
1489}
1490
1491void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
1492{
1493 Address tos = pasm()->jsAlloca(4);
1494
1495 int32_t argcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argc;
1496 int32_t argvOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argv;
1497 int32_t thisOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_thisObject;
1498 int32_t funcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_function;
1499
1500 pasm()->storeInt32AsValue(argc, Address(tos.base, argcOffset));
1501 pasm()->storeInt32AsValue(argv, Address(tos.base, argvOffset));
1502 pasm()->moveReg(regAddr(thisObject), Address(tos.base, thisOffset));
1503 pasm()->moveReg(regAddr(func), Address(tos.base, funcOffset));
1504 pasm()->tailCallRuntime(
1505 reinterpret_cast<void *>(TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing),
1506 "TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing");
1507}
1508
1509void BaselineAssembler::checkException()
1510{
1511 pasm()->checkException();
1512}
1513
1514void BaselineAssembler::gotoCatchException()
1515{
1516 pasm()->addCatchyJump(pasm()->jump());
1517}
1518
1519void BaselineAssembler::getException()
1520{
1521 Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
1522
1523 Address hasExceptionAddr(PlatformAssembler::EngineRegister,
1524 offsetof(EngineBase, hasException));
1525 PlatformAssembler::Jump nope = pasm()->branch8(PlatformAssembler::Equal,
1526 hasExceptionAddr,
1527 TrustedImm32(0));
1528 pasm()->loadPtr(Address(PlatformAssembler::EngineRegister,
1529 offsetof(EngineBase, exceptionValue)),
1530 PlatformAssembler::ScratchRegister);
1531 pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
1532 pasm()->store8(TrustedImm32(0), hasExceptionAddr);
1533 auto done = pasm()->jump();
1534 nope.link(pasm());
1535 pasm()->loadValue(Value::emptyValue().asReturnedValue());
1536
1537 done.link(pasm());
1538}
1539
1540void BaselineAssembler::setException()
1541{
1542 auto noException = pasm()->jumpEmpty();
1543 Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
1544 pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
1545 pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
1546 addr.offset = offsetof(EngineBase, hasException);
1547 Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
1548 pasm()->store8(TrustedImm32(1), addr);
1549 noException.link(pasm());
1550}
1551
1552int BaselineAssembler::setUnwindHandler(int offset)
1553{
1554 auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
1555 pasm()->addEHTarget(l, offset);
1556 return offset;
1557}
1558
1559
1560void BaselineAssembler::clearUnwindHandler()
1561{
1562 pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
1563}
1564
1565void JIT::BaselineAssembler::unwindDispatch()
1566{
1567 checkException();
1568 pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
1569 auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
1570 pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
1571 pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)));
1572 auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
1573 gotoCatchException();
1574 jump.link(pasm());
1575
1576 pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
1577 pasm()->jump(PlatformAssembler::ScratchRegister);
1578
1579 noUnwind.link(pasm());
1580}
1581
1582int JIT::BaselineAssembler::unwindToLabel(int level, int offset)
1583{
1584 auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLabel)));
1585 pasm()->addEHTarget(l, offset);
1586 pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)));
1587 gotoCatchException();
1588 return offset;
1589}
1590
1591void BaselineAssembler::pushCatchContext(int index, int name)
1592{
1593 pasm()->prepareCallWithArgCount(3);
1594 pasm()->passInt32AsArg(name, 2);
1595 pasm()->passInt32AsArg(index, 1);
1596 pasm()->passEngineAsArg(0);
1597 ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore);
1598}
1599
1600void BaselineAssembler::popContext()
1601{
1602 Heap::CallContext ctx;
1603 Q_UNUSED(ctx);
1604 pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
1605 pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
1606 pasm()->storeHeapObject(PlatformAssembler::ScratchRegister, regAddr(CallData::Context));
1607}
1608
1609void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
1610{
1611 auto valueIsAliveJump = pasm()->jumpNotEmpty();
1612 storeInstructionPointer(offsetForSavedIP);
1613 prepareCallWithArgCount(2);
1614 passInt32AsArg(variableName, 1);
1615 passEngineAsArg(0);
1616 ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore);
1617 gotoCatchException();
1618 valueIsAliveJump.link(pasm());
1619}
1620
1621void BaselineAssembler::ret()
1622{
1623 pasm()->generateFunctionExit();
1624}
1625
1626} // JIT namespace
1627} // QV4 namepsace
1628
1629QT_END_NAMESPACE
1630
1631#endif // QT_CONFIG(qml_jit)