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

source code of qtdeclarative/src/qml/jit/qv4baselineassembler.cpp