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