1/*
2 * Copyright (C) 2013-2019 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 "JITOperations.h"
28
29#if ENABLE(JIT)
30
31#include "ArithProfile.h"
32#include "ArrayConstructor.h"
33#include "CommonSlowPaths.h"
34#include "DFGCompilationMode.h"
35#include "DFGDriver.h"
36#include "DFGOSREntry.h"
37#include "DFGThunks.h"
38#include "DFGWorklist.h"
39#include "Debugger.h"
40#include "DirectArguments.h"
41#include "Error.h"
42#include "ErrorHandlingScope.h"
43#include "EvalCodeBlock.h"
44#include "ExceptionFuzz.h"
45#include "ExecutableBaseInlines.h"
46#include "FTLOSREntry.h"
47#include "FrameTracers.h"
48#include "FunctionCodeBlock.h"
49#include "GetterSetter.h"
50#include "HostCallReturnValue.h"
51#include "ICStats.h"
52#include "Interpreter.h"
53#include "JIT.h"
54#include "JITExceptions.h"
55#include "JITToDFGDeferredCompilationCallback.h"
56#include "JSAsyncFunction.h"
57#include "JSAsyncGeneratorFunction.h"
58#include "JSCInlines.h"
59#include "JSCPtrTag.h"
60#include "JSGeneratorFunction.h"
61#include "JSGlobalObjectFunctions.h"
62#include "JSLexicalEnvironment.h"
63#include "JSWithScope.h"
64#include "ModuleProgramCodeBlock.h"
65#include "ObjectConstructor.h"
66#include "PolymorphicAccess.h"
67#include "ProgramCodeBlock.h"
68#include "PropertyName.h"
69#include "RegExpObject.h"
70#include "Repatch.h"
71#include "ScopedArguments.h"
72#include "ShadowChicken.h"
73#include "StructureStubInfo.h"
74#include "SuperSampler.h"
75#include "TestRunnerUtils.h"
76#include "ThunkGenerators.h"
77#include "TypeProfilerLog.h"
78#include "VMInlines.h"
79#include "WebAssemblyFunction.h"
80#include <wtf/InlineASM.h>
81
82namespace JSC {
83
84extern "C" {
85
86#if COMPILER(MSVC)
87void * _ReturnAddress(void);
88#pragma intrinsic(_ReturnAddress)
89
90#define OUR_RETURN_ADDRESS _ReturnAddress()
91#else
92#define OUR_RETURN_ADDRESS __builtin_return_address(0)
93#endif
94
95#if ENABLE(OPCODE_SAMPLING)
96#define CTI_SAMPLER vm->interpreter->sampler()
97#else
98#define CTI_SAMPLER 0
99#endif
100
101
102void JIT_OPERATION operationThrowStackOverflowError(ExecState* exec, CodeBlock* codeBlock)
103{
104 // We pass in our own code block, because the callframe hasn't been populated.
105 VM* vm = codeBlock->vm();
106 auto scope = DECLARE_THROW_SCOPE(*vm);
107 exec->convertToStackOverflowFrame(*vm, codeBlock);
108 NativeCallFrameTracer tracer(vm, exec);
109 throwStackOverflowError(exec, scope);
110}
111
112int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
113{
114 VM* vm = &exec->vm();
115 auto scope = DECLARE_THROW_SCOPE(*vm);
116
117 int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, *vm, CodeForCall);
118 if (UNLIKELY(missingArgCount < 0)) {
119 CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(exec, CodeForCall);
120 exec->convertToStackOverflowFrame(*vm, codeBlock);
121 NativeCallFrameTracer tracer(vm, exec);
122 throwStackOverflowError(vm->topCallFrame, scope);
123 }
124
125 return missingArgCount;
126}
127
128int32_t JIT_OPERATION operationConstructArityCheck(ExecState* exec)
129{
130 VM* vm = &exec->vm();
131 auto scope = DECLARE_THROW_SCOPE(*vm);
132
133 int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, *vm, CodeForConstruct);
134 if (UNLIKELY(missingArgCount < 0)) {
135 CodeBlock* codeBlock = CommonSlowPaths::codeBlockFromCallFrameCallee(exec, CodeForConstruct);
136 exec->convertToStackOverflowFrame(*vm, codeBlock);
137 NativeCallFrameTracer tracer(vm, exec);
138 throwStackOverflowError(vm->topCallFrame, scope);
139 }
140
141 return missingArgCount;
142}
143
144EncodedJSValue JIT_OPERATION operationTryGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
145{
146 VM* vm = &exec->vm();
147 NativeCallFrameTracer tracer(vm, exec);
148 Identifier ident = Identifier::fromUid(vm, uid);
149 stubInfo->tookSlowPath = true;
150
151 JSValue baseValue = JSValue::decode(base);
152 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
153 baseValue.getPropertySlot(exec, ident, slot);
154
155 return JSValue::encode(slot.getPureResult());
156}
157
158
159EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
160{
161 VM* vm = &exec->vm();
162 NativeCallFrameTracer tracer(vm, exec);
163 Identifier ident = Identifier::fromUid(vm, uid);
164
165 JSValue baseValue = JSValue::decode(base);
166 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
167 baseValue.getPropertySlot(exec, ident, slot);
168
169 return JSValue::encode(slot.getPureResult());
170}
171
172EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
173{
174 VM* vm = &exec->vm();
175 NativeCallFrameTracer tracer(vm, exec);
176 auto scope = DECLARE_THROW_SCOPE(*vm);
177 Identifier ident = Identifier::fromUid(vm, uid);
178
179 JSValue baseValue = JSValue::decode(base);
180 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
181
182 baseValue.getPropertySlot(exec, ident, slot);
183 RETURN_IF_EXCEPTION(scope, encodedJSValue());
184
185 if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()) && !slot.isTaintedByOpaqueObject() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset()))
186 repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Try);
187
188 return JSValue::encode(slot.getPureResult());
189}
190
191EncodedJSValue JIT_OPERATION operationGetByIdDirect(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
192{
193 VM& vm = exec->vm();
194 NativeCallFrameTracer tracer(&vm, exec);
195 auto scope = DECLARE_THROW_SCOPE(vm);
196 Identifier ident = Identifier::fromUid(&vm, uid);
197 stubInfo->tookSlowPath = true;
198
199 JSValue baseValue = JSValue::decode(base);
200 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
201
202 bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
203 RETURN_IF_EXCEPTION(scope, encodedJSValue());
204
205 RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined()));
206}
207
208EncodedJSValue JIT_OPERATION operationGetByIdDirectGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
209{
210 VM& vm = exec->vm();
211 NativeCallFrameTracer tracer(&vm, exec);
212 auto scope = DECLARE_THROW_SCOPE(vm);
213 Identifier ident = Identifier::fromUid(&vm, uid);
214
215 JSValue baseValue = JSValue::decode(base);
216 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
217
218 bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
219 RETURN_IF_EXCEPTION(scope, encodedJSValue());
220
221 RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined()));
222}
223
224EncodedJSValue JIT_OPERATION operationGetByIdDirectOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
225{
226 VM& vm = exec->vm();
227 NativeCallFrameTracer tracer(&vm, exec);
228 auto scope = DECLARE_THROW_SCOPE(vm);
229 Identifier ident = Identifier::fromUid(&vm, uid);
230
231 JSValue baseValue = JSValue::decode(base);
232 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty);
233
234 bool found = baseValue.getOwnPropertySlot(exec, ident, slot);
235 RETURN_IF_EXCEPTION(scope, encodedJSValue());
236
237 if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()))
238 repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Direct);
239
240 RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(exec, ident) : jsUndefined()));
241}
242
243EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
244{
245 SuperSamplerScope superSamplerScope(false);
246
247 VM* vm = &exec->vm();
248 NativeCallFrameTracer tracer(vm, exec);
249
250 stubInfo->tookSlowPath = true;
251
252 JSValue baseValue = JSValue::decode(base);
253 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
254 Identifier ident = Identifier::fromUid(vm, uid);
255 JSValue result = baseValue.get(exec, ident, slot);
256
257 LOG_IC((ICEvent::OperationGetById, baseValue.classInfoOrNull(*vm), ident, baseValue == slot.slotBase()));
258
259 return JSValue::encode(result);
260}
261
262EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
263{
264 SuperSamplerScope superSamplerScope(false);
265
266 VM* vm = &exec->vm();
267 NativeCallFrameTracer tracer(vm, exec);
268
269 JSValue baseValue = JSValue::decode(base);
270 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
271 Identifier ident = Identifier::fromUid(vm, uid);
272 JSValue result = baseValue.get(exec, ident, slot);
273
274 LOG_IC((ICEvent::OperationGetByIdGeneric, baseValue.classInfoOrNull(*vm), ident, baseValue == slot.slotBase()));
275
276 return JSValue::encode(result);
277}
278
279EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
280{
281 SuperSamplerScope superSamplerScope(false);
282
283 VM* vm = &exec->vm();
284 NativeCallFrameTracer tracer(vm, exec);
285 Identifier ident = Identifier::fromUid(vm, uid);
286
287 JSValue baseValue = JSValue::decode(base);
288
289 return JSValue::encode(baseValue.getPropertySlot(exec, ident, [&] (bool found, PropertySlot& slot) -> JSValue {
290
291 LOG_IC((ICEvent::OperationGetByIdOptimize, baseValue.classInfoOrNull(*vm), ident, baseValue == slot.slotBase()));
292
293 if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()))
294 repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Normal);
295 return found ? slot.getValue(exec, ident) : jsUndefined();
296 }));
297}
298
299EncodedJSValue JIT_OPERATION operationGetByIdWithThis(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
300{
301 SuperSamplerScope superSamplerScope(false);
302
303 VM* vm = &exec->vm();
304 NativeCallFrameTracer tracer(vm, exec);
305 Identifier ident = Identifier::fromUid(vm, uid);
306
307 stubInfo->tookSlowPath = true;
308
309 JSValue baseValue = JSValue::decode(base);
310 JSValue thisValue = JSValue::decode(thisEncoded);
311 PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get);
312
313 return JSValue::encode(baseValue.get(exec, ident, slot));
314}
315
316EncodedJSValue JIT_OPERATION operationGetByIdWithThisGeneric(ExecState* exec, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
317{
318 SuperSamplerScope superSamplerScope(false);
319
320 VM* vm = &exec->vm();
321 NativeCallFrameTracer tracer(vm, exec);
322 Identifier ident = Identifier::fromUid(vm, uid);
323
324 JSValue baseValue = JSValue::decode(base);
325 JSValue thisValue = JSValue::decode(thisEncoded);
326 PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get);
327
328 return JSValue::encode(baseValue.get(exec, ident, slot));
329}
330
331EncodedJSValue JIT_OPERATION operationGetByIdWithThisOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, UniquedStringImpl* uid)
332{
333 SuperSamplerScope superSamplerScope(false);
334
335 VM* vm = &exec->vm();
336 NativeCallFrameTracer tracer(vm, exec);
337 Identifier ident = Identifier::fromUid(vm, uid);
338
339 JSValue baseValue = JSValue::decode(base);
340 JSValue thisValue = JSValue::decode(thisEncoded);
341
342 PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get);
343 return JSValue::encode(baseValue.getPropertySlot(exec, ident, slot, [&] (bool found, PropertySlot& slot) -> JSValue {
344 LOG_IC((ICEvent::OperationGetByIdWithThisOptimize, baseValue.classInfoOrNull(*vm), ident, baseValue == slot.slotBase()));
345
346 if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()))
347 repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::WithThis);
348 return found ? slot.getValue(exec, ident) : jsUndefined();
349 }));
350}
351
352EncodedJSValue JIT_OPERATION operationInById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
353{
354 SuperSamplerScope superSamplerScope(false);
355
356 VM& vm = exec->vm();
357 NativeCallFrameTracer tracer(&vm, exec);
358 auto scope = DECLARE_THROW_SCOPE(vm);
359
360 stubInfo->tookSlowPath = true;
361
362 Identifier ident = Identifier::fromUid(&vm, uid);
363
364 JSValue baseValue = JSValue::decode(base);
365 if (!baseValue.isObject()) {
366 throwException(exec, scope, createInvalidInParameterError(exec, baseValue));
367 return JSValue::encode(jsUndefined());
368 }
369 JSObject* baseObject = asObject(baseValue);
370
371 LOG_IC((ICEvent::OperationInById, baseObject->classInfo(vm), ident));
372
373 scope.release();
374 PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty);
375 return JSValue::encode(jsBoolean(baseObject->getPropertySlot(exec, ident, slot)));
376}
377
378EncodedJSValue JIT_OPERATION operationInByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
379{
380 SuperSamplerScope superSamplerScope(false);
381
382 VM& vm = exec->vm();
383 NativeCallFrameTracer tracer(&vm, exec);
384 auto scope = DECLARE_THROW_SCOPE(vm);
385
386 Identifier ident = Identifier::fromUid(&vm, uid);
387
388 JSValue baseValue = JSValue::decode(base);
389 if (!baseValue.isObject()) {
390 throwException(exec, scope, createInvalidInParameterError(exec, baseValue));
391 return JSValue::encode(jsUndefined());
392 }
393 JSObject* baseObject = asObject(baseValue);
394
395 LOG_IC((ICEvent::OperationInByIdGeneric, baseObject->classInfo(vm), ident));
396
397 scope.release();
398 PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty);
399 return JSValue::encode(jsBoolean(baseObject->getPropertySlot(exec, ident, slot)));
400}
401
402EncodedJSValue JIT_OPERATION operationInByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
403{
404 SuperSamplerScope superSamplerScope(false);
405
406 VM& vm = exec->vm();
407 NativeCallFrameTracer tracer(&vm, exec);
408 auto scope = DECLARE_THROW_SCOPE(vm);
409
410 Identifier ident = Identifier::fromUid(&vm, uid);
411
412 JSValue baseValue = JSValue::decode(base);
413 if (!baseValue.isObject()) {
414 throwException(exec, scope, createInvalidInParameterError(exec, baseValue));
415 return JSValue::encode(jsUndefined());
416 }
417 JSObject* baseObject = asObject(baseValue);
418
419 LOG_IC((ICEvent::OperationInByIdOptimize, baseObject->classInfo(vm), ident));
420
421 scope.release();
422 PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty);
423 bool found = baseObject->getPropertySlot(exec, ident, slot);
424 if (stubInfo->considerCaching(exec->codeBlock(), baseObject->structure(vm)))
425 repatchInByID(exec, baseObject, ident, found, slot, *stubInfo);
426 return JSValue::encode(jsBoolean(found));
427}
428
429EncodedJSValue JIT_OPERATION operationInByVal(ExecState* exec, JSCell* base, EncodedJSValue key)
430{
431 SuperSamplerScope superSamplerScope(false);
432
433 VM* vm = &exec->vm();
434 NativeCallFrameTracer tracer(vm, exec);
435
436 return JSValue::encode(jsBoolean(CommonSlowPaths::opInByVal(exec, base, JSValue::decode(key))));
437}
438
439void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
440{
441 SuperSamplerScope superSamplerScope(false);
442
443 VM* vm = &exec->vm();
444 NativeCallFrameTracer tracer(vm, exec);
445
446 stubInfo->tookSlowPath = true;
447
448 JSValue baseValue = JSValue::decode(encodedBase);
449 Identifier ident = Identifier::fromUid(vm, uid);
450 PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext());
451 baseValue.putInline(exec, ident, JSValue::decode(encodedValue), slot);
452
453 LOG_IC((ICEvent::OperationPutByIdStrict, baseValue.classInfoOrNull(*vm), ident, slot.base() == baseValue));
454}
455
456void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
457{
458 SuperSamplerScope superSamplerScope(false);
459
460 VM* vm = &exec->vm();
461 NativeCallFrameTracer tracer(vm, exec);
462
463 stubInfo->tookSlowPath = true;
464
465 JSValue baseValue = JSValue::decode(encodedBase);
466 Identifier ident = Identifier::fromUid(vm, uid);
467 PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext());
468 baseValue.putInline(exec, ident, JSValue::decode(encodedValue), slot);
469
470 LOG_IC((ICEvent::OperationPutByIdNonStrict, baseValue.classInfoOrNull(*vm), ident, slot.base() == baseValue));
471}
472
473void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
474{
475 SuperSamplerScope superSamplerScope(false);
476
477 VM& vm = exec->vm();
478 NativeCallFrameTracer tracer(&vm, exec);
479
480 stubInfo->tookSlowPath = true;
481
482 JSValue baseValue = JSValue::decode(encodedBase);
483 Identifier ident = Identifier::fromUid(&vm, uid);
484 PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext());
485 CommonSlowPaths::putDirectWithReify(vm, exec, asObject(baseValue), ident, JSValue::decode(encodedValue), slot);
486
487 LOG_IC((ICEvent::OperationPutByIdDirectStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue));
488}
489
490void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
491{
492 SuperSamplerScope superSamplerScope(false);
493
494 VM& vm = exec->vm();
495 NativeCallFrameTracer tracer(&vm, exec);
496
497 stubInfo->tookSlowPath = true;
498
499 JSValue baseValue = JSValue::decode(encodedBase);
500 Identifier ident = Identifier::fromUid(&vm, uid);
501 PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext());
502 CommonSlowPaths::putDirectWithReify(vm, exec, asObject(baseValue), ident, JSValue::decode(encodedValue), slot);
503
504 LOG_IC((ICEvent::OperationPutByIdDirectNonStrict, baseValue.classInfoOrNull(vm), ident, slot.base() == baseValue));
505}
506
507void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
508{
509 SuperSamplerScope superSamplerScope(false);
510
511 VM* vm = &exec->vm();
512 NativeCallFrameTracer tracer(vm, exec);
513 auto scope = DECLARE_THROW_SCOPE(*vm);
514
515 Identifier ident = Identifier::fromUid(vm, uid);
516 AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
517
518 JSValue value = JSValue::decode(encodedValue);
519 JSValue baseValue = JSValue::decode(encodedBase);
520 CodeBlock* codeBlock = exec->codeBlock();
521 PutPropertySlot slot(baseValue, true, codeBlock->putByIdContext());
522
523 Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr;
524 baseValue.putInline(exec, ident, value, slot);
525
526 LOG_IC((ICEvent::OperationPutByIdStrictOptimize, baseValue.classInfoOrNull(*vm), ident, slot.base() == baseValue));
527
528 RETURN_IF_EXCEPTION(scope, void());
529
530 if (accessType != static_cast<AccessType>(stubInfo->accessType))
531 return;
532
533 if (stubInfo->considerCaching(codeBlock, structure))
534 repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect);
535}
536
537void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
538{
539 SuperSamplerScope superSamplerScope(false);
540
541 VM* vm = &exec->vm();
542 NativeCallFrameTracer tracer(vm, exec);
543 auto scope = DECLARE_THROW_SCOPE(*vm);
544
545 Identifier ident = Identifier::fromUid(vm, uid);
546 AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
547
548 JSValue value = JSValue::decode(encodedValue);
549 JSValue baseValue = JSValue::decode(encodedBase);
550 CodeBlock* codeBlock = exec->codeBlock();
551 PutPropertySlot slot(baseValue, false, codeBlock->putByIdContext());
552
553 Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr;
554 baseValue.putInline(exec, ident, value, slot);
555
556 LOG_IC((ICEvent::OperationPutByIdNonStrictOptimize, baseValue.classInfoOrNull(*vm), ident, slot.base() == baseValue));
557
558 RETURN_IF_EXCEPTION(scope, void());
559
560 if (accessType != static_cast<AccessType>(stubInfo->accessType))
561 return;
562
563 if (stubInfo->considerCaching(codeBlock, structure))
564 repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect);
565}
566
567void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
568{
569 SuperSamplerScope superSamplerScope(false);
570
571 VM& vm = exec->vm();
572 NativeCallFrameTracer tracer(&vm, exec);
573 auto scope = DECLARE_THROW_SCOPE(vm);
574
575 Identifier ident = Identifier::fromUid(&vm, uid);
576 AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
577
578 JSValue value = JSValue::decode(encodedValue);
579 JSObject* baseObject = asObject(JSValue::decode(encodedBase));
580 CodeBlock* codeBlock = exec->codeBlock();
581 PutPropertySlot slot(baseObject, true, codeBlock->putByIdContext());
582 Structure* structure = nullptr;
583 CommonSlowPaths::putDirectWithReify(vm, exec, baseObject, ident, value, slot, &structure);
584
585 LOG_IC((ICEvent::OperationPutByIdDirectStrictOptimize, baseObject->classInfo(vm), ident, slot.base() == baseObject));
586
587 RETURN_IF_EXCEPTION(scope, void());
588
589 if (accessType != static_cast<AccessType>(stubInfo->accessType))
590 return;
591
592 if (stubInfo->considerCaching(codeBlock, structure))
593 repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct);
594}
595
596void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
597{
598 SuperSamplerScope superSamplerScope(false);
599
600 VM& vm = exec->vm();
601 NativeCallFrameTracer tracer(&vm, exec);
602 auto scope = DECLARE_THROW_SCOPE(vm);
603
604 Identifier ident = Identifier::fromUid(&vm, uid);
605 AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
606
607 JSValue value = JSValue::decode(encodedValue);
608 JSObject* baseObject = asObject(JSValue::decode(encodedBase));
609 CodeBlock* codeBlock = exec->codeBlock();
610 PutPropertySlot slot(baseObject, false, codeBlock->putByIdContext());
611 Structure* structure = nullptr;
612 CommonSlowPaths::putDirectWithReify(vm, exec, baseObject, ident, value, slot, &structure);
613
614 LOG_IC((ICEvent::OperationPutByIdDirectNonStrictOptimize, baseObject->classInfo(vm), ident, slot.base() == baseObject));
615
616 RETURN_IF_EXCEPTION(scope, void());
617
618 if (accessType != static_cast<AccessType>(stubInfo->accessType))
619 return;
620
621 if (stubInfo->considerCaching(codeBlock, structure))
622 repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct);
623}
624
625ALWAYS_INLINE static bool isStringOrSymbol(JSValue value)
626{
627 return value.isString() || value.isSymbol();
628}
629
630static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value, ByValInfo* byValInfo)
631{
632 VM& vm = callFrame->vm();
633 auto scope = DECLARE_THROW_SCOPE(vm);
634 if (LIKELY(subscript.isUInt32())) {
635 byValInfo->tookSlowPath = true;
636 uint32_t i = subscript.asUInt32();
637 if (baseValue.isObject()) {
638 JSObject* object = asObject(baseValue);
639 if (object->canSetIndexQuickly(i)) {
640 object->setIndexQuickly(vm, i, value);
641 return;
642 }
643
644 // FIXME: This will make us think that in-bounds typed array accesses are actually
645 // out-of-bounds.
646 // https://bugs.webkit.org/show_bug.cgi?id=149886
647 byValInfo->arrayProfile->setOutOfBounds();
648 scope.release();
649 object->methodTable(vm)->putByIndex(object, callFrame, i, value, callFrame->codeBlock()->isStrictMode());
650 return;
651 }
652
653 scope.release();
654 baseValue.putByIndex(callFrame, i, value, callFrame->codeBlock()->isStrictMode());
655 return;
656 }
657
658 auto property = subscript.toPropertyKey(callFrame);
659 // Don't put to an object if toString threw an exception.
660 RETURN_IF_EXCEPTION(scope, void());
661
662 if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property))
663 byValInfo->tookSlowPath = true;
664
665 scope.release();
666 PutPropertySlot slot(baseValue, callFrame->codeBlock()->isStrictMode());
667 baseValue.putInline(callFrame, property, value, slot);
668}
669
670static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value, ByValInfo* byValInfo)
671{
672 VM& vm = callFrame->vm();
673 auto scope = DECLARE_THROW_SCOPE(vm);
674 bool isStrictMode = callFrame->codeBlock()->isStrictMode();
675
676 if (LIKELY(subscript.isUInt32())) {
677 // Despite its name, JSValue::isUInt32 will return true only for positive boxed int32_t; all those values are valid array indices.
678 byValInfo->tookSlowPath = true;
679 uint32_t index = subscript.asUInt32();
680 ASSERT(isIndex(index));
681
682 switch (baseObject->indexingType()) {
683 case ALL_INT32_INDEXING_TYPES:
684 case ALL_DOUBLE_INDEXING_TYPES:
685 case ALL_CONTIGUOUS_INDEXING_TYPES:
686 case ALL_ARRAY_STORAGE_INDEXING_TYPES:
687 if (index < baseObject->butterfly()->vectorLength())
688 break;
689 FALLTHROUGH;
690 default:
691 byValInfo->arrayProfile->setOutOfBounds();
692 break;
693 }
694
695 scope.release();
696 baseObject->putDirectIndex(callFrame, index, value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
697 return;
698 }
699
700 if (subscript.isDouble()) {
701 double subscriptAsDouble = subscript.asDouble();
702 uint32_t subscriptAsUInt32 = static_cast<uint32_t>(subscriptAsDouble);
703 if (subscriptAsDouble == subscriptAsUInt32 && isIndex(subscriptAsUInt32)) {
704 byValInfo->tookSlowPath = true;
705 scope.release();
706 baseObject->putDirectIndex(callFrame, subscriptAsUInt32, value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
707 return;
708 }
709 }
710
711 // Don't put to an object if toString threw an exception.
712 auto property = subscript.toPropertyKey(callFrame);
713 RETURN_IF_EXCEPTION(scope, void());
714
715 if (Optional<uint32_t> index = parseIndex(property)) {
716 byValInfo->tookSlowPath = true;
717 scope.release();
718 baseObject->putDirectIndex(callFrame, index.value(), value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
719 return;
720 }
721
722 if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property))
723 byValInfo->tookSlowPath = true;
724
725 scope.release();
726 PutPropertySlot slot(baseObject, isStrictMode);
727 CommonSlowPaths::putDirectWithReify(vm, callFrame, baseObject, property, value, slot);
728}
729
730enum class OptimizationResult {
731 NotOptimized,
732 SeenOnce,
733 Optimized,
734 GiveUp,
735};
736
737static OptimizationResult tryPutByValOptimize(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
738{
739 // See if it's worth optimizing at all.
740 OptimizationResult optimizationResult = OptimizationResult::NotOptimized;
741
742 VM& vm = exec->vm();
743 auto scope = DECLARE_THROW_SCOPE(vm);
744
745 if (baseValue.isObject() && isCopyOnWrite(baseValue.getObject()->indexingMode()))
746 return OptimizationResult::GiveUp;
747
748 if (baseValue.isObject() && subscript.isInt32()) {
749 JSObject* object = asObject(baseValue);
750
751 ASSERT(exec->bytecodeOffset());
752 ASSERT(!byValInfo->stubRoutine);
753
754 Structure* structure = object->structure(vm);
755 if (hasOptimizableIndexing(structure)) {
756 // Attempt to optimize.
757 JITArrayMode arrayMode = jitArrayModeForStructure(structure);
758 if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo->arrayMode) {
759 CodeBlock* codeBlock = exec->codeBlock();
760 ConcurrentJSLocker locker(codeBlock->m_lock);
761 byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
762 JIT::compilePutByVal(locker, &vm, codeBlock, byValInfo, returnAddress, arrayMode);
763 optimizationResult = OptimizationResult::Optimized;
764 }
765 }
766
767 // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times.
768 if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero())
769 optimizationResult = OptimizationResult::GiveUp;
770 }
771
772 if (baseValue.isObject() && isStringOrSymbol(subscript)) {
773 const Identifier propertyName = subscript.toPropertyKey(exec);
774 RETURN_IF_EXCEPTION(scope, OptimizationResult::GiveUp);
775 if (subscript.isSymbol() || !parseIndex(propertyName)) {
776 ASSERT(exec->bytecodeOffset());
777 ASSERT(!byValInfo->stubRoutine);
778 if (byValInfo->seen) {
779 if (byValInfo->cachedId == propertyName) {
780 JIT::compilePutByValWithCachedId<OpPutByVal>(&vm, exec->codeBlock(), byValInfo, returnAddress, NotDirect, propertyName);
781 optimizationResult = OptimizationResult::Optimized;
782 } else {
783 // Seem like a generic property access site.
784 optimizationResult = OptimizationResult::GiveUp;
785 }
786 } else {
787 CodeBlock* codeBlock = exec->codeBlock();
788 ConcurrentJSLocker locker(codeBlock->m_lock);
789 byValInfo->seen = true;
790 byValInfo->cachedId = propertyName;
791 if (subscript.isSymbol())
792 byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
793 optimizationResult = OptimizationResult::SeenOnce;
794 }
795 }
796 }
797
798 if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) {
799 // If we take slow path more than 10 times without patching then make sure we
800 // never make that mistake again. For cases where we see non-index-intercepting
801 // objects, this gives 10 iterations worth of opportunity for us to observe
802 // that the put_by_val may be polymorphic. We count up slowPathCount even if
803 // the result is GiveUp.
804 if (++byValInfo->slowPathCount >= 10)
805 optimizationResult = OptimizationResult::GiveUp;
806 }
807
808 return optimizationResult;
809}
810
811void JIT_OPERATION operationPutByValOptimize(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
812{
813 VM& vm = exec->vm();
814 NativeCallFrameTracer tracer(&vm, exec);
815 auto scope = DECLARE_THROW_SCOPE(vm);
816
817 JSValue baseValue = JSValue::decode(encodedBaseValue);
818 JSValue subscript = JSValue::decode(encodedSubscript);
819 JSValue value = JSValue::decode(encodedValue);
820 OptimizationResult result = tryPutByValOptimize(exec, baseValue, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS));
821 RETURN_IF_EXCEPTION(scope, void());
822 if (result == OptimizationResult::GiveUp) {
823 // Don't ever try to optimize.
824 byValInfo->tookSlowPath = true;
825 ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), operationPutByValGeneric);
826 }
827 RELEASE_AND_RETURN(scope, putByVal(exec, baseValue, subscript, value, byValInfo));
828}
829
830static OptimizationResult tryDirectPutByValOptimize(ExecState* exec, JSObject* object, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
831{
832 // See if it's worth optimizing at all.
833 OptimizationResult optimizationResult = OptimizationResult::NotOptimized;
834
835 VM& vm = exec->vm();
836 auto scope = DECLARE_THROW_SCOPE(vm);
837
838 if (subscript.isInt32()) {
839 ASSERT(exec->bytecodeOffset());
840 ASSERT(!byValInfo->stubRoutine);
841
842 Structure* structure = object->structure(vm);
843 if (hasOptimizableIndexing(structure)) {
844 // Attempt to optimize.
845 JITArrayMode arrayMode = jitArrayModeForStructure(structure);
846 if (jitArrayModePermitsPutDirect(arrayMode) && arrayMode != byValInfo->arrayMode) {
847 CodeBlock* codeBlock = exec->codeBlock();
848 ConcurrentJSLocker locker(codeBlock->m_lock);
849 byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
850
851 JIT::compileDirectPutByVal(locker, &vm, codeBlock, byValInfo, returnAddress, arrayMode);
852 optimizationResult = OptimizationResult::Optimized;
853 }
854 }
855
856 // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times.
857 if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero())
858 optimizationResult = OptimizationResult::GiveUp;
859 } else if (isStringOrSymbol(subscript)) {
860 const Identifier propertyName = subscript.toPropertyKey(exec);
861 RETURN_IF_EXCEPTION(scope, OptimizationResult::GiveUp);
862 if (subscript.isSymbol() || !parseIndex(propertyName)) {
863 ASSERT(exec->bytecodeOffset());
864 ASSERT(!byValInfo->stubRoutine);
865 if (byValInfo->seen) {
866 if (byValInfo->cachedId == propertyName) {
867 JIT::compilePutByValWithCachedId<OpPutByValDirect>(&vm, exec->codeBlock(), byValInfo, returnAddress, Direct, propertyName);
868 optimizationResult = OptimizationResult::Optimized;
869 } else {
870 // Seem like a generic property access site.
871 optimizationResult = OptimizationResult::GiveUp;
872 }
873 } else {
874 CodeBlock* codeBlock = exec->codeBlock();
875 ConcurrentJSLocker locker(codeBlock->m_lock);
876 byValInfo->seen = true;
877 byValInfo->cachedId = propertyName;
878 if (subscript.isSymbol())
879 byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
880 optimizationResult = OptimizationResult::SeenOnce;
881 }
882 }
883 }
884
885 if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) {
886 // If we take slow path more than 10 times without patching then make sure we
887 // never make that mistake again. For cases where we see non-index-intercepting
888 // objects, this gives 10 iterations worth of opportunity for us to observe
889 // that the get_by_val may be polymorphic. We count up slowPathCount even if
890 // the result is GiveUp.
891 if (++byValInfo->slowPathCount >= 10)
892 optimizationResult = OptimizationResult::GiveUp;
893 }
894
895 return optimizationResult;
896}
897
898void JIT_OPERATION operationDirectPutByValOptimize(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
899{
900 VM& vm = exec->vm();
901 NativeCallFrameTracer tracer(&vm, exec);
902 auto scope = DECLARE_THROW_SCOPE(vm);
903
904 JSValue baseValue = JSValue::decode(encodedBaseValue);
905 JSValue subscript = JSValue::decode(encodedSubscript);
906 JSValue value = JSValue::decode(encodedValue);
907 RELEASE_ASSERT(baseValue.isObject());
908 JSObject* object = asObject(baseValue);
909 OptimizationResult result = tryDirectPutByValOptimize(exec, object, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS));
910 RETURN_IF_EXCEPTION(scope, void());
911 if (result == OptimizationResult::GiveUp) {
912 // Don't ever try to optimize.
913 byValInfo->tookSlowPath = true;
914 ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), operationDirectPutByValGeneric);
915 }
916
917 RELEASE_AND_RETURN(scope, directPutByVal(exec, object, subscript, value, byValInfo));
918}
919
920void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
921{
922 VM& vm = exec->vm();
923 NativeCallFrameTracer tracer(&vm, exec);
924
925 JSValue baseValue = JSValue::decode(encodedBaseValue);
926 JSValue subscript = JSValue::decode(encodedSubscript);
927 JSValue value = JSValue::decode(encodedValue);
928
929 putByVal(exec, baseValue, subscript, value, byValInfo);
930}
931
932
933void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
934{
935 VM& vm = exec->vm();
936 NativeCallFrameTracer tracer(&vm, exec);
937
938 JSValue baseValue = JSValue::decode(encodedBaseValue);
939 JSValue subscript = JSValue::decode(encodedSubscript);
940 JSValue value = JSValue::decode(encodedValue);
941 RELEASE_ASSERT(baseValue.isObject());
942 directPutByVal(exec, asObject(baseValue), subscript, value, byValInfo);
943}
944
945EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execCallee)
946{
947 VM* vm = &exec->vm();
948 auto scope = DECLARE_THROW_SCOPE(*vm);
949
950 execCallee->setCodeBlock(0);
951
952 if (!isHostFunction(execCallee->guaranteedJSValueCallee(), globalFuncEval))
953 return JSValue::encode(JSValue());
954
955 JSValue result = eval(execCallee);
956 RETURN_IF_EXCEPTION(scope, encodedJSValue());
957
958 return JSValue::encode(result);
959}
960
961static SlowPathReturnType handleHostCall(ExecState* execCallee, JSValue callee, CallLinkInfo* callLinkInfo)
962{
963 ExecState* exec = execCallee->callerFrame();
964 VM* vm = &exec->vm();
965 auto scope = DECLARE_THROW_SCOPE(*vm);
966
967 execCallee->setCodeBlock(0);
968
969 if (callLinkInfo->specializationKind() == CodeForCall) {
970 CallData callData;
971 CallType callType = getCallData(*vm, callee, callData);
972
973 ASSERT(callType != CallType::JS);
974
975 if (callType == CallType::Host) {
976 NativeCallFrameTracer tracer(vm, execCallee);
977 execCallee->setCallee(asObject(callee));
978 vm->hostCallReturnValue = JSValue::decode(callData.native.function(execCallee));
979 if (UNLIKELY(scope.exception())) {
980 return encodeResult(
981 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
982 reinterpret_cast<void*>(KeepTheFrame));
983 }
984
985 return encodeResult(
986 tagCFunctionPtr<void*, JSEntryPtrTag>(getHostCallReturnValue),
987 reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
988 }
989
990 ASSERT(callType == CallType::None);
991 throwException(exec, scope, createNotAFunctionError(exec, callee));
992 return encodeResult(
993 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
994 reinterpret_cast<void*>(KeepTheFrame));
995 }
996
997 ASSERT(callLinkInfo->specializationKind() == CodeForConstruct);
998
999 ConstructData constructData;
1000 ConstructType constructType = getConstructData(*vm, callee, constructData);
1001
1002 ASSERT(constructType != ConstructType::JS);
1003
1004 if (constructType == ConstructType::Host) {
1005 NativeCallFrameTracer tracer(vm, execCallee);
1006 execCallee->setCallee(asObject(callee));
1007 vm->hostCallReturnValue = JSValue::decode(constructData.native.function(execCallee));
1008 if (UNLIKELY(scope.exception())) {
1009 return encodeResult(
1010 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
1011 reinterpret_cast<void*>(KeepTheFrame));
1012 }
1013
1014 return encodeResult(tagCFunctionPtr<void*, JSEntryPtrTag>(getHostCallReturnValue), reinterpret_cast<void*>(KeepTheFrame));
1015 }
1016
1017 ASSERT(constructType == ConstructType::None);
1018 throwException(exec, scope, createNotAConstructorError(exec, callee));
1019 return encodeResult(
1020 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
1021 reinterpret_cast<void*>(KeepTheFrame));
1022}
1023
1024SlowPathReturnType JIT_OPERATION operationLinkCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
1025{
1026 ExecState* exec = execCallee->callerFrame();
1027 VM* vm = &exec->vm();
1028 auto throwScope = DECLARE_THROW_SCOPE(*vm);
1029
1030 CodeSpecializationKind kind = callLinkInfo->specializationKind();
1031 NativeCallFrameTracer tracer(vm, exec);
1032
1033 RELEASE_ASSERT(!callLinkInfo->isDirect());
1034
1035 JSValue calleeAsValue = execCallee->guaranteedJSValueCallee();
1036 JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue);
1037 if (!calleeAsFunctionCell) {
1038 if (auto* internalFunction = jsDynamicCast<InternalFunction*>(*vm, calleeAsValue)) {
1039 MacroAssemblerCodePtr<JSEntryPtrTag> codePtr = vm->getCTIInternalFunctionTrampolineFor(kind);
1040 RELEASE_ASSERT(!!codePtr);
1041
1042 if (!callLinkInfo->seenOnce())
1043 callLinkInfo->setSeen();
1044 else
1045 linkFor(execCallee, *callLinkInfo, nullptr, internalFunction, codePtr);
1046
1047 void* linkedTarget = codePtr.executableAddress();
1048 return encodeResult(linkedTarget, reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
1049 }
1050 RELEASE_AND_RETURN(throwScope, handleHostCall(execCallee, calleeAsValue, callLinkInfo));
1051 }
1052
1053 JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
1054 JSScope* scope = callee->scopeUnchecked();
1055 ExecutableBase* executable = callee->executable();
1056
1057 MacroAssemblerCodePtr<JSEntryPtrTag> codePtr;
1058 CodeBlock* codeBlock = nullptr;
1059 if (executable->isHostFunction()) {
1060 codePtr = jsToWasmICCodePtr(*vm, kind, callee);
1061 if (!codePtr)
1062 codePtr = executable->entrypointFor(kind, MustCheckArity);
1063 } else {
1064 FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
1065
1066 auto handleThrowException = [&] () {
1067 void* throwTarget = vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress();
1068 return encodeResult(throwTarget, reinterpret_cast<void*>(KeepTheFrame));
1069 };
1070
1071 if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
1072 throwException(exec, throwScope, createNotAConstructorError(exec, callee));
1073 return handleThrowException();
1074 }
1075
1076 CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
1077 Exception* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, callee, scope, kind, *codeBlockSlot);
1078 EXCEPTION_ASSERT(throwScope.exception() == error);
1079 if (UNLIKELY(error))
1080 return handleThrowException();
1081 codeBlock = *codeBlockSlot;
1082 ArityCheckMode arity;
1083 if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs())
1084 arity = MustCheckArity;
1085 else
1086 arity = ArityCheckNotRequired;
1087 codePtr = functionExecutable->entrypointFor(kind, arity);
1088 }
1089
1090 if (!callLinkInfo->seenOnce())
1091 callLinkInfo->setSeen();
1092 else
1093 linkFor(execCallee, *callLinkInfo, codeBlock, callee, codePtr);
1094
1095 return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
1096}
1097
1098void JIT_OPERATION operationLinkDirectCall(ExecState* exec, CallLinkInfo* callLinkInfo, JSFunction* callee)
1099{
1100 VM* vm = &exec->vm();
1101 auto throwScope = DECLARE_THROW_SCOPE(*vm);
1102
1103 CodeSpecializationKind kind = callLinkInfo->specializationKind();
1104 NativeCallFrameTracer tracer(vm, exec);
1105
1106 RELEASE_ASSERT(callLinkInfo->isDirect());
1107
1108 // This would happen if the executable died during GC but the CodeBlock did not die. That should
1109 // not happen because the CodeBlock should have a weak reference to any executable it uses for
1110 // this purpose.
1111 RELEASE_ASSERT(callLinkInfo->executable());
1112
1113 // Having a CodeBlock indicates that this is linked. We shouldn't be taking this path if it's
1114 // linked.
1115 RELEASE_ASSERT(!callLinkInfo->codeBlock());
1116
1117 // We just don't support this yet.
1118 RELEASE_ASSERT(!callLinkInfo->isVarargs());
1119
1120 ExecutableBase* executable = callLinkInfo->executable();
1121 RELEASE_ASSERT(callee->executable() == callLinkInfo->executable());
1122
1123 JSScope* scope = callee->scopeUnchecked();
1124
1125 MacroAssemblerCodePtr<JSEntryPtrTag> codePtr;
1126 CodeBlock* codeBlock = nullptr;
1127 if (executable->isHostFunction())
1128 codePtr = executable->entrypointFor(kind, MustCheckArity);
1129 else {
1130 FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
1131
1132 RELEASE_ASSERT(isCall(kind) || functionExecutable->constructAbility() != ConstructAbility::CannotConstruct);
1133
1134 Exception* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, callee, scope, kind, codeBlock);
1135 EXCEPTION_ASSERT_UNUSED(throwScope, throwScope.exception() == error);
1136 if (UNLIKELY(error))
1137 return;
1138 unsigned argumentStackSlots = callLinkInfo->maxNumArguments();
1139 if (argumentStackSlots < static_cast<size_t>(codeBlock->numParameters()))
1140 codePtr = functionExecutable->entrypointFor(kind, MustCheckArity);
1141 else
1142 codePtr = functionExecutable->entrypointFor(kind, ArityCheckNotRequired);
1143 }
1144
1145 linkDirectFor(exec, *callLinkInfo, codeBlock, codePtr);
1146}
1147
1148inline SlowPathReturnType virtualForWithFunction(
1149 ExecState* execCallee, CallLinkInfo* callLinkInfo, JSCell*& calleeAsFunctionCell)
1150{
1151 ExecState* exec = execCallee->callerFrame();
1152 VM* vm = &exec->vm();
1153 auto throwScope = DECLARE_THROW_SCOPE(*vm);
1154
1155 CodeSpecializationKind kind = callLinkInfo->specializationKind();
1156 NativeCallFrameTracer tracer(vm, exec);
1157
1158 JSValue calleeAsValue = execCallee->guaranteedJSValueCallee();
1159 calleeAsFunctionCell = getJSFunction(calleeAsValue);
1160 if (UNLIKELY(!calleeAsFunctionCell)) {
1161 if (jsDynamicCast<InternalFunction*>(*vm, calleeAsValue)) {
1162 MacroAssemblerCodePtr<JSEntryPtrTag> codePtr = vm->getCTIInternalFunctionTrampolineFor(kind);
1163 ASSERT(!!codePtr);
1164 return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
1165 }
1166 RELEASE_AND_RETURN(throwScope, handleHostCall(execCallee, calleeAsValue, callLinkInfo));
1167 }
1168
1169 JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell);
1170 JSScope* scope = function->scopeUnchecked();
1171 ExecutableBase* executable = function->executable();
1172 if (UNLIKELY(!executable->hasJITCodeFor(kind))) {
1173 FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
1174
1175 if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
1176 throwException(exec, throwScope, createNotAConstructorError(exec, function));
1177 return encodeResult(
1178 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
1179 reinterpret_cast<void*>(KeepTheFrame));
1180 }
1181
1182 CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
1183 Exception* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, function, scope, kind, *codeBlockSlot);
1184 EXCEPTION_ASSERT(throwScope.exception() == error);
1185 if (UNLIKELY(error)) {
1186 return encodeResult(
1187 vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).retaggedCode<JSEntryPtrTag>().executableAddress(),
1188 reinterpret_cast<void*>(KeepTheFrame));
1189 }
1190 }
1191 return encodeResult(executable->entrypointFor(
1192 kind, MustCheckArity).executableAddress(),
1193 reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
1194}
1195
1196SlowPathReturnType JIT_OPERATION operationLinkPolymorphicCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
1197{
1198 ASSERT(callLinkInfo->specializationKind() == CodeForCall);
1199 JSCell* calleeAsFunctionCell;
1200 SlowPathReturnType result = virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCell);
1201
1202 linkPolymorphicCall(execCallee, *callLinkInfo, CallVariant(calleeAsFunctionCell));
1203
1204 return result;
1205}
1206
1207SlowPathReturnType JIT_OPERATION operationVirtualCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
1208{
1209 JSCell* calleeAsFunctionCellIgnored;
1210 return virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCellIgnored);
1211}
1212
1213size_t JIT_OPERATION operationCompareLess(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1214{
1215 VM* vm = &exec->vm();
1216 NativeCallFrameTracer tracer(vm, exec);
1217
1218 return jsLess<true>(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
1219}
1220
1221size_t JIT_OPERATION operationCompareLessEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1222{
1223 VM* vm = &exec->vm();
1224 NativeCallFrameTracer tracer(vm, exec);
1225
1226 return jsLessEq<true>(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
1227}
1228
1229size_t JIT_OPERATION operationCompareGreater(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1230{
1231 VM* vm = &exec->vm();
1232 NativeCallFrameTracer tracer(vm, exec);
1233
1234 return jsLess<false>(exec, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1));
1235}
1236
1237size_t JIT_OPERATION operationCompareGreaterEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1238{
1239 VM* vm = &exec->vm();
1240 NativeCallFrameTracer tracer(vm, exec);
1241
1242 return jsLessEq<false>(exec, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1));
1243}
1244
1245size_t JIT_OPERATION operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1246{
1247 VM* vm = &exec->vm();
1248 NativeCallFrameTracer tracer(vm, exec);
1249
1250 return JSValue::equalSlowCaseInline(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
1251}
1252
1253#if USE(JSVALUE64)
1254EncodedJSValue JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSCell* right)
1255#else
1256size_t JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSCell* right)
1257#endif
1258{
1259 VM* vm = &exec->vm();
1260 NativeCallFrameTracer tracer(vm, exec);
1261
1262 bool result = asString(left)->equal(exec, asString(right));
1263#if USE(JSVALUE64)
1264 return JSValue::encode(jsBoolean(result));
1265#else
1266 return result;
1267#endif
1268}
1269
1270size_t JIT_OPERATION operationCompareStrictEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
1271{
1272 VM* vm = &exec->vm();
1273 NativeCallFrameTracer tracer(vm, exec);
1274
1275 JSValue src1 = JSValue::decode(encodedOp1);
1276 JSValue src2 = JSValue::decode(encodedOp2);
1277
1278 return JSValue::strictEqual(exec, src1, src2);
1279}
1280
1281EncodedJSValue JIT_OPERATION operationNewArrayWithProfile(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, int size)
1282{
1283 VM* vm = &exec->vm();
1284 NativeCallFrameTracer tracer(vm, exec);
1285 return JSValue::encode(constructArrayNegativeIndexed(exec, profile, values, size));
1286}
1287
1288EncodedJSValue JIT_OPERATION operationNewArrayWithSizeAndProfile(ExecState* exec, ArrayAllocationProfile* profile, EncodedJSValue size)
1289{
1290 VM* vm = &exec->vm();
1291 NativeCallFrameTracer tracer(vm, exec);
1292 JSValue sizeValue = JSValue::decode(size);
1293 return JSValue::encode(constructArrayWithSizeQuirk(exec, profile, exec->lexicalGlobalObject(), sizeValue));
1294}
1295
1296}
1297
1298template<typename FunctionType>
1299static EncodedJSValue operationNewFunctionCommon(ExecState* exec, JSScope* scope, JSCell* functionExecutable, bool isInvalidated)
1300{
1301 VM& vm = exec->vm();
1302 ASSERT(functionExecutable->inherits<FunctionExecutable>(vm));
1303 NativeCallFrameTracer tracer(&vm, exec);
1304 if (isInvalidated)
1305 return JSValue::encode(FunctionType::createWithInvalidatedReallocationWatchpoint(vm, static_cast<FunctionExecutable*>(functionExecutable), scope));
1306 return JSValue::encode(FunctionType::create(vm, static_cast<FunctionExecutable*>(functionExecutable), scope));
1307}
1308
1309extern "C" {
1310
1311EncodedJSValue JIT_OPERATION operationNewFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1312{
1313 return operationNewFunctionCommon<JSFunction>(exec, scope, functionExecutable, false);
1314}
1315
1316EncodedJSValue JIT_OPERATION operationNewFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1317{
1318 return operationNewFunctionCommon<JSFunction>(exec, scope, functionExecutable, true);
1319}
1320
1321EncodedJSValue JIT_OPERATION operationNewGeneratorFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1322{
1323 return operationNewFunctionCommon<JSGeneratorFunction>(exec, scope, functionExecutable, false);
1324}
1325
1326EncodedJSValue JIT_OPERATION operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1327{
1328 return operationNewFunctionCommon<JSGeneratorFunction>(exec, scope, functionExecutable, true);
1329}
1330
1331EncodedJSValue JIT_OPERATION operationNewAsyncFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1332{
1333 return operationNewFunctionCommon<JSAsyncFunction>(exec, scope, functionExecutable, false);
1334}
1335
1336EncodedJSValue JIT_OPERATION operationNewAsyncFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1337{
1338 return operationNewFunctionCommon<JSAsyncFunction>(exec, scope, functionExecutable, true);
1339}
1340
1341EncodedJSValue JIT_OPERATION operationNewAsyncGeneratorFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1342{
1343 return operationNewFunctionCommon<JSAsyncGeneratorFunction>(exec, scope, functionExecutable, false);
1344}
1345
1346EncodedJSValue JIT_OPERATION operationNewAsyncGeneratorFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
1347{
1348 return operationNewFunctionCommon<JSAsyncGeneratorFunction>(exec, scope, functionExecutable, true);
1349}
1350
1351void JIT_OPERATION operationSetFunctionName(ExecState* exec, JSCell* funcCell, EncodedJSValue encodedName)
1352{
1353 VM* vm = &exec->vm();
1354 NativeCallFrameTracer tracer(vm, exec);
1355
1356 JSFunction* func = jsCast<JSFunction*>(funcCell);
1357 JSValue name = JSValue::decode(encodedName);
1358 func->setFunctionName(exec, name);
1359}
1360
1361JSCell* JIT_OPERATION operationNewObject(ExecState* exec, Structure* structure)
1362{
1363 VM* vm = &exec->vm();
1364 NativeCallFrameTracer tracer(vm, exec);
1365
1366 return constructEmptyObject(exec, structure);
1367}
1368
1369JSCell* JIT_OPERATION operationNewRegexp(ExecState* exec, JSCell* regexpPtr)
1370{
1371 SuperSamplerScope superSamplerScope(false);
1372 VM& vm = exec->vm();
1373 NativeCallFrameTracer tracer(&vm, exec);
1374
1375 RegExp* regexp = static_cast<RegExp*>(regexpPtr);
1376 ASSERT(regexp->isValid());
1377 return RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp);
1378}
1379
1380// The only reason for returning an UnusedPtr (instead of void) is so that we can reuse the
1381// existing DFG slow path generator machinery when creating the slow path for CheckTraps
1382// in the DFG. If a DFG slow path generator that supports a void return type is added in the
1383// future, we can switch to using that then.
1384UnusedPtr JIT_OPERATION operationHandleTraps(ExecState* exec)
1385{
1386 VM& vm = exec->vm();
1387 NativeCallFrameTracer tracer(&vm, exec);
1388 ASSERT(vm.needTrapHandling());
1389 vm.handleTraps(exec);
1390 return nullptr;
1391}
1392
1393void JIT_OPERATION operationDebug(ExecState* exec, int32_t debugHookType)
1394{
1395 VM& vm = exec->vm();
1396 NativeCallFrameTracer tracer(&vm, exec);
1397
1398 vm.interpreter->debug(exec, static_cast<DebugHookType>(debugHookType));
1399}
1400
1401#if ENABLE(DFG_JIT)
1402static void updateAllPredictionsAndOptimizeAfterWarmUp(CodeBlock* codeBlock)
1403{
1404 codeBlock->updateAllPredictions();
1405 codeBlock->optimizeAfterWarmUp();
1406}
1407
1408SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, uint32_t bytecodeIndex)
1409{
1410 VM& vm = exec->vm();
1411 NativeCallFrameTracer tracer(&vm, exec);
1412
1413 // Defer GC for a while so that it doesn't run between when we enter into this
1414 // slow path and when we figure out the state of our code block. This prevents
1415 // a number of awkward reentrancy scenarios, including:
1416 //
1417 // - The optimized version of our code block being jettisoned by GC right after
1418 // we concluded that we wanted to use it, but have not planted it into the JS
1419 // stack yet.
1420 //
1421 // - An optimized version of our code block being installed just as we decided
1422 // that it wasn't ready yet.
1423 //
1424 // Note that jettisoning won't happen if we already initiated OSR, because in
1425 // that case we would have already planted the optimized code block into the JS
1426 // stack.
1427 DeferGCForAWhile deferGC(vm.heap);
1428
1429 CodeBlock* codeBlock = exec->codeBlock();
1430 if (UNLIKELY(codeBlock->jitType() != JITType::BaselineJIT)) {
1431 dataLog("Unexpected code block in Baseline->DFG tier-up: ", *codeBlock, "\n");
1432 RELEASE_ASSERT_NOT_REACHED();
1433 }
1434
1435 if (bytecodeIndex) {
1436 // If we're attempting to OSR from a loop, assume that this should be
1437 // separately optimized.
1438 codeBlock->m_shouldAlwaysBeInlined = false;
1439 }
1440
1441 if (UNLIKELY(Options::verboseOSR())) {
1442 dataLog(
1443 *codeBlock, ": Entered optimize with bytecodeIndex = ", bytecodeIndex,
1444 ", executeCounter = ", codeBlock->jitExecuteCounter(),
1445 ", optimizationDelayCounter = ", codeBlock->reoptimizationRetryCounter(),
1446 ", exitCounter = ");
1447 if (codeBlock->hasOptimizedReplacement())
1448 dataLog(codeBlock->replacement()->osrExitCounter());
1449 else
1450 dataLog("N/A");
1451 dataLog("\n");
1452 }
1453
1454 if (!codeBlock->checkIfOptimizationThresholdReached()) {
1455 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("counter = ", codeBlock->jitExecuteCounter()));
1456 codeBlock->updateAllPredictions();
1457 if (UNLIKELY(Options::verboseOSR()))
1458 dataLog("Choosing not to optimize ", *codeBlock, " yet, because the threshold hasn't been reached.\n");
1459 return encodeResult(0, 0);
1460 }
1461
1462 Debugger* debugger = codeBlock->globalObject()->debugger();
1463 if (UNLIKELY(debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests()))) {
1464 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("debugger is stepping or has requests"));
1465 updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock);
1466 return encodeResult(0, 0);
1467 }
1468
1469 if (codeBlock->m_shouldAlwaysBeInlined) {
1470 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should always be inlined"));
1471 updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock);
1472 if (UNLIKELY(Options::verboseOSR()))
1473 dataLog("Choosing not to optimize ", *codeBlock, " yet, because m_shouldAlwaysBeInlined == true.\n");
1474 return encodeResult(0, 0);
1475 }
1476
1477 // We cannot be in the process of asynchronous compilation and also have an optimized
1478 // replacement.
1479 DFG::Worklist* worklist = DFG::existingGlobalDFGWorklistOrNull();
1480 ASSERT(
1481 !worklist
1482 || !(worklist->compilationState(DFG::CompilationKey(codeBlock, DFG::DFGMode)) != DFG::Worklist::NotKnown
1483 && codeBlock->hasOptimizedReplacement()));
1484
1485 DFG::Worklist::State worklistState;
1486 if (worklist) {
1487 // The call to DFG::Worklist::completeAllReadyPlansForVM() will complete all ready
1488 // (i.e. compiled) code blocks. But if it completes ours, we also need to know
1489 // what the result was so that we don't plow ahead and attempt OSR or immediate
1490 // reoptimization. This will have already also set the appropriate JIT execution
1491 // count threshold depending on what happened, so if the compilation was anything
1492 // but successful we just want to return early. See the case for worklistState ==
1493 // DFG::Worklist::Compiled, below.
1494
1495 // Note that we could have alternatively just called Worklist::compilationState()
1496 // here, and if it returned Compiled, we could have then called
1497 // completeAndScheduleOSR() below. But that would have meant that it could take
1498 // longer for code blocks to be completed: they would only complete when *their*
1499 // execution count trigger fired; but that could take a while since the firing is
1500 // racy. It could also mean that code blocks that never run again after being
1501 // compiled would sit on the worklist until next GC. That's fine, but it's
1502 // probably a waste of memory. Our goal here is to complete code blocks as soon as
1503 // possible in order to minimize the chances of us executing baseline code after
1504 // optimized code is already available.
1505 worklistState = worklist->completeAllReadyPlansForVM(
1506 vm, DFG::CompilationKey(codeBlock, DFG::DFGMode));
1507 } else
1508 worklistState = DFG::Worklist::NotKnown;
1509
1510 if (worklistState == DFG::Worklist::Compiling) {
1511 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiling"));
1512 // We cannot be in the process of asynchronous compilation and also have an optimized
1513 // replacement.
1514 RELEASE_ASSERT(!codeBlock->hasOptimizedReplacement());
1515 codeBlock->setOptimizationThresholdBasedOnCompilationResult(CompilationDeferred);
1516 return encodeResult(0, 0);
1517 }
1518
1519 if (worklistState == DFG::Worklist::Compiled) {
1520 // If we don't have an optimized replacement but we did just get compiled, then
1521 // the compilation failed or was invalidated, in which case the execution count
1522 // thresholds have already been set appropriately by
1523 // CodeBlock::setOptimizationThresholdBasedOnCompilationResult() and we have
1524 // nothing left to do.
1525 if (!codeBlock->hasOptimizedReplacement()) {
1526 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiled and failed"));
1527 codeBlock->updateAllPredictions();
1528 if (UNLIKELY(Options::verboseOSR()))
1529 dataLog("Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement.\n");
1530 return encodeResult(0, 0);
1531 }
1532 } else if (codeBlock->hasOptimizedReplacement()) {
1533 CodeBlock* replacement = codeBlock->replacement();
1534 if (UNLIKELY(Options::verboseOSR()))
1535 dataLog("Considering OSR ", codeBlock, " -> ", replacement, ".\n");
1536 // If we have an optimized replacement, then it must be the case that we entered
1537 // cti_optimize from a loop. That's because if there's an optimized replacement,
1538 // then all calls to this function will be relinked to the replacement and so
1539 // the prologue OSR will never fire.
1540
1541 // This is an interesting threshold check. Consider that a function OSR exits
1542 // in the middle of a loop, while having a relatively low exit count. The exit
1543 // will reset the execution counter to some target threshold, meaning that this
1544 // code won't be reached until that loop heats up for >=1000 executions. But then
1545 // we do a second check here, to see if we should either reoptimize, or just
1546 // attempt OSR entry. Hence it might even be correct for
1547 // shouldReoptimizeFromLoopNow() to always return true. But we make it do some
1548 // additional checking anyway, to reduce the amount of recompilation thrashing.
1549 if (replacement->shouldReoptimizeFromLoopNow()) {
1550 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize from loop now"));
1551 if (UNLIKELY(Options::verboseOSR())) {
1552 dataLog(
1553 "Triggering reoptimization of ", codeBlock,
1554 "(", replacement, ") (in loop).\n");
1555 }
1556 replacement->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTrigger, CountReoptimization);
1557 return encodeResult(0, 0);
1558 }
1559 } else {
1560 if (!codeBlock->shouldOptimizeNow()) {
1561 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("insufficient profiling"));
1562 if (UNLIKELY(Options::verboseOSR())) {
1563 dataLog(
1564 "Delaying optimization for ", *codeBlock,
1565 " because of insufficient profiling.\n");
1566 }
1567 return encodeResult(0, 0);
1568 }
1569
1570 if (UNLIKELY(Options::verboseOSR()))
1571 dataLog("Triggering optimized compilation of ", *codeBlock, "\n");
1572
1573 unsigned numVarsWithValues;
1574 if (bytecodeIndex)
1575 numVarsWithValues = codeBlock->numCalleeLocals();
1576 else
1577 numVarsWithValues = 0;
1578 Operands<Optional<JSValue>> mustHandleValues(codeBlock->numParameters(), numVarsWithValues);
1579 int localsUsedForCalleeSaves = static_cast<int>(CodeBlock::llintBaselineCalleeSaveSpaceAsVirtualRegisters());
1580 for (size_t i = 0; i < mustHandleValues.size(); ++i) {
1581 int operand = mustHandleValues.operandForIndex(i);
1582 if (operandIsLocal(operand) && VirtualRegister(operand).toLocal() < localsUsedForCalleeSaves)
1583 continue;
1584 mustHandleValues[i] = exec->uncheckedR(operand).jsValue();
1585 }
1586
1587 CodeBlock* replacementCodeBlock = codeBlock->newReplacement();
1588 CompilationResult result = DFG::compile(
1589 vm, replacementCodeBlock, nullptr, DFG::DFGMode, bytecodeIndex,
1590 mustHandleValues, JITToDFGDeferredCompilationCallback::create());
1591
1592 if (result != CompilationSuccessful) {
1593 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compilation failed"));
1594 return encodeResult(0, 0);
1595 }
1596 }
1597
1598 CodeBlock* optimizedCodeBlock = codeBlock->replacement();
1599 ASSERT(optimizedCodeBlock && JITCode::isOptimizingJIT(optimizedCodeBlock->jitType()));
1600
1601 if (void* dataBuffer = DFG::prepareOSREntry(exec, optimizedCodeBlock, bytecodeIndex)) {
1602 CODEBLOCK_LOG_EVENT(optimizedCodeBlock, "osrEntry", ("at bc#", bytecodeIndex));
1603 if (UNLIKELY(Options::verboseOSR())) {
1604 dataLog(
1605 "Performing OSR ", codeBlock, " -> ", optimizedCodeBlock, ".\n");
1606 }
1607
1608 codeBlock->optimizeSoon();
1609 codeBlock->unlinkedCodeBlock()->setDidOptimize(TrueTriState);
1610 void* targetPC = vm.getCTIStub(DFG::osrEntryThunkGenerator).code().executableAddress();
1611 targetPC = retagCodePtr(targetPC, JITThunkPtrTag, bitwise_cast<PtrTag>(exec));
1612 return encodeResult(targetPC, dataBuffer);
1613 }
1614
1615 if (UNLIKELY(Options::verboseOSR())) {
1616 dataLog(
1617 "Optimizing ", codeBlock, " -> ", codeBlock->replacement(),
1618 " succeeded, OSR failed, after a delay of ",
1619 codeBlock->optimizationDelayCounter(), ".\n");
1620 }
1621
1622 // Count the OSR failure as a speculation failure. If this happens a lot, then
1623 // reoptimize.
1624 optimizedCodeBlock->countOSRExit();
1625
1626 // We are a lot more conservative about triggering reoptimization after OSR failure than
1627 // before it. If we enter the optimize_from_loop trigger with a bucket full of fail
1628 // already, then we really would like to reoptimize immediately. But this case covers
1629 // something else: there weren't many (or any) speculation failures before, but we just
1630 // failed to enter the speculative code because some variable had the wrong value or
1631 // because the OSR code decided for any spurious reason that it did not want to OSR
1632 // right now. So, we only trigger reoptimization only upon the more conservative (non-loop)
1633 // reoptimization trigger.
1634 if (optimizedCodeBlock->shouldReoptimizeNow()) {
1635 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize now"));
1636 if (UNLIKELY(Options::verboseOSR())) {
1637 dataLog(
1638 "Triggering reoptimization of ", codeBlock, " -> ",
1639 codeBlock->replacement(), " (after OSR fail).\n");
1640 }
1641 optimizedCodeBlock->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail, CountReoptimization);
1642 return encodeResult(0, 0);
1643 }
1644
1645 // OSR failed this time, but it might succeed next time! Let the code run a bit
1646 // longer and then try again.
1647 codeBlock->optimizeAfterWarmUp();
1648
1649 CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("OSR failed"));
1650 return encodeResult(0, 0);
1651}
1652
1653char* JIT_OPERATION operationTryOSREnterAtCatch(ExecState* exec, uint32_t bytecodeIndex)
1654{
1655 VM& vm = exec->vm();
1656 NativeCallFrameTracer tracer(&vm, exec);
1657
1658 CodeBlock* optimizedReplacement = exec->codeBlock()->replacement();
1659 if (UNLIKELY(!optimizedReplacement))
1660 return nullptr;
1661
1662 switch (optimizedReplacement->jitType()) {
1663 case JITType::DFGJIT:
1664 case JITType::FTLJIT: {
1665 MacroAssemblerCodePtr<ExceptionHandlerPtrTag> entry = DFG::prepareCatchOSREntry(exec, optimizedReplacement, bytecodeIndex);
1666 return entry.executableAddress<char*>();
1667 }
1668 default:
1669 break;
1670 }
1671 return nullptr;
1672}
1673
1674char* JIT_OPERATION operationTryOSREnterAtCatchAndValueProfile(ExecState* exec, uint32_t bytecodeIndex)
1675{
1676 VM& vm = exec->vm();
1677 NativeCallFrameTracer tracer(&vm, exec);
1678
1679 CodeBlock* codeBlock = exec->codeBlock();
1680 CodeBlock* optimizedReplacement = codeBlock->replacement();
1681 if (UNLIKELY(!optimizedReplacement))
1682 return nullptr;
1683
1684 switch (optimizedReplacement->jitType()) {
1685 case JITType::DFGJIT:
1686 case JITType::FTLJIT: {
1687 MacroAssemblerCodePtr<ExceptionHandlerPtrTag> entry = DFG::prepareCatchOSREntry(exec, optimizedReplacement, bytecodeIndex);
1688 return entry.executableAddress<char*>();
1689 }
1690 default:
1691 break;
1692 }
1693
1694 codeBlock->ensureCatchLivenessIsComputedForBytecodeOffset(bytecodeIndex);
1695 auto bytecode = codeBlock->instructions().at(bytecodeIndex)->as<OpCatch>();
1696 auto& metadata = bytecode.metadata(codeBlock);
1697 metadata.m_buffer->forEach([&] (ValueProfileAndOperand& profile) {
1698 profile.m_profile.m_buckets[0] = JSValue::encode(exec->uncheckedR(profile.m_operand).jsValue());
1699 });
1700
1701 return nullptr;
1702}
1703
1704#endif
1705
1706void JIT_OPERATION operationPutByIndex(ExecState* exec, EncodedJSValue encodedArrayValue, int32_t index, EncodedJSValue encodedValue)
1707{
1708 VM& vm = exec->vm();
1709 NativeCallFrameTracer tracer(&vm, exec);
1710
1711 JSValue arrayValue = JSValue::decode(encodedArrayValue);
1712 ASSERT(isJSArray(arrayValue));
1713 asArray(arrayValue)->putDirectIndex(exec, index, JSValue::decode(encodedValue));
1714}
1715
1716enum class AccessorType {
1717 Getter,
1718 Setter
1719};
1720
1721static void putAccessorByVal(ExecState* exec, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType)
1722{
1723 VM& vm = exec->vm();
1724 auto scope = DECLARE_THROW_SCOPE(vm);
1725 auto propertyKey = subscript.toPropertyKey(exec);
1726 RETURN_IF_EXCEPTION(scope, void());
1727
1728 scope.release();
1729 if (accessorType == AccessorType::Getter)
1730 base->putGetter(exec, propertyKey, accessor, attribute);
1731 else
1732 base->putSetter(exec, propertyKey, accessor, attribute);
1733}
1734
1735void JIT_OPERATION operationPutGetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* getter)
1736{
1737 VM& vm = exec->vm();
1738 NativeCallFrameTracer tracer(&vm, exec);
1739
1740 ASSERT(object && object->isObject());
1741 JSObject* baseObj = object->getObject();
1742
1743 ASSERT(getter->isObject());
1744 baseObj->putGetter(exec, uid, getter, options);
1745}
1746
1747void JIT_OPERATION operationPutSetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* setter)
1748{
1749 VM& vm = exec->vm();
1750 NativeCallFrameTracer tracer(&vm, exec);
1751
1752 ASSERT(object && object->isObject());
1753 JSObject* baseObj = object->getObject();
1754
1755 ASSERT(setter->isObject());
1756 baseObj->putSetter(exec, uid, setter, options);
1757}
1758
1759void JIT_OPERATION operationPutGetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter)
1760{
1761 VM& vm = exec->vm();
1762 NativeCallFrameTracer tracer(&vm, exec);
1763
1764 putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter);
1765}
1766
1767void JIT_OPERATION operationPutSetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter)
1768{
1769 VM& vm = exec->vm();
1770 NativeCallFrameTracer tracer(&vm, exec);
1771
1772 putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter);
1773}
1774
1775#if USE(JSVALUE64)
1776void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)
1777{
1778 VM& vm = exec->vm();
1779 NativeCallFrameTracer tracer(&vm, exec);
1780
1781 ASSERT(object && object->isObject());
1782 JSObject* baseObject = asObject(object);
1783
1784 JSValue getter = JSValue::decode(encodedGetterValue);
1785 JSValue setter = JSValue::decode(encodedSetterValue);
1786 ASSERT(getter.isObject() || setter.isObject());
1787 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
1788 CommonSlowPaths::putDirectAccessorWithReify(vm, exec, baseObject, uid, accessor, attribute);
1789}
1790
1791#else
1792void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, JSCell* getterCell, JSCell* setterCell)
1793{
1794 VM& vm = exec->vm();
1795 NativeCallFrameTracer tracer(&vm, exec);
1796
1797 ASSERT(object && object->isObject());
1798 JSObject* baseObject = asObject(object);
1799
1800 ASSERT(getterCell || setterCell);
1801 JSObject* getter = getterCell ? getterCell->getObject() : nullptr;
1802 JSObject* setter = setterCell ? setterCell->getObject() : nullptr;
1803 GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject(), getter, setter);
1804 CommonSlowPaths::putDirectAccessorWithReify(vm, exec, baseObject, uid, accessor, attribute);
1805}
1806#endif
1807
1808void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg)
1809{
1810 VM& vm = exec->vm();
1811 NativeCallFrameTracer tracer(&vm, exec);
1812
1813 JSScope* scope = exec->uncheckedR(scopeReg).Register::scope();
1814 exec->uncheckedR(scopeReg) = scope->next();
1815}
1816
1817int32_t JIT_OPERATION operationInstanceOfCustom(ExecState* exec, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance)
1818{
1819 VM& vm = exec->vm();
1820 NativeCallFrameTracer tracer(&vm, exec);
1821
1822 JSValue value = JSValue::decode(encodedValue);
1823 JSValue hasInstanceValue = JSValue::decode(encodedHasInstance);
1824
1825 if (constructor->hasInstance(exec, value, hasInstanceValue))
1826 return 1;
1827 return 0;
1828}
1829
1830}
1831
1832static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
1833{
1834 VM& vm = exec->vm();
1835 auto scope = DECLARE_THROW_SCOPE(vm);
1836
1837 if (LIKELY(baseValue.isCell() && subscript.isString())) {
1838 Structure& structure = *baseValue.asCell()->structure(vm);
1839 if (JSCell::canUseFastGetOwnProperty(structure)) {
1840 RefPtr<AtomicStringImpl> existingAtomicString = asString(subscript)->toExistingAtomicString(exec);
1841 RETURN_IF_EXCEPTION(scope, JSValue());
1842 if (existingAtomicString) {
1843 if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomicString.get())) {
1844 ASSERT(exec->bytecodeOffset());
1845 if (byValInfo->stubInfo && byValInfo->cachedId.impl() != existingAtomicString)
1846 byValInfo->tookSlowPath = true;
1847 return result;
1848 }
1849 }
1850 }
1851 }
1852
1853 if (subscript.isUInt32()) {
1854 ASSERT(exec->bytecodeOffset());
1855 byValInfo->tookSlowPath = true;
1856
1857 uint32_t i = subscript.asUInt32();
1858 if (isJSString(baseValue)) {
1859 if (asString(baseValue)->canGetIndex(i)) {
1860 ctiPatchCallByReturnAddress(returnAddress, operationGetByValString);
1861 RELEASE_AND_RETURN(scope, asString(baseValue)->getIndex(exec, i));
1862 }
1863 byValInfo->arrayProfile->setOutOfBounds();
1864 } else if (baseValue.isObject()) {
1865 JSObject* object = asObject(baseValue);
1866 if (object->canGetIndexQuickly(i))
1867 return object->getIndexQuickly(i);
1868
1869 bool skipMarkingOutOfBounds = false;
1870
1871 if (object->indexingType() == ArrayWithContiguous && i < object->butterfly()->publicLength()) {
1872 // FIXME: expand this to ArrayStorage, Int32, and maybe Double:
1873 // https://bugs.webkit.org/show_bug.cgi?id=182940
1874 auto* globalObject = object->globalObject(vm);
1875 skipMarkingOutOfBounds = globalObject->isOriginalArrayStructure(object->structure(vm)) && globalObject->arrayPrototypeChainIsSane();
1876 }
1877
1878 if (!skipMarkingOutOfBounds && !CommonSlowPaths::canAccessArgumentIndexQuickly(*object, i)) {
1879 // FIXME: This will make us think that in-bounds typed array accesses are actually
1880 // out-of-bounds.
1881 // https://bugs.webkit.org/show_bug.cgi?id=149886
1882 byValInfo->arrayProfile->setOutOfBounds();
1883 }
1884 }
1885
1886 RELEASE_AND_RETURN(scope, baseValue.get(exec, i));
1887 }
1888
1889 baseValue.requireObjectCoercible(exec);
1890 RETURN_IF_EXCEPTION(scope, JSValue());
1891 auto property = subscript.toPropertyKey(exec);
1892 RETURN_IF_EXCEPTION(scope, JSValue());
1893
1894 ASSERT(exec->bytecodeOffset());
1895 if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property))
1896 byValInfo->tookSlowPath = true;
1897
1898 RELEASE_AND_RETURN(scope, baseValue.get(exec, property));
1899}
1900
1901static OptimizationResult tryGetByValOptimize(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
1902{
1903 // See if it's worth optimizing this at all.
1904 OptimizationResult optimizationResult = OptimizationResult::NotOptimized;
1905
1906 VM& vm = exec->vm();
1907 auto scope = DECLARE_THROW_SCOPE(vm);
1908
1909 if (baseValue.isObject() && subscript.isInt32()) {
1910 JSObject* object = asObject(baseValue);
1911
1912 ASSERT(exec->bytecodeOffset());
1913 ASSERT(!byValInfo->stubRoutine);
1914
1915 if (hasOptimizableIndexing(object->structure(vm))) {
1916 // Attempt to optimize.
1917 Structure* structure = object->structure(vm);
1918 JITArrayMode arrayMode = jitArrayModeForStructure(structure);
1919 if (arrayMode != byValInfo->arrayMode) {
1920 // If we reached this case, we got an interesting array mode we did not expect when we compiled.
1921 // Let's update the profile to do better next time.
1922 CodeBlock* codeBlock = exec->codeBlock();
1923 ConcurrentJSLocker locker(codeBlock->m_lock);
1924 byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
1925
1926 JIT::compileGetByVal(locker, &vm, codeBlock, byValInfo, returnAddress, arrayMode);
1927 optimizationResult = OptimizationResult::Optimized;
1928 }
1929 }
1930
1931 // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times.
1932 if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero())
1933 optimizationResult = OptimizationResult::GiveUp;
1934 }
1935
1936 if (baseValue.isObject() && isStringOrSymbol(subscript)) {
1937 const Identifier propertyName = subscript.toPropertyKey(exec);
1938 RETURN_IF_EXCEPTION(scope, OptimizationResult::GiveUp);
1939 if (subscript.isSymbol() || !parseIndex(propertyName)) {
1940 ASSERT(exec->bytecodeOffset());
1941 ASSERT(!byValInfo->stubRoutine);
1942 if (byValInfo->seen) {
1943 if (byValInfo->cachedId == propertyName) {
1944 JIT::compileGetByValWithCachedId(&vm, exec->codeBlock(), byValInfo, returnAddress, propertyName);
1945 optimizationResult = OptimizationResult::Optimized;
1946 } else {
1947 // Seem like a generic property access site.
1948 optimizationResult = OptimizationResult::GiveUp;
1949 }
1950 } else {
1951 CodeBlock* codeBlock = exec->codeBlock();
1952 ConcurrentJSLocker locker(codeBlock->m_lock);
1953 byValInfo->seen = true;
1954 byValInfo->cachedId = propertyName;
1955 if (subscript.isSymbol())
1956 byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
1957 optimizationResult = OptimizationResult::SeenOnce;
1958 }
1959 }
1960 }
1961
1962 if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) {
1963 // If we take slow path more than 10 times without patching then make sure we
1964 // never make that mistake again. For cases where we see non-index-intercepting
1965 // objects, this gives 10 iterations worth of opportunity for us to observe
1966 // that the get_by_val may be polymorphic. We count up slowPathCount even if
1967 // the result is GiveUp.
1968 if (++byValInfo->slowPathCount >= 10)
1969 optimizationResult = OptimizationResult::GiveUp;
1970 }
1971
1972 return optimizationResult;
1973}
1974
1975extern "C" {
1976
1977EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
1978{
1979 VM& vm = exec->vm();
1980 NativeCallFrameTracer tracer(&vm, exec);
1981 JSValue baseValue = JSValue::decode(encodedBase);
1982 JSValue subscript = JSValue::decode(encodedSubscript);
1983
1984 JSValue result = getByVal(exec, baseValue, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS));
1985 return JSValue::encode(result);
1986}
1987
1988EncodedJSValue JIT_OPERATION operationGetByValOptimize(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
1989{
1990 VM& vm = exec->vm();
1991 NativeCallFrameTracer tracer(&vm, exec);
1992 auto scope = DECLARE_THROW_SCOPE(vm);
1993
1994 JSValue baseValue = JSValue::decode(encodedBase);
1995 JSValue subscript = JSValue::decode(encodedSubscript);
1996 ReturnAddressPtr returnAddress = ReturnAddressPtr(OUR_RETURN_ADDRESS);
1997 OptimizationResult result = tryGetByValOptimize(exec, baseValue, subscript, byValInfo, returnAddress);
1998 RETURN_IF_EXCEPTION(scope, { });
1999 if (result == OptimizationResult::GiveUp) {
2000 // Don't ever try to optimize.
2001 byValInfo->tookSlowPath = true;
2002 ctiPatchCallByReturnAddress(returnAddress, operationGetByValGeneric);
2003 }
2004
2005 RELEASE_AND_RETURN(scope, JSValue::encode(getByVal(exec, baseValue, subscript, byValInfo, returnAddress)));
2006}
2007
2008EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
2009{
2010 VM& vm = exec->vm();
2011 NativeCallFrameTracer tracer(&vm, exec);
2012 JSValue baseValue = JSValue::decode(encodedBase);
2013 JSValue subscript = JSValue::decode(encodedSubscript);
2014
2015 ASSERT(baseValue.isObject());
2016 ASSERT(subscript.isUInt32AsAnyInt());
2017
2018 JSObject* object = asObject(baseValue);
2019 bool didOptimize = false;
2020
2021 ASSERT(exec->bytecodeOffset());
2022 ASSERT(!byValInfo->stubRoutine);
2023
2024 if (hasOptimizableIndexing(object->structure(vm))) {
2025 // Attempt to optimize.
2026 JITArrayMode arrayMode = jitArrayModeForStructure(object->structure(vm));
2027 if (arrayMode != byValInfo->arrayMode) {
2028 JIT::compileHasIndexedProperty(&vm, exec->codeBlock(), byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
2029 didOptimize = true;
2030 }
2031 }
2032
2033 if (!didOptimize) {
2034 // If we take slow path more than 10 times without patching then make sure we
2035 // never make that mistake again. Or, if we failed to patch and we have some object
2036 // that intercepts indexed get, then don't even wait until 10 times. For cases
2037 // where we see non-index-intercepting objects, this gives 10 iterations worth of
2038 // opportunity for us to observe that the get_by_val may be polymorphic.
2039 if (++byValInfo->slowPathCount >= 10
2040 || object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) {
2041 // Don't ever try to optimize.
2042 ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), operationHasIndexedPropertyGeneric);
2043 }
2044 }
2045
2046 uint32_t index = subscript.asUInt32AsAnyInt();
2047 if (object->canGetIndexQuickly(index))
2048 return JSValue::encode(JSValue(JSValue::JSTrue));
2049
2050 if (!CommonSlowPaths::canAccessArgumentIndexQuickly(*object, index)) {
2051 // FIXME: This will make us think that in-bounds typed array accesses are actually
2052 // out-of-bounds.
2053 // https://bugs.webkit.org/show_bug.cgi?id=149886
2054 byValInfo->arrayProfile->setOutOfBounds();
2055 }
2056 return JSValue::encode(jsBoolean(object->hasPropertyGeneric(exec, index, PropertySlot::InternalMethodType::GetOwnProperty)));
2057}
2058
2059EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
2060{
2061 VM& vm = exec->vm();
2062 NativeCallFrameTracer tracer(&vm, exec);
2063 JSValue baseValue = JSValue::decode(encodedBase);
2064 JSValue subscript = JSValue::decode(encodedSubscript);
2065
2066 ASSERT(baseValue.isObject());
2067 ASSERT(subscript.isUInt32AsAnyInt());
2068
2069 JSObject* object = asObject(baseValue);
2070 uint32_t index = subscript.asUInt32AsAnyInt();
2071 if (object->canGetIndexQuickly(index))
2072 return JSValue::encode(JSValue(JSValue::JSTrue));
2073
2074 if (!CommonSlowPaths::canAccessArgumentIndexQuickly(*object, index)) {
2075 // FIXME: This will make us think that in-bounds typed array accesses are actually
2076 // out-of-bounds.
2077 // https://bugs.webkit.org/show_bug.cgi?id=149886
2078 byValInfo->arrayProfile->setOutOfBounds();
2079 }
2080 return JSValue::encode(jsBoolean(object->hasPropertyGeneric(exec, index, PropertySlot::InternalMethodType::GetOwnProperty)));
2081}
2082
2083EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
2084{
2085 VM& vm = exec->vm();
2086 NativeCallFrameTracer tracer(&vm, exec);
2087 auto scope = DECLARE_THROW_SCOPE(vm);
2088 JSValue baseValue = JSValue::decode(encodedBase);
2089 JSValue subscript = JSValue::decode(encodedSubscript);
2090
2091 JSValue result;
2092 if (LIKELY(subscript.isUInt32())) {
2093 uint32_t i = subscript.asUInt32();
2094 if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i))
2095 RELEASE_AND_RETURN(scope, JSValue::encode(asString(baseValue)->getIndex(exec, i)));
2096
2097 result = baseValue.get(exec, i);
2098 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2099 if (!isJSString(baseValue)) {
2100 ASSERT(exec->bytecodeOffset());
2101 auto getByValFunction = byValInfo->stubRoutine ? operationGetByValGeneric : operationGetByValOptimize;
2102 ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), getByValFunction);
2103 }
2104 } else {
2105 baseValue.requireObjectCoercible(exec);
2106 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2107 auto property = subscript.toPropertyKey(exec);
2108 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2109 scope.release();
2110 result = baseValue.get(exec, property);
2111 }
2112
2113 return JSValue::encode(result);
2114}
2115
2116EncodedJSValue JIT_OPERATION operationDeleteByIdJSResult(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
2117{
2118 return JSValue::encode(jsBoolean(operationDeleteById(exec, base, uid)));
2119}
2120
2121size_t JIT_OPERATION operationDeleteById(ExecState* exec, EncodedJSValue encodedBase, UniquedStringImpl* uid)
2122{
2123 VM& vm = exec->vm();
2124 NativeCallFrameTracer tracer(&vm, exec);
2125 auto scope = DECLARE_THROW_SCOPE(vm);
2126
2127 JSObject* baseObj = JSValue::decode(encodedBase).toObject(exec);
2128 RETURN_IF_EXCEPTION(scope, false);
2129 if (!baseObj)
2130 return false;
2131 bool couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, exec, Identifier::fromUid(&vm, uid));
2132 RETURN_IF_EXCEPTION(scope, false);
2133 if (!couldDelete && exec->codeBlock()->isStrictMode())
2134 throwTypeError(exec, scope, UnableToDeletePropertyError);
2135 return couldDelete;
2136}
2137
2138EncodedJSValue JIT_OPERATION operationDeleteByValJSResult(ExecState* exec, EncodedJSValue base, EncodedJSValue key)
2139{
2140 return JSValue::encode(jsBoolean(operationDeleteByVal(exec, base, key)));
2141}
2142
2143size_t JIT_OPERATION operationDeleteByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedKey)
2144{
2145 VM& vm = exec->vm();
2146 NativeCallFrameTracer tracer(&vm, exec);
2147 auto scope = DECLARE_THROW_SCOPE(vm);
2148
2149 JSObject* baseObj = JSValue::decode(encodedBase).toObject(exec);
2150 RETURN_IF_EXCEPTION(scope, false);
2151 JSValue key = JSValue::decode(encodedKey);
2152 if (!baseObj)
2153 return false;
2154
2155 bool couldDelete;
2156 uint32_t index;
2157 if (key.getUInt32(index))
2158 couldDelete = baseObj->methodTable(vm)->deletePropertyByIndex(baseObj, exec, index);
2159 else {
2160 Identifier property = key.toPropertyKey(exec);
2161 RETURN_IF_EXCEPTION(scope, false);
2162 couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, exec, property);
2163 }
2164 RETURN_IF_EXCEPTION(scope, false);
2165 if (!couldDelete && exec->codeBlock()->isStrictMode())
2166 throwTypeError(exec, scope, UnableToDeletePropertyError);
2167 return couldDelete;
2168}
2169
2170JSCell* JIT_OPERATION operationPushWithScope(ExecState* exec, JSCell* currentScopeCell, EncodedJSValue objectValue)
2171{
2172 VM& vm = exec->vm();
2173 NativeCallFrameTracer tracer(&vm, exec);
2174 auto scope = DECLARE_THROW_SCOPE(vm);
2175
2176 JSObject* object = JSValue::decode(objectValue).toObject(exec);
2177 RETURN_IF_EXCEPTION(scope, nullptr);
2178
2179 JSScope* currentScope = jsCast<JSScope*>(currentScopeCell);
2180
2181 return JSWithScope::create(vm, exec->lexicalGlobalObject(), currentScope, object);
2182}
2183
2184JSCell* JIT_OPERATION operationPushWithScopeObject(ExecState* exec, JSCell* currentScopeCell, JSObject* object)
2185{
2186 VM& vm = exec->vm();
2187 NativeCallFrameTracer tracer(&vm, exec);
2188 JSScope* currentScope = jsCast<JSScope*>(currentScopeCell);
2189 return JSWithScope::create(vm, exec->lexicalGlobalObject(), currentScope, object);
2190}
2191
2192EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedProto)
2193{
2194 VM& vm = exec->vm();
2195 NativeCallFrameTracer tracer(&vm, exec);
2196 JSValue value = JSValue::decode(encodedValue);
2197 JSValue proto = JSValue::decode(encodedProto);
2198
2199 bool result = JSObject::defaultHasInstance(exec, value, proto);
2200 return JSValue::encode(jsBoolean(result));
2201}
2202
2203EncodedJSValue JIT_OPERATION operationInstanceOfGeneric(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)
2204{
2205 VM& vm = exec->vm();
2206 NativeCallFrameTracer tracer(&vm, exec);
2207 JSValue value = JSValue::decode(encodedValue);
2208 JSValue proto = JSValue::decode(encodedProto);
2209
2210 stubInfo->tookSlowPath = true;
2211
2212 bool result = JSObject::defaultHasInstance(exec, value, proto);
2213 return JSValue::encode(jsBoolean(result));
2214}
2215
2216EncodedJSValue JIT_OPERATION operationInstanceOfOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)
2217{
2218 VM& vm = exec->vm();
2219 NativeCallFrameTracer tracer(&vm, exec);
2220 auto scope = DECLARE_THROW_SCOPE(vm);
2221 JSValue value = JSValue::decode(encodedValue);
2222 JSValue proto = JSValue::decode(encodedProto);
2223
2224 bool result = JSObject::defaultHasInstance(exec, value, proto);
2225 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
2226
2227 if (stubInfo->considerCaching(exec->codeBlock(), value.structureOrNull()))
2228 repatchInstanceOf(exec, value, proto, *stubInfo, result);
2229
2230 return JSValue::encode(jsBoolean(result));
2231}
2232
2233int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState* exec, EncodedJSValue, int32_t numUsedStackSlots, int32_t)
2234{
2235 VM& vm = exec->vm();
2236 NativeCallFrameTracer tracer(&vm, exec);
2237 return sizeFrameForForwardArguments(exec, vm, numUsedStackSlots);
2238}
2239
2240int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset)
2241{
2242 VM& vm = exec->vm();
2243 NativeCallFrameTracer tracer(&vm, exec);
2244 JSValue arguments = JSValue::decode(encodedArguments);
2245 return sizeFrameForVarargs(exec, vm, arguments, numUsedStackSlots, firstVarArgOffset);
2246}
2247
2248CallFrame* JIT_OPERATION operationSetupForwardArgumentsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue, int32_t, int32_t length)
2249{
2250 VM& vm = exec->vm();
2251 NativeCallFrameTracer tracer(&vm, exec);
2252 setupForwardArgumentsFrame(exec, newCallFrame, length);
2253 return newCallFrame;
2254}
2255
2256CallFrame* JIT_OPERATION operationSetupVarargsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length)
2257{
2258 VM& vm = exec->vm();
2259 NativeCallFrameTracer tracer(&vm, exec);
2260 JSValue arguments = JSValue::decode(encodedArguments);
2261 setupVarargsFrame(exec, newCallFrame, arguments, firstVarArgOffset, length);
2262 return newCallFrame;
2263}
2264
2265char* JIT_OPERATION operationSwitchCharWithUnknownKeyType(ExecState* exec, EncodedJSValue encodedKey, size_t tableIndex)
2266{
2267 VM& vm = exec->vm();
2268 NativeCallFrameTracer tracer(&vm, exec);
2269 JSValue key = JSValue::decode(encodedKey);
2270 CodeBlock* codeBlock = exec->codeBlock();
2271
2272 SimpleJumpTable& jumpTable = codeBlock->switchJumpTable(tableIndex);
2273 void* result = jumpTable.ctiDefault.executableAddress();
2274
2275 if (key.isString()) {
2276 StringImpl* value = asString(key)->value(exec).impl();
2277 if (value->length() == 1)
2278 result = jumpTable.ctiForValue((*value)[0]).executableAddress();
2279 }
2280
2281 assertIsTaggedWith(result, JSSwitchPtrTag);
2282 return reinterpret_cast<char*>(result);
2283}
2284
2285char* JIT_OPERATION operationSwitchImmWithUnknownKeyType(ExecState* exec, EncodedJSValue encodedKey, size_t tableIndex)
2286{
2287 VM& vm = exec->vm();
2288 NativeCallFrameTracer tracer(&vm, exec);
2289 JSValue key = JSValue::decode(encodedKey);
2290 CodeBlock* codeBlock = exec->codeBlock();
2291
2292 SimpleJumpTable& jumpTable = codeBlock->switchJumpTable(tableIndex);
2293 void* result;
2294 if (key.isInt32())
2295 result = jumpTable.ctiForValue(key.asInt32()).executableAddress();
2296 else if (key.isDouble() && key.asDouble() == static_cast<int32_t>(key.asDouble()))
2297 result = jumpTable.ctiForValue(static_cast<int32_t>(key.asDouble())).executableAddress();
2298 else
2299 result = jumpTable.ctiDefault.executableAddress();
2300 assertIsTaggedWith(result, JSSwitchPtrTag);
2301 return reinterpret_cast<char*>(result);
2302}
2303
2304char* JIT_OPERATION operationSwitchStringWithUnknownKeyType(ExecState* exec, EncodedJSValue encodedKey, size_t tableIndex)
2305{
2306 VM& vm = exec->vm();
2307 NativeCallFrameTracer tracer(&vm, exec);
2308 JSValue key = JSValue::decode(encodedKey);
2309 CodeBlock* codeBlock = exec->codeBlock();
2310
2311 void* result;
2312 StringJumpTable& jumpTable = codeBlock->stringSwitchJumpTable(tableIndex);
2313
2314 if (key.isString()) {
2315 StringImpl* value = asString(key)->value(exec).impl();
2316 result = jumpTable.ctiForValue(value).executableAddress();
2317 } else
2318 result = jumpTable.ctiDefault.executableAddress();
2319
2320 assertIsTaggedWith(result, JSSwitchPtrTag);
2321 return reinterpret_cast<char*>(result);
2322}
2323
2324EncodedJSValue JIT_OPERATION operationGetFromScope(ExecState* exec, const Instruction* pc)
2325{
2326 VM& vm = exec->vm();
2327 NativeCallFrameTracer tracer(&vm, exec);
2328 auto throwScope = DECLARE_THROW_SCOPE(vm);
2329
2330 CodeBlock* codeBlock = exec->codeBlock();
2331
2332 auto bytecode = pc->as<OpGetFromScope>();
2333 const Identifier& ident = codeBlock->identifier(bytecode.m_var);
2334 JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(bytecode.m_scope.offset()).jsValue());
2335 GetPutInfo& getPutInfo = bytecode.metadata(codeBlock).m_getPutInfo;
2336
2337 // ModuleVar is always converted to ClosureVar for get_from_scope.
2338 ASSERT(getPutInfo.resolveType() != ModuleVar);
2339
2340 RELEASE_AND_RETURN(throwScope, JSValue::encode(scope->getPropertySlot(exec, ident, [&] (bool found, PropertySlot& slot) -> JSValue {
2341 if (!found) {
2342 if (getPutInfo.resolveMode() == ThrowIfNotFound)
2343 throwException(exec, throwScope, createUndefinedVariableError(exec, ident));
2344 return jsUndefined();
2345 }
2346
2347 JSValue result = JSValue();
2348 if (scope->isGlobalLexicalEnvironment()) {
2349 // When we can't statically prove we need a TDZ check, we must perform the check on the slow path.
2350 result = slot.getValue(exec, ident);
2351 if (result == jsTDZValue()) {
2352 throwException(exec, throwScope, createTDZError(exec));
2353 return jsUndefined();
2354 }
2355 }
2356
2357 CommonSlowPaths::tryCacheGetFromScopeGlobal(exec, vm, bytecode, scope, slot, ident);
2358
2359 if (!result)
2360 return slot.getValue(exec, ident);
2361 return result;
2362 })));
2363}
2364
2365void JIT_OPERATION operationPutToScope(ExecState* exec, const Instruction* pc)
2366{
2367 VM& vm = exec->vm();
2368 NativeCallFrameTracer tracer(&vm, exec);
2369 auto throwScope = DECLARE_THROW_SCOPE(vm);
2370
2371 CodeBlock* codeBlock = exec->codeBlock();
2372 auto bytecode = pc->as<OpPutToScope>();
2373 auto& metadata = bytecode.metadata(codeBlock);
2374
2375 const Identifier& ident = codeBlock->identifier(bytecode.m_var);
2376 JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(bytecode.m_scope.offset()).jsValue());
2377 JSValue value = exec->r(bytecode.m_value.offset()).jsValue();
2378 GetPutInfo& getPutInfo = metadata.m_getPutInfo;
2379
2380 // ModuleVar does not keep the scope register value alive in DFG.
2381 ASSERT(getPutInfo.resolveType() != ModuleVar);
2382
2383 if (getPutInfo.resolveType() == LocalClosureVar) {
2384 JSLexicalEnvironment* environment = jsCast<JSLexicalEnvironment*>(scope);
2385 environment->variableAt(ScopeOffset(metadata.m_operand)).set(vm, environment, value);
2386 if (WatchpointSet* set = metadata.m_watchpointSet)
2387 set->touch(vm, "Executed op_put_scope<LocalClosureVar>");
2388 return;
2389 }
2390
2391 bool hasProperty = scope->hasProperty(exec, ident);
2392 RETURN_IF_EXCEPTION(throwScope, void());
2393 if (hasProperty
2394 && scope->isGlobalLexicalEnvironment()
2395 && !isInitialization(getPutInfo.initializationMode())) {
2396 // When we can't statically prove we need a TDZ check, we must perform the check on the slow path.
2397 PropertySlot slot(scope, PropertySlot::InternalMethodType::Get);
2398 JSGlobalLexicalEnvironment::getOwnPropertySlot(scope, exec, ident, slot);
2399 if (slot.getValue(exec, ident) == jsTDZValue()) {
2400 throwException(exec, throwScope, createTDZError(exec));
2401 return;
2402 }
2403 }
2404
2405 if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) {
2406 throwException(exec, throwScope, createUndefinedVariableError(exec, ident));
2407 return;
2408 }
2409
2410 PutPropertySlot slot(scope, codeBlock->isStrictMode(), PutPropertySlot::UnknownContext, isInitialization(getPutInfo.initializationMode()));
2411 scope->methodTable(vm)->put(scope, exec, ident, value, slot);
2412
2413 RETURN_IF_EXCEPTION(throwScope, void());
2414
2415 CommonSlowPaths::tryCachePutToScopeGlobal(exec, codeBlock, bytecode, scope, slot, ident);
2416}
2417
2418void JIT_OPERATION operationThrow(ExecState* exec, EncodedJSValue encodedExceptionValue)
2419{
2420 VM* vm = &exec->vm();
2421 NativeCallFrameTracer tracer(vm, exec);
2422 auto scope = DECLARE_THROW_SCOPE(*vm);
2423
2424 JSValue exceptionValue = JSValue::decode(encodedExceptionValue);
2425 throwException(exec, scope, exceptionValue);
2426
2427 // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForCatch
2428 genericUnwind(vm, exec);
2429}
2430
2431char* JIT_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState* exec, JSObject* object)
2432{
2433 VM& vm = exec->vm();
2434 NativeCallFrameTracer tracer(&vm, exec);
2435
2436 ASSERT(!object->structure(vm)->outOfLineCapacity());
2437 Butterfly* result = object->allocateMoreOutOfLineStorage(vm, 0, initialOutOfLineCapacity);
2438 object->nukeStructureAndSetButterfly(vm, object->structureID(), result);
2439 return reinterpret_cast<char*>(result);
2440}
2441
2442char* JIT_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState* exec, JSObject* object, size_t newSize)
2443{
2444 VM& vm = exec->vm();
2445 NativeCallFrameTracer tracer(&vm, exec);
2446
2447 Butterfly* result = object->allocateMoreOutOfLineStorage(vm, object->structure(vm)->outOfLineCapacity(), newSize);
2448 object->nukeStructureAndSetButterfly(vm, object->structureID(), result);
2449 return reinterpret_cast<char*>(result);
2450}
2451
2452void JIT_OPERATION operationOSRWriteBarrier(ExecState* exec, JSCell* cell)
2453{
2454 VM* vm = &exec->vm();
2455 NativeCallFrameTracer tracer(vm, exec);
2456 vm->heap.writeBarrier(cell);
2457}
2458
2459void JIT_OPERATION operationWriteBarrierSlowPath(ExecState* exec, JSCell* cell)
2460{
2461 VM* vm = &exec->vm();
2462 NativeCallFrameTracer tracer(vm, exec);
2463 vm->heap.writeBarrierSlowPath(cell);
2464}
2465
2466void JIT_OPERATION lookupExceptionHandler(VM* vm, ExecState* exec)
2467{
2468 NativeCallFrameTracer tracer(vm, exec);
2469 genericUnwind(vm, exec);
2470 ASSERT(vm->targetMachinePCForThrow);
2471}
2472
2473void JIT_OPERATION lookupExceptionHandlerFromCallerFrame(VM* vm, ExecState* exec)
2474{
2475 ASSERT(exec->isStackOverflowFrame());
2476 ASSERT(jsCast<ErrorInstance*>(vm->exceptionForInspection()->value().asCell())->isStackOverflowError());
2477 lookupExceptionHandler(vm, exec);
2478}
2479
2480void JIT_OPERATION operationVMHandleException(ExecState* exec)
2481{
2482 VM* vm = &exec->vm();
2483 NativeCallFrameTracer tracer(vm, exec);
2484 genericUnwind(vm, exec);
2485}
2486
2487// This function "should" just take the ExecState*, but doing so would make it more difficult
2488// to call from exception check sites. So, unlike all of our other functions, we allow
2489// ourselves to play some gnarly ABI tricks just to simplify the calling convention. This is
2490// particularly safe here since this is never called on the critical path - it's only for
2491// testing.
2492void JIT_OPERATION operationExceptionFuzz(ExecState* exec)
2493{
2494 VM* vm = &exec->vm();
2495 NativeCallFrameTracer tracer(vm, exec);
2496 auto scope = DECLARE_THROW_SCOPE(*vm);
2497 UNUSED_PARAM(scope);
2498#if COMPILER(GCC_COMPATIBLE)
2499 void* returnPC = __builtin_return_address(0);
2500 doExceptionFuzzing(exec, scope, "JITOperations", returnPC);
2501#endif // COMPILER(GCC_COMPATIBLE)
2502}
2503
2504ALWAYS_INLINE static EncodedJSValue unprofiledAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2505{
2506 VM* vm = &exec->vm();
2507 NativeCallFrameTracer tracer(vm, exec);
2508
2509 JSValue op1 = JSValue::decode(encodedOp1);
2510 JSValue op2 = JSValue::decode(encodedOp2);
2511
2512 return JSValue::encode(jsAdd(exec, op1, op2));
2513}
2514
2515ALWAYS_INLINE static EncodedJSValue profiledAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile)
2516{
2517 VM* vm = &exec->vm();
2518 NativeCallFrameTracer tracer(vm, exec);
2519
2520 JSValue op1 = JSValue::decode(encodedOp1);
2521 JSValue op2 = JSValue::decode(encodedOp2);
2522
2523 arithProfile.observeLHSAndRHS(op1, op2);
2524 JSValue result = jsAdd(exec, op1, op2);
2525 arithProfile.observeResult(result);
2526
2527 return JSValue::encode(result);
2528}
2529
2530EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2531{
2532 return unprofiledAdd(exec, encodedOp1, encodedOp2);
2533}
2534
2535EncodedJSValue JIT_OPERATION operationValueAddProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
2536{
2537 ASSERT(arithProfile);
2538 return profiledAdd(exec, encodedOp1, encodedOp2, *arithProfile);
2539}
2540
2541EncodedJSValue JIT_OPERATION operationValueAddProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
2542{
2543 VM* vm = &exec->vm();
2544 NativeCallFrameTracer tracer(vm, exec);
2545
2546 JSValue op1 = JSValue::decode(encodedOp1);
2547 JSValue op2 = JSValue::decode(encodedOp2);
2548
2549 ArithProfile* arithProfile = addIC->arithProfile();
2550 ASSERT(arithProfile);
2551 arithProfile->observeLHSAndRHS(op1, op2);
2552 auto nonOptimizeVariant = operationValueAddProfiledNoOptimize;
2553 addIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2554
2555#if ENABLE(MATH_IC_STATS)
2556 exec->codeBlock()->dumpMathICStats();
2557#endif
2558
2559 JSValue result = jsAdd(exec, op1, op2);
2560 arithProfile->observeResult(result);
2561
2562 return JSValue::encode(result);
2563}
2564
2565EncodedJSValue JIT_OPERATION operationValueAddProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
2566{
2567 VM* vm = &exec->vm();
2568 NativeCallFrameTracer tracer(vm, exec);
2569
2570 ArithProfile* arithProfile = addIC->arithProfile();
2571 ASSERT(arithProfile);
2572 return profiledAdd(exec, encodedOp1, encodedOp2, *arithProfile);
2573}
2574
2575EncodedJSValue JIT_OPERATION operationValueAddOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
2576{
2577 VM* vm = &exec->vm();
2578 NativeCallFrameTracer tracer(vm, exec);
2579
2580 JSValue op1 = JSValue::decode(encodedOp1);
2581 JSValue op2 = JSValue::decode(encodedOp2);
2582
2583 auto nonOptimizeVariant = operationValueAddNoOptimize;
2584 if (ArithProfile* arithProfile = addIC->arithProfile())
2585 arithProfile->observeLHSAndRHS(op1, op2);
2586 addIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2587
2588#if ENABLE(MATH_IC_STATS)
2589 exec->codeBlock()->dumpMathICStats();
2590#endif
2591
2592 return JSValue::encode(jsAdd(exec, op1, op2));
2593}
2594
2595EncodedJSValue JIT_OPERATION operationValueAddNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC*)
2596{
2597 VM* vm = &exec->vm();
2598 NativeCallFrameTracer tracer(vm, exec);
2599
2600 JSValue op1 = JSValue::decode(encodedOp1);
2601 JSValue op2 = JSValue::decode(encodedOp2);
2602
2603 JSValue result = jsAdd(exec, op1, op2);
2604
2605 return JSValue::encode(result);
2606}
2607
2608ALWAYS_INLINE static EncodedJSValue unprofiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2609{
2610 JSValue op1 = JSValue::decode(encodedOp1);
2611 JSValue op2 = JSValue::decode(encodedOp2);
2612
2613 return JSValue::encode(jsMul(exec, op1, op2));
2614}
2615
2616ALWAYS_INLINE static EncodedJSValue profiledMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
2617{
2618 VM& vm = exec->vm();
2619 auto scope = DECLARE_THROW_SCOPE(vm);
2620 JSValue op1 = JSValue::decode(encodedOp1);
2621 JSValue op2 = JSValue::decode(encodedOp2);
2622
2623 if (shouldObserveLHSAndRHSTypes)
2624 arithProfile.observeLHSAndRHS(op1, op2);
2625
2626 JSValue result = jsMul(exec, op1, op2);
2627 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2628 arithProfile.observeResult(result);
2629 return JSValue::encode(result);
2630}
2631
2632EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2633{
2634 VM* vm = &exec->vm();
2635 NativeCallFrameTracer tracer(vm, exec);
2636
2637 return unprofiledMul(exec, encodedOp1, encodedOp2);
2638}
2639
2640EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)
2641{
2642 VM* vm = &exec->vm();
2643 NativeCallFrameTracer tracer(vm, exec);
2644
2645 return unprofiledMul(exec, encodedOp1, encodedOp2);
2646}
2647
2648EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
2649{
2650 VM* vm = &exec->vm();
2651 NativeCallFrameTracer tracer(vm, exec);
2652
2653 auto nonOptimizeVariant = operationValueMulNoOptimize;
2654 if (ArithProfile* arithProfile = mulIC->arithProfile())
2655 arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
2656 mulIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2657
2658#if ENABLE(MATH_IC_STATS)
2659 exec->codeBlock()->dumpMathICStats();
2660#endif
2661
2662 return unprofiledMul(exec, encodedOp1, encodedOp2);
2663}
2664
2665EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
2666{
2667 VM* vm = &exec->vm();
2668 NativeCallFrameTracer tracer(vm, exec);
2669
2670 ASSERT(arithProfile);
2671 return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
2672}
2673
2674EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
2675{
2676 VM* vm = &exec->vm();
2677 NativeCallFrameTracer tracer(vm, exec);
2678
2679 ArithProfile* arithProfile = mulIC->arithProfile();
2680 ASSERT(arithProfile);
2681 arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
2682 auto nonOptimizeVariant = operationValueMulProfiledNoOptimize;
2683 mulIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2684
2685#if ENABLE(MATH_IC_STATS)
2686 exec->codeBlock()->dumpMathICStats();
2687#endif
2688
2689 return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile, false);
2690}
2691
2692EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
2693{
2694 VM* vm = &exec->vm();
2695 NativeCallFrameTracer tracer(vm, exec);
2696
2697 ArithProfile* arithProfile = mulIC->arithProfile();
2698 ASSERT(arithProfile);
2699 return profiledMul(exec, encodedOp1, encodedOp2, *arithProfile);
2700}
2701
2702ALWAYS_INLINE static EncodedJSValue unprofiledNegate(ExecState* exec, EncodedJSValue encodedOperand)
2703{
2704 VM& vm = exec->vm();
2705 auto scope = DECLARE_THROW_SCOPE(vm);
2706 NativeCallFrameTracer tracer(&vm, exec);
2707
2708 JSValue operand = JSValue::decode(encodedOperand);
2709
2710 JSValue primValue = operand.toPrimitive(exec, PreferNumber);
2711 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2712
2713 if (primValue.isBigInt())
2714 return JSValue::encode(JSBigInt::unaryMinus(vm, asBigInt(primValue)));
2715
2716 double number = primValue.toNumber(exec);
2717 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2718 return JSValue::encode(jsNumber(-number));
2719}
2720
2721ALWAYS_INLINE static EncodedJSValue profiledNegate(ExecState* exec, EncodedJSValue encodedOperand, ArithProfile& arithProfile)
2722{
2723 VM& vm = exec->vm();
2724 auto scope = DECLARE_THROW_SCOPE(vm);
2725 NativeCallFrameTracer tracer(&vm, exec);
2726
2727 JSValue operand = JSValue::decode(encodedOperand);
2728 arithProfile.observeLHS(operand);
2729
2730 JSValue primValue = operand.toPrimitive(exec);
2731 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2732
2733 if (primValue.isBigInt()) {
2734 JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue));
2735 arithProfile.observeResult(result);
2736
2737 return JSValue::encode(result);
2738 }
2739
2740 double number = primValue.toNumber(exec);
2741 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2742 JSValue result = jsNumber(-number);
2743 arithProfile.observeResult(result);
2744 return JSValue::encode(result);
2745}
2746
2747EncodedJSValue JIT_OPERATION operationArithNegate(ExecState* exec, EncodedJSValue operand)
2748{
2749 return unprofiledNegate(exec, operand);
2750}
2751
2752EncodedJSValue JIT_OPERATION operationArithNegateProfiled(ExecState* exec, EncodedJSValue operand, ArithProfile* arithProfile)
2753{
2754 ASSERT(arithProfile);
2755 return profiledNegate(exec, operand, *arithProfile);
2756}
2757
2758EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(ExecState* exec, EncodedJSValue encodedOperand, JITNegIC* negIC)
2759{
2760 VM& vm = exec->vm();
2761 auto scope = DECLARE_THROW_SCOPE(vm);
2762 NativeCallFrameTracer tracer(&vm, exec);
2763
2764 JSValue operand = JSValue::decode(encodedOperand);
2765
2766 ArithProfile* arithProfile = negIC->arithProfile();
2767 ASSERT(arithProfile);
2768 arithProfile->observeLHS(operand);
2769 negIC->generateOutOfLine(exec->codeBlock(), operationArithNegateProfiled);
2770
2771#if ENABLE(MATH_IC_STATS)
2772 exec->codeBlock()->dumpMathICStats();
2773#endif
2774
2775 JSValue primValue = operand.toPrimitive(exec);
2776 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2777
2778 if (primValue.isBigInt()) {
2779 JSBigInt* result = JSBigInt::unaryMinus(vm, asBigInt(primValue));
2780 arithProfile->observeResult(result);
2781 return JSValue::encode(result);
2782 }
2783
2784 double number = primValue.toNumber(exec);
2785 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2786 JSValue result = jsNumber(-number);
2787 arithProfile->observeResult(result);
2788 return JSValue::encode(result);
2789}
2790
2791EncodedJSValue JIT_OPERATION operationArithNegateOptimize(ExecState* exec, EncodedJSValue encodedOperand, JITNegIC* negIC)
2792{
2793 VM& vm = exec->vm();
2794 auto scope = DECLARE_THROW_SCOPE(vm);
2795 NativeCallFrameTracer tracer(&vm, exec);
2796
2797 JSValue operand = JSValue::decode(encodedOperand);
2798
2799 if (ArithProfile* arithProfile = negIC->arithProfile())
2800 arithProfile->observeLHS(operand);
2801 negIC->generateOutOfLine(exec->codeBlock(), operationArithNegate);
2802
2803#if ENABLE(MATH_IC_STATS)
2804 exec->codeBlock()->dumpMathICStats();
2805#endif
2806
2807 JSValue primValue = operand.toPrimitive(exec);
2808 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2809
2810 if (primValue.isBigInt())
2811 return JSValue::encode(JSBigInt::unaryMinus(vm, asBigInt(primValue)));
2812
2813 double number = primValue.toNumber(exec);
2814 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2815 return JSValue::encode(jsNumber(-number));
2816}
2817
2818ALWAYS_INLINE static EncodedJSValue unprofiledSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2819{
2820 JSValue op1 = JSValue::decode(encodedOp1);
2821 JSValue op2 = JSValue::decode(encodedOp2);
2822
2823 return JSValue::encode(jsSub(exec, op1, op2));
2824}
2825
2826ALWAYS_INLINE static EncodedJSValue profiledSub(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
2827{
2828 auto scope = DECLARE_THROW_SCOPE(vm);
2829
2830 JSValue op1 = JSValue::decode(encodedOp1);
2831 JSValue op2 = JSValue::decode(encodedOp2);
2832
2833 if (shouldObserveLHSAndRHSTypes)
2834 arithProfile.observeLHSAndRHS(op1, op2);
2835
2836 JSValue result = jsSub(exec, op1, op2);
2837 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2838 arithProfile.observeResult(result);
2839 return JSValue::encode(result);
2840}
2841
2842EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
2843{
2844 VM* vm = &exec->vm();
2845 NativeCallFrameTracer tracer(vm, exec);
2846 return unprofiledSub(exec, encodedOp1, encodedOp2);
2847}
2848
2849EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
2850{
2851 ASSERT(arithProfile);
2852
2853 VM* vm = &exec->vm();
2854 NativeCallFrameTracer tracer(vm, exec);
2855
2856 return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
2857}
2858
2859EncodedJSValue JIT_OPERATION operationValueSubOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
2860{
2861 VM* vm = &exec->vm();
2862 NativeCallFrameTracer tracer(vm, exec);
2863
2864 auto nonOptimizeVariant = operationValueSubNoOptimize;
2865 if (ArithProfile* arithProfile = subIC->arithProfile())
2866 arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
2867 subIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2868
2869#if ENABLE(MATH_IC_STATS)
2870 exec->codeBlock()->dumpMathICStats();
2871#endif
2872
2873 return unprofiledSub(exec, encodedOp1, encodedOp2);
2874}
2875
2876EncodedJSValue JIT_OPERATION operationValueSubNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC*)
2877{
2878 VM* vm = &exec->vm();
2879 NativeCallFrameTracer tracer(vm, exec);
2880
2881 return unprofiledSub(exec, encodedOp1, encodedOp2);
2882}
2883
2884EncodedJSValue JIT_OPERATION operationValueSubProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
2885{
2886 VM* vm = &exec->vm();
2887 NativeCallFrameTracer tracer(vm, exec);
2888
2889 ArithProfile* arithProfile = subIC->arithProfile();
2890 ASSERT(arithProfile);
2891 arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
2892 auto nonOptimizeVariant = operationValueSubProfiledNoOptimize;
2893 subIC->generateOutOfLine(exec->codeBlock(), nonOptimizeVariant);
2894
2895#if ENABLE(MATH_IC_STATS)
2896 exec->codeBlock()->dumpMathICStats();
2897#endif
2898
2899 return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile, false);
2900}
2901
2902EncodedJSValue JIT_OPERATION operationValueSubProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
2903{
2904 VM* vm = &exec->vm();
2905 NativeCallFrameTracer tracer(vm, exec);
2906
2907 ArithProfile* arithProfile = subIC->arithProfile();
2908 ASSERT(arithProfile);
2909 return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
2910}
2911
2912void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec)
2913{
2914 VM& vm = exec->vm();
2915 NativeCallFrameTracer tracer(&vm, exec);
2916 vm.typeProfilerLog()->processLogEntries(vm, "Log Full, called from inside baseline JIT"_s);
2917}
2918
2919void JIT_OPERATION operationProcessShadowChickenLog(ExecState* exec)
2920{
2921 VM& vm = exec->vm();
2922 NativeCallFrameTracer tracer(&vm, exec);
2923 RELEASE_ASSERT(vm.shadowChicken());
2924 vm.shadowChicken()->update(vm, exec);
2925}
2926
2927int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState* exec)
2928{
2929 VM& vm = exec->vm();
2930 NativeCallFrameTracer tracer(&vm, exec);
2931 auto scope = DECLARE_THROW_SCOPE(vm);
2932 RELEASE_ASSERT(!!scope.exception());
2933
2934 if (isTerminatedExecutionException(vm, scope.exception())) {
2935 genericUnwind(&vm, exec);
2936 return 1;
2937 }
2938 return 0;
2939}
2940
2941} // extern "C"
2942
2943} // namespace JSC
2944
2945#endif // ENABLE(JIT)
2946