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 // check if we need a write barrier
887 auto skipBarrier = pasm()->branch8(
888 cond: PlatformAssembler::Equal,
889 left: PlatformAssembler::Address(PlatformAssembler::EngineRegister,
890 offsetof(EngineBase, isGCOngoing)),
891 right: TrustedImm32(0));
892 saveAccumulatorInFrame();
893 // if so, do a runtime call
894 pasm()->prepareCallWithArgCount(argc: 1);
895 pasm()->passAccumulatorAsArg(arg: 0);
896 pasm()->callRuntime(funcPtr: (void*)Runtime::MarkCustom::call, dest: CallResultDestination::Ignore);
897 loadAccumulatorFromFrame();
898 skipBarrier.link(pasm());
899
900}
901
902void BaselineAssembler::loadString(int stringId)
903{
904 pasm()->loadString(stringId);
905}
906
907void BaselineAssembler::loadValue(ReturnedValue value)
908{
909 pasm()->loadValue(value);
910}
911
912void BaselineAssembler::storeHeapObject(int reg)
913{
914 pasm()->storeHeapObject(source: PlatformAssembler::ReturnValueRegisterValue, addr: regAddr(reg));
915}
916
917void BaselineAssembler::loadImport(int index)
918{
919 Address addr = pasm()->loadCompilationUnitPtr(target: PlatformAssembler::ScratchRegister);
920 addr.offset = offsetof(QV4::CompilationUnitRuntimeData, imports);
921 pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister);
922 addr.offset = index * int(sizeof(QV4::Value*));
923 pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister);
924 pasm()->loadAccumulator(addr: Address(PlatformAssembler::ScratchRegister));
925}
926
927void BaselineAssembler::toNumber()
928{
929 pasm()->toNumber();
930}
931
932void BaselineAssembler::uminus()
933{
934 saveAccumulatorInFrame();
935 pasm()->prepareCallWithArgCount(argc: 1);
936 pasm()->passAccumulatorAsArg(arg: 0);
937 ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator);
938 checkException();
939}
940
941void BaselineAssembler::ucompl()
942{
943 pasm()->toInt32();
944 pasm()->xor32(imm: TrustedImm32(-1), dest: PlatformAssembler::AccumulatorRegisterValue);
945 pasm()->setAccumulatorTag(tag: IntegerTag);
946}
947
948static ReturnedValue incHelper(const Value v)
949{
950 double d;
951 if (Q_LIKELY(v.isDouble()))
952 d = v.doubleValue();
953 else
954 d = v.toNumberImpl();
955 return Encode(d + 1.);
956}
957
958void BaselineAssembler::inc()
959{
960 auto done = pasm()->unopIntPath(fastPath: [this](){
961 auto overflowed = pasm()->branchAdd32(cond: PlatformAssembler::Overflow,
962 src: PlatformAssembler::AccumulatorRegisterValue,
963 imm: TrustedImm32(1),
964 dest: PlatformAssembler::ScratchRegister);
965 pasm()->setAccumulatorTag(tag: IntegerTag, sourceReg: PlatformAssembler::ScratchRegister);
966 return overflowed;
967 });
968
969 // slow path:
970 pasm()->callWithAccumulatorByValueAsFirstArgument(doCall: [this]() {
971 pasm()->callHelper(incHelper);
972 pasm()->saveReturnValueInAccumulator();
973 });
974 checkException();
975
976 // done.
977 done.link(pasm());
978}
979
980static ReturnedValue decHelper(const Value v)
981{
982 double d;
983 if (Q_LIKELY(v.isDouble()))
984 d = v.doubleValue();
985 else
986 d = v.toNumberImpl();
987 return Encode(d - 1.);
988}
989
990void BaselineAssembler::dec()
991{
992 auto done = pasm()->unopIntPath(fastPath: [this](){
993 auto overflowed = pasm()->branchSub32(cond: PlatformAssembler::Overflow,
994 src1: PlatformAssembler::AccumulatorRegisterValue,
995 src2: TrustedImm32(1),
996 dest: PlatformAssembler::ScratchRegister);
997 pasm()->setAccumulatorTag(tag: IntegerTag, sourceReg: PlatformAssembler::ScratchRegister);
998 return overflowed;
999 });
1000
1001 // slow path:
1002 pasm()->callWithAccumulatorByValueAsFirstArgument(doCall: [this]() {
1003 pasm()->callHelper(decHelper);
1004 pasm()->saveReturnValueInAccumulator();
1005 });
1006 checkException();
1007
1008 // done.
1009 done.link(pasm());
1010}
1011
1012void BaselineAssembler::unot()
1013{
1014 pasm()->toBoolean(continuation: [this](PlatformAssembler::RegisterID resultReg){
1015 pasm()->compare32(cond: PlatformAssembler::Equal, left: resultReg,
1016 right: TrustedImm32(0), dest: PlatformAssembler::AccumulatorRegisterValue);
1017 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1018 });
1019}
1020
1021void BaselineAssembler::add(int lhs)
1022{
1023 auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){
1024 auto overflowed = pasm()->branchAdd32(cond: PlatformAssembler::Overflow,
1025 src: PlatformAssembler::AccumulatorRegisterValue,
1026 dest: PlatformAssembler::ScratchRegister);
1027 pasm()->setAccumulatorTag(tag: IntegerTag,
1028 sourceReg: PlatformAssembler::ScratchRegister);
1029 return overflowed;
1030 });
1031
1032 // slow path:
1033 saveAccumulatorInFrame();
1034 pasm()->prepareCallWithArgCount(argc: 3);
1035 pasm()->passAccumulatorAsArg(arg: 2);
1036 pasm()->passJSSlotAsArg(reg: lhs, arg: 1);
1037 pasm()->passEngineAsArg(arg: 0);
1038 ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator);
1039 checkException();
1040
1041 // done.
1042 done.link(pasm());
1043}
1044
1045void BaselineAssembler::bitAnd(int lhs)
1046{
1047 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1048 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1049 pasm()->and32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1050 pasm()->setAccumulatorTag(tag: IntegerTag);
1051}
1052
1053void BaselineAssembler::bitOr(int lhs)
1054{
1055 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1056 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1057 pasm()->or32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1058 pasm()->setAccumulatorTag(tag: IntegerTag);
1059}
1060
1061void BaselineAssembler::bitXor(int lhs)
1062{
1063 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1064 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1065 pasm()->xor32(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1066 pasm()->setAccumulatorTag(tag: IntegerTag);
1067}
1068
1069void BaselineAssembler::ushr(int lhs)
1070{
1071 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1072 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1073 pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue);
1074 pasm()->urshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister);
1075 pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1076 auto doubleEncode = pasm()->branch32(cond: PlatformAssembler::LessThan,
1077 left: PlatformAssembler::AccumulatorRegisterValue,
1078 right: TrustedImm32(0));
1079 pasm()->setAccumulatorTag(tag: IntegerTag);
1080 auto done = pasm()->jump();
1081
1082 doubleEncode.link(pasm());
1083 pasm()->convertUInt32ToDouble(src: PlatformAssembler::AccumulatorRegisterValue,
1084 dest: PlatformAssembler::FPScratchRegister,
1085 PlatformAssembler::ScratchRegister);
1086 pasm()->encodeDoubleIntoAccumulator(src: PlatformAssembler::FPScratchRegister);
1087 done.link(pasm());
1088}
1089
1090void BaselineAssembler::shr(int lhs)
1091{
1092 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1093 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1094 pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue);
1095 pasm()->rshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister);
1096 pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1097 pasm()->setAccumulatorTag(tag: IntegerTag);
1098}
1099
1100void BaselineAssembler::shl(int lhs)
1101{
1102 PlatformAssembler::Address lhsAddr = regAddr(reg: lhs);
1103 pasm()->toInt32LhsAcc(lhs: lhsAddr, lhsTarget: PlatformAssembler::ScratchRegister);
1104 pasm()->and32(imm: TrustedImm32(0x1f), dest: PlatformAssembler::AccumulatorRegisterValue);
1105 pasm()->lshift32(shift_amount: PlatformAssembler::AccumulatorRegisterValue, dest: PlatformAssembler::ScratchRegister);
1106 pasm()->move(src: PlatformAssembler::ScratchRegister, dest: PlatformAssembler::AccumulatorRegisterValue);
1107 pasm()->setAccumulatorTag(tag: IntegerTag);
1108}
1109
1110void BaselineAssembler::bitAndConst(int rhs)
1111{
1112 pasm()->toInt32();
1113 pasm()->and32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1114 pasm()->setAccumulatorTag(tag: IntegerTag);
1115}
1116
1117void BaselineAssembler::bitOrConst(int rhs)
1118{
1119 pasm()->toInt32();
1120 pasm()->or32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1121 pasm()->setAccumulatorTag(tag: IntegerTag);
1122}
1123
1124void BaselineAssembler::bitXorConst(int rhs)
1125{
1126 pasm()->toInt32();
1127 pasm()->xor32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1128 pasm()->setAccumulatorTag(tag: IntegerTag);
1129}
1130
1131void BaselineAssembler::ushrConst(int rhs)
1132{
1133 rhs &= 0x1f;
1134 pasm()->toInt32();
1135 if (rhs) {
1136 // a non zero shift will always give a number encodable as an int
1137 pasm()->urshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1138 pasm()->setAccumulatorTag(tag: IntegerTag);
1139 } else {
1140 // shift with 0 can lead to a negative result
1141 auto doubleEncode = pasm()->branch32(cond: PlatformAssembler::LessThan,
1142 left: PlatformAssembler::AccumulatorRegisterValue,
1143 right: TrustedImm32(0));
1144 pasm()->setAccumulatorTag(tag: IntegerTag);
1145 auto done = pasm()->jump();
1146
1147 doubleEncode.link(pasm());
1148 pasm()->convertUInt32ToDouble(src: PlatformAssembler::AccumulatorRegisterValue,
1149 dest: PlatformAssembler::FPScratchRegister,
1150 PlatformAssembler::ScratchRegister);
1151 pasm()->encodeDoubleIntoAccumulator(src: PlatformAssembler::FPScratchRegister);
1152 done.link(pasm());
1153 }
1154}
1155
1156void BaselineAssembler::shrConst(int rhs)
1157{
1158 rhs &= 0x1f;
1159 pasm()->toInt32();
1160 if (rhs)
1161 pasm()->rshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1162 pasm()->setAccumulatorTag(tag: IntegerTag);
1163}
1164
1165void BaselineAssembler::shlConst(int rhs)
1166{
1167 rhs &= 0x1f;
1168 pasm()->toInt32();
1169 if (rhs)
1170 pasm()->lshift32(imm: TrustedImm32(rhs), dest: PlatformAssembler::AccumulatorRegisterValue);
1171 pasm()->setAccumulatorTag(tag: IntegerTag);
1172}
1173
1174void BaselineAssembler::mul(int lhs)
1175{
1176 auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){
1177 auto overflowed = pasm()->branchMul32(cond: PlatformAssembler::Overflow,
1178 src: PlatformAssembler::AccumulatorRegisterValue,
1179 dest: PlatformAssembler::ScratchRegister);
1180 pasm()->setAccumulatorTag(tag: IntegerTag,
1181 sourceReg: PlatformAssembler::ScratchRegister);
1182 return overflowed;
1183 });
1184
1185 // slow path:
1186 saveAccumulatorInFrame();
1187 pasm()->prepareCallWithArgCount(argc: 2);
1188 pasm()->passAccumulatorAsArg(arg: 1);
1189 pasm()->passJSSlotAsArg(reg: lhs, arg: 0);
1190 ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator);
1191 checkException();
1192
1193 // done.
1194 done.link(pasm());
1195}
1196
1197void BaselineAssembler::div(int lhs)
1198{
1199 saveAccumulatorInFrame();
1200 pasm()->prepareCallWithArgCount(argc: 2);
1201 pasm()->passAccumulatorAsArg(arg: 1);
1202 pasm()->passJSSlotAsArg(reg: lhs, arg: 0);
1203 ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator);
1204 checkException();
1205}
1206
1207void BaselineAssembler::mod(int lhs)
1208{
1209 saveAccumulatorInFrame();
1210 pasm()->prepareCallWithArgCount(argc: 2);
1211 pasm()->passAccumulatorAsArg(arg: 1);
1212 pasm()->passJSSlotAsArg(reg: lhs, arg: 0);
1213 ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator);
1214 checkException();
1215}
1216
1217void BaselineAssembler::sub(int lhs)
1218{
1219 auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this](){
1220 auto overflowed = pasm()->branchSub32(cond: PlatformAssembler::Overflow,
1221 src: PlatformAssembler::AccumulatorRegisterValue,
1222 dest: PlatformAssembler::ScratchRegister);
1223 pasm()->setAccumulatorTag(tag: IntegerTag,
1224 sourceReg: PlatformAssembler::ScratchRegister);
1225 return overflowed;
1226 });
1227
1228 // slow path:
1229 saveAccumulatorInFrame();
1230 pasm()->prepareCallWithArgCount(argc: 2);
1231 pasm()->passAccumulatorAsArg(arg: 1);
1232 pasm()->passJSSlotAsArg(reg: lhs, arg: 0);
1233 ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator);
1234 checkException();
1235
1236 // done.
1237 done.link(pasm());
1238}
1239
1240void BaselineAssembler::cmpeqNull()
1241{
1242 pasm()->isNullOrUndefined();
1243 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1244}
1245
1246void BaselineAssembler::cmpneNull()
1247{
1248 pasm()->isNullOrUndefined();
1249 pasm()->xor32(imm: TrustedImm32(1), dest: PlatformAssembler::AccumulatorRegisterValue);
1250 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1251}
1252
1253void BaselineAssembler::cmpeqInt(int lhs)
1254{
1255 auto isIntOrBool = pasm()->isIntOrBool();
1256 saveAccumulatorInFrame();
1257 pasm()->pushValueAligned(v: Encode(lhs));
1258 if (PlatformAssembler::ArgInRegCount < 2)
1259 pasm()->push(src: PlatformAssembler::StackPointerRegister);
1260 else
1261 pasm()->move(src: PlatformAssembler::StackPointerRegister, pasm()->registerForArg(arg: 1));
1262 pasm()->pushAccumulatorAsArg(arg: 0);
1263 pasm()->callRuntimeUnchecked(funcPtr: (void*)Runtime::Equal::call);
1264 pasm()->saveReturnValueInAccumulator();
1265 if (PlatformAssembler::ArgInRegCount < 2)
1266 pasm()->addPtr(imm: TrustedImm32(2 * PlatformAssembler::PointerSize), srcDest: PlatformAssembler::StackPointerRegister);
1267 pasm()->popValueAligned();
1268 auto done = pasm()->jump();
1269 isIntOrBool.link(pasm());
1270 pasm()->compare32(cond: PlatformAssembler::Equal, left: PlatformAssembler::AccumulatorRegisterValue,
1271 right: TrustedImm32(lhs),
1272 dest: PlatformAssembler::AccumulatorRegisterValue);
1273 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1274 done.link(pasm());
1275}
1276
1277void BaselineAssembler::cmpneInt(int lhs)
1278{
1279 auto isIntOrBool = pasm()->isIntOrBool();
1280 saveAccumulatorInFrame();
1281 pasm()->pushValueAligned(v: Encode(lhs));
1282 if (PlatformAssembler::ArgInRegCount < 2)
1283 pasm()->push(src: PlatformAssembler::StackPointerRegister);
1284 else
1285 pasm()->move(src: PlatformAssembler::StackPointerRegister, pasm()->registerForArg(arg: 1));
1286 pasm()->pushAccumulatorAsArg(arg: 0);
1287 pasm()->callRuntimeUnchecked(funcPtr: (void*)Runtime::NotEqual::call);
1288 pasm()->saveReturnValueInAccumulator();
1289 if (PlatformAssembler::ArgInRegCount < 2)
1290 pasm()->addPtr(imm: TrustedImm32(2 * PlatformAssembler::PointerSize), srcDest: PlatformAssembler::StackPointerRegister);
1291 pasm()->popValueAligned();
1292 auto done = pasm()->jump();
1293 isIntOrBool.link(pasm());
1294 pasm()->compare32(cond: PlatformAssembler::NotEqual, left: PlatformAssembler::AccumulatorRegisterValue,
1295 right: TrustedImm32(lhs),
1296 dest: PlatformAssembler::AccumulatorRegisterValue);
1297 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1298 done.link(pasm());
1299}
1300
1301void BaselineAssembler::cmp(int cond, CmpFunc function, int lhs)
1302{
1303 auto c = static_cast<PlatformAssembler::RelationalCondition>(cond);
1304 auto done = pasm()->binopBothIntPath(lhsAddr: regAddr(reg: lhs), fastPath: [this, c](){
1305 pasm()->compare32(cond: c, left: PlatformAssembler::ScratchRegister,
1306 right: PlatformAssembler::AccumulatorRegisterValue,
1307 dest: PlatformAssembler::AccumulatorRegisterValue);
1308 return PlatformAssembler::Jump();
1309 });
1310
1311 // slow path:
1312 saveAccumulatorInFrame();
1313 pasm()->prepareCallWithArgCount(argc: 2);
1314 pasm()->passAccumulatorAsArg(arg: 1);
1315 pasm()->passJSSlotAsArg(reg: lhs, arg: 0);
1316
1317 callRuntime(funcPtr: reinterpret_cast<void*>(function), dest: CallResultDestination::InAccumulator);
1318 checkException();
1319
1320 // done.
1321 done.link(pasm());
1322 pasm()->setAccumulatorTag(tag: QV4::Value::ValueTypeInternal::Boolean);
1323}
1324
1325void BaselineAssembler::cmpeq(int lhs)
1326{
1327 cmp(cond: PlatformAssembler::Equal, function: &Runtime::CompareEqual::call, lhs);
1328}
1329
1330void BaselineAssembler::cmpne(int lhs)
1331{
1332 cmp(cond: PlatformAssembler::NotEqual, function: &Runtime::CompareNotEqual::call, lhs);
1333}
1334
1335void BaselineAssembler::cmpgt(int lhs)
1336{
1337 cmp(cond: PlatformAssembler::GreaterThan, function: &Runtime::CompareGreaterThan::call, lhs);
1338}
1339
1340void BaselineAssembler::cmpge(int lhs)
1341{
1342 cmp(cond: PlatformAssembler::GreaterThanOrEqual, function: &Runtime::CompareGreaterEqual::call, lhs);
1343}
1344
1345void BaselineAssembler::cmplt(int lhs)
1346{
1347 cmp(cond: PlatformAssembler::LessThan, function: &Runtime::CompareLessThan::call, lhs);
1348}
1349
1350void BaselineAssembler::cmple(int lhs)
1351{
1352 cmp(cond: PlatformAssembler::LessThanOrEqual, function: &Runtime::CompareLessEqual::call, lhs);
1353}
1354
1355void BaselineAssembler::cmpStrictEqual(int lhs)
1356{
1357 cmp(cond: PlatformAssembler::Equal, function: &Runtime::CompareStrictEqual::call, lhs);
1358}
1359
1360void BaselineAssembler::cmpStrictNotEqual(int lhs)
1361{
1362 cmp(cond: PlatformAssembler::NotEqual, function: &Runtime::CompareStrictNotEqual::call, lhs);
1363}
1364
1365int BaselineAssembler::jump(int offset)
1366{
1367 pasm()->addJumpToOffset(pasm()->jump(), offset);
1368 return offset;
1369}
1370
1371int BaselineAssembler::jumpTrue(int offset)
1372{
1373 pasm()->toBoolean(continuation: [this, offset](PlatformAssembler::RegisterID resultReg) {
1374 auto jump = pasm()->branch32(cond: PlatformAssembler::NotEqual, left: TrustedImm32(0), right: resultReg);
1375 pasm()->addJumpToOffset(jump, offset);
1376 });
1377 return offset;
1378}
1379
1380int BaselineAssembler::jumpFalse(int offset)
1381{
1382 pasm()->toBoolean(continuation: [this, offset](PlatformAssembler::RegisterID resultReg) {
1383 auto jump = pasm()->branch32(cond: PlatformAssembler::Equal, left: TrustedImm32(0), right: resultReg);
1384 pasm()->addJumpToOffset(jump, offset);
1385 });
1386 return offset;
1387}
1388
1389int BaselineAssembler::jumpNoException(int offset)
1390{
1391 auto jump = pasm()->branch32(
1392 cond: PlatformAssembler::Equal,
1393 left: PlatformAssembler::Address(PlatformAssembler::EngineRegister,
1394 offsetof(EngineBase, hasException)),
1395 right: TrustedImm32(0));
1396 pasm()->addJumpToOffset(jump, offset);
1397 return offset;
1398}
1399
1400int BaselineAssembler::jumpNotUndefined(int offset)
1401{
1402 pasm()->jumpNotUndefined(offset);
1403 return offset;
1404}
1405
1406int BaselineAssembler::jumpEqNull(int offset)
1407{
1408 saveAccumulatorInFrame();
1409 cmpeqNull();
1410
1411 pasm()->toBoolean(continuation: [this, offset](PlatformAssembler::RegisterID resultReg) {
1412 auto isFalse = pasm()->branch32(cond: PlatformAssembler::Equal, left: TrustedImm32(0), right: resultReg);
1413 loadValue(value: Encode::undefined());
1414 pasm()->addJumpToOffset(pasm()->jump(), offset);
1415 isFalse.link(pasm());
1416 loadAccumulatorFromFrame();
1417 });
1418
1419 return offset;
1420}
1421
1422
1423void BaselineAssembler::prepareCallWithArgCount(int argc)
1424{
1425 pasm()->prepareCallWithArgCount(argc);
1426}
1427
1428void BaselineAssembler::storeInstructionPointer(int instructionOffset)
1429{
1430 pasm()->storeInstructionPointer(instructionOffset);
1431}
1432
1433void BaselineAssembler::passAccumulatorAsArg(int arg)
1434{
1435 pasm()->passAccumulatorAsArg(arg);
1436}
1437
1438void BaselineAssembler::passFunctionAsArg(int arg)
1439{
1440 pasm()->passFunctionAsArg(arg);
1441}
1442
1443void BaselineAssembler::passEngineAsArg(int arg)
1444{
1445 pasm()->passEngineAsArg(arg);
1446}
1447
1448void BaselineAssembler::passJSSlotAsArg(int reg, int arg)
1449{
1450 pasm()->passJSSlotAsArg(reg, arg);
1451}
1452
1453void BaselineAssembler::passCppFrameAsArg(int arg)
1454{
1455 pasm()->passCppFrameAsArg(arg);
1456}
1457
1458void BaselineAssembler::passInt32AsArg(int value, int arg)
1459{
1460 pasm()->passInt32AsArg(value, arg);
1461}
1462
1463void BaselineAssembler::passPointerAsArg(void *ptr, int arg)
1464{
1465 pasm()->passPointerAsArg(ptr, arg);
1466}
1467
1468void BaselineAssembler::callRuntime(const void *funcPtr, CallResultDestination dest)
1469{
1470 pasm()->callRuntime(funcPtr, dest);
1471}
1472
1473void BaselineAssembler::saveAccumulatorInFrame()
1474{
1475 pasm()->storeAccumulator(addr: PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
1476 offsetof(CallData, accumulator)));
1477}
1478
1479void BaselineAssembler::loadAccumulatorFromFrame()
1480{
1481 pasm()->loadAccumulator(addr: PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
1482 offsetof(CallData, accumulator)));
1483}
1484
1485static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(JSTypesStackFrame *frame, ExecutionEngine *engine)
1486{
1487 return Runtime::TailCall::call(frame, engine);
1488}
1489
1490void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
1491{
1492 Address tos = pasm()->jsAlloca(slotCount: 4);
1493
1494 int32_t argcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argc;
1495 int32_t argvOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argv;
1496 int32_t thisOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_thisObject;
1497 int32_t funcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_function;
1498
1499 pasm()->storeInt32AsValue(srcInt: argc, destAddr: Address(tos.base, argcOffset));
1500 pasm()->storeInt32AsValue(srcInt: argv, destAddr: Address(tos.base, argvOffset));
1501 pasm()->moveReg(sourceRegAddress: regAddr(reg: thisObject), destRegAddress: Address(tos.base, thisOffset));
1502 pasm()->moveReg(sourceRegAddress: regAddr(reg: func), destRegAddress: Address(tos.base, funcOffset));
1503 pasm()->tailCallRuntime(
1504 funcPtr: reinterpret_cast<void *>(TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing),
1505 functionName: "TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing");
1506}
1507
1508void BaselineAssembler::checkException()
1509{
1510 pasm()->checkException();
1511}
1512
1513void BaselineAssembler::gotoCatchException()
1514{
1515 pasm()->addCatchyJump(pasm()->jump());
1516}
1517
1518void BaselineAssembler::getException()
1519{
1520 Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
1521
1522 Address hasExceptionAddr(PlatformAssembler::EngineRegister,
1523 offsetof(EngineBase, hasException));
1524 PlatformAssembler::Jump nope = pasm()->branch8(cond: PlatformAssembler::Equal,
1525 left: hasExceptionAddr,
1526 right: TrustedImm32(0));
1527 pasm()->loadPtr(address: Address(PlatformAssembler::EngineRegister,
1528 offsetof(EngineBase, exceptionValue)),
1529 dest: PlatformAssembler::ScratchRegister);
1530 pasm()->loadAccumulator(addr: Address(PlatformAssembler::ScratchRegister));
1531 pasm()->store8(imm: TrustedImm32(0), address: hasExceptionAddr);
1532 auto done = pasm()->jump();
1533 nope.link(pasm());
1534 pasm()->loadValue(value: Value::emptyValue().asReturnedValue());
1535
1536 done.link(pasm());
1537}
1538
1539void BaselineAssembler::setException()
1540{
1541 auto noException = pasm()->jumpEmpty();
1542 Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
1543 pasm()->loadPtr(address: addr, dest: PlatformAssembler::ScratchRegister);
1544 pasm()->storeAccumulator(addr: Address(PlatformAssembler::ScratchRegister));
1545 addr.offset = offsetof(EngineBase, hasException);
1546 Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
1547 pasm()->store8(imm: TrustedImm32(1), address: addr);
1548 noException.link(pasm());
1549}
1550
1551int BaselineAssembler::setUnwindHandler(int offset)
1552{
1553 auto l = pasm()->storePtrWithPatch(initialValue: TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
1554 pasm()->addEHTarget(label: l, offset);
1555 return offset;
1556}
1557
1558
1559void BaselineAssembler::clearUnwindHandler()
1560{
1561 pasm()->storePtr(imm: TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
1562}
1563
1564void JIT::BaselineAssembler::unwindDispatch()
1565{
1566 checkException();
1567 pasm()->load32(address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)), dest: PlatformAssembler::ScratchRegister);
1568 auto noUnwind = pasm()->branch32(cond: PlatformAssembler::Equal, left: PlatformAssembler::ScratchRegister, right: TrustedImm32(0));
1569 pasm()->sub32(imm: TrustedImm32(1), dest: PlatformAssembler::ScratchRegister);
1570 pasm()->store32(src: PlatformAssembler::ScratchRegister, address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)));
1571 auto jump = pasm()->branch32(cond: PlatformAssembler::Equal, left: PlatformAssembler::ScratchRegister, right: TrustedImm32(0));
1572 gotoCatchException();
1573 jump.link(pasm());
1574
1575 pasm()->loadPtr(address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLabel)), dest: PlatformAssembler::ScratchRegister);
1576 pasm()->jump(target: PlatformAssembler::ScratchRegister);
1577
1578 noUnwind.link(pasm());
1579}
1580
1581int JIT::BaselineAssembler::unwindToLabel(int level, int offset)
1582{
1583 auto l = pasm()->storePtrWithPatch(initialValue: TrustedImmPtr(nullptr), address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLabel)));
1584 pasm()->addEHTarget(label: l, offset);
1585 pasm()->store32(imm: TrustedImm32(level), address: Address(PlatformAssembler::CppStackFrameRegister, offsetof(JSTypesStackFrame, unwindLevel)));
1586 gotoCatchException();
1587 return offset;
1588}
1589
1590void BaselineAssembler::pushCatchContext(int index, int name)
1591{
1592 pasm()->prepareCallWithArgCount(argc: 3);
1593 pasm()->passInt32AsArg(value: name, arg: 2);
1594 pasm()->passInt32AsArg(value: index, arg: 1);
1595 pasm()->passEngineAsArg(arg: 0);
1596 ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore);
1597}
1598
1599void BaselineAssembler::popContext()
1600{
1601 Heap::CallContext ctx;
1602 Q_UNUSED(ctx);
1603 pasm()->loadPointerFromValue(addr: regAddr(reg: CallData::Context), dest: PlatformAssembler::ScratchRegister);
1604 pasm()->loadPtr(address: Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), dest: PlatformAssembler::ScratchRegister);
1605 pasm()->storeHeapObject(source: PlatformAssembler::ScratchRegister, addr: regAddr(reg: CallData::Context));
1606}
1607
1608void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
1609{
1610 auto valueIsAliveJump = pasm()->jumpNotEmpty();
1611 storeInstructionPointer(instructionOffset: offsetForSavedIP);
1612 prepareCallWithArgCount(argc: 2);
1613 passInt32AsArg(value: variableName, arg: 1);
1614 passEngineAsArg(arg: 0);
1615 ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore);
1616 gotoCatchException();
1617 valueIsAliveJump.link(pasm());
1618}
1619
1620void BaselineAssembler::ret()
1621{
1622 pasm()->generateFunctionExit();
1623}
1624
1625} // JIT namespace
1626} // QV4 namepsace
1627
1628QT_END_NAMESPACE
1629
1630#endif // QT_CONFIG(qml_jit)
1631

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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