[JSC] Handle new_async_func / new_async_func_exp in DFG / FTL
[WebKit-https.git] / Source / JavaScriptCore / ftl / FTLOperations.cpp
1 /*
2  * Copyright (C) 2014, 2015 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 "FTLOperations.h"
28
29 #if ENABLE(FTL_JIT)
30
31 #include "ClonedArguments.h"
32 #include "DirectArguments.h"
33 #include "FTLJITCode.h"
34 #include "FTLLazySlowPath.h"
35 #include "InlineCallFrame.h"
36 #include "JSAsyncFunction.h"
37 #include "JSCInlines.h"
38 #include "JSGeneratorFunction.h"
39 #include "JSLexicalEnvironment.h"
40
41 namespace JSC { namespace FTL {
42
43 using namespace JSC::DFG;
44
45 extern "C" void JIT_OPERATION operationPopulateObjectInOSR(
46     ExecState* exec, ExitTimeObjectMaterialization* materialization,
47     EncodedJSValue* encodedValue, EncodedJSValue* values)
48 {
49     VM& vm = exec->vm();
50     CodeBlock* codeBlock = exec->codeBlock();
51
52     // We cannot GC. We've got pointers in evil places.
53     // FIXME: We are not doing anything that can GC here, and this is
54     // probably unnecessary.
55     DeferGCForAWhile deferGC(vm.heap);
56
57     switch (materialization->type()) {
58     case PhantomNewObject: {
59         JSFinalObject* object = jsCast<JSFinalObject*>(JSValue::decode(*encodedValue));
60         Structure* structure = object->structure();
61
62         // Figure out what the heck to populate the object with. Use
63         // getPropertiesConcurrently() because that happens to be
64         // lower-level and more convenient. It doesn't change the
65         // materialization of the property table. We want to have
66         // minimal visible effects on the system. Also, don't mind
67         // that this is O(n^2). It doesn't matter. We only get here
68         // from OSR exit.
69         for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
70             for (unsigned i = materialization->properties().size(); i--;) {
71                 const ExitPropertyValue& property = materialization->properties()[i];
72                 if (property.location().kind() != NamedPropertyPLoc)
73                     continue;
74                 if (codeBlock->identifier(property.location().info()).impl() != entry.key)
75                     continue;
76
77                 object->putDirect(vm, entry.offset, JSValue::decode(values[i]));
78             }
79         }
80         break;
81     }
82
83     case PhantomNewFunction:
84     case PhantomNewGeneratorFunction:
85     case PhantomNewAsyncFunction:
86     case PhantomDirectArguments:
87     case PhantomClonedArguments:
88     case PhantomCreateRest:
89         // Those are completely handled by operationMaterializeObjectInOSR
90         break;
91
92     case PhantomCreateActivation: {
93         JSLexicalEnvironment* activation = jsCast<JSLexicalEnvironment*>(JSValue::decode(*encodedValue));
94
95         // Figure out what to populate the activation with
96         for (unsigned i = materialization->properties().size(); i--;) {
97             const ExitPropertyValue& property = materialization->properties()[i];
98             if (property.location().kind() != ClosureVarPLoc)
99                 continue;
100
101             activation->variableAt(ScopeOffset(property.location().info())).set(exec->vm(), activation, JSValue::decode(values[i]));
102         }
103
104         break;
105     }
106
107
108     default:
109         RELEASE_ASSERT_NOT_REACHED();
110         break;
111
112     }
113 }
114
115 extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
116     ExecState* exec, ExitTimeObjectMaterialization* materialization, EncodedJSValue* values)
117 {
118     VM& vm = exec->vm();
119
120     // We cannot GC. We've got pointers in evil places.
121     DeferGCForAWhile deferGC(vm.heap);
122     
123     switch (materialization->type()) {
124     case PhantomNewObject: {
125         // Figure out what the structure is
126         Structure* structure = nullptr;
127         for (unsigned i = materialization->properties().size(); i--;) {
128             const ExitPropertyValue& property = materialization->properties()[i];
129             if (property.location() != PromotedLocationDescriptor(StructurePLoc))
130                 continue;
131
132             structure = jsCast<Structure*>(JSValue::decode(values[i]));
133             break;
134         }
135         RELEASE_ASSERT(structure);
136
137         JSFinalObject* result = JSFinalObject::create(vm, structure);
138
139         // The real values will be put subsequently by
140         // operationPopulateNewObjectInOSR. We can't fill them in
141         // now, because they may not be available yet (typically
142         // because we have a cyclic dependency graph).
143
144         // We put a dummy value here in order to avoid super-subtle
145         // GC-and-OSR-exit crashes in case we have a bug and some
146         // field is, for any reason, not filled later.
147         // We use a random-ish number instead of a sensible value like
148         // undefined to make possible bugs easier to track.
149         for (PropertyMapEntry entry : structure->getPropertiesConcurrently())
150             result->putDirect(vm, entry.offset, jsNumber(19723));
151
152         return result;
153     }
154
155     case PhantomNewFunction:
156     case PhantomNewGeneratorFunction:
157     case PhantomNewAsyncFunction: {
158         // Figure out what the executable and activation are
159         FunctionExecutable* executable = nullptr;
160         JSScope* activation = nullptr;
161         for (unsigned i = materialization->properties().size(); i--;) {
162             const ExitPropertyValue& property = materialization->properties()[i];
163             if (property.location() == PromotedLocationDescriptor(FunctionExecutablePLoc))
164                 executable = jsCast<FunctionExecutable*>(JSValue::decode(values[i]));
165             if (property.location() == PromotedLocationDescriptor(FunctionActivationPLoc))
166                 activation = jsCast<JSScope*>(JSValue::decode(values[i]));
167         }
168         RELEASE_ASSERT(executable && activation);
169
170         if (materialization->type() == PhantomNewFunction)
171             return JSFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
172         else if (materialization->type() == PhantomNewGeneratorFunction)
173             return JSGeneratorFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);    
174         ASSERT(materialization->type() == PhantomNewAsyncFunction);
175         return JSAsyncFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
176     }
177
178     case PhantomCreateActivation: {
179         // Figure out what the scope and symbol table are
180         JSScope* scope = nullptr;
181         SymbolTable* table = nullptr;
182         for (unsigned i = materialization->properties().size(); i--;) {
183             const ExitPropertyValue& property = materialization->properties()[i];
184             if (property.location() == PromotedLocationDescriptor(ActivationScopePLoc))
185                 scope = jsCast<JSScope*>(JSValue::decode(values[i]));
186             else if (property.location() == PromotedLocationDescriptor(ActivationSymbolTablePLoc))
187                 table = jsCast<SymbolTable*>(JSValue::decode(values[i]));
188         }
189         RELEASE_ASSERT(scope);
190         RELEASE_ASSERT(table);
191
192         CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
193             materialization->origin(), exec->codeBlock());
194         Structure* structure = codeBlock->globalObject()->activationStructure();
195
196         // It doesn't matter what values we initialize as bottom values inside the activation constructor because
197         // activation sinking will set bottom values for each slot.
198         // FIXME: Slight optimization would be to create a constructor that doesn't initialize all slots.
199         JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table, jsUndefined());
200
201         RELEASE_ASSERT(materialization->properties().size() - 2 == table->scopeSize());
202
203         // The real values will be put subsequently by
204         // operationPopulateNewObjectInOSR. See the PhantomNewObject
205         // case for details.
206         for (unsigned i = materialization->properties().size(); i--;) {
207             const ExitPropertyValue& property = materialization->properties()[i];
208             if (property.location().kind() != ClosureVarPLoc)
209                 continue;
210
211             result->variableAt(ScopeOffset(property.location().info())).set(
212                 exec->vm(), result, jsNumber(29834));
213         }
214
215         if (validationEnabled()) {
216             // Validate to make sure every slot in the scope has one value.
217             ConcurrentJITLocker locker(table->m_lock);
218             for (auto iter = table->begin(locker), end = table->end(locker); iter != end; ++iter) {
219                 bool found = false;
220                 for (unsigned i = materialization->properties().size(); i--;) {
221                     const ExitPropertyValue& property = materialization->properties()[i];
222                     if (property.location().kind() != ClosureVarPLoc)
223                         continue;
224                     if (ScopeOffset(property.location().info()) == iter->value.scopeOffset()) {
225                         found = true;
226                         break;
227                     }
228                 }
229                 ASSERT_UNUSED(found, found);
230             }
231             unsigned numberOfClosureVarPloc = 0;
232             for (unsigned i = materialization->properties().size(); i--;) {
233                 const ExitPropertyValue& property = materialization->properties()[i];
234                 if (property.location().kind() == ClosureVarPLoc)
235                     numberOfClosureVarPloc++;
236             }
237             ASSERT(numberOfClosureVarPloc == table->scopeSize());
238         }
239
240         return result;
241     }
242
243     case PhantomCreateRest:
244     case PhantomDirectArguments:
245     case PhantomClonedArguments: {
246         if (!materialization->origin().inlineCallFrame) {
247             switch (materialization->type()) {
248             case PhantomDirectArguments:
249                 return DirectArguments::createByCopying(exec);
250             case PhantomClonedArguments:
251                 return ClonedArguments::createWithMachineFrame(exec, exec, ArgumentsMode::Cloned);
252             case PhantomCreateRest: {
253                 CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
254                     materialization->origin(), exec->codeBlock());
255
256                 unsigned numberOfArgumentsToSkip = codeBlock->numParameters() - 1;
257                 JSGlobalObject* globalObject = codeBlock->globalObject();
258                 Structure* structure = globalObject->restParameterStructure();
259                 JSValue* argumentsToCopyRegion = exec->addressOfArgumentsStart() + numberOfArgumentsToSkip;
260                 unsigned arraySize = exec->argumentCount() > numberOfArgumentsToSkip ? exec->argumentCount() - numberOfArgumentsToSkip : 0;
261                 return constructArray(exec, structure, argumentsToCopyRegion, arraySize);
262             }
263             default:
264                 RELEASE_ASSERT_NOT_REACHED();
265                 return nullptr;
266             }
267         }
268
269         // First figure out the argument count. If there isn't one then we represent the machine frame.
270         unsigned argumentCount = 0;
271         if (materialization->origin().inlineCallFrame->isVarargs()) {
272             for (unsigned i = materialization->properties().size(); i--;) {
273                 const ExitPropertyValue& property = materialization->properties()[i];
274                 if (property.location() != PromotedLocationDescriptor(ArgumentCountPLoc))
275                     continue;
276                 argumentCount = JSValue::decode(values[i]).asUInt32();
277                 break;
278             }
279         } else
280             argumentCount = materialization->origin().inlineCallFrame->arguments.size();
281         RELEASE_ASSERT(argumentCount);
282         
283         JSFunction* callee = nullptr;
284         if (materialization->origin().inlineCallFrame->isClosureCall) {
285             for (unsigned i = materialization->properties().size(); i--;) {
286                 const ExitPropertyValue& property = materialization->properties()[i];
287                 if (property.location() != PromotedLocationDescriptor(ArgumentsCalleePLoc))
288                     continue;
289                 
290                 callee = jsCast<JSFunction*>(JSValue::decode(values[i]));
291                 break;
292             }
293         } else
294             callee = materialization->origin().inlineCallFrame->calleeConstant();
295         RELEASE_ASSERT(callee);
296         
297         CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
298             materialization->origin(), exec->codeBlock());
299         
300         // We have an inline frame and we have all of the data we need to recreate it.
301         switch (materialization->type()) {
302         case PhantomDirectArguments: {
303             unsigned length = argumentCount - 1;
304             unsigned capacity = std::max(length, static_cast<unsigned>(codeBlock->numParameters() - 1));
305             DirectArguments* result = DirectArguments::create(
306                 vm, codeBlock->globalObject()->directArgumentsStructure(), length, capacity);
307             result->callee().set(vm, result, callee);
308             for (unsigned i = materialization->properties().size(); i--;) {
309                 const ExitPropertyValue& property = materialization->properties()[i];
310                 if (property.location().kind() != ArgumentPLoc)
311                     continue;
312                 
313                 unsigned index = property.location().info();
314                 if (index >= capacity)
315                     continue;
316                 
317                 // We don't want to use setIndexQuickly(), since that's only for the passed-in
318                 // arguments but sometimes the number of named arguments is greater. For
319                 // example:
320                 //
321                 // function foo(a, b, c) { ... }
322                 // foo();
323                 //
324                 // setIndexQuickly() would fail for indices 0, 1, 2 - but we need to recover
325                 // those here.
326                 result->argument(DirectArgumentsOffset(index)).set(
327                     vm, result, JSValue::decode(values[i]));
328             }
329             return result;
330         }
331         case PhantomClonedArguments: {
332             unsigned length = argumentCount - 1;
333             ClonedArguments* result = ClonedArguments::createEmpty(
334                 vm, codeBlock->globalObject()->clonedArgumentsStructure(), callee, length);
335             
336             for (unsigned i = materialization->properties().size(); i--;) {
337                 const ExitPropertyValue& property = materialization->properties()[i];
338                 if (property.location().kind() != ArgumentPLoc)
339                     continue;
340                 
341                 unsigned index = property.location().info();
342                 if (index >= length)
343                     continue;
344                 result->initializeIndex(vm, index, JSValue::decode(values[i]));
345             }
346             
347             return result;
348         }
349         case PhantomCreateRest: {
350             unsigned numberOfArgumentsToSkip = codeBlock->numParameters() - 1;
351             JSGlobalObject* globalObject = codeBlock->globalObject();
352             Structure* structure = globalObject->restParameterStructure();
353             ASSERT(argumentCount > 0);
354             unsigned arraySize = (argumentCount - 1) > numberOfArgumentsToSkip ? argumentCount - 1 - numberOfArgumentsToSkip : 0;
355             JSArray* array = JSArray::tryCreateUninitialized(vm, structure, arraySize);
356             RELEASE_ASSERT(array);
357
358             for (unsigned i = materialization->properties().size(); i--;) {
359                 const ExitPropertyValue& property = materialization->properties()[i];
360                 if (property.location().kind() != ArgumentPLoc)
361                     continue;
362
363                 unsigned argIndex = property.location().info();
364                 if (numberOfArgumentsToSkip > argIndex)
365                     continue;
366                 unsigned arrayIndex = argIndex - numberOfArgumentsToSkip;
367                 if (arrayIndex >= arraySize)
368                     continue;
369                 array->initializeIndex(vm, arrayIndex, JSValue::decode(values[i]));
370             }
371
372 #if !ASSERT_DISABLED
373             // We avoid this O(n^2) loop when asserts are disabled, but the condition checked here
374             // must hold to ensure the correctness of the above loop because of how we allocate the array.
375             for (unsigned targetIndex = 0; targetIndex < arraySize; ++targetIndex) {
376                 bool found = false;
377                 for (unsigned i = materialization->properties().size(); i--;) {
378                     const ExitPropertyValue& property = materialization->properties()[i];
379                     if (property.location().kind() != ArgumentPLoc)
380                         continue;
381
382                     unsigned argIndex = property.location().info();
383                     if (numberOfArgumentsToSkip > argIndex)
384                         continue;
385                     unsigned arrayIndex = argIndex - numberOfArgumentsToSkip;
386                     if (arrayIndex >= arraySize)
387                         continue;
388                     if (arrayIndex == targetIndex) {
389                         found = true;
390                         break;
391                     }
392                 }
393                 ASSERT(found);
394             }
395 #endif
396
397             return array;
398         }
399         default:
400             RELEASE_ASSERT_NOT_REACHED();
401             return nullptr;
402         }
403     }
404         
405     default:
406         RELEASE_ASSERT_NOT_REACHED();
407         return nullptr;
408     }
409 }
410
411 extern "C" void* JIT_OPERATION compileFTLLazySlowPath(ExecState* exec, unsigned index)
412 {
413     VM& vm = exec->vm();
414
415     // We cannot GC. We've got pointers in evil places.
416     DeferGCForAWhile deferGC(vm.heap);
417
418     CodeBlock* codeBlock = exec->codeBlock();
419     JITCode* jitCode = codeBlock->jitCode()->ftl();
420
421     LazySlowPath& lazySlowPath = *jitCode->lazySlowPaths[index];
422     lazySlowPath.generate(codeBlock);
423
424     return lazySlowPath.stub().code().executableAddress();
425 }
426
427 } } // namespace JSC::FTL
428
429 #endif // ENABLE(FTL_JIT)
430