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

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