1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "jit/IonAnalysis.h"
9 #include "jit/MIRGenerator.h"
10 #include "jit/MIRGraph.h"
11 #include "jit/RangeAnalysis.h"
12 #include "jit/ValueNumbering.h"
14 #include "jsapi-tests/testJitMinimalFunc.h"
15 #include "jsapi-tests/tests.h"
18 using namespace js::jit
;
20 static MBasicBlock
* FollowTrivialGotos(MBasicBlock
* block
) {
21 while (block
->phisEmpty() && *block
->begin() == block
->lastIns() &&
22 block
->lastIns()->isGoto()) {
23 block
= block
->lastIns()->toGoto()->getSuccessor(0);
28 BEGIN_TEST(testJitGVN_FixupOSROnlyLoop
) {
29 // This is a testcase which constructs the very rare circumstances that
30 // require the FixupOSROnlyLoop logic.
34 MBasicBlock
* entry
= func
.createEntryBlock();
35 MBasicBlock
* osrEntry
= func
.createOsrEntryBlock();
36 MBasicBlock
* outerHeader
= func
.createBlock(entry
);
37 MBasicBlock
* merge
= func
.createBlock(outerHeader
);
38 MBasicBlock
* innerHeader
= func
.createBlock(merge
);
39 MBasicBlock
* innerBackedge
= func
.createBlock(innerHeader
);
40 MBasicBlock
* outerBackedge
= func
.createBlock(innerHeader
);
41 MBasicBlock
* exit
= func
.createBlock(outerHeader
);
43 MConstant
* c
= MConstant::New(func
.alloc
, BooleanValue(false));
45 entry
->end(MTest::New(func
.alloc
, c
, outerHeader
, exit
));
46 osrEntry
->end(MGoto::New(func
.alloc
, merge
));
48 merge
->end(MGoto::New(func
.alloc
, innerHeader
));
50 // Use Beta nodes to hide the constants and suppress folding.
51 MConstant
* x
= MConstant::New(func
.alloc
, BooleanValue(false));
54 MBeta::New(func
.alloc
, x
, Range::NewInt32Range(func
.alloc
, 0, 1));
55 outerHeader
->add(xBeta
);
56 outerHeader
->end(MTest::New(func
.alloc
, xBeta
, merge
, exit
));
58 MConstant
* y
= MConstant::New(func
.alloc
, BooleanValue(false));
61 MBeta::New(func
.alloc
, y
, Range::NewInt32Range(func
.alloc
, 0, 1));
62 innerHeader
->add(yBeta
);
63 innerHeader
->end(MTest::New(func
.alloc
, yBeta
, innerBackedge
, outerBackedge
));
65 innerBackedge
->end(MGoto::New(func
.alloc
, innerHeader
));
66 outerBackedge
->end(MGoto::New(func
.alloc
, outerHeader
));
68 MConstant
* u
= MConstant::New(func
.alloc
, UndefinedValue());
70 exit
->end(MReturn::New(func
.alloc
, u
));
72 MOZ_ALWAYS_TRUE(innerHeader
->addPredecessorWithoutPhis(innerBackedge
));
73 MOZ_ALWAYS_TRUE(outerHeader
->addPredecessorWithoutPhis(outerBackedge
));
74 MOZ_ALWAYS_TRUE(exit
->addPredecessorWithoutPhis(entry
));
75 MOZ_ALWAYS_TRUE(merge
->addPredecessorWithoutPhis(osrEntry
));
77 outerHeader
->setLoopHeader(outerBackedge
);
78 innerHeader
->setLoopHeader(innerBackedge
);
84 // The loops are no longer reachable from the normal entry. They are
85 // doinated by the osrEntry.
86 MOZ_RELEASE_ASSERT(func
.graph
.osrBlock() == osrEntry
);
87 MBasicBlock
* newInner
=
88 FollowTrivialGotos(osrEntry
->lastIns()->toGoto()->target());
89 MBasicBlock
* newOuter
=
90 FollowTrivialGotos(newInner
->lastIns()->toTest()->ifFalse());
91 MBasicBlock
* newExit
= FollowTrivialGotos(entry
);
92 MOZ_RELEASE_ASSERT(newInner
->isLoopHeader());
93 MOZ_RELEASE_ASSERT(newOuter
->isLoopHeader());
94 MOZ_RELEASE_ASSERT(newExit
->lastIns()->isReturn());
97 ClearDominatorTree(func
.graph
);
102 // The loops are no longer reachable from the normal entry. They are
103 // doinated by the osrEntry.
104 MOZ_RELEASE_ASSERT(func
.graph
.osrBlock() == osrEntry
);
105 newInner
= FollowTrivialGotos(osrEntry
->lastIns()->toGoto()->target());
106 newOuter
= FollowTrivialGotos(newInner
->lastIns()->toTest()->ifFalse());
107 newExit
= FollowTrivialGotos(entry
);
108 MOZ_RELEASE_ASSERT(newInner
->isLoopHeader());
109 MOZ_RELEASE_ASSERT(newOuter
->isLoopHeader());
110 MOZ_RELEASE_ASSERT(newExit
->lastIns()->isReturn());
114 END_TEST(testJitGVN_FixupOSROnlyLoop
)
116 BEGIN_TEST(testJitGVN_FixupOSROnlyLoopNested
) {
117 // Same as testJitGVN_FixupOSROnlyLoop but adds another level of loop
118 // nesting for added excitement.
122 MBasicBlock
* entry
= func
.createEntryBlock();
123 MBasicBlock
* osrEntry
= func
.createOsrEntryBlock();
124 MBasicBlock
* outerHeader
= func
.createBlock(entry
);
125 MBasicBlock
* middleHeader
= func
.createBlock(outerHeader
);
126 MBasicBlock
* merge
= func
.createBlock(middleHeader
);
127 MBasicBlock
* innerHeader
= func
.createBlock(merge
);
128 MBasicBlock
* innerBackedge
= func
.createBlock(innerHeader
);
129 MBasicBlock
* middleBackedge
= func
.createBlock(innerHeader
);
130 MBasicBlock
* outerBackedge
= func
.createBlock(middleHeader
);
131 MBasicBlock
* exit
= func
.createBlock(outerHeader
);
133 MConstant
* c
= MConstant::New(func
.alloc
, BooleanValue(false));
135 entry
->end(MTest::New(func
.alloc
, c
, outerHeader
, exit
));
136 osrEntry
->end(MGoto::New(func
.alloc
, merge
));
138 merge
->end(MGoto::New(func
.alloc
, innerHeader
));
140 // Use Beta nodes to hide the constants and suppress folding.
141 MConstant
* x
= MConstant::New(func
.alloc
, BooleanValue(false));
144 MBeta::New(func
.alloc
, x
, Range::NewInt32Range(func
.alloc
, 0, 1));
145 outerHeader
->add(xBeta
);
146 outerHeader
->end(MTest::New(func
.alloc
, xBeta
, middleHeader
, exit
));
148 MConstant
* y
= MConstant::New(func
.alloc
, BooleanValue(false));
149 middleHeader
->add(y
);
151 MBeta::New(func
.alloc
, y
, Range::NewInt32Range(func
.alloc
, 0, 1));
152 middleHeader
->add(yBeta
);
153 middleHeader
->end(MTest::New(func
.alloc
, yBeta
, merge
, outerBackedge
));
155 MConstant
* w
= MConstant::New(func
.alloc
, BooleanValue(false));
158 MBeta::New(func
.alloc
, w
, Range::NewInt32Range(func
.alloc
, 0, 1));
159 innerHeader
->add(wBeta
);
161 MTest::New(func
.alloc
, wBeta
, innerBackedge
, middleBackedge
));
163 innerBackedge
->end(MGoto::New(func
.alloc
, innerHeader
));
164 middleBackedge
->end(MGoto::New(func
.alloc
, middleHeader
));
165 outerBackedge
->end(MGoto::New(func
.alloc
, outerHeader
));
167 MConstant
* u
= MConstant::New(func
.alloc
, UndefinedValue());
169 exit
->end(MReturn::New(func
.alloc
, u
));
171 MOZ_ALWAYS_TRUE(innerHeader
->addPredecessorWithoutPhis(innerBackedge
));
172 MOZ_ALWAYS_TRUE(middleHeader
->addPredecessorWithoutPhis(middleBackedge
));
173 MOZ_ALWAYS_TRUE(outerHeader
->addPredecessorWithoutPhis(outerBackedge
));
174 MOZ_ALWAYS_TRUE(exit
->addPredecessorWithoutPhis(entry
));
175 MOZ_ALWAYS_TRUE(merge
->addPredecessorWithoutPhis(osrEntry
));
177 outerHeader
->setLoopHeader(outerBackedge
);
178 middleHeader
->setLoopHeader(middleBackedge
);
179 innerHeader
->setLoopHeader(innerBackedge
);
181 if (!func
.runGVN()) {
185 // The loops are no longer reachable from the normal entry. They are
186 // doinated by the osrEntry.
187 MOZ_RELEASE_ASSERT(func
.graph
.osrBlock() == osrEntry
);
188 MBasicBlock
* newInner
=
189 FollowTrivialGotos(osrEntry
->lastIns()->toGoto()->target());
190 MBasicBlock
* newMiddle
=
191 FollowTrivialGotos(newInner
->lastIns()->toTest()->ifFalse());
192 MBasicBlock
* newOuter
=
193 FollowTrivialGotos(newMiddle
->lastIns()->toTest()->ifFalse());
194 MBasicBlock
* newExit
= FollowTrivialGotos(entry
);
195 MOZ_RELEASE_ASSERT(newInner
->isLoopHeader());
196 MOZ_RELEASE_ASSERT(newMiddle
->isLoopHeader());
197 MOZ_RELEASE_ASSERT(newOuter
->isLoopHeader());
198 MOZ_RELEASE_ASSERT(newExit
->lastIns()->isReturn());
201 ClearDominatorTree(func
.graph
);
202 if (!func
.runGVN()) {
206 // The loops are no longer reachable from the normal entry. They are
207 // doinated by the osrEntry.
208 MOZ_RELEASE_ASSERT(func
.graph
.osrBlock() == osrEntry
);
209 newInner
= FollowTrivialGotos(osrEntry
->lastIns()->toGoto()->target());
210 newMiddle
= FollowTrivialGotos(newInner
->lastIns()->toTest()->ifFalse());
211 newOuter
= FollowTrivialGotos(newMiddle
->lastIns()->toTest()->ifFalse());
212 newExit
= FollowTrivialGotos(entry
);
213 MOZ_RELEASE_ASSERT(newInner
->isLoopHeader());
214 MOZ_RELEASE_ASSERT(newMiddle
->isLoopHeader());
215 MOZ_RELEASE_ASSERT(newOuter
->isLoopHeader());
216 MOZ_RELEASE_ASSERT(newExit
->lastIns()->isReturn());
220 END_TEST(testJitGVN_FixupOSROnlyLoopNested
)
222 BEGIN_TEST(testJitGVN_PinnedPhis
) {
223 // Set up a loop which gets optimized away, with phis which must be
224 // cleaned up, permitting more phis to be cleaned up.
228 MBasicBlock
* entry
= func
.createEntryBlock();
229 MBasicBlock
* outerHeader
= func
.createBlock(entry
);
230 MBasicBlock
* outerBlock
= func
.createBlock(outerHeader
);
231 MBasicBlock
* innerHeader
= func
.createBlock(outerBlock
);
232 MBasicBlock
* innerBackedge
= func
.createBlock(innerHeader
);
233 MBasicBlock
* exit
= func
.createBlock(innerHeader
);
235 MPhi
* phi0
= MPhi::New(func
.alloc
);
236 MPhi
* phi1
= MPhi::New(func
.alloc
);
237 MPhi
* phi2
= MPhi::New(func
.alloc
);
238 MPhi
* phi3
= MPhi::New(func
.alloc
);
240 MParameter
* p
= func
.createParameter();
242 MConstant
* z0
= MConstant::New(func
.alloc
, Int32Value(0));
243 MConstant
* z1
= MConstant::New(func
.alloc
, Int32Value(1));
244 MConstant
* z2
= MConstant::New(func
.alloc
, Int32Value(2));
245 MConstant
* z3
= MConstant::New(func
.alloc
, Int32Value(2));
246 MOZ_RELEASE_ASSERT(phi0
->addInputSlow(z0
));
247 MOZ_RELEASE_ASSERT(phi1
->addInputSlow(z1
));
248 MOZ_RELEASE_ASSERT(phi2
->addInputSlow(z2
));
249 MOZ_RELEASE_ASSERT(phi3
->addInputSlow(z3
));
254 entry
->end(MGoto::New(func
.alloc
, outerHeader
));
256 outerHeader
->addPhi(phi0
);
257 outerHeader
->addPhi(phi1
);
258 outerHeader
->addPhi(phi2
);
259 outerHeader
->addPhi(phi3
);
260 outerHeader
->end(MGoto::New(func
.alloc
, outerBlock
));
262 outerBlock
->end(MGoto::New(func
.alloc
, innerHeader
));
264 MConstant
* true_
= MConstant::New(func
.alloc
, BooleanValue(true));
265 innerHeader
->add(true_
);
266 innerHeader
->end(MTest::New(func
.alloc
, true_
, innerBackedge
, exit
));
268 innerBackedge
->end(MGoto::New(func
.alloc
, innerHeader
));
270 MInstruction
* z4
= MAdd::New(func
.alloc
, phi0
, phi1
, MIRType::Int32
);
271 MConstant
* z5
= MConstant::New(func
.alloc
, Int32Value(4));
272 MInstruction
* z6
= MAdd::New(func
.alloc
, phi2
, phi3
, MIRType::Int32
);
273 MConstant
* z7
= MConstant::New(func
.alloc
, Int32Value(6));
274 MOZ_RELEASE_ASSERT(phi0
->addInputSlow(z4
));
275 MOZ_RELEASE_ASSERT(phi1
->addInputSlow(z5
));
276 MOZ_RELEASE_ASSERT(phi2
->addInputSlow(z6
));
277 MOZ_RELEASE_ASSERT(phi3
->addInputSlow(z7
));
282 exit
->end(MGoto::New(func
.alloc
, outerHeader
));
284 MOZ_ALWAYS_TRUE(innerHeader
->addPredecessorWithoutPhis(innerBackedge
));
285 MOZ_ALWAYS_TRUE(outerHeader
->addPredecessorWithoutPhis(exit
));
287 outerHeader
->setLoopHeader(exit
);
288 innerHeader
->setLoopHeader(innerBackedge
);
290 if (!func
.runGVN()) {
294 MOZ_RELEASE_ASSERT(innerHeader
->phisEmpty());
295 MOZ_RELEASE_ASSERT(exit
->isDead());
299 END_TEST(testJitGVN_PinnedPhis
)