Try ripping out inferred types because it might be a performance improvement
[WebKit-https.git] / Source / JavaScriptCore / jit / AssemblyHelpers.cpp
1 /*
2  * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "AssemblyHelpers.h"
28
29 #if ENABLE(JIT)
30
31 #include "JITOperations.h"
32 #include "JSCInlines.h"
33 #include "LinkBuffer.h"
34 #include "MaxFrameExtentForSlowPathCall.h"
35 #include "SuperSampler.h"
36 #include "ThunkGenerators.h"
37
38 #if ENABLE(WEBASSEMBLY)
39 #include "WasmContextInlines.h"
40 #include "WasmMemoryInformation.h"
41 #endif
42
43 namespace JSC {
44
45 ExecutableBase* AssemblyHelpers::executableFor(const CodeOrigin& codeOrigin)
46 {
47     if (!codeOrigin.inlineCallFrame)
48         return m_codeBlock->ownerExecutable();
49     
50     return codeOrigin.inlineCallFrame->baselineCodeBlock->ownerExecutable();
51 }
52
53 AssemblyHelpers::Jump AssemblyHelpers::branchIfFastTypedArray(GPRReg baseGPR)
54 {
55     return branch32(
56         Equal,
57         Address(baseGPR, JSArrayBufferView::offsetOfMode()),
58         TrustedImm32(FastTypedArray));
59 }
60
61 AssemblyHelpers::Jump AssemblyHelpers::branchIfNotFastTypedArray(GPRReg baseGPR)
62 {
63     return branch32(
64         NotEqual,
65         Address(baseGPR, JSArrayBufferView::offsetOfMode()),
66         TrustedImm32(FastTypedArray));
67 }
68
69 void AssemblyHelpers::incrementSuperSamplerCount()
70 {
71     add32(TrustedImm32(1), AbsoluteAddress(bitwise_cast<const void*>(&g_superSamplerCount)));
72 }
73
74 void AssemblyHelpers::decrementSuperSamplerCount()
75 {
76     sub32(TrustedImm32(1), AbsoluteAddress(bitwise_cast<const void*>(&g_superSamplerCount)));
77 }
78     
79 void AssemblyHelpers::purifyNaN(FPRReg fpr)
80 {
81     MacroAssembler::Jump notNaN = branchIfNotNaN(fpr);
82     static const double NaN = PNaN;
83     loadDouble(TrustedImmPtr(&NaN), fpr);
84     notNaN.link(this);
85 }
86
87 #if ENABLE(SAMPLING_FLAGS)
88 void AssemblyHelpers::setSamplingFlag(int32_t flag)
89 {
90     ASSERT(flag >= 1);
91     ASSERT(flag <= 32);
92     or32(TrustedImm32(1u << (flag - 1)), AbsoluteAddress(SamplingFlags::addressOfFlags()));
93 }
94
95 void AssemblyHelpers::clearSamplingFlag(int32_t flag)
96 {
97     ASSERT(flag >= 1);
98     ASSERT(flag <= 32);
99     and32(TrustedImm32(~(1u << (flag - 1))), AbsoluteAddress(SamplingFlags::addressOfFlags()));
100 }
101 #endif
102
103 #if !ASSERT_DISABLED
104 #if USE(JSVALUE64)
105 void AssemblyHelpers::jitAssertIsInt32(GPRReg gpr)
106 {
107 #if CPU(X86_64) || CPU(ARM64)
108     Jump checkInt32 = branch64(BelowOrEqual, gpr, TrustedImm64(static_cast<uintptr_t>(0xFFFFFFFFu)));
109     abortWithReason(AHIsNotInt32);
110     checkInt32.link(this);
111 #else
112     UNUSED_PARAM(gpr);
113 #endif
114 }
115
116 void AssemblyHelpers::jitAssertIsJSInt32(GPRReg gpr)
117 {
118     Jump checkJSInt32 = branch64(AboveOrEqual, gpr, GPRInfo::tagTypeNumberRegister);
119     abortWithReason(AHIsNotJSInt32);
120     checkJSInt32.link(this);
121 }
122
123 void AssemblyHelpers::jitAssertIsJSNumber(GPRReg gpr)
124 {
125     Jump checkJSNumber = branchTest64(MacroAssembler::NonZero, gpr, GPRInfo::tagTypeNumberRegister);
126     abortWithReason(AHIsNotJSNumber);
127     checkJSNumber.link(this);
128 }
129
130 void AssemblyHelpers::jitAssertIsJSDouble(GPRReg gpr)
131 {
132     Jump checkJSInt32 = branch64(AboveOrEqual, gpr, GPRInfo::tagTypeNumberRegister);
133     Jump checkJSNumber = branchTest64(MacroAssembler::NonZero, gpr, GPRInfo::tagTypeNumberRegister);
134     checkJSInt32.link(this);
135     abortWithReason(AHIsNotJSDouble);
136     checkJSNumber.link(this);
137 }
138
139 void AssemblyHelpers::jitAssertIsCell(GPRReg gpr)
140 {
141     Jump checkCell = branchTest64(MacroAssembler::Zero, gpr, GPRInfo::tagMaskRegister);
142     abortWithReason(AHIsNotCell);
143     checkCell.link(this);
144 }
145
146 void AssemblyHelpers::jitAssertTagsInPlace()
147 {
148     Jump ok = branch64(Equal, GPRInfo::tagTypeNumberRegister, TrustedImm64(TagTypeNumber));
149     abortWithReason(AHTagTypeNumberNotInPlace);
150     breakpoint();
151     ok.link(this);
152     
153     ok = branch64(Equal, GPRInfo::tagMaskRegister, TrustedImm64(TagMask));
154     abortWithReason(AHTagMaskNotInPlace);
155     ok.link(this);
156 }
157 #elif USE(JSVALUE32_64)
158 void AssemblyHelpers::jitAssertIsInt32(GPRReg gpr)
159 {
160     UNUSED_PARAM(gpr);
161 }
162
163 void AssemblyHelpers::jitAssertIsJSInt32(GPRReg gpr)
164 {
165     Jump checkJSInt32 = branch32(Equal, gpr, TrustedImm32(JSValue::Int32Tag));
166     abortWithReason(AHIsNotJSInt32);
167     checkJSInt32.link(this);
168 }
169
170 void AssemblyHelpers::jitAssertIsJSNumber(GPRReg gpr)
171 {
172     Jump checkJSInt32 = branch32(Equal, gpr, TrustedImm32(JSValue::Int32Tag));
173     Jump checkJSDouble = branch32(Below, gpr, TrustedImm32(JSValue::LowestTag));
174     abortWithReason(AHIsNotJSNumber);
175     checkJSInt32.link(this);
176     checkJSDouble.link(this);
177 }
178
179 void AssemblyHelpers::jitAssertIsJSDouble(GPRReg gpr)
180 {
181     Jump checkJSDouble = branch32(Below, gpr, TrustedImm32(JSValue::LowestTag));
182     abortWithReason(AHIsNotJSDouble);
183     checkJSDouble.link(this);
184 }
185
186 void AssemblyHelpers::jitAssertIsCell(GPRReg gpr)
187 {
188     Jump checkCell = branch32(Equal, gpr, TrustedImm32(JSValue::CellTag));
189     abortWithReason(AHIsNotCell);
190     checkCell.link(this);
191 }
192
193 void AssemblyHelpers::jitAssertTagsInPlace()
194 {
195 }
196 #endif // USE(JSVALUE32_64)
197
198 void AssemblyHelpers::jitAssertHasValidCallFrame()
199 {
200     Jump checkCFR = branchTestPtr(Zero, GPRInfo::callFrameRegister, TrustedImm32(7));
201     abortWithReason(AHCallFrameMisaligned);
202     checkCFR.link(this);
203 }
204
205 void AssemblyHelpers::jitAssertIsNull(GPRReg gpr)
206 {
207     Jump checkNull = branchTestPtr(Zero, gpr);
208     abortWithReason(AHIsNotNull);
209     checkNull.link(this);
210 }
211
212 void AssemblyHelpers::jitAssertArgumentCountSane()
213 {
214     Jump ok = branch32(Below, payloadFor(CallFrameSlot::argumentCount), TrustedImm32(10000000));
215     abortWithReason(AHInsaneArgumentCount);
216     ok.link(this);
217 }
218
219 #endif // !ASSERT_DISABLED
220
221 void AssemblyHelpers::jitReleaseAssertNoException(VM& vm)
222 {
223     Jump noException;
224 #if USE(JSVALUE64)
225     noException = branchTest64(Zero, AbsoluteAddress(vm.addressOfException()));
226 #elif USE(JSVALUE32_64)
227     noException = branch32(Equal, AbsoluteAddress(vm.addressOfException()), TrustedImm32(0));
228 #endif
229     abortWithReason(JITUncoughtExceptionAfterCall);
230     noException.link(this);
231 }
232
233 void AssemblyHelpers::callExceptionFuzz(VM& vm)
234 {
235     if (!Options::useExceptionFuzz())
236         return;
237
238     EncodedJSValue* buffer = vm.exceptionFuzzingBuffer(sizeof(EncodedJSValue) * (GPRInfo::numberOfRegisters + FPRInfo::numberOfRegisters));
239
240     for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) {
241 #if USE(JSVALUE64)
242         store64(GPRInfo::toRegister(i), buffer + i);
243 #else
244         store32(GPRInfo::toRegister(i), buffer + i);
245 #endif
246     }
247     for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
248         move(TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0);
249         storeDouble(FPRInfo::toRegister(i), Address(GPRInfo::regT0));
250     }
251
252     // Set up one argument.
253 #if CPU(X86)
254     poke(GPRInfo::callFrameRegister, 0);
255 #else
256     move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
257 #endif
258     move(TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(operationExceptionFuzz)), GPRInfo::nonPreservedNonReturnGPR);
259     call(GPRInfo::nonPreservedNonReturnGPR, OperationPtrTag);
260
261     for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
262         move(TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0);
263         loadDouble(Address(GPRInfo::regT0), FPRInfo::toRegister(i));
264     }
265     for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) {
266 #if USE(JSVALUE64)
267         load64(buffer + i, GPRInfo::toRegister(i));
268 #else
269         load32(buffer + i, GPRInfo::toRegister(i));
270 #endif
271     }
272 }
273
274 AssemblyHelpers::Jump AssemblyHelpers::emitJumpIfException(VM& vm)
275 {
276     return emitExceptionCheck(vm, NormalExceptionCheck);
277 }
278
279 AssemblyHelpers::Jump AssemblyHelpers::emitExceptionCheck(VM& vm, ExceptionCheckKind kind, ExceptionJumpWidth width)
280 {
281     callExceptionFuzz(vm);
282
283     if (width == FarJumpWidth)
284         kind = (kind == NormalExceptionCheck ? InvertedExceptionCheck : NormalExceptionCheck);
285     
286     Jump result;
287 #if USE(JSVALUE64)
288     result = branchTest64(kind == NormalExceptionCheck ? NonZero : Zero, AbsoluteAddress(vm.addressOfException()));
289 #elif USE(JSVALUE32_64)
290     result = branch32(kind == NormalExceptionCheck ? NotEqual : Equal, AbsoluteAddress(vm.addressOfException()), TrustedImm32(0));
291 #endif
292     
293     if (width == NormalJumpWidth)
294         return result;
295
296     PatchableJump realJump = patchableJump();
297     result.link(this);
298     
299     return realJump.m_jump;
300 }
301
302 AssemblyHelpers::Jump AssemblyHelpers::emitNonPatchableExceptionCheck(VM& vm)
303 {
304     callExceptionFuzz(vm);
305
306     Jump result;
307 #if USE(JSVALUE64)
308     result = branchTest64(NonZero, AbsoluteAddress(vm.addressOfException()));
309 #elif USE(JSVALUE32_64)
310     result = branch32(NotEqual, AbsoluteAddress(vm.addressOfException()), TrustedImm32(0));
311 #endif
312     
313     return result;
314 }
315
316 void AssemblyHelpers::emitStoreStructureWithTypeInfo(AssemblyHelpers& jit, TrustedImmPtr structure, RegisterID dest)
317 {
318     const Structure* structurePtr = reinterpret_cast<const Structure*>(structure.m_value);
319 #if USE(JSVALUE64)
320     jit.store64(TrustedImm64(structurePtr->idBlob()), MacroAssembler::Address(dest, JSCell::structureIDOffset()));
321     if (!ASSERT_DISABLED) {
322         Jump correctStructure = jit.branch32(Equal, MacroAssembler::Address(dest, JSCell::structureIDOffset()), TrustedImm32(structurePtr->id()));
323         jit.abortWithReason(AHStructureIDIsValid);
324         correctStructure.link(&jit);
325
326         Jump correctIndexingType = jit.branch8(Equal, MacroAssembler::Address(dest, JSCell::indexingTypeAndMiscOffset()), TrustedImm32(structurePtr->indexingModeIncludingHistory()));
327         jit.abortWithReason(AHIndexingTypeIsValid);
328         correctIndexingType.link(&jit);
329
330         Jump correctType = jit.branch8(Equal, MacroAssembler::Address(dest, JSCell::typeInfoTypeOffset()), TrustedImm32(structurePtr->typeInfo().type()));
331         jit.abortWithReason(AHTypeInfoIsValid);
332         correctType.link(&jit);
333
334         Jump correctFlags = jit.branch8(Equal, MacroAssembler::Address(dest, JSCell::typeInfoFlagsOffset()), TrustedImm32(structurePtr->typeInfo().inlineTypeFlags()));
335         jit.abortWithReason(AHTypeInfoInlineTypeFlagsAreValid);
336         correctFlags.link(&jit);
337     }
338 #else
339     // Do a 32-bit wide store to initialize the cell's fields.
340     jit.store32(TrustedImm32(structurePtr->objectInitializationBlob()), MacroAssembler::Address(dest, JSCell::indexingTypeAndMiscOffset()));
341     jit.storePtr(structure, MacroAssembler::Address(dest, JSCell::structureIDOffset()));
342 #endif
343 }
344
345 void AssemblyHelpers::loadProperty(GPRReg object, GPRReg offset, JSValueRegs result)
346 {
347     Jump isInline = branch32(LessThan, offset, TrustedImm32(firstOutOfLineOffset));
348     
349     loadPtr(Address(object, JSObject::butterflyOffset()), result.payloadGPR());
350     neg32(offset);
351     signExtend32ToPtr(offset, offset);
352     Jump ready = jump();
353     
354     isInline.link(this);
355     addPtr(
356         TrustedImm32(
357             static_cast<int32_t>(sizeof(JSObject)) -
358             (static_cast<int32_t>(firstOutOfLineOffset) - 2) * static_cast<int32_t>(sizeof(EncodedJSValue))),
359         object, result.payloadGPR());
360     
361     ready.link(this);
362     
363     loadValue(
364         BaseIndex(
365             result.payloadGPR(), offset, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)),
366         result);
367 }
368
369 void AssemblyHelpers::emitLoadStructure(VM& vm, RegisterID source, RegisterID dest, RegisterID scratch)
370 {
371 #if USE(JSVALUE64)
372     ASSERT(dest != scratch);
373     load32(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest);
374     loadPtr(vm.heap.structureIDTable().base(), scratch);
375     loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest);
376 #else
377     UNUSED_PARAM(scratch);
378     UNUSED_PARAM(vm);
379     loadPtr(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest);
380 #endif
381 }
382
383 void AssemblyHelpers::makeSpaceOnStackForCCall()
384 {
385     unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), maxFrameExtentForSlowPathCall);
386     if (stackOffset)
387         subPtr(TrustedImm32(stackOffset), stackPointerRegister);
388 }
389
390 void AssemblyHelpers::reclaimSpaceOnStackForCCall()
391 {
392     unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), maxFrameExtentForSlowPathCall);
393     if (stackOffset)
394         addPtr(TrustedImm32(stackOffset), stackPointerRegister);
395 }
396
397 #if USE(JSVALUE64)
398 template<typename LoadFromHigh, typename StoreToHigh, typename LoadFromLow, typename StoreToLow>
399 void emitRandomThunkImpl(AssemblyHelpers& jit, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, FPRReg result, const LoadFromHigh& loadFromHigh, const StoreToHigh& storeToHigh, const LoadFromLow& loadFromLow, const StoreToLow& storeToLow)
400 {
401     // Inlined WeakRandom::advance().
402     // uint64_t x = m_low;
403     loadFromLow(scratch0);
404     // uint64_t y = m_high;
405     loadFromHigh(scratch1);
406     // m_low = y;
407     storeToLow(scratch1);
408
409     // x ^= x << 23;
410     jit.move(scratch0, scratch2);
411     jit.lshift64(AssemblyHelpers::TrustedImm32(23), scratch2);
412     jit.xor64(scratch2, scratch0);
413
414     // x ^= x >> 17;
415     jit.move(scratch0, scratch2);
416     jit.rshift64(AssemblyHelpers::TrustedImm32(17), scratch2);
417     jit.xor64(scratch2, scratch0);
418
419     // x ^= y ^ (y >> 26);
420     jit.move(scratch1, scratch2);
421     jit.rshift64(AssemblyHelpers::TrustedImm32(26), scratch2);
422     jit.xor64(scratch1, scratch2);
423     jit.xor64(scratch2, scratch0);
424
425     // m_high = x;
426     storeToHigh(scratch0);
427
428     // return x + y;
429     jit.add64(scratch1, scratch0);
430
431     // Extract random 53bit. [0, 53] bit is safe integer number ranges in double representation.
432     jit.move(AssemblyHelpers::TrustedImm64((1ULL << 53) - 1), scratch1);
433     jit.and64(scratch1, scratch0);
434     // Now, scratch0 is always in range of int64_t. Safe to convert it to double with cvtsi2sdq.
435     jit.convertInt64ToDouble(scratch0, result);
436
437     // Convert `(53bit double integer value) / (1 << 53)` to `(53bit double integer value) * (1.0 / (1 << 53))`.
438     // In latter case, `1.0 / (1 << 53)` will become a double value represented as (mantissa = 0 & exp = 970, it means 1e-(2**54)).
439     static const double scale = 1.0 / (1ULL << 53);
440
441     // Multiplying 1e-(2**54) with the double integer does not change anything of the mantissa part of the double integer.
442     // It just reduces the exp part of the given 53bit double integer.
443     // (Except for 0.0. This is specially handled and in this case, exp just becomes 0.)
444     // Now we get 53bit precision random double value in [0, 1).
445     jit.move(AssemblyHelpers::TrustedImmPtr(&scale), scratch1);
446     jit.mulDouble(AssemblyHelpers::Address(scratch1), result);
447 }
448
449 void AssemblyHelpers::emitRandomThunk(JSGlobalObject* globalObject, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, FPRReg result)
450 {
451     void* lowAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset();
452     void* highAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset();
453
454     auto loadFromHigh = [&](GPRReg high) {
455         load64(highAddress, high);
456     };
457     auto storeToHigh = [&](GPRReg high) {
458         store64(high, highAddress);
459     };
460     auto loadFromLow = [&](GPRReg low) {
461         load64(lowAddress, low);
462     };
463     auto storeToLow = [&](GPRReg low) {
464         store64(low, lowAddress);
465     };
466
467     emitRandomThunkImpl(*this, scratch0, scratch1, scratch2, result, loadFromHigh, storeToHigh, loadFromLow, storeToLow);
468 }
469
470 void AssemblyHelpers::emitRandomThunk(VM& vm, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, FPRReg result)
471 {
472     emitGetFromCallFrameHeaderPtr(CallFrameSlot::callee, scratch3);
473     emitLoadStructure(vm, scratch3, scratch3, scratch0);
474     loadPtr(Address(scratch3, Structure::globalObjectOffset()), scratch3);
475     // Now, scratch3 holds JSGlobalObject*.
476
477     auto loadFromHigh = [&](GPRReg high) {
478         load64(Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset()), high);
479     };
480     auto storeToHigh = [&](GPRReg high) {
481         store64(high, Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset()));
482     };
483     auto loadFromLow = [&](GPRReg low) {
484         load64(Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset()), low);
485     };
486     auto storeToLow = [&](GPRReg low) {
487         store64(low, Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset()));
488     };
489
490     emitRandomThunkImpl(*this, scratch0, scratch1, scratch2, result, loadFromHigh, storeToHigh, loadFromLow, storeToLow);
491 }
492 #endif
493
494 void AssemblyHelpers::emitAllocateWithNonNullAllocator(GPRReg resultGPR, const JITAllocator& allocator, GPRReg allocatorGPR, GPRReg scratchGPR, JumpList& slowPath)
495 {
496     if (Options::forceGCSlowPaths()) {
497         slowPath.append(jump());
498         return;
499     }
500
501     // NOTE, some invariants of this function:
502     // - When going to the slow path, we must leave resultGPR with zero in it.
503     // - We *can not* use RegisterSet::macroScratchRegisters on x86.
504     // - We *can* use RegisterSet::macroScratchRegisters on ARM.
505
506     Jump popPath;
507     Jump done;
508     
509     if (allocator.isConstant())
510         move(TrustedImmPtr(allocator.allocator().localAllocator()), allocatorGPR);
511
512     load32(Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfRemaining()), resultGPR);
513     popPath = branchTest32(Zero, resultGPR);
514     if (allocator.isConstant())
515         add32(TrustedImm32(-allocator.allocator().cellSize()), resultGPR, scratchGPR);
516     else {
517         move(resultGPR, scratchGPR);
518         sub32(Address(allocatorGPR, LocalAllocator::offsetOfCellSize()), scratchGPR);
519     }
520     negPtr(resultGPR);
521     store32(scratchGPR, Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfRemaining()));
522     Address payloadEndAddr = Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfPayloadEnd());
523     addPtr(payloadEndAddr, resultGPR);
524
525     done = jump();
526         
527     popPath.link(this);
528         
529     loadPtr(Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfScrambledHead()), resultGPR);
530     xorPtr(Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfSecret()), resultGPR);
531     slowPath.append(branchTestPtr(Zero, resultGPR));
532         
533     // The object is half-allocated: we have what we know is a fresh object, but
534     // it's still on the GC's free list.
535     loadPtr(Address(resultGPR), scratchGPR);
536     storePtr(scratchGPR, Address(allocatorGPR, LocalAllocator::offsetOfFreeList() + FreeList::offsetOfScrambledHead()));
537         
538     done.link(this);
539 }
540
541 void AssemblyHelpers::emitAllocate(GPRReg resultGPR, const JITAllocator& allocator, GPRReg allocatorGPR, GPRReg scratchGPR, JumpList& slowPath)
542 {
543     if (allocator.isConstant()) {
544         if (!allocator.allocator()) {
545             slowPath.append(jump());
546             return;
547         }
548     } else
549         slowPath.append(branchTestPtr(Zero, allocatorGPR));
550     emitAllocateWithNonNullAllocator(resultGPR, allocator, allocatorGPR, scratchGPR, slowPath);
551 }
552
553 void AssemblyHelpers::emitAllocateVariableSized(GPRReg resultGPR, CompleteSubspace& subspace, GPRReg allocationSize, GPRReg scratchGPR1, GPRReg scratchGPR2, JumpList& slowPath)
554 {
555     static_assert(!(MarkedSpace::sizeStep & (MarkedSpace::sizeStep - 1)), "MarkedSpace::sizeStep must be a power of two.");
556     
557     unsigned stepShift = getLSBSet(MarkedSpace::sizeStep);
558     
559     add32(TrustedImm32(MarkedSpace::sizeStep - 1), allocationSize, scratchGPR1);
560     urshift32(TrustedImm32(stepShift), scratchGPR1);
561     slowPath.append(branch32(Above, scratchGPR1, TrustedImm32(MarkedSpace::largeCutoff >> stepShift)));
562     move(TrustedImmPtr(subspace.allocatorForSizeStep()), scratchGPR2);
563     loadPtr(BaseIndex(scratchGPR2, scratchGPR1, timesPtr()), scratchGPR1);
564     
565     emitAllocate(resultGPR, JITAllocator::variable(), scratchGPR1, scratchGPR2, slowPath);
566 }
567
568 void AssemblyHelpers::restoreCalleeSavesFromEntryFrameCalleeSavesBuffer(EntryFrame*& topEntryFrame)
569 {
570 #if NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
571     RegisterAtOffsetList* allCalleeSaves = RegisterSet::vmCalleeSaveRegisterOffsets();
572     RegisterSet dontRestoreRegisters = RegisterSet::stackRegisters();
573     unsigned registerCount = allCalleeSaves->size();
574
575     GPRReg scratch = InvalidGPRReg;
576     unsigned scratchGPREntryIndex = 0;
577
578     // Use the first GPR entry's register as our scratch.
579     for (unsigned i = 0; i < registerCount; i++) {
580         RegisterAtOffset entry = allCalleeSaves->at(i);
581         if (dontRestoreRegisters.get(entry.reg()))
582             continue;
583         if (entry.reg().isGPR()) {
584             scratchGPREntryIndex = i;
585             scratch = entry.reg().gpr();
586             break;
587         }
588     }
589     ASSERT(scratch != InvalidGPRReg);
590
591     loadPtr(&topEntryFrame, scratch);
592     addPtr(TrustedImm32(EntryFrame::calleeSaveRegistersBufferOffset()), scratch);
593
594     // Restore all callee saves except for the scratch.
595     for (unsigned i = 0; i < registerCount; i++) {
596         RegisterAtOffset entry = allCalleeSaves->at(i);
597         if (dontRestoreRegisters.get(entry.reg()))
598             continue;
599         if (entry.reg().isGPR()) {
600             if (i != scratchGPREntryIndex)
601                 loadPtr(Address(scratch, entry.offset()), entry.reg().gpr());
602         } else
603             loadDouble(Address(scratch, entry.offset()), entry.reg().fpr());
604     }
605
606     // Restore the callee save value of the scratch.
607     RegisterAtOffset entry = allCalleeSaves->at(scratchGPREntryIndex);
608     ASSERT(!dontRestoreRegisters.get(entry.reg()));
609     ASSERT(entry.reg().isGPR());
610     ASSERT(scratch == entry.reg().gpr());
611     loadPtr(Address(scratch, entry.offset()), scratch);
612 #else
613     UNUSED_PARAM(topEntryFrame);
614 #endif
615 }
616
617 void AssemblyHelpers::emitDumbVirtualCall(VM& vm, CallLinkInfo* info)
618 {
619     move(TrustedImmPtr(info), GPRInfo::regT2);
620     Call call = nearCall();
621     addLinkTask(
622         [=, &vm] (LinkBuffer& linkBuffer) {
623             MacroAssemblerCodeRef<JITStubRoutinePtrTag> virtualThunk = virtualThunkFor(&vm, *info);
624             info->setSlowStub(createJITStubRoutine(virtualThunk, vm, nullptr, true));
625             linkBuffer.link(call, CodeLocationLabel<JITStubRoutinePtrTag>(virtualThunk.code()));
626         });
627 }
628
629 #if USE(JSVALUE64)
630 void AssemblyHelpers::wangsInt64Hash(GPRReg inputAndResult, GPRReg scratch)
631 {
632     GPRReg input = inputAndResult;
633     // key += ~(key << 32);
634     move(input, scratch);
635     lshift64(TrustedImm32(32), scratch);
636     not64(scratch);
637     add64(scratch, input);
638     // key ^= (key >> 22);
639     move(input, scratch);
640     urshift64(TrustedImm32(22), scratch);
641     xor64(scratch, input);
642     // key += ~(key << 13);
643     move(input, scratch);
644     lshift64(TrustedImm32(13), scratch);
645     not64(scratch);
646     add64(scratch, input);
647     // key ^= (key >> 8);
648     move(input, scratch);
649     urshift64(TrustedImm32(8), scratch);
650     xor64(scratch, input);
651     // key += (key << 3);
652     move(input, scratch);
653     lshift64(TrustedImm32(3), scratch);
654     add64(scratch, input);
655     // key ^= (key >> 15);
656     move(input, scratch);
657     urshift64(TrustedImm32(15), scratch);
658     xor64(scratch, input);
659     // key += ~(key << 27);
660     move(input, scratch);
661     lshift64(TrustedImm32(27), scratch);
662     not64(scratch);
663     add64(scratch, input);
664     // key ^= (key >> 31);
665     move(input, scratch);
666     urshift64(TrustedImm32(31), scratch);
667     xor64(scratch, input);
668
669     // return static_cast<unsigned>(result)
670     void* mask = bitwise_cast<void*>(static_cast<uintptr_t>(UINT_MAX));
671     and64(TrustedImmPtr(mask), inputAndResult);
672 }
673 #endif // USE(JSVALUE64)
674
675 void AssemblyHelpers::emitConvertValueToBoolean(VM& vm, JSValueRegs value, GPRReg result, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg valueAsFPR, FPRReg tempFPR, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject, bool invert)
676 {
677     // Implements the following control flow structure:
678     // if (value is cell) {
679     //     if (value is string or value is BigInt)
680     //         result = !!value->length
681     //     else {
682     //         do evil things for masquerades-as-undefined
683     //         result = true
684     //     }
685     // } else if (value is int32) {
686     //     result = !!unboxInt32(value)
687     // } else if (value is number) {
688     //     result = !!unboxDouble(value)
689     // } else {
690     //     result = value == jsTrue
691     // }
692
693     JumpList done;
694
695     auto notCell = branchIfNotCell(value);
696     auto isString = branchIfString(value.payloadGPR());
697     auto isBigInt = branchIfBigInt(value.payloadGPR());
698
699     if (shouldCheckMasqueradesAsUndefined) {
700         ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
701         JumpList isNotMasqueradesAsUndefined;
702         isNotMasqueradesAsUndefined.append(branchTest8(Zero, Address(value.payloadGPR(), JSCell::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)));
703         emitLoadStructure(vm, value.payloadGPR(), result, scratchIfShouldCheckMasqueradesAsUndefined);
704         move(TrustedImmPtr(globalObject), scratchIfShouldCheckMasqueradesAsUndefined);
705         isNotMasqueradesAsUndefined.append(branchPtr(NotEqual, Address(result, Structure::globalObjectOffset()), scratchIfShouldCheckMasqueradesAsUndefined));
706
707         // We act like we are "undefined" here.
708         move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
709         done.append(jump());
710         isNotMasqueradesAsUndefined.link(this);
711     }
712     move(invert ? TrustedImm32(0) : TrustedImm32(1), result);
713     done.append(jump());
714
715     isString.link(this);
716     isBigInt.link(this);
717     RELEASE_ASSERT(JSString::offsetOfLength() == JSBigInt::offsetOfLength());
718     load32(Address(value.payloadGPR(), JSBigInt::offsetOfLength()), result);
719     compare32(invert ? Equal : NotEqual, result, TrustedImm32(0), result);
720     done.append(jump());
721
722     notCell.link(this);
723     auto notInt32 = branchIfNotInt32(value);
724     compare32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
725     done.append(jump());
726
727     notInt32.link(this);
728     auto notDouble = branchIfNotDoubleKnownNotInt32(value);
729 #if USE(JSVALUE64)
730     unboxDouble(value.gpr(), result, valueAsFPR);
731 #else
732     unboxDouble(value, valueAsFPR, tempFPR);
733 #endif
734     move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
735     done.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
736     move(invert ? TrustedImm32(0) : TrustedImm32(1), result);
737     done.append(jump());
738
739     notDouble.link(this);
740 #if USE(JSVALUE64)
741     static_assert(static_cast<int32_t>(ValueTrue) == ValueTrue, "");
742     compare64(invert ? NotEqual : Equal, value.gpr(), TrustedImm32(ValueTrue), result);
743 #else
744     move(invert ? TrustedImm32(1) : TrustedImm32(0), result);
745     done.append(branchIfNotBoolean(value, InvalidGPRReg));
746     compare32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0), result);
747 #endif
748
749     done.link(this);
750 }
751
752 AssemblyHelpers::JumpList AssemblyHelpers::branchIfValue(VM& vm, JSValueRegs value, GPRReg scratch, GPRReg scratchIfShouldCheckMasqueradesAsUndefined, FPRReg valueAsFPR, FPRReg tempFPR, bool shouldCheckMasqueradesAsUndefined, JSGlobalObject* globalObject, bool invert)
753 {
754     // Implements the following control flow structure:
755     // if (value is cell) {
756     //     if (value is string or value is BigInt)
757     //         result = !!value->length
758     //     else {
759     //         do evil things for masquerades-as-undefined
760     //         result = true
761     //     }
762     // } else if (value is int32) {
763     //     result = !!unboxInt32(value)
764     // } else if (value is number) {
765     //     result = !!unboxDouble(value)
766     // } else {
767     //     result = value == jsTrue
768     // }
769
770     JumpList done;
771     JumpList truthy;
772
773     auto notCell = branchIfNotCell(value);
774     auto isString = branchIfString(value.payloadGPR());
775     auto isBigInt = branchIfBigInt(value.payloadGPR());
776
777     if (shouldCheckMasqueradesAsUndefined) {
778         ASSERT(scratchIfShouldCheckMasqueradesAsUndefined != InvalidGPRReg);
779         JumpList isNotMasqueradesAsUndefined;
780         isNotMasqueradesAsUndefined.append(branchTest8(Zero, Address(value.payloadGPR(), JSCell::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)));
781         emitLoadStructure(vm, value.payloadGPR(), scratch, scratchIfShouldCheckMasqueradesAsUndefined);
782         move(TrustedImmPtr(globalObject), scratchIfShouldCheckMasqueradesAsUndefined);
783         isNotMasqueradesAsUndefined.append(branchPtr(NotEqual, Address(scratch, Structure::globalObjectOffset()), scratchIfShouldCheckMasqueradesAsUndefined));
784
785         // We act like we are "undefined" here.
786         if (invert)
787             truthy.append(jump());
788         else
789             done.append(jump());
790
791         if (invert)
792             done.append(isNotMasqueradesAsUndefined);
793         else
794             truthy.append(isNotMasqueradesAsUndefined);
795     } else {
796         if (invert)
797             done.append(jump());
798         else
799             truthy.append(jump());
800     }
801
802     isString.link(this);
803     isBigInt.link(this);
804     RELEASE_ASSERT(JSString::offsetOfLength() == JSBigInt::offsetOfLength());
805     truthy.append(branchTest32(invert ? Zero : NonZero, Address(value.payloadGPR(), JSBigInt::offsetOfLength())));
806     done.append(jump());
807
808     notCell.link(this);
809     auto notInt32 = branchIfNotInt32(value);
810     truthy.append(branchTest32(invert ? Zero : NonZero, value.payloadGPR()));
811     done.append(jump());
812
813     notInt32.link(this);
814     auto notDouble = branchIfNotDoubleKnownNotInt32(value);
815 #if USE(JSVALUE64)
816     unboxDouble(value.gpr(), scratch, valueAsFPR);
817 #else
818     unboxDouble(value, valueAsFPR, tempFPR);
819 #endif
820     if (invert) {
821         truthy.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
822         done.append(jump());
823     } else {
824         done.append(branchDoubleZeroOrNaN(valueAsFPR, tempFPR));
825         truthy.append(jump());
826     }
827
828     notDouble.link(this);
829 #if USE(JSVALUE64)
830     truthy.append(branch64(invert ? NotEqual : Equal, value.gpr(), TrustedImm64(JSValue::encode(jsBoolean(true)))));
831 #else
832     auto notBoolean = branchIfNotBoolean(value, InvalidGPRReg);
833     if (invert)
834         truthy.append(notBoolean);
835     else
836         done.append(notBoolean);
837     truthy.append(branch32(invert ? Equal : NotEqual, value.payloadGPR(), TrustedImm32(0)));
838 #endif
839
840     done.link(this);
841
842     return truthy;
843 }
844
845 #if ENABLE(WEBASSEMBLY)
846 void AssemblyHelpers::loadWasmContextInstance(GPRReg dst)
847 {
848 #if ENABLE(FAST_TLS_JIT)
849     if (Wasm::Context::useFastTLS()) {
850         loadFromTLSPtr(fastTLSOffsetForKey(WTF_WASM_CONTEXT_KEY), dst);
851         return;
852     }
853 #endif
854     move(Wasm::PinnedRegisterInfo::get().wasmContextInstancePointer, dst);
855 }
856
857 void AssemblyHelpers::storeWasmContextInstance(GPRReg src)
858 {
859 #if ENABLE(FAST_TLS_JIT)
860     if (Wasm::Context::useFastTLS()) {
861         storeToTLSPtr(src, fastTLSOffsetForKey(WTF_WASM_CONTEXT_KEY));
862         return;
863     }
864 #endif
865     move(src, Wasm::PinnedRegisterInfo::get().wasmContextInstancePointer);
866 }
867
868 bool AssemblyHelpers::loadWasmContextInstanceNeedsMacroScratchRegister()
869 {
870 #if ENABLE(FAST_TLS_JIT)
871     if (Wasm::Context::useFastTLS())
872         return loadFromTLSPtrNeedsMacroScratchRegister();
873 #endif
874     return false;
875 }
876
877 bool AssemblyHelpers::storeWasmContextInstanceNeedsMacroScratchRegister()
878 {
879 #if ENABLE(FAST_TLS_JIT)
880     if (Wasm::Context::useFastTLS())
881         return storeToTLSPtrNeedsMacroScratchRegister();
882 #endif
883     return false;
884 }
885
886 #endif // ENABLE(WEBASSEMBLY)
887
888 void AssemblyHelpers::debugCall(VM& vm, V_DebugOperation_EPP function, void* argument)
889 {
890     size_t scratchSize = sizeof(EncodedJSValue) * (GPRInfo::numberOfRegisters + FPRInfo::numberOfRegisters);
891     ScratchBuffer* scratchBuffer = vm.scratchBufferForSize(scratchSize);
892     EncodedJSValue* buffer = static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer());
893
894     for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) {
895 #if USE(JSVALUE64)
896         store64(GPRInfo::toRegister(i), buffer + i);
897 #else
898         store32(GPRInfo::toRegister(i), buffer + i);
899 #endif
900     }
901
902     for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
903         move(TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0);
904         storeDouble(FPRInfo::toRegister(i), GPRInfo::regT0);
905     }
906
907     // Tell GC mark phase how much of the scratch buffer is active during call.
908     move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), GPRInfo::regT0);
909     storePtr(TrustedImmPtr(scratchSize), GPRInfo::regT0);
910
911 #if CPU(X86_64) || CPU(ARM_THUMB2) || CPU(ARM64) || CPU(MIPS)
912     move(TrustedImmPtr(buffer), GPRInfo::argumentGPR2);
913     move(TrustedImmPtr(argument), GPRInfo::argumentGPR1);
914     move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
915     GPRReg scratch = selectScratchGPR(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, GPRInfo::argumentGPR2);
916 #elif CPU(X86)
917     poke(GPRInfo::callFrameRegister, 0);
918     poke(TrustedImmPtr(argument), 1);
919     poke(TrustedImmPtr(buffer), 2);
920     GPRReg scratch = GPRInfo::regT0;
921 #else
922 #error "JIT not supported on this platform."
923 #endif
924     move(TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(function)), scratch);
925     call(scratch, OperationPtrTag);
926
927     move(TrustedImmPtr(scratchBuffer->addressOfActiveLength()), GPRInfo::regT0);
928     storePtr(TrustedImmPtr(nullptr), GPRInfo::regT0);
929
930     for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
931         move(TrustedImmPtr(buffer + GPRInfo::numberOfRegisters + i), GPRInfo::regT0);
932         loadDouble(GPRInfo::regT0, FPRInfo::toRegister(i));
933     }
934     for (unsigned i = 0; i < GPRInfo::numberOfRegisters; ++i) {
935 #if USE(JSVALUE64)
936         load64(buffer + i, GPRInfo::toRegister(i));
937 #else
938         load32(buffer + i, GPRInfo::toRegister(i));
939 #endif
940     }
941 }
942
943 void AssemblyHelpers::copyCalleeSavesToEntryFrameCalleeSavesBufferImpl(GPRReg calleeSavesBuffer)
944 {
945 #if NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
946     addPtr(TrustedImm32(EntryFrame::calleeSaveRegistersBufferOffset()), calleeSavesBuffer);
947
948     RegisterAtOffsetList* allCalleeSaves = RegisterSet::vmCalleeSaveRegisterOffsets();
949     RegisterSet dontCopyRegisters = RegisterSet::stackRegisters();
950     unsigned registerCount = allCalleeSaves->size();
951     
952     for (unsigned i = 0; i < registerCount; i++) {
953         RegisterAtOffset entry = allCalleeSaves->at(i);
954         if (dontCopyRegisters.get(entry.reg()))
955             continue;
956         if (entry.reg().isGPR())
957             storePtr(entry.reg().gpr(), Address(calleeSavesBuffer, entry.offset()));
958         else
959             storeDouble(entry.reg().fpr(), Address(calleeSavesBuffer, entry.offset()));
960     }
961 #else
962     UNUSED_PARAM(calleeSavesBuffer);
963 #endif
964 }
965
966 void AssemblyHelpers::sanitizeStackInline(VM& vm, GPRReg scratch)
967 {
968     loadPtr(vm.addressOfLastStackTop(), scratch);
969     Jump done = branchPtr(BelowOrEqual, stackPointerRegister, scratch);
970     Label loop = label();
971     storePtr(TrustedImmPtr(nullptr), scratch);
972     addPtr(TrustedImmPtr(sizeof(void*)), scratch);
973     branchPtr(Above, stackPointerRegister, scratch).linkTo(loop, this);
974     done.link(this);
975     move(stackPointerRegister, scratch);
976     storePtr(scratch, vm.addressOfLastStackTop());
977 }
978
979 void AssemblyHelpers::emitPreparePreciseIndexMask32(GPRReg index, GPRReg length, GPRReg result)
980 {
981     if (length == result) {
982         negPtr(length);
983         addPtr(index, length);
984     } else {
985         move(index, result);
986         subPtr(length, result);
987     }
988     rshiftPtr(TrustedImm32(preciseIndexMaskShift<void*>()), result);
989 }
990
991 void AssemblyHelpers::emitDynamicPoison(GPRReg base, GPRReg poisonValue)
992 {
993 #if CPU(X86_64) || (CPU(ARM64) && !defined(__ILP32__))
994     lshiftPtr(TrustedImm32(40), poisonValue);
995     addPtr(poisonValue, base);
996 #else
997     UNUSED_PARAM(base);
998     UNUSED_PARAM(poisonValue);
999 #endif
1000 }
1001
1002 void AssemblyHelpers::emitDynamicPoisonOnLoadedType(GPRReg base, GPRReg actualType, JSType expectedType)
1003 {
1004 #if CPU(X86_64) || (CPU(ARM64) && !defined(__ILP32__))
1005     xor32(TrustedImm32(expectedType), actualType);
1006     emitDynamicPoison(base, actualType);
1007 #else
1008     UNUSED_PARAM(base);
1009     UNUSED_PARAM(actualType);
1010     UNUSED_PARAM(expectedType);
1011 #endif
1012 }
1013
1014 void AssemblyHelpers::emitDynamicPoisonOnType(GPRReg base, GPRReg scratch, JSType expectedType)
1015 {
1016 #if CPU(X86_64) || (CPU(ARM64) && !defined(__ILP32__))
1017     load8(Address(base, JSCell::typeInfoTypeOffset()), scratch);
1018     emitDynamicPoisonOnLoadedType(base, scratch, expectedType);
1019 #else
1020     UNUSED_PARAM(base);
1021     UNUSED_PARAM(scratch);
1022     UNUSED_PARAM(expectedType);
1023 #endif
1024 }
1025
1026 } // namespace JSC
1027
1028 #endif // ENABLE(JIT)
1029