2 * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DFGInPlaceAbstractState.h"
31 #include "CodeBlock.h"
32 #include "DFGBasicBlock.h"
33 #include "GetByIdStatus.h"
34 #include "JSCInlines.h"
35 #include "PutByIdStatus.h"
36 #include "StringObject.h"
38 namespace JSC
{ namespace DFG
{
40 static const bool verbose
= false;
42 InPlaceAbstractState::InPlaceAbstractState(Graph
& graph
)
44 , m_variables(m_graph
.m_codeBlock
->numParameters(), graph
.m_localVars
)
49 InPlaceAbstractState::~InPlaceAbstractState() { }
51 void InPlaceAbstractState::beginBasicBlock(BasicBlock
* basicBlock
)
55 ASSERT(basicBlock
->variablesAtHead
.numberOfLocals() == basicBlock
->valuesAtHead
.numberOfLocals());
56 ASSERT(basicBlock
->variablesAtTail
.numberOfLocals() == basicBlock
->valuesAtTail
.numberOfLocals());
57 ASSERT(basicBlock
->variablesAtHead
.numberOfLocals() == basicBlock
->variablesAtTail
.numberOfLocals());
59 for (size_t i
= 0; i
< basicBlock
->size(); i
++)
60 forNode(basicBlock
->at(i
)).clear();
62 m_variables
= basicBlock
->valuesAtHead
;
64 if (m_graph
.m_form
== SSA
) {
65 HashMap
<Node
*, AbstractValue
>::iterator iter
= basicBlock
->ssa
->valuesAtHead
.begin();
66 HashMap
<Node
*, AbstractValue
>::iterator end
= basicBlock
->ssa
->valuesAtHead
.end();
67 for (; iter
!= end
; ++iter
)
68 forNode(iter
->key
) = iter
->value
;
70 basicBlock
->cfaShouldRevisit
= false;
71 basicBlock
->cfaHasVisited
= true;
74 m_foundConstants
= false;
75 m_branchDirection
= InvalidBranchDirection
;
76 m_structureClobberState
= basicBlock
->cfaStructureClobberStateAtHead
;
79 static void setLiveValues(HashMap
<Node
*, AbstractValue
>& values
, HashSet
<Node
*>& live
)
83 HashSet
<Node
*>::iterator iter
= live
.begin();
84 HashSet
<Node
*>::iterator end
= live
.end();
85 for (; iter
!= end
; ++iter
)
86 values
.add(*iter
, AbstractValue());
89 void InPlaceAbstractState::initialize()
91 BasicBlock
* root
= m_graph
.block(0);
92 root
->cfaShouldRevisit
= true;
93 root
->cfaHasVisited
= false;
94 root
->cfaFoundConstants
= false;
95 root
->cfaStructureClobberStateAtHead
= StructuresAreWatched
;
96 root
->cfaStructureClobberStateAtTail
= StructuresAreWatched
;
97 for (size_t i
= 0; i
< root
->valuesAtHead
.numberOfArguments(); ++i
) {
98 root
->valuesAtTail
.argument(i
).clear();
101 if (m_graph
.m_form
== SSA
)
102 format
= m_graph
.m_argumentFormats
[i
];
104 Node
* node
= m_graph
.m_arguments
[i
];
106 format
= FlushedJSValue
;
108 ASSERT(node
->op() == SetArgument
);
109 format
= node
->variableAccessData()->flushFormat();
115 root
->valuesAtHead
.argument(i
).setType(SpecInt32
);
118 root
->valuesAtHead
.argument(i
).setType(SpecBoolean
);
121 root
->valuesAtHead
.argument(i
).setType(m_graph
, SpecCell
);
124 root
->valuesAtHead
.argument(i
).makeHeapTop();
127 DFG_CRASH(m_graph
, nullptr, "Bad flush format for argument");
131 for (size_t i
= 0; i
< root
->valuesAtHead
.numberOfLocals(); ++i
) {
132 root
->valuesAtHead
.local(i
).clear();
133 root
->valuesAtTail
.local(i
).clear();
135 for (BlockIndex blockIndex
= 1 ; blockIndex
< m_graph
.numBlocks(); ++blockIndex
) {
136 BasicBlock
* block
= m_graph
.block(blockIndex
);
139 ASSERT(block
->isReachable
);
140 block
->cfaShouldRevisit
= false;
141 block
->cfaHasVisited
= false;
142 block
->cfaFoundConstants
= false;
143 block
->cfaStructureClobberStateAtHead
= StructuresAreWatched
;
144 block
->cfaStructureClobberStateAtTail
= StructuresAreWatched
;
145 for (size_t i
= 0; i
< block
->valuesAtHead
.numberOfArguments(); ++i
) {
146 block
->valuesAtHead
.argument(i
).clear();
147 block
->valuesAtTail
.argument(i
).clear();
149 for (size_t i
= 0; i
< block
->valuesAtHead
.numberOfLocals(); ++i
) {
150 block
->valuesAtHead
.local(i
).clear();
151 block
->valuesAtTail
.local(i
).clear();
154 if (m_graph
.m_form
== SSA
) {
155 for (BlockIndex blockIndex
= 0; blockIndex
< m_graph
.numBlocks(); ++blockIndex
) {
156 BasicBlock
* block
= m_graph
.block(blockIndex
);
159 setLiveValues(block
->ssa
->valuesAtHead
, block
->ssa
->liveAtHead
);
160 setLiveValues(block
->ssa
->valuesAtTail
, block
->ssa
->liveAtTail
);
165 bool InPlaceAbstractState::endBasicBlock(MergeMode mergeMode
)
169 BasicBlock
* block
= m_block
; // Save the block for successor merging.
171 block
->cfaFoundConstants
= m_foundConstants
;
172 block
->cfaDidFinish
= m_isValid
;
173 block
->cfaBranchDirection
= m_branchDirection
;
180 bool changed
= false;
182 if ((mergeMode
!= DontMerge
) || !ASSERT_DISABLED
) {
183 changed
|= checkAndSet(block
->cfaStructureClobberStateAtTail
, m_structureClobberState
);
185 switch (m_graph
.m_form
) {
187 for (size_t argument
= 0; argument
< block
->variablesAtTail
.numberOfArguments(); ++argument
) {
188 AbstractValue
& destination
= block
->valuesAtTail
.argument(argument
);
189 changed
|= mergeStateAtTail(destination
, m_variables
.argument(argument
), block
->variablesAtTail
.argument(argument
));
192 for (size_t local
= 0; local
< block
->variablesAtTail
.numberOfLocals(); ++local
) {
193 AbstractValue
& destination
= block
->valuesAtTail
.local(local
);
194 changed
|= mergeStateAtTail(destination
, m_variables
.local(local
), block
->variablesAtTail
.local(local
));
200 for (size_t i
= 0; i
< block
->valuesAtTail
.size(); ++i
)
201 changed
|= block
->valuesAtTail
[i
].merge(m_variables
[i
]);
203 HashSet
<Node
*>::iterator iter
= block
->ssa
->liveAtTail
.begin();
204 HashSet
<Node
*>::iterator end
= block
->ssa
->liveAtTail
.end();
205 for (; iter
!= end
; ++iter
) {
207 changed
|= block
->ssa
->valuesAtTail
.find(node
)->value
.merge(forNode(node
));
213 RELEASE_ASSERT_NOT_REACHED();
217 ASSERT(mergeMode
!= DontMerge
|| !changed
);
221 if (mergeMode
!= MergeToSuccessors
)
224 return mergeToSuccessors(block
);
227 void InPlaceAbstractState::reset()
231 m_branchDirection
= InvalidBranchDirection
;
232 m_structureClobberState
= StructuresAreWatched
;
235 bool InPlaceAbstractState::mergeStateAtTail(AbstractValue
& destination
, AbstractValue
& inVariable
, Node
* node
)
240 AbstractValue source
;
242 switch (node
->op()) {
247 // The block transfers the value from head to tail.
252 // The block refines the value with additional speculations.
253 source
= forNode(node
);
257 // The block sets the variable, and potentially refines it, both
258 // before and after setting it.
259 source
= forNode(node
->child1());
260 if (node
->variableAccessData()->flushFormat() == FlushedDouble
)
261 RELEASE_ASSERT(!(source
.m_type
& ~SpecFullDouble
));
265 RELEASE_ASSERT_NOT_REACHED();
269 if (destination
== source
) {
270 // Abstract execution did not change the output value of the variable, for this
271 // basic block, on this iteration.
275 // Abstract execution reached a new conclusion about the speculations reached about
276 // this variable after execution of this basic block. Update the state, and return
277 // true to indicate that the fixpoint must go on!
278 destination
= source
;
282 bool InPlaceAbstractState::merge(BasicBlock
* from
, BasicBlock
* to
)
285 dataLog(" Merging from ", pointerDump(from
), " to ", pointerDump(to
), "\n");
286 ASSERT(from
->variablesAtTail
.numberOfArguments() == to
->variablesAtHead
.numberOfArguments());
287 ASSERT(from
->variablesAtTail
.numberOfLocals() == to
->variablesAtHead
.numberOfLocals());
289 bool changed
= false;
291 changed
|= checkAndSet(
292 to
->cfaStructureClobberStateAtHead
,
293 DFG::merge(from
->cfaStructureClobberStateAtTail
, to
->cfaStructureClobberStateAtHead
));
295 switch (m_graph
.m_form
) {
297 for (size_t argument
= 0; argument
< from
->variablesAtTail
.numberOfArguments(); ++argument
) {
298 AbstractValue
& destination
= to
->valuesAtHead
.argument(argument
);
299 changed
|= mergeVariableBetweenBlocks(destination
, from
->valuesAtTail
.argument(argument
), to
->variablesAtHead
.argument(argument
), from
->variablesAtTail
.argument(argument
));
302 for (size_t local
= 0; local
< from
->variablesAtTail
.numberOfLocals(); ++local
) {
303 AbstractValue
& destination
= to
->valuesAtHead
.local(local
);
304 changed
|= mergeVariableBetweenBlocks(destination
, from
->valuesAtTail
.local(local
), to
->variablesAtHead
.local(local
), from
->variablesAtTail
.local(local
));
310 for (size_t i
= from
->valuesAtTail
.size(); i
--;)
311 changed
|= to
->valuesAtHead
[i
].merge(from
->valuesAtTail
[i
]);
313 HashSet
<Node
*>::iterator iter
= to
->ssa
->liveAtHead
.begin();
314 HashSet
<Node
*>::iterator end
= to
->ssa
->liveAtHead
.end();
315 for (; iter
!= end
; ++iter
) {
318 dataLog(" Merging for ", node
, ": from ", from
->ssa
->valuesAtTail
.find(node
)->value
, " to ", to
->ssa
->valuesAtHead
.find(node
)->value
, "\n");
319 changed
|= to
->ssa
->valuesAtHead
.find(node
)->value
.merge(
320 from
->ssa
->valuesAtTail
.find(node
)->value
);
322 dataLog(" Result: ", to
->ssa
->valuesAtHead
.find(node
)->value
, "\n");
328 RELEASE_ASSERT_NOT_REACHED();
332 if (!to
->cfaHasVisited
)
336 dataLog(" Will revisit: ", changed
, "\n");
337 to
->cfaShouldRevisit
|= changed
;
342 inline bool InPlaceAbstractState::mergeToSuccessors(BasicBlock
* basicBlock
)
344 Node
* terminal
= basicBlock
->terminal();
346 ASSERT(terminal
->isTerminal());
348 switch (terminal
->op()) {
350 ASSERT(basicBlock
->cfaBranchDirection
== InvalidBranchDirection
);
351 return merge(basicBlock
, terminal
->targetBlock());
355 ASSERT(basicBlock
->cfaBranchDirection
!= InvalidBranchDirection
);
356 bool changed
= false;
357 if (basicBlock
->cfaBranchDirection
!= TakeFalse
)
358 changed
|= merge(basicBlock
, terminal
->branchData()->taken
.block
);
359 if (basicBlock
->cfaBranchDirection
!= TakeTrue
)
360 changed
|= merge(basicBlock
, terminal
->branchData()->notTaken
.block
);
365 // FIXME: It would be cool to be sparse conditional for Switch's. Currently
366 // we're not. However I somehow doubt that this will ever be a big deal.
367 ASSERT(basicBlock
->cfaBranchDirection
== InvalidBranchDirection
);
368 SwitchData
* data
= terminal
->switchData();
369 bool changed
= merge(basicBlock
, data
->fallThrough
.block
);
370 for (unsigned i
= data
->cases
.size(); i
--;)
371 changed
|= merge(basicBlock
, data
->cases
[i
].target
.block
);
377 ASSERT(basicBlock
->cfaBranchDirection
== InvalidBranchDirection
);
381 RELEASE_ASSERT_NOT_REACHED();
386 inline bool InPlaceAbstractState::mergeVariableBetweenBlocks(AbstractValue
& destination
, AbstractValue
& source
, Node
* destinationNode
, Node
* sourceNode
)
388 if (!destinationNode
)
391 ASSERT_UNUSED(sourceNode
, sourceNode
);
393 // FIXME: We could do some sparse conditional propagation here!
395 return destination
.merge(source
);
398 } } // namespace JSC::DFG
400 #endif // ENABLE(DFG_JIT)