2 * Copyright (C) 2014 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 "DFGIntegerCheckCombiningPhase.h"
32 #include "DFGInsertionSet.h"
34 #include "DFGPredictionPropagationPhase.h"
35 #include "DFGVariableAccessDataDump.h"
36 #include "JSCInlines.h"
37 #include <unordered_map>
38 #include <wtf/HashMethod.h>
40 namespace JSC
{ namespace DFG
{
44 static const bool verbose
= false;
49 // This means we did ArithAdd with CheckOverflow.
52 // This means we did CheckInBounds on some length.
58 : m_kind(InvalidRangeKind
)
63 static RangeKey
addition(Edge edge
)
66 result
.m_kind
= Addition
;
67 result
.m_source
= edge
.sanitized();
72 static RangeKey
arrayBounds(Edge edge
, Node
* key
)
75 result
.m_kind
= ArrayBounds
;
76 result
.m_source
= edge
.sanitized();
81 bool operator!() const { return m_kind
== InvalidRangeKind
; }
85 return m_kind
+ m_source
.hash() + PtrHash
<Node
*>::hash(m_key
);
88 bool operator==(const RangeKey
& other
) const
90 return m_kind
== other
.m_kind
91 && m_source
== other
.m_source
92 && m_key
== other
.m_key
;
95 void dump(PrintStream
& out
) const
98 case InvalidRangeKind
:
99 out
.print("InvalidRangeKind(");
102 out
.print("Addition(");
105 out
.print("ArrayBounds(");
108 out
.print(m_source
, ", ", m_key
, ")");
116 struct RangeKeyAndAddend
{
122 RangeKeyAndAddend(RangeKey key
, int32_t addend
)
128 bool operator!() const { return !m_key
&& !m_addend
; }
130 void dump(PrintStream
& out
) const
132 out
.print(m_key
, " + ", m_addend
);
148 void dump(PrintStream
& out
) const
150 out
.print("(", m_minBound
, " @", m_minOrigin
, ") .. (", m_maxBound
, " @", m_maxOrigin
, "), count = ", m_count
, ", hoisted = ", m_hoisted
);
154 CodeOrigin m_minOrigin
;
156 CodeOrigin m_maxOrigin
;
157 unsigned m_count
; // If this is zero then the bounds won't necessarily make sense.
161 } // anonymous namespace
163 class IntegerCheckCombiningPhase
: public Phase
{
165 IntegerCheckCombiningPhase(Graph
& graph
)
166 : Phase(graph
, "integer check combining")
167 , m_insertionSet(graph
)
173 ASSERT(m_graph
.m_form
== SSA
);
177 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;)
178 handleBlock(blockIndex
);
184 void handleBlock(BlockIndex blockIndex
)
186 BasicBlock
* block
= m_graph
.block(blockIndex
);
192 // First we collect Ranges. If operations within the range have enough redundancy,
193 // we hoist. And then we remove additions and checks that fall within the max range.
195 for (unsigned nodeIndex
= 0; nodeIndex
< block
->size(); ++nodeIndex
) {
196 Node
* node
= block
->at(nodeIndex
);
197 RangeKeyAndAddend data
= rangeKeyAndAddend(node
);
199 dataLog("For ", node
, ": ", data
, "\n");
203 Range
& range
= m_map
[data
.m_key
];
205 dataLog(" Range: ", range
, "\n");
207 if (data
.m_addend
> range
.m_maxBound
) {
208 range
.m_maxBound
= data
.m_addend
;
209 range
.m_maxOrigin
= node
->origin
.semantic
;
210 } else if (data
.m_addend
< range
.m_minBound
) {
211 range
.m_minBound
= data
.m_addend
;
212 range
.m_minOrigin
= node
->origin
.semantic
;
215 range
.m_maxBound
= data
.m_addend
;
216 range
.m_minBound
= data
.m_addend
;
217 range
.m_minOrigin
= node
->origin
.semantic
;
218 range
.m_maxOrigin
= node
->origin
.semantic
;
222 dataLog(" New range: ", range
, "\n");
225 for (unsigned nodeIndex
= 0; nodeIndex
< block
->size(); ++nodeIndex
) {
226 Node
* node
= block
->at(nodeIndex
);
227 RangeKeyAndAddend data
= rangeKeyAndAddend(node
);
230 Range range
= m_map
[data
.m_key
];
231 if (!isValid(data
.m_key
, range
))
235 if (!range
.m_hoisted
) {
236 switch (data
.m_key
.m_kind
) {
238 if (range
.m_minBound
< 0) {
240 nodeIndex
, NodeOrigin(range
.m_minOrigin
, node
->origin
.forExit
),
241 data
.m_key
.m_source
, range
.m_minBound
);
243 if (range
.m_maxBound
> 0) {
245 nodeIndex
, NodeOrigin(range
.m_maxOrigin
, node
->origin
.forExit
),
246 data
.m_key
.m_source
, range
.m_maxBound
);
255 if (!data
.m_key
.m_source
) {
257 maxNode
= m_insertionSet
.insertConstant(
258 nodeIndex
, range
.m_maxOrigin
, jsNumber(range
.m_maxBound
));
261 nodeIndex
, NodeOrigin(range
.m_minOrigin
, node
->origin
.forExit
),
262 data
.m_key
.m_source
, range
.m_minBound
, Arith::Unchecked
);
264 nodeIndex
, NodeOrigin(range
.m_maxOrigin
, node
->origin
.forExit
),
265 data
.m_key
.m_source
, range
.m_maxBound
, Arith::Unchecked
);
269 m_insertionSet
.insertNode(
270 nodeIndex
, SpecNone
, CheckInBounds
, node
->origin
,
271 Edge(minNode
, Int32Use
), Edge(data
.m_key
.m_key
, Int32Use
));
273 m_insertionSet
.insertNode(
274 nodeIndex
, SpecNone
, CheckInBounds
, node
->origin
,
275 Edge(maxNode
, Int32Use
), Edge(data
.m_key
.m_key
, Int32Use
));
280 RELEASE_ASSERT_NOT_REACHED();
284 m_map
[data
.m_key
].m_hoisted
= true;
287 // Do the elimination.
288 switch (data
.m_key
.m_kind
) {
290 node
->setArithMode(Arith::Unchecked
);
295 node
->convertToPhantom();
300 RELEASE_ASSERT_NOT_REACHED();
304 m_insertionSet
.execute(block
);
307 RangeKeyAndAddend
rangeKeyAndAddend(Node
* node
)
309 switch (node
->op()) {
311 if (node
->arithMode() != Arith::CheckOverflow
312 && node
->arithMode() != Arith::CheckOverflowAndNegativeZero
)
314 if (!m_graph
.isInt32Constant(node
->child2().node()))
316 return RangeKeyAndAddend(
317 RangeKey::addition(node
->child1()),
318 m_graph
.valueOfInt32Constant(node
->child2().node()));
321 case CheckInBounds
: {
324 Node
* key
= node
->child2().node();
326 Edge index
= node
->child1();
328 if (m_graph
.isInt32Constant(index
.node())) {
330 addend
= m_graph
.valueOfInt32Constant(index
.node());
332 index
->op() == ArithAdd
333 && index
->isBinaryUseKind(Int32Use
)
334 && m_graph
.isInt32Constant(index
->child2().node())) {
335 source
= index
->child1();
336 addend
= m_graph
.valueOfInt32Constant(index
->child2().node());
342 return RangeKeyAndAddend(RangeKey::arrayBounds(source
, key
), addend
);
349 return RangeKeyAndAddend();
352 bool isValid(const RangeKey
& key
, const Range
& range
)
354 if (range
.m_count
< 2)
357 switch (key
.m_kind
) {
359 return (range
.m_maxBound
- range
.m_minBound
) >= 0;
367 unsigned nodeIndex
, NodeOrigin origin
, Edge source
, int32_t addend
,
368 Arith::Mode arithMode
= Arith::CheckOverflow
)
371 return source
.node();
372 return m_insertionSet
.insertNode(
373 nodeIndex
, source
->prediction(), source
->result(),
374 ArithAdd
, origin
, OpInfo(arithMode
), source
,
375 m_insertionSet
.insertConstantForUse(
376 nodeIndex
, origin
, jsNumber(addend
), source
.useKind()));
380 unsigned nodeIndex
, NodeOrigin origin
, Edge source
, int32_t addend
)
382 Node
* result
= insertAdd(nodeIndex
, origin
, source
, addend
);
383 m_insertionSet
.insertNode(
384 nodeIndex
, SpecNone
, HardPhantom
, origin
, result
->defaultEdge());
388 typedef std::unordered_map
<RangeKey
, Range
, HashMethod
<RangeKey
>> RangeMap
;
391 InsertionSet m_insertionSet
;
395 bool performIntegerCheckCombining(Graph
& graph
)
397 SamplingRegion
samplingRegion("DFG Integer Check Combining Phase");
398 return runPhase
<IntegerCheckCombiningPhase
>(graph
);
401 } } // namespace JSC::DFG
403 #endif // ENABLE(DFG_JIT)