1 | /* |
2 | * Copyright (C) 2011-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 | #pragma once |
27 | |
28 | #if ENABLE(DFG_JIT) |
29 | |
30 | #include "AssemblyHelpers.h" |
31 | #include "BytecodeLivenessAnalysisInlines.h" |
32 | #include "CodeBlock.h" |
33 | #include "DFGArgumentPosition.h" |
34 | #include "DFGBasicBlock.h" |
35 | #include "DFGFrozenValue.h" |
36 | #include "DFGNode.h" |
37 | #include "DFGPlan.h" |
38 | #include "DFGPropertyTypeKey.h" |
39 | #include "DFGScannable.h" |
40 | #include "FullBytecodeLiveness.h" |
41 | #include "MethodOfGettingAValueProfile.h" |
42 | #include <wtf/BitVector.h> |
43 | #include <wtf/HashMap.h> |
44 | #include <wtf/Vector.h> |
45 | #include <wtf/StdLibExtras.h> |
46 | #include <wtf/StdUnorderedMap.h> |
47 | |
48 | namespace WTF { |
49 | template <typename T> class SingleRootGraph; |
50 | } |
51 | |
52 | namespace JSC { |
53 | |
54 | class CodeBlock; |
55 | class ExecState; |
56 | |
57 | namespace DFG { |
58 | |
59 | class BackwardsCFG; |
60 | class BackwardsDominators; |
61 | class CFG; |
62 | class CPSCFG; |
63 | class ControlEquivalenceAnalysis; |
64 | template <typename T> class Dominators; |
65 | template <typename T> class NaturalLoops; |
66 | class FlowIndexing; |
67 | template<typename> class FlowMap; |
68 | |
69 | using ArgumentsVector = Vector<Node*, 8>; |
70 | |
71 | using SSACFG = CFG; |
72 | using CPSDominators = Dominators<CPSCFG>; |
73 | using SSADominators = Dominators<SSACFG>; |
74 | using CPSNaturalLoops = NaturalLoops<CPSCFG>; |
75 | using SSANaturalLoops = NaturalLoops<SSACFG>; |
76 | |
77 | #define DFG_NODE_DO_TO_CHILDREN(graph, node, thingToDo) do { \ |
78 | Node* _node = (node); \ |
79 | if (_node->flags() & NodeHasVarArgs) { \ |
80 | for (unsigned _childIdx = _node->firstChild(); \ |
81 | _childIdx < _node->firstChild() + _node->numChildren(); \ |
82 | _childIdx++) { \ |
83 | if (!!(graph).m_varArgChildren[_childIdx]) \ |
84 | thingToDo(_node, (graph).m_varArgChildren[_childIdx]); \ |
85 | } \ |
86 | } else { \ |
87 | for (unsigned _edgeIndex = 0; _edgeIndex < AdjacencyList::Size; _edgeIndex++) { \ |
88 | Edge& _edge = _node->children.child(_edgeIndex); \ |
89 | if (!_edge) \ |
90 | break; \ |
91 | thingToDo(_node, _edge); \ |
92 | } \ |
93 | } \ |
94 | } while (false) |
95 | |
96 | #define DFG_ASSERT(graph, node, assertion, ...) do { \ |
97 | if (!!(assertion)) \ |
98 | break; \ |
99 | (graph).logAssertionFailure( \ |
100 | (node), __FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ |
101 | CRASH_WITH_SECURITY_IMPLICATION_AND_INFO(__VA_ARGS__); \ |
102 | } while (false) |
103 | |
104 | #define DFG_CRASH(graph, node, reason, ...) do { \ |
105 | (graph).logAssertionFailure( \ |
106 | (node), __FILE__, __LINE__, WTF_PRETTY_FUNCTION, (reason)); \ |
107 | CRASH_WITH_SECURITY_IMPLICATION_AND_INFO(__VA_ARGS__); \ |
108 | } while (false) |
109 | |
110 | struct InlineVariableData { |
111 | InlineCallFrame* inlineCallFrame; |
112 | unsigned argumentPositionStart; |
113 | VariableAccessData* calleeVariable; |
114 | }; |
115 | |
116 | enum AddSpeculationMode { |
117 | DontSpeculateInt32, |
118 | SpeculateInt32AndTruncateConstants, |
119 | SpeculateInt32 |
120 | }; |
121 | |
122 | // |
123 | // === Graph === |
124 | // |
125 | // The order may be significant for nodes with side-effects (property accesses, value conversions). |
126 | // Nodes that are 'dead' remain in the vector with refCount 0. |
127 | class Graph : public virtual Scannable { |
128 | public: |
129 | Graph(VM&, Plan&); |
130 | ~Graph(); |
131 | |
132 | void changeChild(Edge& edge, Node* newNode) |
133 | { |
134 | edge.setNode(newNode); |
135 | } |
136 | |
137 | void changeEdge(Edge& edge, Edge newEdge) |
138 | { |
139 | edge = newEdge; |
140 | } |
141 | |
142 | void compareAndSwap(Edge& edge, Node* oldNode, Node* newNode) |
143 | { |
144 | if (edge.node() != oldNode) |
145 | return; |
146 | changeChild(edge, newNode); |
147 | } |
148 | |
149 | void compareAndSwap(Edge& edge, Edge oldEdge, Edge newEdge) |
150 | { |
151 | if (edge != oldEdge) |
152 | return; |
153 | changeEdge(edge, newEdge); |
154 | } |
155 | |
156 | void performSubstitution(Node* node) |
157 | { |
158 | if (node->flags() & NodeHasVarArgs) { |
159 | for (unsigned childIdx = node->firstChild(); childIdx < node->firstChild() + node->numChildren(); childIdx++) |
160 | performSubstitutionForEdge(m_varArgChildren[childIdx]); |
161 | } else { |
162 | performSubstitutionForEdge(node->child1()); |
163 | performSubstitutionForEdge(node->child2()); |
164 | performSubstitutionForEdge(node->child3()); |
165 | } |
166 | } |
167 | |
168 | void performSubstitutionForEdge(Edge& child) |
169 | { |
170 | // Check if this operand is actually unused. |
171 | if (!child) |
172 | return; |
173 | |
174 | // Check if there is any replacement. |
175 | Node* replacement = child->replacement(); |
176 | if (!replacement) |
177 | return; |
178 | |
179 | child.setNode(replacement); |
180 | |
181 | // There is definitely a replacement. Assert that the replacement does not |
182 | // have a replacement. |
183 | ASSERT(!child->replacement()); |
184 | } |
185 | |
186 | template<typename... Params> |
187 | Node* addNode(Params... params) |
188 | { |
189 | return m_nodes.addNew(params...); |
190 | } |
191 | |
192 | template<typename... Params> |
193 | Node* addNode(SpeculatedType type, Params... params) |
194 | { |
195 | Node* node = m_nodes.addNew(params...); |
196 | node->predict(type); |
197 | return node; |
198 | } |
199 | |
200 | void deleteNode(Node*); |
201 | unsigned maxNodeCount() const { return m_nodes.size(); } |
202 | Node* nodeAt(unsigned index) const { return m_nodes[index]; } |
203 | void packNodeIndices(); |
204 | |
205 | void dethread(); |
206 | |
207 | FrozenValue* freeze(JSValue); // We use weak freezing by default. |
208 | FrozenValue* freezeStrong(JSValue); // Shorthand for freeze(value)->strengthenTo(StrongValue). |
209 | |
210 | void convertToConstant(Node* node, FrozenValue* value); |
211 | void convertToConstant(Node* node, JSValue value); |
212 | void convertToStrongConstant(Node* node, JSValue value); |
213 | |
214 | RegisteredStructure registerStructure(Structure* structure) |
215 | { |
216 | StructureRegistrationResult ignored; |
217 | return registerStructure(structure, ignored); |
218 | } |
219 | RegisteredStructure registerStructure(Structure*, StructureRegistrationResult&); |
220 | void registerAndWatchStructureTransition(Structure*); |
221 | void assertIsRegistered(Structure* structure); |
222 | |
223 | // CodeBlock is optional, but may allow additional information to be dumped (e.g. Identifier names). |
224 | void dump(PrintStream& = WTF::dataFile(), DumpContext* = 0); |
225 | |
226 | bool terminalsAreValid(); |
227 | |
228 | enum PhiNodeDumpMode { DumpLivePhisOnly, DumpAllPhis }; |
229 | void (PrintStream&, const char* prefix, BasicBlock*, PhiNodeDumpMode, DumpContext*); |
230 | void dump(PrintStream&, Edge); |
231 | void dump(PrintStream&, const char* prefix, Node*, DumpContext* = 0); |
232 | static int amountOfNodeWhiteSpace(Node*); |
233 | static void printNodeWhiteSpace(PrintStream&, Node*); |
234 | |
235 | // Dump the code origin of the given node as a diff from the code origin of the |
236 | // preceding node. Returns true if anything was printed. |
237 | bool dumpCodeOrigin(PrintStream&, const char* prefix, Node*& previousNode, Node* currentNode, DumpContext*); |
238 | |
239 | AddSpeculationMode addSpeculationMode(Node* add, bool leftShouldSpeculateInt32, bool rightShouldSpeculateInt32, PredictionPass pass) |
240 | { |
241 | ASSERT(add->op() == ValueAdd || add->op() == ValueSub || add->op() == ArithAdd || add->op() == ArithSub); |
242 | |
243 | RareCaseProfilingSource source = add->sourceFor(pass); |
244 | |
245 | Node* left = add->child1().node(); |
246 | Node* right = add->child2().node(); |
247 | |
248 | if (left->hasConstant()) |
249 | return addImmediateShouldSpeculateInt32(add, rightShouldSpeculateInt32, right, left, source); |
250 | if (right->hasConstant()) |
251 | return addImmediateShouldSpeculateInt32(add, leftShouldSpeculateInt32, left, right, source); |
252 | |
253 | return (leftShouldSpeculateInt32 && rightShouldSpeculateInt32 && add->canSpeculateInt32(source)) ? SpeculateInt32 : DontSpeculateInt32; |
254 | } |
255 | |
256 | AddSpeculationMode valueAddSpeculationMode(Node* add, PredictionPass pass) |
257 | { |
258 | return addSpeculationMode( |
259 | add, |
260 | add->child1()->shouldSpeculateInt32OrBooleanExpectingDefined(), |
261 | add->child2()->shouldSpeculateInt32OrBooleanExpectingDefined(), |
262 | pass); |
263 | } |
264 | |
265 | AddSpeculationMode arithAddSpeculationMode(Node* add, PredictionPass pass) |
266 | { |
267 | return addSpeculationMode( |
268 | add, |
269 | add->child1()->shouldSpeculateInt32OrBooleanForArithmetic(), |
270 | add->child2()->shouldSpeculateInt32OrBooleanForArithmetic(), |
271 | pass); |
272 | } |
273 | |
274 | AddSpeculationMode addSpeculationMode(Node* add, PredictionPass pass) |
275 | { |
276 | if (add->op() == ValueAdd) |
277 | return valueAddSpeculationMode(add, pass); |
278 | |
279 | return arithAddSpeculationMode(add, pass); |
280 | } |
281 | |
282 | bool addShouldSpeculateInt32(Node* add, PredictionPass pass) |
283 | { |
284 | return addSpeculationMode(add, pass) != DontSpeculateInt32; |
285 | } |
286 | |
287 | bool addShouldSpeculateInt52(Node* add) |
288 | { |
289 | if (!enableInt52()) |
290 | return false; |
291 | |
292 | Node* left = add->child1().node(); |
293 | Node* right = add->child2().node(); |
294 | |
295 | if (hasExitSite(add, Int52Overflow)) |
296 | return false; |
297 | |
298 | if (Node::shouldSpeculateInt52(left, right)) |
299 | return true; |
300 | |
301 | auto shouldSpeculateInt52ForAdd = [] (Node* node) { |
302 | // When DoubleConstant node appears, it means that users explicitly write a constant in their code with double form instead of integer form (1.0 instead of 1). |
303 | // In that case, we should honor this decision: using it as integer is not appropriate. |
304 | if (node->op() == DoubleConstant) |
305 | return false; |
306 | return isIntAnyFormat(node->prediction()); |
307 | }; |
308 | |
309 | // Allow Int52 ArithAdd only when the one side of the binary operation should be speculated Int52. It is a bit conservative |
310 | // decision. This is because Double to Int52 conversion is not so cheap. Frequent back-and-forth conversions between Double and Int52 |
311 | // rather hurt the performance. If the one side of the operation is already Int52, the cost for constructing ArithAdd becomes |
312 | // cheap since only one Double to Int52 conversion could be required. |
313 | // This recovers some regression in assorted tests while keeping kraken crypto improvements. |
314 | if (!left->shouldSpeculateInt52() && !right->shouldSpeculateInt52()) |
315 | return false; |
316 | |
317 | auto usesAsNumbers = [](Node* node) { |
318 | NodeFlags flags = node->flags() & NodeBytecodeBackPropMask; |
319 | if (!flags) |
320 | return false; |
321 | return (flags & (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex)) == flags; |
322 | }; |
323 | |
324 | // Wrapping Int52 to Value is also not so cheap. Thus, we allow Int52 addition only when the node is used as number. |
325 | if (!usesAsNumbers(add)) |
326 | return false; |
327 | |
328 | return shouldSpeculateInt52ForAdd(left) && shouldSpeculateInt52ForAdd(right); |
329 | } |
330 | |
331 | bool binaryArithShouldSpeculateInt32(Node* node, PredictionPass pass) |
332 | { |
333 | Node* left = node->child1().node(); |
334 | Node* right = node->child2().node(); |
335 | |
336 | return Node::shouldSpeculateInt32OrBooleanForArithmetic(left, right) |
337 | && node->canSpeculateInt32(node->sourceFor(pass)); |
338 | } |
339 | |
340 | bool binaryArithShouldSpeculateInt52(Node* node, PredictionPass pass) |
341 | { |
342 | if (!enableInt52()) |
343 | return false; |
344 | |
345 | Node* left = node->child1().node(); |
346 | Node* right = node->child2().node(); |
347 | |
348 | return Node::shouldSpeculateInt52(left, right) |
349 | && node->canSpeculateInt52(pass) |
350 | && !hasExitSite(node, Int52Overflow); |
351 | } |
352 | |
353 | bool unaryArithShouldSpeculateInt32(Node* node, PredictionPass pass) |
354 | { |
355 | return node->child1()->shouldSpeculateInt32OrBooleanForArithmetic() |
356 | && node->canSpeculateInt32(pass); |
357 | } |
358 | |
359 | bool unaryArithShouldSpeculateInt52(Node* node, PredictionPass pass) |
360 | { |
361 | if (!enableInt52()) |
362 | return false; |
363 | return node->child1()->shouldSpeculateInt52() |
364 | && node->canSpeculateInt52(pass) |
365 | && !hasExitSite(node, Int52Overflow); |
366 | } |
367 | |
368 | bool canOptimizeStringObjectAccess(const CodeOrigin&); |
369 | |
370 | bool getRegExpPrototypeProperty(JSObject* regExpPrototype, Structure* regExpPrototypeStructure, UniquedStringImpl* uid, JSValue& returnJSValue); |
371 | |
372 | bool roundShouldSpeculateInt32(Node* arithRound, PredictionPass pass) |
373 | { |
374 | ASSERT(arithRound->op() == ArithRound || arithRound->op() == ArithFloor || arithRound->op() == ArithCeil || arithRound->op() == ArithTrunc); |
375 | return arithRound->canSpeculateInt32(pass) && !hasExitSite(arithRound->origin.semantic, Overflow) && !hasExitSite(arithRound->origin.semantic, NegativeZero); |
376 | } |
377 | |
378 | static const char *opName(NodeType); |
379 | |
380 | RegisteredStructureSet* addStructureSet(const StructureSet& structureSet) |
381 | { |
382 | m_structureSets.append(); |
383 | RegisteredStructureSet* result = &m_structureSets.last(); |
384 | |
385 | for (Structure* structure : structureSet) |
386 | result->add(registerStructure(structure)); |
387 | |
388 | return result; |
389 | } |
390 | |
391 | RegisteredStructureSet* addStructureSet(const RegisteredStructureSet& structureSet) |
392 | { |
393 | m_structureSets.append(); |
394 | RegisteredStructureSet* result = &m_structureSets.last(); |
395 | |
396 | for (RegisteredStructure structure : structureSet) |
397 | result->add(structure); |
398 | |
399 | return result; |
400 | } |
401 | |
402 | JSGlobalObject* globalObjectFor(CodeOrigin codeOrigin) |
403 | { |
404 | return m_codeBlock->globalObjectFor(codeOrigin); |
405 | } |
406 | |
407 | JSObject* globalThisObjectFor(CodeOrigin codeOrigin) |
408 | { |
409 | JSGlobalObject* object = globalObjectFor(codeOrigin); |
410 | return jsCast<JSObject*>(object->methodTable(m_vm)->toThis(object, object->globalExec(), NotStrictMode)); |
411 | } |
412 | |
413 | ScriptExecutable* executableFor(InlineCallFrame* inlineCallFrame) |
414 | { |
415 | if (!inlineCallFrame) |
416 | return m_codeBlock->ownerExecutable(); |
417 | |
418 | return inlineCallFrame->baselineCodeBlock->ownerExecutable(); |
419 | } |
420 | |
421 | ScriptExecutable* executableFor(const CodeOrigin& codeOrigin) |
422 | { |
423 | return executableFor(codeOrigin.inlineCallFrame()); |
424 | } |
425 | |
426 | CodeBlock* baselineCodeBlockFor(InlineCallFrame* inlineCallFrame) |
427 | { |
428 | if (!inlineCallFrame) |
429 | return m_profiledBlock; |
430 | return baselineCodeBlockForInlineCallFrame(inlineCallFrame); |
431 | } |
432 | |
433 | CodeBlock* baselineCodeBlockFor(const CodeOrigin& codeOrigin) |
434 | { |
435 | return baselineCodeBlockForOriginAndBaselineCodeBlock(codeOrigin, m_profiledBlock); |
436 | } |
437 | |
438 | bool isStrictModeFor(CodeOrigin codeOrigin) |
439 | { |
440 | if (!codeOrigin.inlineCallFrame()) |
441 | return m_codeBlock->isStrictMode(); |
442 | return codeOrigin.inlineCallFrame()->isStrictMode(); |
443 | } |
444 | |
445 | ECMAMode ecmaModeFor(CodeOrigin codeOrigin) |
446 | { |
447 | return isStrictModeFor(codeOrigin) ? StrictMode : NotStrictMode; |
448 | } |
449 | |
450 | bool masqueradesAsUndefinedWatchpointIsStillValid(const CodeOrigin& codeOrigin) |
451 | { |
452 | return globalObjectFor(codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid(); |
453 | } |
454 | |
455 | bool hasGlobalExitSite(const CodeOrigin& codeOrigin, ExitKind exitKind) |
456 | { |
457 | return baselineCodeBlockFor(codeOrigin)->unlinkedCodeBlock()->hasExitSite(FrequentExitSite(exitKind)); |
458 | } |
459 | |
460 | bool hasExitSite(const CodeOrigin& codeOrigin, ExitKind exitKind) |
461 | { |
462 | return baselineCodeBlockFor(codeOrigin)->unlinkedCodeBlock()->hasExitSite(FrequentExitSite(codeOrigin.bytecodeIndex(), exitKind)); |
463 | } |
464 | |
465 | bool hasExitSite(Node* node, ExitKind exitKind) |
466 | { |
467 | return hasExitSite(node->origin.semantic, exitKind); |
468 | } |
469 | |
470 | MethodOfGettingAValueProfile methodOfGettingAValueProfileFor(Node* currentNode, Node* operandNode); |
471 | |
472 | BlockIndex numBlocks() const { return m_blocks.size(); } |
473 | BasicBlock* block(BlockIndex blockIndex) const { return m_blocks[blockIndex].get(); } |
474 | BasicBlock* lastBlock() const { return block(numBlocks() - 1); } |
475 | |
476 | void appendBlock(Ref<BasicBlock>&& basicBlock) |
477 | { |
478 | basicBlock->index = m_blocks.size(); |
479 | m_blocks.append(WTFMove(basicBlock)); |
480 | } |
481 | |
482 | void killBlock(BlockIndex blockIndex) |
483 | { |
484 | m_blocks[blockIndex] = nullptr; |
485 | } |
486 | |
487 | void killBlock(BasicBlock* basicBlock) |
488 | { |
489 | killBlock(basicBlock->index); |
490 | } |
491 | |
492 | void killBlockAndItsContents(BasicBlock*); |
493 | |
494 | void killUnreachableBlocks(); |
495 | |
496 | void determineReachability(); |
497 | void resetReachability(); |
498 | |
499 | void computeRefCounts(); |
500 | |
501 | unsigned varArgNumChildren(Node* node) |
502 | { |
503 | ASSERT(node->flags() & NodeHasVarArgs); |
504 | return node->numChildren(); |
505 | } |
506 | |
507 | unsigned numChildren(Node* node) |
508 | { |
509 | if (node->flags() & NodeHasVarArgs) |
510 | return varArgNumChildren(node); |
511 | return AdjacencyList::Size; |
512 | } |
513 | |
514 | template <typename Function = bool(*)(Edge)> |
515 | AdjacencyList copyVarargChildren(Node* node, Function filter = [] (Edge) { return true; }) |
516 | { |
517 | ASSERT(node->flags() & NodeHasVarArgs); |
518 | unsigned firstChild = m_varArgChildren.size(); |
519 | unsigned numChildren = 0; |
520 | doToChildren(node, [&] (Edge edge) { |
521 | if (filter(edge)) { |
522 | ++numChildren; |
523 | m_varArgChildren.append(edge); |
524 | } |
525 | }); |
526 | |
527 | return AdjacencyList(AdjacencyList::Variable, firstChild, numChildren); |
528 | } |
529 | |
530 | Edge& varArgChild(Node* node, unsigned index) |
531 | { |
532 | ASSERT(node->flags() & NodeHasVarArgs); |
533 | return m_varArgChildren[node->firstChild() + index]; |
534 | } |
535 | |
536 | Edge& child(Node* node, unsigned index) |
537 | { |
538 | if (node->flags() & NodeHasVarArgs) |
539 | return varArgChild(node, index); |
540 | return node->children.child(index); |
541 | } |
542 | |
543 | void voteNode(Node* node, unsigned ballot, float weight = 1) |
544 | { |
545 | switch (node->op()) { |
546 | case ValueToInt32: |
547 | case UInt32ToNumber: |
548 | node = node->child1().node(); |
549 | break; |
550 | default: |
551 | break; |
552 | } |
553 | |
554 | if (node->op() == GetLocal) |
555 | node->variableAccessData()->vote(ballot, weight); |
556 | } |
557 | |
558 | void voteNode(Edge edge, unsigned ballot, float weight = 1) |
559 | { |
560 | voteNode(edge.node(), ballot, weight); |
561 | } |
562 | |
563 | void voteChildren(Node* node, unsigned ballot, float weight = 1) |
564 | { |
565 | if (node->flags() & NodeHasVarArgs) { |
566 | for (unsigned childIdx = node->firstChild(); |
567 | childIdx < node->firstChild() + node->numChildren(); |
568 | childIdx++) { |
569 | if (!!m_varArgChildren[childIdx]) |
570 | voteNode(m_varArgChildren[childIdx], ballot, weight); |
571 | } |
572 | return; |
573 | } |
574 | |
575 | if (!node->child1()) |
576 | return; |
577 | voteNode(node->child1(), ballot, weight); |
578 | if (!node->child2()) |
579 | return; |
580 | voteNode(node->child2(), ballot, weight); |
581 | if (!node->child3()) |
582 | return; |
583 | voteNode(node->child3(), ballot, weight); |
584 | } |
585 | |
586 | template<typename T> // T = Node* or Edge |
587 | void substitute(BasicBlock& block, unsigned startIndexInBlock, T oldThing, T newThing) |
588 | { |
589 | for (unsigned indexInBlock = startIndexInBlock; indexInBlock < block.size(); ++indexInBlock) { |
590 | Node* node = block[indexInBlock]; |
591 | if (node->flags() & NodeHasVarArgs) { |
592 | for (unsigned childIdx = node->firstChild(); childIdx < node->firstChild() + node->numChildren(); ++childIdx) { |
593 | if (!!m_varArgChildren[childIdx]) |
594 | compareAndSwap(m_varArgChildren[childIdx], oldThing, newThing); |
595 | } |
596 | continue; |
597 | } |
598 | if (!node->child1()) |
599 | continue; |
600 | compareAndSwap(node->children.child1(), oldThing, newThing); |
601 | if (!node->child2()) |
602 | continue; |
603 | compareAndSwap(node->children.child2(), oldThing, newThing); |
604 | if (!node->child3()) |
605 | continue; |
606 | compareAndSwap(node->children.child3(), oldThing, newThing); |
607 | } |
608 | } |
609 | |
610 | // Use this if you introduce a new GetLocal and you know that you introduced it *before* |
611 | // any GetLocals in the basic block. |
612 | // FIXME: it may be appropriate, in the future, to generalize this to handle GetLocals |
613 | // introduced anywhere in the basic block. |
614 | void substituteGetLocal(BasicBlock& block, unsigned startIndexInBlock, VariableAccessData* variableAccessData, Node* newGetLocal); |
615 | |
616 | void invalidateCFG(); |
617 | void invalidateNodeLiveness(); |
618 | |
619 | void clearFlagsOnAllNodes(NodeFlags); |
620 | |
621 | void clearReplacements(); |
622 | void clearEpochs(); |
623 | void initializeNodeOwners(); |
624 | |
625 | BlockList blocksInPreOrder(); |
626 | BlockList blocksInPostOrder(bool isSafeToValidate = true); |
627 | |
628 | class NaturalBlockIterable { |
629 | public: |
630 | NaturalBlockIterable() |
631 | : m_graph(nullptr) |
632 | { |
633 | } |
634 | |
635 | NaturalBlockIterable(Graph& graph) |
636 | : m_graph(&graph) |
637 | { |
638 | } |
639 | |
640 | class iterator { |
641 | public: |
642 | iterator() |
643 | : m_graph(nullptr) |
644 | , m_index(0) |
645 | { |
646 | } |
647 | |
648 | iterator(Graph& graph, BlockIndex index) |
649 | : m_graph(&graph) |
650 | , m_index(findNext(index)) |
651 | { |
652 | } |
653 | |
654 | BasicBlock *operator*() |
655 | { |
656 | return m_graph->block(m_index); |
657 | } |
658 | |
659 | iterator& operator++() |
660 | { |
661 | m_index = findNext(m_index + 1); |
662 | return *this; |
663 | } |
664 | |
665 | bool operator==(const iterator& other) const |
666 | { |
667 | return m_index == other.m_index; |
668 | } |
669 | |
670 | bool operator!=(const iterator& other) const |
671 | { |
672 | return !(*this == other); |
673 | } |
674 | |
675 | private: |
676 | BlockIndex findNext(BlockIndex index) |
677 | { |
678 | while (index < m_graph->numBlocks() && !m_graph->block(index)) |
679 | index++; |
680 | return index; |
681 | } |
682 | |
683 | Graph* m_graph; |
684 | BlockIndex m_index; |
685 | }; |
686 | |
687 | iterator begin() |
688 | { |
689 | return iterator(*m_graph, 0); |
690 | } |
691 | |
692 | iterator end() |
693 | { |
694 | return iterator(*m_graph, m_graph->numBlocks()); |
695 | } |
696 | |
697 | private: |
698 | Graph* m_graph; |
699 | }; |
700 | |
701 | NaturalBlockIterable blocksInNaturalOrder() |
702 | { |
703 | return NaturalBlockIterable(*this); |
704 | } |
705 | |
706 | template<typename ChildFunctor> |
707 | ALWAYS_INLINE void doToChildrenWithNode(Node* node, const ChildFunctor& functor) |
708 | { |
709 | DFG_NODE_DO_TO_CHILDREN(*this, node, functor); |
710 | } |
711 | |
712 | template<typename ChildFunctor> |
713 | ALWAYS_INLINE void doToChildren(Node* node, const ChildFunctor& functor) |
714 | { |
715 | class ForwardingFunc { |
716 | public: |
717 | ForwardingFunc(const ChildFunctor& functor) |
718 | : m_functor(functor) |
719 | { |
720 | } |
721 | |
722 | // This is a manually written func because we want ALWAYS_INLINE. |
723 | ALWAYS_INLINE void operator()(Node*, Edge& edge) const |
724 | { |
725 | m_functor(edge); |
726 | } |
727 | |
728 | private: |
729 | const ChildFunctor& m_functor; |
730 | }; |
731 | |
732 | doToChildrenWithNode(node, ForwardingFunc(functor)); |
733 | } |
734 | |
735 | bool uses(Node* node, Node* child) |
736 | { |
737 | bool result = false; |
738 | doToChildren(node, [&] (Edge edge) { result |= edge == child; }); |
739 | return result; |
740 | } |
741 | |
742 | bool isWatchingHavingABadTimeWatchpoint(Node* node) |
743 | { |
744 | JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic); |
745 | return watchpoints().isWatched(globalObject->havingABadTimeWatchpoint()); |
746 | } |
747 | |
748 | bool isWatchingGlobalObjectWatchpoint(JSGlobalObject* globalObject, InlineWatchpointSet& set) |
749 | { |
750 | if (watchpoints().isWatched(set)) |
751 | return true; |
752 | |
753 | if (set.isStillValid()) { |
754 | // Since the global object owns this watchpoint, we make ourselves have a weak pointer to it. |
755 | // If the global object got deallocated, it wouldn't fire the watchpoint. It's unlikely the |
756 | // global object would get deallocated without this code ever getting thrown away, however, |
757 | // it's more sound logically to depend on the global object lifetime weakly. |
758 | freeze(globalObject); |
759 | watchpoints().addLazily(set); |
760 | return true; |
761 | } |
762 | |
763 | return false; |
764 | } |
765 | |
766 | bool isWatchingArrayIteratorProtocolWatchpoint(Node* node) |
767 | { |
768 | JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic); |
769 | InlineWatchpointSet& set = globalObject->arrayIteratorProtocolWatchpoint(); |
770 | return isWatchingGlobalObjectWatchpoint(globalObject, set); |
771 | } |
772 | |
773 | bool isWatchingNumberToStringWatchpoint(Node* node) |
774 | { |
775 | JSGlobalObject* globalObject = globalObjectFor(node->origin.semantic); |
776 | InlineWatchpointSet& set = globalObject->numberToStringWatchpoint(); |
777 | return isWatchingGlobalObjectWatchpoint(globalObject, set); |
778 | } |
779 | |
780 | Profiler::Compilation* compilation() { return m_plan.compilation(); } |
781 | |
782 | DesiredIdentifiers& identifiers() { return m_plan.identifiers(); } |
783 | DesiredWatchpoints& watchpoints() { return m_plan.watchpoints(); } |
784 | DesiredGlobalProperties& globalProperties() { return m_plan.globalProperties(); } |
785 | |
786 | // Returns false if the key is already invalid or unwatchable. If this is a Presence condition, |
787 | // this also makes it cheap to query if the condition holds. Also makes sure that the GC knows |
788 | // what's going on. |
789 | bool watchCondition(const ObjectPropertyCondition&); |
790 | bool watchConditions(const ObjectPropertyConditionSet&); |
791 | |
792 | bool watchGlobalProperty(JSGlobalObject*, unsigned identifierNumber); |
793 | |
794 | // Checks if it's known that loading from the given object at the given offset is fine. This is |
795 | // computed by tracking which conditions we track with watchCondition(). |
796 | bool isSafeToLoad(JSObject* base, PropertyOffset); |
797 | |
798 | // This uses either constant property inference or property type inference to derive a good abstract |
799 | // value for some property accessed with the given abstract value base. |
800 | AbstractValue inferredValueForProperty( |
801 | const AbstractValue& base, PropertyOffset, StructureClobberState); |
802 | |
803 | FullBytecodeLiveness& livenessFor(CodeBlock*); |
804 | FullBytecodeLiveness& livenessFor(InlineCallFrame*); |
805 | |
806 | // Quickly query if a single local is live at the given point. This is faster than calling |
807 | // forAllLiveInBytecode() if you will only query one local. But, if you want to know all of the |
808 | // locals live, then calling this for each local is much slower than forAllLiveInBytecode(). |
809 | bool isLiveInBytecode(VirtualRegister, CodeOrigin); |
810 | |
811 | // Quickly get all of the non-argument locals live at the given point. This doesn't give you |
812 | // any arguments because those are all presumed live. You can call forAllLiveInBytecode() to |
813 | // also get the arguments. This is much faster than calling isLiveInBytecode() for each local. |
814 | template<typename Functor> |
815 | void forAllLocalsLiveInBytecode(CodeOrigin codeOrigin, const Functor& functor) |
816 | { |
817 | // Support for not redundantly reporting arguments. Necessary because in case of a varargs |
818 | // call, only the callee knows that arguments are live while in the case of a non-varargs |
819 | // call, both callee and caller will see the variables live. |
820 | VirtualRegister exclusionStart; |
821 | VirtualRegister exclusionEnd; |
822 | |
823 | CodeOrigin* codeOriginPtr = &codeOrigin; |
824 | |
825 | for (;;) { |
826 | InlineCallFrame* inlineCallFrame = codeOriginPtr->inlineCallFrame(); |
827 | VirtualRegister stackOffset(inlineCallFrame ? inlineCallFrame->stackOffset : 0); |
828 | |
829 | if (inlineCallFrame) { |
830 | if (inlineCallFrame->isClosureCall) |
831 | functor(stackOffset + CallFrameSlot::callee); |
832 | if (inlineCallFrame->isVarargs()) |
833 | functor(stackOffset + CallFrameSlot::argumentCount); |
834 | } |
835 | |
836 | CodeBlock* codeBlock = baselineCodeBlockFor(inlineCallFrame); |
837 | FullBytecodeLiveness& fullLiveness = livenessFor(codeBlock); |
838 | const FastBitVector& liveness = fullLiveness.getLiveness(codeOriginPtr->bytecodeIndex()); |
839 | for (unsigned relativeLocal = codeBlock->numCalleeLocals(); relativeLocal--;) { |
840 | VirtualRegister reg = stackOffset + virtualRegisterForLocal(relativeLocal); |
841 | |
842 | // Don't report if our callee already reported. |
843 | if (reg >= exclusionStart && reg < exclusionEnd) |
844 | continue; |
845 | |
846 | if (liveness[relativeLocal]) |
847 | functor(reg); |
848 | } |
849 | |
850 | if (!inlineCallFrame) |
851 | break; |
852 | |
853 | // Arguments are always live. This would be redundant if it wasn't for our |
854 | // op_call_varargs inlining. See the comment above. |
855 | exclusionStart = stackOffset + CallFrame::argumentOffsetIncludingThis(0); |
856 | exclusionEnd = stackOffset + CallFrame::argumentOffsetIncludingThis(inlineCallFrame->argumentsWithFixup.size()); |
857 | |
858 | // We will always have a "this" argument and exclusionStart should be a smaller stack |
859 | // offset than exclusionEnd. |
860 | ASSERT(exclusionStart < exclusionEnd); |
861 | |
862 | for (VirtualRegister reg = exclusionStart; reg < exclusionEnd; reg += 1) |
863 | functor(reg); |
864 | |
865 | // We need to handle tail callers because we may decide to exit to the |
866 | // the return bytecode following the tail call. |
867 | codeOriginPtr = &inlineCallFrame->directCaller; |
868 | } |
869 | } |
870 | |
871 | // Get a BitVector of all of the non-argument locals live right now. This is mostly useful if |
872 | // you want to compare two sets of live locals from two different CodeOrigins. |
873 | BitVector localsLiveInBytecode(CodeOrigin); |
874 | |
875 | // Tells you all of the arguments and locals live at the given CodeOrigin. This is a small |
876 | // extension to forAllLocalsLiveInBytecode(), since all arguments are always presumed live. |
877 | template<typename Functor> |
878 | void forAllLiveInBytecode(CodeOrigin codeOrigin, const Functor& functor) |
879 | { |
880 | forAllLocalsLiveInBytecode(codeOrigin, functor); |
881 | |
882 | // Report all arguments as being live. |
883 | for (unsigned argument = block(0)->variablesAtHead.numberOfArguments(); argument--;) |
884 | functor(virtualRegisterForArgument(argument)); |
885 | } |
886 | |
887 | BytecodeKills& killsFor(CodeBlock*); |
888 | BytecodeKills& killsFor(InlineCallFrame*); |
889 | |
890 | static unsigned parameterSlotsForArgCount(unsigned); |
891 | |
892 | unsigned frameRegisterCount(); |
893 | unsigned stackPointerOffset(); |
894 | unsigned requiredRegisterCountForExit(); |
895 | unsigned requiredRegisterCountForExecutionAndExit(); |
896 | |
897 | JSValue tryGetConstantProperty(JSValue base, const RegisteredStructureSet&, PropertyOffset); |
898 | JSValue tryGetConstantProperty(JSValue base, Structure*, PropertyOffset); |
899 | JSValue tryGetConstantProperty(JSValue base, const StructureAbstractValue&, PropertyOffset); |
900 | JSValue tryGetConstantProperty(const AbstractValue&, PropertyOffset); |
901 | |
902 | JSValue tryGetConstantClosureVar(JSValue base, ScopeOffset); |
903 | JSValue tryGetConstantClosureVar(const AbstractValue&, ScopeOffset); |
904 | JSValue tryGetConstantClosureVar(Node*, ScopeOffset); |
905 | |
906 | JSArrayBufferView* tryGetFoldableView(JSValue); |
907 | JSArrayBufferView* tryGetFoldableView(JSValue, ArrayMode arrayMode); |
908 | |
909 | bool canDoFastSpread(Node*, const AbstractValue&); |
910 | |
911 | void registerFrozenValues(); |
912 | |
913 | void visitChildren(SlotVisitor&) override; |
914 | |
915 | void logAssertionFailure( |
916 | std::nullptr_t, const char* file, int line, const char* function, |
917 | const char* assertion); |
918 | void logAssertionFailure( |
919 | Node*, const char* file, int line, const char* function, |
920 | const char* assertion); |
921 | void logAssertionFailure( |
922 | BasicBlock*, const char* file, int line, const char* function, |
923 | const char* assertion); |
924 | |
925 | bool hasDebuggerEnabled() const { return m_hasDebuggerEnabled; } |
926 | |
927 | CPSDominators& ensureCPSDominators(); |
928 | SSADominators& ensureSSADominators(); |
929 | CPSNaturalLoops& ensureCPSNaturalLoops(); |
930 | SSANaturalLoops& ensureSSANaturalLoops(); |
931 | BackwardsCFG& ensureBackwardsCFG(); |
932 | BackwardsDominators& ensureBackwardsDominators(); |
933 | ControlEquivalenceAnalysis& ensureControlEquivalenceAnalysis(); |
934 | CPSCFG& ensureCPSCFG(); |
935 | |
936 | // These functions only makes sense to call after bytecode parsing |
937 | // because it queries the m_hasExceptionHandlers boolean whose value |
938 | // is only fully determined after bytcode parsing. |
939 | bool willCatchExceptionInMachineFrame(CodeOrigin codeOrigin) |
940 | { |
941 | CodeOrigin ignored; |
942 | HandlerInfo* ignored2; |
943 | return willCatchExceptionInMachineFrame(codeOrigin, ignored, ignored2); |
944 | } |
945 | bool willCatchExceptionInMachineFrame(CodeOrigin, CodeOrigin& opCatchOriginOut, HandlerInfo*& catchHandlerOut); |
946 | |
947 | bool needsScopeRegister() const { return m_hasDebuggerEnabled || m_codeBlock->usesEval(); } |
948 | bool needsFlushedThis() const { return m_codeBlock->usesEval(); } |
949 | |
950 | void clearCPSCFGData(); |
951 | |
952 | bool isRoot(BasicBlock* block) const |
953 | { |
954 | ASSERT_WITH_MESSAGE(!m_isInSSAConversion, "This is not written to work during SSA conversion." ); |
955 | |
956 | if (m_form == SSA) { |
957 | ASSERT(m_roots.size() == 1); |
958 | ASSERT(m_roots.contains(this->block(0))); |
959 | return block == this->block(0); |
960 | } |
961 | |
962 | if (m_roots.size() <= 4) { |
963 | bool result = m_roots.contains(block); |
964 | ASSERT(result == m_rootToArguments.contains(block)); |
965 | return result; |
966 | } |
967 | bool result = m_rootToArguments.contains(block); |
968 | ASSERT(result == m_roots.contains(block)); |
969 | return result; |
970 | } |
971 | |
972 | VM& m_vm; |
973 | Plan& m_plan; |
974 | CodeBlock* m_codeBlock; |
975 | CodeBlock* m_profiledBlock; |
976 | |
977 | Vector<RefPtr<BasicBlock>, 8> m_blocks; |
978 | Vector<BasicBlock*, 1> m_roots; |
979 | Vector<Edge, 16> m_varArgChildren; |
980 | |
981 | HashMap<EncodedJSValue, FrozenValue*, EncodedJSValueHash, EncodedJSValueHashTraits> m_frozenValueMap; |
982 | Bag<FrozenValue> m_frozenValues; |
983 | |
984 | Vector<uint32_t> m_uint32ValuesInUse; |
985 | |
986 | Bag<StorageAccessData> m_storageAccessData; |
987 | |
988 | // In CPS, this is all of the SetArgumentDefinitely nodes for the arguments in the machine code block |
989 | // that survived DCE. All of them except maybe "this" will survive DCE, because of the Flush |
990 | // nodes. In SSA, this has no meaning. It's empty. |
991 | HashMap<BasicBlock*, ArgumentsVector> m_rootToArguments; |
992 | |
993 | // In SSA, this is the argument speculation that we've locked in for an entrypoint block. |
994 | // |
995 | // We must speculate on the argument types at each entrypoint even if operations involving |
996 | // arguments get killed. For example: |
997 | // |
998 | // function foo(x) { |
999 | // var tmp = x + 1; |
1000 | // } |
1001 | // |
1002 | // Assume that x is always int during profiling. The ArithAdd for "x + 1" will be dead and will |
1003 | // have a proven check for the edge to "x". So, we will not insert a Check node and we will |
1004 | // kill the GetStack for "x". But, we must do the int check in the progolue, because that's the |
1005 | // thing we used to allow DCE of ArithAdd. Otherwise the add could be impure: |
1006 | // |
1007 | // var o = { |
1008 | // valueOf: function() { do side effects; } |
1009 | // }; |
1010 | // foo(o); |
1011 | // |
1012 | // If we DCE the ArithAdd and we remove the int check on x, then this won't do the side |
1013 | // effects. |
1014 | // |
1015 | // By convention, entrypoint index 0 is used for the CodeBlock's op_enter entrypoint. |
1016 | // So argumentFormats[0] are the argument formats for the normal call entrypoint. |
1017 | Vector<Vector<FlushFormat>> m_argumentFormats; |
1018 | |
1019 | SegmentedVector<VariableAccessData, 16> m_variableAccessData; |
1020 | SegmentedVector<ArgumentPosition, 8> m_argumentPositions; |
1021 | Bag<Transition> m_transitions; |
1022 | Bag<BranchData> m_branchData; |
1023 | Bag<SwitchData> m_switchData; |
1024 | Bag<MultiGetByOffsetData> m_multiGetByOffsetData; |
1025 | Bag<MultiPutByOffsetData> m_multiPutByOffsetData; |
1026 | Bag<MatchStructureData> m_matchStructureData; |
1027 | Bag<ObjectMaterializationData> m_objectMaterializationData; |
1028 | Bag<CallVarargsData> m_callVarargsData; |
1029 | Bag<LoadVarargsData> m_loadVarargsData; |
1030 | Bag<StackAccessData> m_stackAccessData; |
1031 | Bag<LazyJSValue> m_lazyJSValues; |
1032 | Bag<CallDOMGetterData> m_callDOMGetterData; |
1033 | Bag<BitVector> m_bitVectors; |
1034 | Vector<InlineVariableData, 4> m_inlineVariableData; |
1035 | HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness; |
1036 | HashMap<CodeBlock*, std::unique_ptr<BytecodeKills>> m_bytecodeKills; |
1037 | HashSet<std::pair<JSObject*, PropertyOffset>> m_safeToLoad; |
1038 | Vector<Ref<Snippet>> m_domJITSnippets; |
1039 | std::unique_ptr<CPSDominators> m_cpsDominators; |
1040 | std::unique_ptr<SSADominators> m_ssaDominators; |
1041 | std::unique_ptr<CPSNaturalLoops> m_cpsNaturalLoops; |
1042 | std::unique_ptr<SSANaturalLoops> m_ssaNaturalLoops; |
1043 | std::unique_ptr<SSACFG> m_ssaCFG; |
1044 | std::unique_ptr<CPSCFG> m_cpsCFG; |
1045 | std::unique_ptr<BackwardsCFG> m_backwardsCFG; |
1046 | std::unique_ptr<BackwardsDominators> m_backwardsDominators; |
1047 | std::unique_ptr<ControlEquivalenceAnalysis> m_controlEquivalenceAnalysis; |
1048 | unsigned m_localVars; |
1049 | unsigned m_nextMachineLocal; |
1050 | unsigned m_parameterSlots; |
1051 | |
1052 | // This is the number of logical entrypoints that we're compiling. This is only used |
1053 | // in SSA. Each EntrySwitch node must have m_numberOfEntrypoints cases. Note, this is |
1054 | // not the same as m_roots.size(). m_roots.size() represents the number of roots in |
1055 | // the CFG. In SSA, m_roots.size() == 1 even if we're compiling more than one entrypoint. |
1056 | unsigned m_numberOfEntrypoints { UINT_MAX }; |
1057 | |
1058 | // This maps an entrypoint index to a particular op_catch bytecode offset. By convention, |
1059 | // it'll never have zero as a key because we use zero to mean the op_enter entrypoint. |
1060 | HashMap<unsigned, unsigned> m_entrypointIndexToCatchBytecodeOffset; |
1061 | |
1062 | HashSet<String> m_localStrings; |
1063 | HashMap<const StringImpl*, String> m_copiedStrings; |
1064 | |
1065 | #if USE(JSVALUE32_64) |
1066 | StdUnorderedMap<int64_t, double*> m_doubleConstantsMap; |
1067 | std::unique_ptr<Bag<double>> m_doubleConstants; |
1068 | #endif |
1069 | |
1070 | OptimizationFixpointState m_fixpointState; |
1071 | StructureRegistrationState m_structureRegistrationState; |
1072 | GraphForm m_form; |
1073 | UnificationState m_unificationState; |
1074 | PlanStage m_planStage { PlanStage::Initial }; |
1075 | RefCountState m_refCountState; |
1076 | bool m_hasDebuggerEnabled; |
1077 | bool m_hasExceptionHandlers { false }; |
1078 | bool m_isInSSAConversion { false }; |
1079 | Optional<uint32_t> m_maxLocalsForCatchOSREntry; |
1080 | std::unique_ptr<FlowIndexing> m_indexingCache; |
1081 | std::unique_ptr<FlowMap<AbstractValue>> m_abstractValuesCache; |
1082 | Bag<EntrySwitchData> m_entrySwitchData; |
1083 | |
1084 | RegisteredStructure stringStructure; |
1085 | RegisteredStructure symbolStructure; |
1086 | |
1087 | private: |
1088 | bool isStringPrototypeMethodSane(JSGlobalObject*, UniquedStringImpl*); |
1089 | |
1090 | void handleSuccessor(Vector<BasicBlock*, 16>& worklist, BasicBlock*, BasicBlock* successor); |
1091 | |
1092 | AddSpeculationMode addImmediateShouldSpeculateInt32(Node* add, bool variableShouldSpeculateInt32, Node* operand, Node*immediate, RareCaseProfilingSource source) |
1093 | { |
1094 | ASSERT(immediate->hasConstant()); |
1095 | |
1096 | JSValue immediateValue = immediate->asJSValue(); |
1097 | if (!immediateValue.isNumber() && !immediateValue.isBoolean()) |
1098 | return DontSpeculateInt32; |
1099 | |
1100 | if (!variableShouldSpeculateInt32) |
1101 | return DontSpeculateInt32; |
1102 | |
1103 | // Integer constants can be typed Double if they are written like a double in the source code (e.g. 42.0). |
1104 | // In that case, we stay conservative unless the other operand was explicitly typed as integer. |
1105 | NodeFlags operandResultType = operand->result(); |
1106 | if (operandResultType != NodeResultInt32 && immediateValue.isDouble()) |
1107 | return DontSpeculateInt32; |
1108 | |
1109 | if (immediateValue.isBoolean() || jsNumber(immediateValue.asNumber()).isInt32()) |
1110 | return add->canSpeculateInt32(source) ? SpeculateInt32 : DontSpeculateInt32; |
1111 | |
1112 | double doubleImmediate = immediateValue.asDouble(); |
1113 | const double twoToThe48 = 281474976710656.0; |
1114 | if (doubleImmediate < -twoToThe48 || doubleImmediate > twoToThe48) |
1115 | return DontSpeculateInt32; |
1116 | |
1117 | return bytecodeCanTruncateInteger(add->arithNodeFlags()) ? SpeculateInt32AndTruncateConstants : DontSpeculateInt32; |
1118 | } |
1119 | |
1120 | B3::SparseCollection<Node> m_nodes; |
1121 | SegmentedVector<RegisteredStructureSet, 16> m_structureSets; |
1122 | }; |
1123 | |
1124 | } } // namespace JSC::DFG |
1125 | |
1126 | #endif |
1127 | |