1/*
2 * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DFGAbstractValue.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGGraph.h"
32#include "JSCInlines.h"
33#include "TrackedReferences.h"
34
35namespace JSC { namespace DFG {
36
37void AbstractValue::observeTransitions(const TransitionVector& vector)
38{
39 if (m_type & SpecCell) {
40 m_structure.observeTransitions(vector);
41 ArrayModes newModes = 0;
42 for (unsigned i = vector.size(); i--;) {
43 if (m_arrayModes & arrayModesFromStructure(vector[i].previous.get()))
44 newModes |= arrayModesFromStructure(vector[i].next.get());
45 }
46 m_arrayModes |= newModes;
47 }
48 checkConsistency();
49}
50
51void AbstractValue::set(Graph& graph, const FrozenValue& value, StructureClobberState clobberState)
52{
53 if (!!value && value.value().isCell()) {
54 Structure* structure = value.structure();
55 StructureRegistrationResult result;
56 RegisteredStructure registeredStructure = graph.registerStructure(structure, result);
57 if (result == StructureRegisteredAndWatched) {
58 m_structure = registeredStructure;
59 if (clobberState == StructuresAreClobbered) {
60 m_arrayModes = ALL_ARRAY_MODES;
61 m_structure.clobber();
62 } else
63 m_arrayModes = arrayModesFromStructure(structure);
64 } else {
65 m_structure.makeTop();
66 m_arrayModes = ALL_ARRAY_MODES;
67 }
68 } else {
69 m_structure.clear();
70 m_arrayModes = 0;
71 }
72
73 m_type = speculationFromValue(value.value());
74 m_value = value.value();
75
76 checkConsistency();
77 assertIsRegistered(graph);
78}
79
80void AbstractValue::set(Graph& graph, Structure* structure)
81{
82 set(graph, graph.registerStructure(structure));
83}
84
85void AbstractValue::set(Graph& graph, RegisteredStructure structure)
86{
87 RELEASE_ASSERT(structure);
88
89 m_structure = structure;
90 m_arrayModes = arrayModesFromStructure(structure.get());
91 m_type = speculationFromStructure(structure.get());
92 m_value = JSValue();
93
94 checkConsistency();
95 assertIsRegistered(graph);
96}
97
98void AbstractValue::set(Graph& graph, const RegisteredStructureSet& set)
99{
100 m_structure = set;
101 m_arrayModes = set.arrayModesFromStructures();
102 m_type = set.speculationFromStructures();
103 m_value = JSValue();
104
105 checkConsistency();
106 assertIsRegistered(graph);
107}
108
109void AbstractValue::setType(Graph& graph, SpeculatedType type)
110{
111 SpeculatedType cellType = type & SpecCell;
112 if (cellType) {
113 if (!(cellType & ~SpecString))
114 m_structure = graph.stringStructure;
115 else if (isSymbolSpeculation(cellType))
116 m_structure = graph.symbolStructure;
117 else
118 m_structure.makeTop();
119 m_arrayModes = ALL_ARRAY_MODES;
120 } else {
121 m_structure.clear();
122 m_arrayModes = 0;
123 }
124 m_type = type;
125 m_value = JSValue();
126 checkConsistency();
127}
128
129void AbstractValue::fixTypeForRepresentation(Graph& graph, NodeFlags representation, Node* node)
130{
131 if (representation == NodeResultDouble) {
132 if (m_value) {
133 DFG_ASSERT(graph, node, m_value.isNumber());
134 if (m_value.isInt32())
135 m_value = jsDoubleNumber(m_value.asNumber());
136 }
137 if (m_type & SpecIntAnyFormat) {
138 m_type &= ~SpecIntAnyFormat;
139 m_type |= SpecAnyIntAsDouble;
140 }
141 if (m_type & ~SpecFullDouble)
142 DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for double node has type outside SpecFullDouble.\n").data());
143 } else if (representation == NodeResultInt52) {
144 if (m_type & SpecAnyIntAsDouble) {
145 // AnyIntAsDouble can produce i32 or i52. SpecAnyIntAsDouble doesn't bound the magnitude of the value.
146 m_type &= ~SpecAnyIntAsDouble;
147 m_type |= SpecInt52Any;
148 }
149
150 if (m_type & SpecInt32Only) {
151 m_type &= ~SpecInt32Only;
152 m_type |= SpecInt32AsInt52;
153 }
154
155 if (m_type & ~SpecInt52Any)
156 DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for int52 node has type outside SpecInt52Any.\n").data());
157
158 if (m_value) {
159 DFG_ASSERT(graph, node, m_value.isAnyInt());
160 m_type = int52AwareSpeculationFromValue(m_value);
161 }
162 } else {
163 if (m_type & SpecInt32AsInt52) {
164 m_type &= ~SpecInt32AsInt52;
165 m_type |= SpecInt32Only;
166 }
167 if (m_type & SpecNonInt32AsInt52) {
168 m_type &= ~SpecNonInt32AsInt52;
169 m_type |= SpecAnyIntAsDouble;
170 }
171 if (m_type & ~SpecBytecodeTop)
172 DFG_CRASH(graph, node, toCString("Abstract value ", *this, " for value node has type outside SpecBytecodeTop.\n").data());
173 }
174
175 checkConsistency();
176}
177
178void AbstractValue::fixTypeForRepresentation(Graph& graph, Node* node)
179{
180 fixTypeForRepresentation(graph, node->result(), node);
181}
182
183bool AbstractValue::mergeOSREntryValue(Graph& graph, JSValue value)
184{
185 AbstractValue oldMe = *this;
186
187 if (isClear()) {
188 FrozenValue* frozenValue = graph.freeze(value);
189 if (frozenValue->pointsToHeap()) {
190 m_structure = graph.registerStructure(frozenValue->structure());
191 m_arrayModes = arrayModesFromStructure(frozenValue->structure());
192 } else {
193 m_structure.clear();
194 m_arrayModes = 0;
195 }
196
197 m_type = speculationFromValue(value);
198 m_value = value;
199 } else {
200 mergeSpeculation(m_type, speculationFromValue(value));
201 if (!!value && value.isCell()) {
202 RegisteredStructure structure = graph.registerStructure(value.asCell()->structure(graph.m_vm));
203 mergeArrayModes(m_arrayModes, arrayModesFromStructure(structure.get()));
204 m_structure.merge(RegisteredStructureSet(structure));
205 }
206 if (m_value != value)
207 m_value = JSValue();
208 }
209
210 checkConsistency();
211 assertIsRegistered(graph);
212
213 return oldMe != *this;
214}
215
216FiltrationResult AbstractValue::filter(
217 Graph& graph, const RegisteredStructureSet& other, SpeculatedType admittedTypes)
218{
219 ASSERT(!(admittedTypes & SpecCell));
220
221 if (isClear())
222 return FiltrationOK;
223
224 // FIXME: This could be optimized for the common case of m_type not
225 // having structures, array modes, or a specific value.
226 // https://bugs.webkit.org/show_bug.cgi?id=109663
227
228 m_type &= other.speculationFromStructures() | admittedTypes;
229 m_arrayModes &= other.arrayModesFromStructures();
230 m_structure.filter(other);
231
232 // It's possible that prior to the above two statements we had (Foo, TOP), where
233 // Foo is a SpeculatedType that is disjoint with the passed RegisteredStructureSet. In that
234 // case, we will now have (None, [someStructure]). In general, we need to make
235 // sure that new information gleaned from the SpeculatedType needs to be fed back
236 // into the information gleaned from the RegisteredStructureSet.
237 m_structure.filter(m_type);
238
239 filterArrayModesByType();
240 filterValueByType();
241 return normalizeClarity(graph);
242}
243
244FiltrationResult AbstractValue::changeStructure(Graph& graph, const RegisteredStructureSet& other)
245{
246 m_type &= other.speculationFromStructures();
247 m_arrayModes = other.arrayModesFromStructures();
248 m_structure = other;
249
250 filterValueByType();
251
252 return normalizeClarity(graph);
253}
254
255FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes)
256{
257 ASSERT(arrayModes);
258
259 if (isClear())
260 return FiltrationOK;
261
262 m_type &= SpecCell;
263 m_arrayModes &= arrayModes;
264 return normalizeClarity();
265}
266
267FiltrationResult AbstractValue::filterClassInfo(Graph& graph, const ClassInfo* classInfo)
268{
269 // FIXME: AI should track ClassInfo to leverage hierarchical class information.
270 // https://bugs.webkit.org/show_bug.cgi?id=162989
271 if (isClear())
272 return FiltrationOK;
273
274 m_type &= speculationFromClassInfo(classInfo);
275 m_structure.filterClassInfo(classInfo);
276
277 m_structure.filter(m_type);
278
279 filterArrayModesByType();
280 filterValueByType();
281 return normalizeClarity(graph);
282}
283
284FiltrationResult AbstractValue::filterSlow(SpeculatedType type)
285{
286 m_type &= type;
287
288 // It's possible that prior to this filter() call we had, say, (Final, TOP), and
289 // the passed type is Array. At this point we'll have (None, TOP). The best way
290 // to ensure that the structure filtering does the right thing is to filter on
291 // the new type (None) rather than the one passed (Array).
292 m_structure.filter(m_type);
293 filterArrayModesByType();
294 filterValueByType();
295 return normalizeClarity();
296}
297
298FiltrationResult AbstractValue::fastForwardToAndFilterSlow(AbstractValueClobberEpoch newEpoch, SpeculatedType type)
299{
300 if (newEpoch != m_effectEpoch)
301 fastForwardToSlow(newEpoch);
302
303 return filterSlow(type);
304}
305
306FiltrationResult AbstractValue::filterByValue(const FrozenValue& value)
307{
308 FiltrationResult result = filter(speculationFromValue(value.value()));
309 if (m_type)
310 m_value = value.value();
311 return result;
312}
313
314bool AbstractValue::contains(RegisteredStructure structure) const
315{
316 return couldBeType(speculationFromStructure(structure.get()))
317 && (m_arrayModes & arrayModesFromStructure(structure.get()))
318 && m_structure.contains(structure);
319}
320
321FiltrationResult AbstractValue::filter(const AbstractValue& other)
322{
323 m_type &= other.m_type;
324 m_structure.filter(other.m_structure);
325 m_arrayModes &= other.m_arrayModes;
326
327 m_structure.filter(m_type);
328 filterArrayModesByType();
329 filterValueByType();
330
331 if (normalizeClarity() == Contradiction)
332 return Contradiction;
333
334 if (m_value == other.m_value)
335 return FiltrationOK;
336
337 // Neither of us are BOTTOM, so an empty value means TOP.
338 if (!m_value) {
339 // We previously didn't prove a value but now we have done so.
340 m_value = other.m_value;
341 return FiltrationOK;
342 }
343
344 if (!other.m_value) {
345 // We had proved a value but the other guy hadn't, so keep our proof.
346 return FiltrationOK;
347 }
348
349 // We both proved there to be a specific value but they are different.
350 clear();
351 return Contradiction;
352}
353
354void AbstractValue::filterValueByType()
355{
356 // We could go further, and ensure that if the futurePossibleStructure contravenes
357 // the value, then we could clear both of those things. But that's unlikely to help
358 // in any realistic scenario, so we don't do it. Simpler is better.
359
360 if (!!m_type) {
361 // The type is still non-empty. It may be that the new type renders
362 // the value empty because it contravenes the constant value we had.
363 if (m_value && !validateTypeAcceptingBoxedInt52(m_value))
364 clear();
365 return;
366 }
367
368 // The type has been rendered empty. That means that the value must now be invalid,
369 // as well.
370 ASSERT(!m_value || !validateTypeAcceptingBoxedInt52(m_value));
371 m_value = JSValue();
372}
373
374void AbstractValue::filterArrayModesByType()
375{
376 if (!(m_type & SpecCell))
377 m_arrayModes = 0;
378 else if (!(m_type & ~SpecArray))
379 m_arrayModes &= ALL_ARRAY_ARRAY_MODES;
380
381 // NOTE: If m_type doesn't have SpecArray set, that doesn't mean that the
382 // array modes have to be a subset of ALL_NON_ARRAY_ARRAY_MODES, since
383 // in the speculated type type-system, RegExpMatchesArry and ArrayPrototype
384 // are Otherobj (since they are not *exactly* JSArray) but in the ArrayModes
385 // type system they are arrays (since they expose the magical length
386 // property and are otherwise allocated using array allocation). Hence the
387 // following would be wrong:
388 //
389 // if (!(m_type & SpecArray))
390 // m_arrayModes &= ALL_NON_ARRAY_ARRAY_MODES;
391}
392
393bool AbstractValue::shouldBeClear() const
394{
395 if (m_type == SpecNone)
396 return true;
397
398 if (!(m_type & ~SpecCell)
399 && (!m_arrayModes || m_structure.isClear()))
400 return true;
401
402 return false;
403}
404
405FiltrationResult AbstractValue::normalizeClarity()
406{
407 // It's useful to be able to quickly check if an abstract value is clear.
408 // This normalizes everything to make that easy.
409
410 FiltrationResult result;
411
412 if (shouldBeClear()) {
413 clear();
414 result = Contradiction;
415 } else
416 result = FiltrationOK;
417
418 checkConsistency();
419
420 return result;
421}
422
423FiltrationResult AbstractValue::normalizeClarity(Graph& graph)
424{
425 FiltrationResult result = normalizeClarity();
426 assertIsRegistered(graph);
427 return result;
428}
429
430#if !ASSERT_DISABLED
431void AbstractValue::checkConsistency() const
432{
433 if (!(m_type & SpecCell)) {
434 RELEASE_ASSERT(m_structure.isClear());
435 RELEASE_ASSERT(!m_arrayModes);
436 }
437
438 if (isClear())
439 RELEASE_ASSERT(!m_value);
440
441 if (m_type & SpecInt52Any) {
442 if (m_type != SpecFullTop)
443 RELEASE_ASSERT(isAnyInt52Speculation(m_type));
444 }
445
446 if (!!m_value)
447 RELEASE_ASSERT(validateTypeAcceptingBoxedInt52(m_value));
448
449 // Note that it's possible for a prediction like (Final, []). This really means that
450 // the value is bottom and that any code that uses the value is unreachable. But
451 // we don't want to get pedantic about this as it would only increase the computational
452 // complexity of the code.
453}
454
455void AbstractValue::assertIsRegistered(Graph& graph) const
456{
457 m_structure.assertIsRegistered(graph);
458}
459#endif // !ASSERT_DISABLED
460
461ResultType AbstractValue::resultType() const
462{
463 ASSERT(isType(SpecBytecodeTop));
464 if (isType(SpecBoolean))
465 return ResultType::booleanType();
466 if (isType(SpecInt32Only))
467 return ResultType::numberTypeIsInt32();
468 if (isType(SpecBytecodeNumber))
469 return ResultType::numberType();
470 if (isType(SpecString))
471 return ResultType::stringType();
472 if (isType(SpecString | SpecBytecodeNumber))
473 return ResultType::stringOrNumberType();
474 return ResultType::unknownType();
475}
476
477void AbstractValue::dump(PrintStream& out) const
478{
479 dumpInContext(out, 0);
480}
481
482void AbstractValue::dumpInContext(PrintStream& out, DumpContext* context) const
483{
484 out.print("(", SpeculationDump(m_type));
485 if (m_type & SpecCell) {
486 out.print(
487 ", ", ArrayModesDump(m_arrayModes), ", ",
488 inContext(m_structure, context));
489 }
490 if (!!m_value)
491 out.print(", ", inContext(m_value, context));
492 out.print(", ", m_effectEpoch);
493 out.print(")");
494}
495
496void AbstractValue::validateReferences(const TrackedReferences& trackedReferences)
497{
498 trackedReferences.check(m_value);
499 m_structure.validateReferences(trackedReferences);
500}
501
502#if USE(JSVALUE64) && !defined(NDEBUG)
503void AbstractValue::ensureCanInitializeWithZeros()
504{
505 std::aligned_storage<sizeof(AbstractValue), alignof(AbstractValue)>::type zeroFilledStorage;
506 memset(static_cast<void*>(&zeroFilledStorage), 0, sizeof(AbstractValue));
507 ASSERT(*this == *static_cast<AbstractValue*>(static_cast<void*>(&zeroFilledStorage)));
508}
509#endif
510
511void AbstractValue::fastForwardToSlow(AbstractValueClobberEpoch newEpoch)
512{
513 ASSERT(newEpoch != m_effectEpoch);
514
515 if (newEpoch.clobberEpoch() != m_effectEpoch.clobberEpoch())
516 clobberStructures();
517 if (newEpoch.structureClobberState() == StructuresAreWatched)
518 m_structure.observeInvalidationPoint();
519
520 m_effectEpoch = newEpoch;
521
522 checkConsistency();
523}
524
525} } // namespace JSC::DFG
526
527#endif // ENABLE(DFG_JIT)
528
529