blob: 8ceab6f11660807c488837c0dbffb25a75626bba [file] [log] [blame]
oliver@apple.comea771492013-07-25 03:58:38 +00001/*
fpizlo@apple.comda834ae2015-03-26 04:28:43 +00002 * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
oliver@apple.comea771492013-07-25 03:58:38 +00003 *
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 "FTLOSRExitCompiler.h"
28
29#if ENABLE(FTL_JIT)
30
31#include "DFGOSRExitCompilerCommon.h"
32#include "DFGOSRExitPreparation.h"
oliver@apple.comea771492013-07-25 03:58:38 +000033#include "FTLExitArgumentForOperand.h"
34#include "FTLJITCode.h"
sbarati@apple.com5bebda72015-11-10 07:48:54 +000035#include "FTLLocation.h"
oliver@apple.comea771492013-07-25 03:58:38 +000036#include "FTLOSRExit.h"
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +000037#include "FTLOperations.h"
fpizlo@apple.com40750682014-03-13 22:18:50 +000038#include "FTLState.h"
fpizlo@apple.comea92c202013-10-10 04:24:57 +000039#include "FTLSaveRestore.h"
fpizlo@apple.comb426f862014-02-10 02:51:13 +000040#include "LinkBuffer.h"
msaboff@apple.com95894332014-01-29 19:18:54 +000041#include "MaxFrameExtentForSlowPathCall.h"
commit-queue@webkit.orga936a262014-01-14 15:20:19 +000042#include "OperandsInlines.h"
fpizlo@apple.comfb7eff22014-02-11 01:45:50 +000043#include "JSCInlines.h"
oliver@apple.comea771492013-07-25 03:58:38 +000044
45namespace JSC { namespace FTL {
46
47using namespace DFG;
48
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +000049static void reboxAccordingToFormat(
50 DataFormat format, AssemblyHelpers& jit, GPRReg value, GPRReg scratch1, GPRReg scratch2)
51{
52 switch (format) {
53 case DataFormatInt32: {
54 jit.zeroExtend32ToPtr(value, value);
55 jit.or64(GPRInfo::tagTypeNumberRegister, value);
56 break;
57 }
58
59 case DataFormatInt52: {
60 jit.rshift64(AssemblyHelpers::TrustedImm32(JSValue::int52ShiftAmount), value);
61 jit.moveDoubleTo64(FPRInfo::fpRegT0, scratch2);
62 jit.boxInt52(value, value, scratch1, FPRInfo::fpRegT0);
63 jit.move64ToDouble(scratch2, FPRInfo::fpRegT0);
64 break;
65 }
66
67 case DataFormatStrictInt52: {
68 jit.moveDoubleTo64(FPRInfo::fpRegT0, scratch2);
69 jit.boxInt52(value, value, scratch1, FPRInfo::fpRegT0);
70 jit.move64ToDouble(scratch2, FPRInfo::fpRegT0);
71 break;
72 }
73
74 case DataFormatBoolean: {
75 jit.zeroExtend32ToPtr(value, value);
76 jit.or32(MacroAssembler::TrustedImm32(ValueFalse), value);
77 break;
78 }
79
80 case DataFormatJS: {
81 // Done already!
82 break;
83 }
84
85 case DataFormatDouble: {
86 jit.moveDoubleTo64(FPRInfo::fpRegT0, scratch1);
87 jit.move64ToDouble(value, FPRInfo::fpRegT0);
88 jit.purifyNaN(FPRInfo::fpRegT0);
89 jit.boxDouble(FPRInfo::fpRegT0, value);
90 jit.move64ToDouble(scratch1, FPRInfo::fpRegT0);
91 break;
92 }
93
94 default:
95 RELEASE_ASSERT_NOT_REACHED();
96 break;
97 }
98}
99
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000100static void compileRecovery(
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000101 CCallHelpers& jit, const ExitValue& value,
102#if FTL_USES_B3
103 Vector<B3::ValueRep>& valueReps,
104#else // FTL_USES_B3
105 StackMaps::Record* record, StackMaps& stackmaps,
106#endif // FTL_USES_B3
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000107 char* registerScratch,
108 const HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*>& materializationToPointer)
109{
110 switch (value.kind()) {
111 case ExitValueDead:
112 jit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::regT0);
113 break;
114
115 case ExitValueConstant:
116 jit.move(MacroAssembler::TrustedImm64(JSValue::encode(value.constant())), GPRInfo::regT0);
117 break;
118
119 case ExitValueArgument:
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000120#if FTL_USES_B3
121 Location::forValueRep(valueReps[value.exitArgument().argument()]).restoreInto(
122 jit, registerScratch, GPRInfo::regT0);
123#else // FTL_USES_B3
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000124 record->locations[value.exitArgument().argument()].restoreInto(
125 jit, stackmaps, registerScratch, GPRInfo::regT0);
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000126#endif // FTL_USES_B3
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000127 break;
128
129 case ExitValueInJSStack:
130 case ExitValueInJSStackAsInt32:
131 case ExitValueInJSStackAsInt52:
132 case ExitValueInJSStackAsDouble:
133 jit.load64(AssemblyHelpers::addressFor(value.virtualRegister()), GPRInfo::regT0);
134 break;
135
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000136 case ExitValueRecovery:
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000137#if FTL_USES_B3
138 Location::forValueRep(valueReps[value.rightRecoveryArgument()]).restoreInto(
139 jit, registerScratch, GPRInfo::regT1);
140 Location::forValueRep(valueReps[value.leftRecoveryArgument()]).restoreInto(
141 jit, registerScratch, GPRInfo::regT0);
142#else // FTL_USES_B3
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000143 record->locations[value.rightRecoveryArgument()].restoreInto(
144 jit, stackmaps, registerScratch, GPRInfo::regT1);
145 record->locations[value.leftRecoveryArgument()].restoreInto(
146 jit, stackmaps, registerScratch, GPRInfo::regT0);
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000147#endif // FTL_USES_B3
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000148 switch (value.recoveryOpcode()) {
149 case AddRecovery:
150 switch (value.recoveryFormat()) {
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +0000151 case DataFormatInt32:
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000152 jit.add32(GPRInfo::regT1, GPRInfo::regT0);
153 break;
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +0000154 case DataFormatInt52:
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000155 jit.add64(GPRInfo::regT1, GPRInfo::regT0);
156 break;
157 default:
158 RELEASE_ASSERT_NOT_REACHED();
159 break;
160 }
161 break;
162 case SubRecovery:
163 switch (value.recoveryFormat()) {
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +0000164 case DataFormatInt32:
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000165 jit.sub32(GPRInfo::regT1, GPRInfo::regT0);
166 break;
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +0000167 case DataFormatInt52:
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000168 jit.sub64(GPRInfo::regT1, GPRInfo::regT0);
169 break;
170 default:
171 RELEASE_ASSERT_NOT_REACHED();
172 break;
173 }
174 break;
175 default:
176 RELEASE_ASSERT_NOT_REACHED();
177 break;
178 }
179 break;
180
181 case ExitValueMaterializeNewObject:
182 jit.loadPtr(materializationToPointer.get(value.objectMaterialization()), GPRInfo::regT0);
183 break;
184
185 default:
186 RELEASE_ASSERT_NOT_REACHED();
187 break;
188 }
189
190 reboxAccordingToFormat(
basile_clement@apple.com3ed5a3a2015-09-04 18:24:38 +0000191 value.dataFormat(), jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000192}
193
fpizlo@apple.com6bf11982013-11-03 18:24:20 +0000194static void compileStub(
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000195 unsigned exitID, JITCode* jitCode, OSRExit& exit, VM* vm, CodeBlock* codeBlock)
196{
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000197#if FTL_USES_B3
198 UNUSED_PARAM(jitCode);
199#else // FTL_USES_B3
sbarati@apple.comcda241d2015-10-19 20:29:45 +0000200 StackMaps::Record* record = &jitCode->stackmaps.records[exit.m_stackmapRecordIndex];
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000201 RELEASE_ASSERT(record->patchpointID == exit.m_descriptor->m_stackmapID);
202#endif // FTL_USES_B3
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000203
msaboff@apple.comcb9adb02013-11-07 23:45:56 +0000204 // This code requires framePointerRegister is the same as callFrameRegister
205 static_assert(MacroAssembler::framePointerRegister == GPRInfo::callFrameRegister, "MacroAssembler::framePointerRegister and GPRInfo::callFrameRegister must be the same");
206
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000207 CCallHelpers jit(vm, codeBlock);
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000208
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000209 // We need scratch space to save all registers, to build up the JS stack, to deal with unwind
210 // fixup, pointers to all of the objects we materialize, and the elements inside those objects
211 // that we materialize.
212
213 // Figure out how much space we need for those object allocations.
214 unsigned numMaterializations = 0;
215 size_t maxMaterializationNumArguments = 0;
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000216 for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000217 numMaterializations++;
218
219 maxMaterializationNumArguments = std::max(
220 maxMaterializationNumArguments,
221 materialization->properties().size());
222 }
223
224 ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(
225 sizeof(EncodedJSValue) * (
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000226 exit.m_descriptor->m_values.size() + numMaterializations + maxMaterializationNumArguments) +
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000227 requiredScratchMemorySizeInBytes() +
msaboff@apple.com02085462015-09-10 17:47:16 +0000228 codeBlock->calleeSaveRegisters()->size() * sizeof(uint64_t));
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000229 EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000230 EncodedJSValue* materializationPointers = scratch + exit.m_descriptor->m_values.size();
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000231 EncodedJSValue* materializationArguments = materializationPointers + numMaterializations;
232 char* registerScratch = bitwise_cast<char*>(materializationArguments + maxMaterializationNumArguments);
msaboff@apple.com95894332014-01-29 19:18:54 +0000233 uint64_t* unwindScratch = bitwise_cast<uint64_t*>(registerScratch + requiredScratchMemorySizeInBytes());
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000234
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000235 HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*> materializationToPointer;
236 unsigned materializationCount = 0;
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000237 for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000238 materializationToPointer.add(
239 materialization, materializationPointers + materializationCount++);
240 }
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000241
242 auto recoverValue = [&] (const ExitValue& value) {
243 compileRecovery(
244 jit, value,
245#if FTL_USES_B3
246 exit.m_valueReps,
247#else // FTL_USES_B3
248 record, jitCode->stackmaps,
249#endif // FTL_USES_B3
250 registerScratch, materializationToPointer);
251 };
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000252
fpizlo@apple.com720e05a2014-02-17 18:59:13 +0000253 // Note that we come in here, the stack used to be as LLVM left it except that someone called pushToSave().
254 // We don't care about the value they saved. But, we do appreciate the fact that they did it, because we use
255 // that slot for saveAllRegisters().
256
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000257 saveAllRegisters(jit, registerScratch);
258
fpizlo@apple.com68952692014-03-23 18:56:56 +0000259 // Bring the stack back into a sane form and assert that it's sane.
fpizlo@apple.com720e05a2014-02-17 18:59:13 +0000260 jit.popToRestore(GPRInfo::regT0);
fpizlo@apple.com68952692014-03-23 18:56:56 +0000261 jit.checkStackPointerAlignment();
fpizlo@apple.come5192ec2013-10-12 01:33:16 +0000262
msaboff@apple.com95894332014-01-29 19:18:54 +0000263 if (vm->m_perBytecodeProfiler && codeBlock->jitCode()->dfgCommon()->compilation) {
264 Profiler::Database& database = *vm->m_perBytecodeProfiler;
265 Profiler::Compilation* compilation = codeBlock->jitCode()->dfgCommon()->compilation.get();
266
267 Profiler::OSRExit* profilerExit = compilation->addOSRExit(
268 exitID, Profiler::OriginStack(database, codeBlock, exit.m_codeOrigin),
fpizlo@apple.comb41e6822014-07-25 20:55:17 +0000269 exit.m_kind, exit.m_kind == UncountableInvalidation);
msaboff@apple.com95894332014-01-29 19:18:54 +0000270 jit.add64(CCallHelpers::TrustedImm32(1), CCallHelpers::AbsoluteAddress(profilerExit->counterAddress()));
271 }
272
fpizlo@apple.come5192ec2013-10-12 01:33:16 +0000273 // The remaining code assumes that SP/FP are in the same state that they were in the FTL's
274 // call frame.
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000275
276 // Get the call frame and tag thingies.
msaboff@apple.comcb9adb02013-11-07 23:45:56 +0000277 // Restore the exiting function's callFrame value into a regT4
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000278 jit.move(MacroAssembler::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister);
279 jit.move(MacroAssembler::TrustedImm64(TagMask), GPRInfo::tagMaskRegister);
280
281 // Do some value profiling.
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000282 if (exit.m_descriptor->m_profileDataFormat != DataFormatNone) {
283#if FTL_USES_B3
284 Location::forValueRep(exit.m_valueReps[0]).restoreInto(jit, registerScratch, GPRInfo::regT0);
285#else // FTL_USES_B3
msaboff@apple.com95894332014-01-29 19:18:54 +0000286 record->locations[0].restoreInto(jit, jitCode->stackmaps, registerScratch, GPRInfo::regT0);
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000287#endif // FTL_USES_B3
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000288 reboxAccordingToFormat(
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000289 exit.m_descriptor->m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000290
291 if (exit.m_kind == BadCache || exit.m_kind == BadIndexingType) {
292 CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile;
293 if (ArrayProfile* arrayProfile = jit.baselineCodeBlockFor(codeOrigin)->getArrayProfile(codeOrigin.bytecodeIndex)) {
mhahnenberg@apple.comb6f85192014-02-27 01:27:18 +0000294 jit.load32(MacroAssembler::Address(GPRInfo::regT0, JSCell::structureIDOffset()), GPRInfo::regT1);
295 jit.store32(GPRInfo::regT1, arrayProfile->addressOfLastSeenStructureID());
296 jit.load8(MacroAssembler::Address(GPRInfo::regT0, JSCell::indexingTypeOffset()), GPRInfo::regT1);
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000297 jit.move(MacroAssembler::TrustedImm32(1), GPRInfo::regT2);
298 jit.lshift32(GPRInfo::regT1, GPRInfo::regT2);
299 jit.or32(GPRInfo::regT2, MacroAssembler::AbsoluteAddress(arrayProfile->addressOfArrayModes()));
300 }
301 }
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000302
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000303 if (!!exit.m_descriptor->m_valueProfile)
304 jit.store64(GPRInfo::regT0, exit.m_descriptor->m_valueProfile.getSpecFailBucket(0));
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000305 }
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000306
307 // Materialize all objects. Don't materialize an object until all
308 // of the objects it needs have been materialized. We break cycles
309 // by populating objects late - we only consider an object as
310 // needing another object if the later is needed for the
311 // allocation of the former.
312
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000313 HashSet<ExitTimeObjectMaterialization*> toMaterialize;
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000314 for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations)
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000315 toMaterialize.add(materialization);
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000316
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000317 while (!toMaterialize.isEmpty()) {
commit-queue@webkit.org877bfc32015-03-10 21:54:21 +0000318 unsigned previousToMaterializeSize = toMaterialize.size();
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000319
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000320 Vector<ExitTimeObjectMaterialization*> worklist;
321 worklist.appendRange(toMaterialize.begin(), toMaterialize.end());
322 for (ExitTimeObjectMaterialization* materialization : worklist) {
323 // Check if we can do anything about this right now.
324 bool allGood = true;
325 for (ExitPropertyValue value : materialization->properties()) {
326 if (!value.value().isObjectMaterialization())
327 continue;
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000328 if (!value.location().neededForMaterialization())
329 continue;
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000330 if (toMaterialize.contains(value.value().objectMaterialization())) {
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000331 // Gotta skip this one, since it needs a
332 // materialization that hasn't been materialized.
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000333 allGood = false;
334 break;
335 }
336 }
337 if (!allGood)
338 continue;
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000339
340 // All systems go for materializing the object. First we
341 // recover the values of all of its fields and then we
342 // call a function to actually allocate the beast.
343 // We only recover the fields that are needed for the allocation.
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000344 for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) {
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000345 const ExitPropertyValue& property = materialization->properties()[propertyIndex];
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000346 if (!property.location().neededForMaterialization())
347 continue;
348
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000349 recoverValue(property.value());
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000350 jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex);
351 }
352
353 // This call assumes that we don't pass arguments on the stack.
354 jit.setupArgumentsWithExecState(
355 CCallHelpers::TrustedImmPtr(materialization),
356 CCallHelpers::TrustedImmPtr(materializationArguments));
357 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationMaterializeObjectInOSR)), GPRInfo::nonArgGPR0);
358 jit.call(GPRInfo::nonArgGPR0);
359 jit.storePtr(GPRInfo::returnValueGPR, materializationToPointer.get(materialization));
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000360
fpizlo@apple.comfc70ba62014-09-26 03:59:33 +0000361 // Let everyone know that we're done.
362 toMaterialize.remove(materialization);
363 }
364
365 // We expect progress! This ensures that we crash rather than looping infinitely if there
366 // is something broken about this fixpoint. Or, this could happen if we ever violate the
367 // "materializations form a DAG" rule.
368 RELEASE_ASSERT(toMaterialize.size() < previousToMaterializeSize);
369 }
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000370
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000371 // Now that all the objects have been allocated, we populate them
372 // with the correct values. This time we can recover all the
373 // fields, including those that are only needed for the allocation.
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000374 for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations) {
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000375 for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) {
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000376 recoverValue(materialization->properties()[propertyIndex].value());
basile_clement@apple.comd7930292015-07-13 23:27:30 +0000377 jit.storePtr(GPRInfo::regT0, materializationArguments + propertyIndex);
378 }
379
380 // This call assumes that we don't pass arguments on the stack
381 jit.setupArgumentsWithExecState(
382 CCallHelpers::TrustedImmPtr(materialization),
383 CCallHelpers::TrustedImmPtr(materializationToPointer.get(materialization)),
384 CCallHelpers::TrustedImmPtr(materializationArguments));
385 jit.move(CCallHelpers::TrustedImmPtr(bitwise_cast<void*>(operationPopulateObjectInOSR)), GPRInfo::nonArgGPR0);
386 jit.call(GPRInfo::nonArgGPR0);
387 }
388
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000389 // Save all state from wherever the exit data tells us it was, into the appropriate place in
fpizlo@apple.com2a69df42014-09-20 18:45:54 +0000390 // the scratch buffer. This also does the reboxing.
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000391
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000392 for (unsigned index = exit.m_descriptor->m_values.size(); index--;) {
393 recoverValue(exit.m_descriptor->m_values[index]);
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000394 jit.store64(GPRInfo::regT0, scratch + index);
395 }
396
msaboff@apple.com95894332014-01-29 19:18:54 +0000397 // Henceforth we make it look like the exiting function was called through a register
398 // preservation wrapper. This implies that FP must be nudged down by a certain amount. Then
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000399 // we restore the various things according to either exit.m_descriptor->m_values or by copying from the
msaboff@apple.com95894332014-01-29 19:18:54 +0000400 // old frame, and finally we save the various callee-save registers into where the
401 // restoration thunk would restore them from.
402
msaboff@apple.com95894332014-01-29 19:18:54 +0000403 // Before we start messing with the frame, we need to set aside any registers that the
404 // FTL code was preserving.
msaboff@apple.com02085462015-09-10 17:47:16 +0000405 for (unsigned i = codeBlock->calleeSaveRegisters()->size(); i--;) {
406 RegisterAtOffset entry = codeBlock->calleeSaveRegisters()->at(i);
msaboff@apple.com95894332014-01-29 19:18:54 +0000407 jit.load64(
408 MacroAssembler::Address(MacroAssembler::framePointerRegister, entry.offset()),
409 GPRInfo::regT0);
410 jit.store64(GPRInfo::regT0, unwindScratch + i);
411 }
412
413 jit.load32(CCallHelpers::payloadFor(JSStack::ArgumentCount), GPRInfo::regT2);
414
415 // Let's say that the FTL function had failed its arity check. In that case, the stack will
416 // contain some extra stuff.
417 //
msaboff@apple.com21bd7372015-09-18 16:21:08 +0000418 // We compute the padded stack space:
msaboff@apple.com95894332014-01-29 19:18:54 +0000419 //
420 // paddedStackSpace = roundUp(codeBlock->numParameters - regT2 + 1)
421 //
msaboff@apple.com21bd7372015-09-18 16:21:08 +0000422 // The stack will have regT2 + CallFrameHeaderSize stuff.
423 // We want to make the stack look like this, from higher addresses down:
msaboff@apple.com95894332014-01-29 19:18:54 +0000424 //
msaboff@apple.com95894332014-01-29 19:18:54 +0000425 // - argument padding
426 // - actual arguments
427 // - call frame header
msaboff@apple.com95894332014-01-29 19:18:54 +0000428
429 // This code assumes that we're dealing with FunctionCode.
430 RELEASE_ASSERT(codeBlock->codeType() == FunctionCode);
431
432 jit.add32(
433 MacroAssembler::TrustedImm32(-codeBlock->numParameters()), GPRInfo::regT2,
434 GPRInfo::regT3);
fpizlo@apple.com0cec6dea2014-03-04 21:27:37 +0000435 MacroAssembler::Jump arityIntact = jit.branch32(
436 MacroAssembler::GreaterThanOrEqual, GPRInfo::regT3, MacroAssembler::TrustedImm32(0));
msaboff@apple.com95894332014-01-29 19:18:54 +0000437 jit.neg32(GPRInfo::regT3);
438 jit.add32(MacroAssembler::TrustedImm32(1 + stackAlignmentRegisters() - 1), GPRInfo::regT3);
439 jit.and32(MacroAssembler::TrustedImm32(-stackAlignmentRegisters()), GPRInfo::regT3);
440 jit.add32(GPRInfo::regT3, GPRInfo::regT2);
441 arityIntact.link(&jit);
442
msaboff@apple.com02085462015-09-10 17:47:16 +0000443 CodeBlock* baselineCodeBlock = jit.baselineCodeBlockFor(exit.m_codeOrigin);
444
msaboff@apple.com95894332014-01-29 19:18:54 +0000445 // First set up SP so that our data doesn't get clobbered by signals.
fpizlo@apple.com68952692014-03-23 18:56:56 +0000446 unsigned conservativeStackDelta =
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000447 (exit.m_descriptor->m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) +
fpizlo@apple.com68952692014-03-23 18:56:56 +0000448 maxFrameExtentForSlowPathCall;
449 conservativeStackDelta = WTF::roundUpToMultipleOf(
450 stackAlignmentBytes(), conservativeStackDelta);
msaboff@apple.com95894332014-01-29 19:18:54 +0000451 jit.addPtr(
fpizlo@apple.com68952692014-03-23 18:56:56 +0000452 MacroAssembler::TrustedImm32(-conservativeStackDelta),
msaboff@apple.com95894332014-01-29 19:18:54 +0000453 MacroAssembler::framePointerRegister, MacroAssembler::stackPointerRegister);
fpizlo@apple.com68952692014-03-23 18:56:56 +0000454 jit.checkStackPointerAlignment();
msaboff@apple.com95894332014-01-29 19:18:54 +0000455
msaboff@apple.com21bd7372015-09-18 16:21:08 +0000456 RegisterSet allFTLCalleeSaves = RegisterSet::ftlCalleeSaveRegisters();
msaboff@apple.com02085462015-09-10 17:47:16 +0000457 RegisterAtOffsetList* baselineCalleeSaves = baselineCodeBlock->calleeSaveRegisters();
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000458 RegisterAtOffsetList* vmCalleeSaves = vm->getAllCalleeSaveRegisterOffsets();
459 RegisterSet vmCalleeSavesToSkip = RegisterSet::stackRegisters();
460 if (exit.m_isExceptionHandler)
461 jit.move(CCallHelpers::TrustedImmPtr(vm->calleeSaveRegistersBuffer), GPRInfo::regT1);
msaboff@apple.com02085462015-09-10 17:47:16 +0000462
463 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000464 if (!allFTLCalleeSaves.get(reg)) {
465 if (exit.m_isExceptionHandler)
466 RELEASE_ASSERT(!vmCalleeSaves->find(reg));
msaboff@apple.com02085462015-09-10 17:47:16 +0000467 continue;
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000468 }
msaboff@apple.com02085462015-09-10 17:47:16 +0000469 unsigned unwindIndex = codeBlock->calleeSaveRegisters()->indexOf(reg);
470 RegisterAtOffset* baselineRegisterOffset = baselineCalleeSaves->find(reg);
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000471 RegisterAtOffset* vmCalleeSave = nullptr;
472 if (exit.m_isExceptionHandler)
473 vmCalleeSave = vmCalleeSaves->find(reg);
msaboff@apple.com02085462015-09-10 17:47:16 +0000474
475 if (reg.isGPR()) {
476 GPRReg regToLoad = baselineRegisterOffset ? GPRInfo::regT0 : reg.gpr();
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000477 RELEASE_ASSERT(regToLoad != GPRInfo::regT1);
msaboff@apple.com02085462015-09-10 17:47:16 +0000478
479 if (unwindIndex == UINT_MAX) {
480 // The FTL compilation didn't preserve this register. This means that it also
481 // didn't use the register. So its value at the beginning of OSR exit should be
482 // preserved by the thunk. Luckily, we saved all registers into the register
483 // scratch buffer, so we can restore them from there.
484 jit.load64(registerScratch + offsetOfReg(reg), regToLoad);
485 } else {
486 // The FTL compilation preserved the register. Its new value is therefore
487 // irrelevant, but we can get the value that was preserved by using the unwind
488 // data. We've already copied all unwind-able preserved registers into the unwind
489 // scratch buffer, so we can get it from there.
490 jit.load64(unwindScratch + unwindIndex, regToLoad);
491 }
492
493 if (baselineRegisterOffset)
494 jit.store64(regToLoad, MacroAssembler::Address(MacroAssembler::framePointerRegister, baselineRegisterOffset->offset()));
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000495 if (vmCalleeSave && !vmCalleeSavesToSkip.get(vmCalleeSave->reg()))
496 jit.store64(regToLoad, MacroAssembler::Address(GPRInfo::regT1, vmCalleeSave->offset()));
msaboff@apple.com02085462015-09-10 17:47:16 +0000497 } else {
498 FPRReg fpRegToLoad = baselineRegisterOffset ? FPRInfo::fpRegT0 : reg.fpr();
499
500 if (unwindIndex == UINT_MAX)
501 jit.loadDouble(MacroAssembler::TrustedImmPtr(registerScratch + offsetOfReg(reg)), fpRegToLoad);
502 else
503 jit.loadDouble(MacroAssembler::TrustedImmPtr(unwindScratch + unwindIndex), fpRegToLoad);
504
505 if (baselineRegisterOffset)
506 jit.storeDouble(fpRegToLoad, MacroAssembler::Address(MacroAssembler::framePointerRegister, baselineRegisterOffset->offset()));
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000507 if (vmCalleeSave && !vmCalleeSavesToSkip.get(vmCalleeSave->reg()))
508 jit.storeDouble(fpRegToLoad, MacroAssembler::Address(GPRInfo::regT1, vmCalleeSave->offset()));
msaboff@apple.com02085462015-09-10 17:47:16 +0000509 }
510 }
511
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000512 if (exit.m_isExceptionHandler) {
513 RegisterAtOffset* vmCalleeSave = vmCalleeSaves->find(GPRInfo::tagTypeNumberRegister);
514 jit.store64(GPRInfo::tagTypeNumberRegister, MacroAssembler::Address(GPRInfo::regT1, vmCalleeSave->offset()));
515
516 vmCalleeSave = vmCalleeSaves->find(GPRInfo::tagMaskRegister);
517 jit.store64(GPRInfo::tagMaskRegister, MacroAssembler::Address(GPRInfo::regT1, vmCalleeSave->offset()));
518 }
519
msaboff@apple.com02085462015-09-10 17:47:16 +0000520 size_t baselineVirtualRegistersForCalleeSaves = baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters();
521
fpizlo@apple.com2a69df42014-09-20 18:45:54 +0000522 // Now get state out of the scratch buffer and place it back into the stack. The values are
523 // already reboxed so we just move them.
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000524 for (unsigned index = exit.m_descriptor->m_values.size(); index--;) {
525 VirtualRegister reg = exit.m_descriptor->m_values.virtualRegisterForIndex(index);
msaboff@apple.com02085462015-09-10 17:47:16 +0000526
527 if (reg.isLocal() && reg.toLocal() < static_cast<int>(baselineVirtualRegistersForCalleeSaves))
528 continue;
529
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000530 jit.load64(scratch + index, GPRInfo::regT0);
msaboff@apple.com02085462015-09-10 17:47:16 +0000531 jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(reg));
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000532 }
533
534 handleExitCounts(jit, exit);
535 reifyInlinedCallFrames(jit, exit);
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000536 adjustAndJumpToTarget(jit, exit, exit.m_isExceptionHandler);
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000537
benjamin@webkit.orgf766fd92014-07-08 04:23:30 +0000538 LinkBuffer patchBuffer(*vm, jit, codeBlock);
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000539 exit.m_code = FINALIZE_CODE_IF(
mark.lam@apple.comee3c4102015-10-14 18:57:07 +0000540 shouldDumpDisassembly() || Options::verboseOSR() || Options::verboseFTLOSRExit(),
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000541 patchBuffer,
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000542#if FTL_USES_B3
543 ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s",
544 exitID, toCString(exit.m_codeOrigin).data(),
545 exitKindToString(exit.m_kind), toCString(*codeBlock).data(),
546 toCString(ignoringContext<DumpContext>(exit.m_descriptor->m_values)).data())
547#else // FTL_USES_B3
fpizlo@apple.comd2ceb392013-11-11 07:30:50 +0000548 ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s, and record = %s",
549 exitID, toCString(exit.m_codeOrigin).data(),
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000550 exitKindToString(exit.m_kind), toCString(*codeBlock).data(),
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000551 toCString(ignoringContext<DumpContext>(exit.m_descriptor->m_values)).data(),
552 toCString(*record).data())
553#endif // FTL_USES_B3
554 );
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000555}
556
oliver@apple.comea771492013-07-25 03:58:38 +0000557extern "C" void* compileFTLOSRExit(ExecState* exec, unsigned exitID)
558{
559 SamplingRegion samplingRegion("FTL OSR Exit Compilation");
mhahnenberg@apple.com2385e392014-02-27 01:09:39 +0000560
mark.lam@apple.comee3c4102015-10-14 18:57:07 +0000561 if (shouldDumpDisassembly() || Options::verboseOSR() || Options::verboseFTLOSRExit())
mhahnenberg@apple.com2385e392014-02-27 01:09:39 +0000562 dataLog("Compiling OSR exit with exitID = ", exitID, "\n");
fpizlo@apple.com41bed902014-02-25 22:18:21 +0000563
oliver@apple.comea771492013-07-25 03:58:38 +0000564 CodeBlock* codeBlock = exec->codeBlock();
565
566 ASSERT(codeBlock);
oliver@apple.com5a24fdd2013-07-25 04:00:54 +0000567 ASSERT(codeBlock->jitType() == JITCode::FTLJIT);
oliver@apple.comea771492013-07-25 03:58:38 +0000568
569 VM* vm = &exec->vm();
570
fpizlo@apple.comfe6bd742013-09-17 20:56:57 +0000571 // It's sort of preferable that we don't GC while in here. Anyways, doing so wouldn't
572 // really be profitable.
573 DeferGCForAWhile deferGC(vm->heap);
574
fpizlo@apple.comea92c202013-10-10 04:24:57 +0000575 JITCode* jitCode = codeBlock->jitCode()->ftl();
576 OSRExit& exit = jitCode->osrExit[exitID];
oliver@apple.comea771492013-07-25 03:58:38 +0000577
mark.lam@apple.comee3c4102015-10-14 18:57:07 +0000578 if (shouldDumpDisassembly() || Options::verboseOSR() || Options::verboseFTLOSRExit()) {
fpizlo@apple.com09b14f92015-05-13 05:21:16 +0000579 dataLog(" Owning block: ", pointerDump(codeBlock), "\n");
580 dataLog(" Origin: ", exit.m_codeOrigin, "\n");
581 if (exit.m_codeOriginForExitProfile != exit.m_codeOrigin)
582 dataLog(" Origin for exit profile: ", exit.m_codeOriginForExitProfile, "\n");
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000583 dataLog(" Exit stackmap ID: ", exit.m_descriptor->m_stackmapID, "\n");
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000584 dataLog(" Current call site index: ", exec->callSiteIndex().bits(), "\n");
585 dataLog(" Exit is exception handler: ", exit.m_isExceptionHandler,
sbarati@apple.comb887f0d2015-12-01 00:55:32 +0000586 " will arrive at exit from genericUnwind(): ", exit.willArriveAtOSRExitFromGenericUnwind(),
sbarati@apple.com936707d2015-12-03 21:09:41 +0000587 " will arrive at exit from lazy slow path: ", exit.m_exceptionType == ExceptionType::LazySlowPath, "\n");
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000588 dataLog(" Exit values: ", exit.m_descriptor->m_values, "\n");
589 if (!exit.m_descriptor->m_materializations.isEmpty()) {
fpizlo@apple.com09b14f92015-05-13 05:21:16 +0000590 dataLog(" Materializations:\n");
fpizlo@apple.come362fbc2015-12-03 20:01:57 +0000591 for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor->m_materializations)
fpizlo@apple.com09b14f92015-05-13 05:21:16 +0000592 dataLog(" ", pointerDump(materialization), "\n");
593 }
594 }
595
oliver@apple.comea771492013-07-25 03:58:38 +0000596 prepareCodeOriginForOSRExit(exec, exit.m_codeOrigin);
597
fpizlo@apple.com6bf11982013-11-03 18:24:20 +0000598 compileStub(exitID, jitCode, exit, vm, codeBlock);
sbarati@apple.com5bebda72015-11-10 07:48:54 +0000599
fpizlo@apple.com7a797262015-09-03 21:11:59 +0000600 MacroAssembler::repatchJump(
oliver@apple.comea771492013-07-25 03:58:38 +0000601 exit.codeLocationForRepatch(codeBlock), CodeLocationLabel(exit.m_code.code()));
602
603 return exit.m_code.code().executableAddress();
604}
605
606} } // namespace JSC::FTL
607
608#endif // ENABLE(FTL_JIT)
609