Object cycles should not prevent allocation elimination/sinking
[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 "JSCInlines.h"
34 #include "JSLexicalEnvironment.h"
35
36 namespace JSC { namespace FTL {
37
38 using namespace JSC::DFG;
39
40 extern "C" JSCell* JIT_OPERATION operationNewObjectWithButterfly(ExecState* exec, Structure* structure)
41 {
42     VM& vm = exec->vm();
43     NativeCallFrameTracer tracer(&vm, exec);
44     
45     Butterfly* butterfly = Butterfly::create(
46         vm, nullptr, 0, structure->outOfLineCapacity(), false, IndexingHeader(), 0);
47     
48     return JSFinalObject::create(exec, structure, butterfly);
49 }
50
51 extern "C" void JIT_OPERATION operationPopulateObjectInOSR(
52     ExecState* exec, ExitTimeObjectMaterialization* materialization,
53     EncodedJSValue* encodedValue, EncodedJSValue* values)
54 {
55     VM& vm = exec->vm();
56     CodeBlock* codeBlock = exec->codeBlock();
57
58     // We cannot GC. We've got pointers in evil places.
59     // FIXME: We are not doing anything that can GC here, and this is
60     // probably unnecessary.
61     DeferGCForAWhile deferGC(vm.heap);
62
63     switch (materialization->type()) {
64     case PhantomNewObject: {
65         JSFinalObject* object = jsCast<JSFinalObject*>(JSValue::decode(*encodedValue));
66         Structure* structure = object->structure();
67
68         // Figure out what the heck to populate the object with. Use
69         // getPropertiesConcurrently() because that happens to be
70         // lower-level and more convenient. It doesn't change the
71         // materialization of the property table. We want to have
72         // minimal visible effects on the system. Also, don't mind
73         // that this is O(n^2). It doesn't matter. We only get here
74         // from OSR exit.
75         for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
76             for (unsigned i = materialization->properties().size(); i--;) {
77                 const ExitPropertyValue& property = materialization->properties()[i];
78                 if (property.location().kind() != NamedPropertyPLoc)
79                     continue;
80                 if (codeBlock->identifier(property.location().info()).impl() != entry.key)
81                     continue;
82
83                 object->putDirect(vm, entry.offset, JSValue::decode(values[i]));
84             }
85         }
86         break;
87     }
88
89     case PhantomNewFunction:
90     case PhantomDirectArguments:
91     case PhantomClonedArguments:
92         // Those are completely handled by operationMaterializeObjectInOSR
93         break;
94
95     case PhantomCreateActivation: {
96         JSLexicalEnvironment* activation = jsCast<JSLexicalEnvironment*>(JSValue::decode(*encodedValue));
97
98         // Figure out what to populate the activation with
99         for (unsigned i = materialization->properties().size(); i--;) {
100             const ExitPropertyValue& property = materialization->properties()[i];
101             if (property.location().kind() != ClosureVarPLoc)
102                 continue;
103
104             activation->variableAt(ScopeOffset(property.location().info())).set(exec->vm(), activation, JSValue::decode(values[i]));
105         }
106
107         break;
108     }
109
110
111     default:
112         RELEASE_ASSERT_NOT_REACHED();
113         break;
114
115     }
116 }
117
118 extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
119     ExecState* exec, ExitTimeObjectMaterialization* materialization, EncodedJSValue* values)
120 {
121     VM& vm = exec->vm();
122
123     // We cannot GC. We've got pointers in evil places.
124     DeferGCForAWhile deferGC(vm.heap);
125     
126     switch (materialization->type()) {
127     case PhantomNewObject: {
128         // Figure out what the structure is
129         Structure* structure = nullptr;
130         for (unsigned i = materialization->properties().size(); i--;) {
131             const ExitPropertyValue& property = materialization->properties()[i];
132             if (property.location() != PromotedLocationDescriptor(StructurePLoc))
133                 continue;
134
135             structure = jsCast<Structure*>(JSValue::decode(values[i]));
136             break;
137         }
138         RELEASE_ASSERT(structure);
139
140         JSFinalObject* result = JSFinalObject::create(vm, structure);
141
142         // The real values will be put subsequently by
143         // operationPopulateNewObjectInOSR. We can't fill them in
144         // now, because they may not be available yet (typically
145         // because we have a cyclic dependency graph).
146
147         // We put a dummy value here in order to avoid super-subtle
148         // GC-and-OSR-exit crashes in case we have a bug and some
149         // field is, for any reason, not filled later.
150         // We use a random-ish number instead of a sensible value like
151         // undefined to make possible bugs easier to track.
152         for (PropertyMapEntry entry : structure->getPropertiesConcurrently())
153             result->putDirect(vm, entry.offset, jsNumber(19723));
154
155         return result;
156     }
157
158     case PhantomNewFunction: {
159         // Figure out what the executable and activation are
160         FunctionExecutable* executable = nullptr;
161         JSScope* activation = nullptr;
162         for (unsigned i = materialization->properties().size(); i--;) {
163             const ExitPropertyValue& property = materialization->properties()[i];
164             if (property.location() == PromotedLocationDescriptor(FunctionExecutablePLoc))
165                 executable = jsCast<FunctionExecutable*>(JSValue::decode(values[i]));
166             if (property.location() == PromotedLocationDescriptor(FunctionActivationPLoc))
167                 activation = jsCast<JSScope*>(JSValue::decode(values[i]));
168         }
169         RELEASE_ASSERT(executable && activation);
170
171         JSFunction* result = JSFunction::createWithInvalidatedReallocationWatchpoint(vm, executable, activation);
172
173         return result;
174     }
175
176     case PhantomCreateActivation: {
177         // Figure out what the scope and symbol table are
178         JSScope* scope = nullptr;
179         SymbolTable* table = nullptr;
180         for (unsigned i = materialization->properties().size(); i--;) {
181             const ExitPropertyValue& property = materialization->properties()[i];
182             if (property.location() == PromotedLocationDescriptor(ActivationScopePLoc))
183                 scope = jsCast<JSScope*>(JSValue::decode(values[i]));
184             else if (property.location() == PromotedLocationDescriptor(ActivationSymbolTablePLoc))
185                 table = jsCast<SymbolTable*>(JSValue::decode(values[i]));
186         }
187         RELEASE_ASSERT(scope);
188         RELEASE_ASSERT(table);
189
190         CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
191             materialization->origin(), exec->codeBlock());
192         Structure* structure = codeBlock->globalObject()->activationStructure();
193
194         JSLexicalEnvironment* result = JSLexicalEnvironment::create(vm, structure, scope, table);
195
196         RELEASE_ASSERT(materialization->properties().size() - 2 == table->scopeSize());
197
198         // The real values will be put subsequently by
199         // operationPopulateNewObjectInOSR. See the PhantomNewObject
200         // case for details.
201         for (unsigned i = materialization->properties().size(); i--;) {
202             const ExitPropertyValue& property = materialization->properties()[i];
203             if (property.location().kind() != ClosureVarPLoc)
204                 continue;
205
206             result->variableAt(ScopeOffset(property.location().info())).set(
207                 exec->vm(), result, jsNumber(29834));
208         }
209
210         if (validationEnabled()) {
211             // Validate to make sure every slot in the scope has one value.
212             ConcurrentJITLocker locker(table->m_lock);
213             for (auto iter = table->begin(locker), end = table->end(locker); iter != end; ++iter) {
214                 bool found = false;
215                 for (unsigned i = materialization->properties().size(); i--;) {
216                     const ExitPropertyValue& property = materialization->properties()[i];
217                     if (property.location().kind() != ClosureVarPLoc)
218                         continue;
219                     if (ScopeOffset(property.location().info()) == iter->value.scopeOffset()) {
220                         found = true;
221                         break;
222                     }
223                 }
224                 ASSERT_UNUSED(found, found);
225             }
226             unsigned numberOfClosureVarPloc = 0;
227             for (unsigned i = materialization->properties().size(); i--;) {
228                 const ExitPropertyValue& property = materialization->properties()[i];
229                 if (property.location().kind() == ClosureVarPLoc)
230                     numberOfClosureVarPloc++;
231             }
232             ASSERT(numberOfClosureVarPloc == table->scopeSize());
233         }
234
235         return result;
236     }
237
238     case PhantomDirectArguments:
239     case PhantomClonedArguments: {
240         if (!materialization->origin().inlineCallFrame) {
241             switch (materialization->type()) {
242             case PhantomDirectArguments:
243                 return DirectArguments::createByCopying(exec);
244             case PhantomClonedArguments:
245                 return ClonedArguments::createWithMachineFrame(exec, exec, ArgumentsMode::Cloned);
246             default:
247                 RELEASE_ASSERT_NOT_REACHED();
248                 return nullptr;
249             }
250         }
251
252         // First figure out the argument count. If there isn't one then we represent the machine frame.
253         unsigned argumentCount = 0;
254         if (materialization->origin().inlineCallFrame->isVarargs()) {
255             for (unsigned i = materialization->properties().size(); i--;) {
256                 const ExitPropertyValue& property = materialization->properties()[i];
257                 if (property.location() != PromotedLocationDescriptor(ArgumentCountPLoc))
258                     continue;
259                 
260                 argumentCount = JSValue::decode(values[i]).asUInt32();
261                 RELEASE_ASSERT(argumentCount);
262                 break;
263             }
264             RELEASE_ASSERT(argumentCount);
265         } else
266             argumentCount = materialization->origin().inlineCallFrame->arguments.size();
267         
268         JSFunction* callee = nullptr;
269         if (materialization->origin().inlineCallFrame->isClosureCall) {
270             for (unsigned i = materialization->properties().size(); i--;) {
271                 const ExitPropertyValue& property = materialization->properties()[i];
272                 if (property.location() != PromotedLocationDescriptor(ArgumentsCalleePLoc))
273                     continue;
274                 
275                 callee = jsCast<JSFunction*>(JSValue::decode(values[i]));
276                 break;
277             }
278         } else
279             callee = materialization->origin().inlineCallFrame->calleeConstant();
280         RELEASE_ASSERT(callee);
281         
282         CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
283             materialization->origin(), exec->codeBlock());
284         
285         // We have an inline frame and we have all of the data we need to recreate it.
286         switch (materialization->type()) {
287         case PhantomDirectArguments: {
288             unsigned length = argumentCount - 1;
289             unsigned capacity = std::max(length, static_cast<unsigned>(codeBlock->numParameters() - 1));
290             DirectArguments* result = DirectArguments::create(
291                 vm, codeBlock->globalObject()->directArgumentsStructure(), length, capacity);
292             result->callee().set(vm, result, callee);
293             for (unsigned i = materialization->properties().size(); i--;) {
294                 const ExitPropertyValue& property = materialization->properties()[i];
295                 if (property.location().kind() != ArgumentPLoc)
296                     continue;
297                 
298                 unsigned index = property.location().info();
299                 if (index >= capacity)
300                     continue;
301                 
302                 // We don't want to use setIndexQuickly(), since that's only for the passed-in
303                 // arguments but sometimes the number of named arguments is greater. For
304                 // example:
305                 //
306                 // function foo(a, b, c) { ... }
307                 // foo();
308                 //
309                 // setIndexQuickly() would fail for indices 0, 1, 2 - but we need to recover
310                 // those here.
311                 result->argument(DirectArgumentsOffset(index)).set(
312                     vm, result, JSValue::decode(values[i]));
313             }
314             return result;
315         }
316         case PhantomClonedArguments: {
317             unsigned length = argumentCount - 1;
318             ClonedArguments* result = ClonedArguments::createEmpty(
319                 vm, codeBlock->globalObject()->outOfBandArgumentsStructure(), callee);
320             
321             for (unsigned i = materialization->properties().size(); i--;) {
322                 const ExitPropertyValue& property = materialization->properties()[i];
323                 if (property.location().kind() != ArgumentPLoc)
324                     continue;
325                 
326                 unsigned index = property.location().info();
327                 if (index >= length)
328                     continue;
329                 result->putDirectIndex(exec, index, JSValue::decode(values[i]));
330             }
331             
332             result->putDirect(vm, vm.propertyNames->length, jsNumber(length));
333             return result;
334         }
335         default:
336             RELEASE_ASSERT_NOT_REACHED();
337             return nullptr;
338         }
339     }
340         
341     default:
342         RELEASE_ASSERT_NOT_REACHED();
343         return nullptr;
344     }
345 }
346
347 } } // namespace JSC::FTL
348
349 #endif // ENABLE(FTL_JIT)
350