OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/compiler/access-builder.h" |
| 6 #include "src/compiler/graph.h" |
| 7 #include "src/compiler/graph-visualizer.h" |
| 8 #include "src/compiler/js-graph.h" |
| 9 #include "src/compiler/js-operator.h" |
| 10 #include "src/compiler/js-typed-lowering.h" |
| 11 #include "src/compiler/loop-peeling.h" |
| 12 #include "src/compiler/machine-operator.h" |
| 13 #include "src/compiler/node.h" |
| 14 #include "src/compiler/node-properties-inl.h" |
| 15 #include "test/unittests/compiler/compiler-test-utils.h" |
| 16 #include "test/unittests/compiler/graph-unittest.h" |
| 17 #include "test/unittests/compiler/node-test-utils.h" |
| 18 #include "testing/gmock-support.h" |
| 19 |
| 20 using testing::AllOf; |
| 21 using testing::BitEq; |
| 22 using testing::Capture; |
| 23 using testing::CaptureEq; |
| 24 |
| 25 namespace v8 { |
| 26 namespace internal { |
| 27 namespace compiler { |
| 28 |
| 29 class LoopPeelingTest : public GraphTest { |
| 30 public: |
| 31 LoopPeelingTest() : GraphTest(1), machine_(zone()) {} |
| 32 ~LoopPeelingTest() OVERRIDE {} |
| 33 |
| 34 protected: |
| 35 MachineOperatorBuilder machine_; |
| 36 |
| 37 MachineOperatorBuilder* machine() { return &machine_; } |
| 38 |
| 39 LoopTree* GetLoopTree() { |
| 40 if (FLAG_trace_turbo_graph) { |
| 41 OFStream os(stdout); |
| 42 os << AsRPO(*graph()); |
| 43 } |
| 44 Zone zone(isolate()); |
| 45 return LoopFinder::BuildLoopTree(graph(), &zone); |
| 46 } |
| 47 |
| 48 |
| 49 PeeledIteration* PeelOne() { |
| 50 LoopTree* loop_tree = GetLoopTree(); |
| 51 return Peel(loop_tree, loop_tree->outer_loops()[0]); |
| 52 } |
| 53 |
| 54 PeeledIteration* Peel(LoopTree* loop_tree, LoopTree::Loop* loop) { |
| 55 PeeledIteration* peeled = |
| 56 LoopPeeler::Peel(graph(), common(), loop_tree, loop, zone()); |
| 57 if (FLAG_trace_turbo_graph) { |
| 58 OFStream os(stdout); |
| 59 os << AsRPO(*graph()); |
| 60 } |
| 61 return peeled; |
| 62 } |
| 63 |
| 64 Node* InsertReturn(Node* val, Node* effect, Node* control) { |
| 65 Node* r = graph()->NewNode(common()->Return(), val, effect, control); |
| 66 graph()->SetEnd(r); |
| 67 return r; |
| 68 } |
| 69 |
| 70 Node* ExpectPeeled(Node* node, PeeledIteration* iter) { |
| 71 Node* p = iter->map(node); |
| 72 EXPECT_NE(node, p); |
| 73 return p; |
| 74 } |
| 75 |
| 76 void ExpectNotPeeled(Node* node, PeeledIteration* iter) { |
| 77 EXPECT_EQ(node, iter->map(node)); |
| 78 } |
| 79 }; |
| 80 |
| 81 |
| 82 TEST_F(LoopPeelingTest, SimpleLoop) { |
| 83 Node* p0 = Parameter(0); |
| 84 While w(this, p0); |
| 85 Node* r = InsertReturn(p0, start(), w.exit); |
| 86 |
| 87 PeeledIteration* peeled = PeelOne(); |
| 88 |
| 89 Node* br1 = ExpectPeeled(w.branch, peeled); |
| 90 Node* if_true1 = ExpectPeeled(w.if_true, peeled); |
| 91 Node* if_false1 = ExpectPeeled(w.exit, peeled); |
| 92 |
| 93 EXPECT_THAT(br1, IsBranch(p0, start())); |
| 94 EXPECT_THAT(if_true1, IsIfTrue(br1)); |
| 95 EXPECT_THAT(if_false1, IsIfFalse(br1)); |
| 96 |
| 97 EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true)); |
| 98 EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(w.exit, if_false1))); |
| 99 } |
| 100 |
| 101 |
| 102 TEST_F(LoopPeelingTest, SimpleLoopWithCounter) { |
| 103 Node* p0 = Parameter(0); |
| 104 While w(this, p0); |
| 105 Counter c(w, machine(), 0, 1); |
| 106 Node* r = InsertReturn(c.phi, start(), w.exit); |
| 107 |
| 108 PeeledIteration* peeled = PeelOne(); |
| 109 |
| 110 Node* br1 = ExpectPeeled(w.branch, peeled); |
| 111 Node* if_true1 = ExpectPeeled(w.if_true, peeled); |
| 112 Node* if_false1 = ExpectPeeled(w.exit, peeled); |
| 113 |
| 114 EXPECT_THAT(br1, IsBranch(p0, start())); |
| 115 EXPECT_THAT(if_true1, IsIfTrue(br1)); |
| 116 EXPECT_THAT(if_false1, IsIfFalse(br1)); |
| 117 EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true)); |
| 118 |
| 119 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc)); |
| 120 |
| 121 Capture<Node*> merge; |
| 122 EXPECT_THAT( |
| 123 r, IsReturn(IsPhi(kMachAnyTagged, c.phi, c.base, |
| 124 AllOf(CaptureEq(&merge), IsMerge(w.exit, if_false1))), |
| 125 start(), CaptureEq(&merge))); |
| 126 } |
| 127 |
| 128 |
| 129 TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_outer) { |
| 130 Node* p0 = Parameter(0); |
| 131 While outer(this, p0); |
| 132 While inner(this, p0); |
| 133 inner.Nest(&outer); |
| 134 |
| 135 Counter c(outer, machine(), 0, 1); |
| 136 Node* r = InsertReturn(c.phi, start(), outer.exit); |
| 137 |
| 138 PeeledIteration* peeled = PeelOne(); |
| 139 |
| 140 Node* bro = ExpectPeeled(outer.branch, peeled); |
| 141 Node* if_trueo = ExpectPeeled(outer.if_true, peeled); |
| 142 Node* if_falseo = ExpectPeeled(outer.exit, peeled); |
| 143 |
| 144 EXPECT_THAT(bro, IsBranch(p0, start())); |
| 145 EXPECT_THAT(if_trueo, IsIfTrue(bro)); |
| 146 EXPECT_THAT(if_falseo, IsIfFalse(bro)); |
| 147 |
| 148 Node* bri = ExpectPeeled(inner.branch, peeled); |
| 149 Node* if_truei = ExpectPeeled(inner.if_true, peeled); |
| 150 Node* if_falsei = ExpectPeeled(inner.exit, peeled); |
| 151 |
| 152 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled))); |
| 153 EXPECT_THAT(if_truei, IsIfTrue(bri)); |
| 154 EXPECT_THAT(if_falsei, IsIfFalse(bri)); |
| 155 |
| 156 EXPECT_THAT(outer.loop, IsLoop(if_falsei, inner.exit)); |
| 157 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc)); |
| 158 |
| 159 Capture<Node*> merge; |
| 160 EXPECT_THAT( |
| 161 r, |
| 162 IsReturn(IsPhi(kMachAnyTagged, c.phi, c.base, |
| 163 AllOf(CaptureEq(&merge), IsMerge(outer.exit, if_falseo))), |
| 164 start(), CaptureEq(&merge))); |
| 165 } |
| 166 |
| 167 |
| 168 TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_inner) { |
| 169 Node* p0 = Parameter(0); |
| 170 While outer(this, p0); |
| 171 While inner(this, p0); |
| 172 inner.Nest(&outer); |
| 173 |
| 174 Counter c(outer, machine(), 0, 1); |
| 175 Node* r = InsertReturn(c.phi, start(), outer.exit); |
| 176 |
| 177 LoopTree* loop_tree = GetLoopTree(); |
| 178 LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop); |
| 179 EXPECT_NE(nullptr, loop); |
| 180 EXPECT_EQ(1, loop->depth()); |
| 181 |
| 182 PeeledIteration* peeled = Peel(loop_tree, loop); |
| 183 |
| 184 ExpectNotPeeled(outer.loop, peeled); |
| 185 ExpectNotPeeled(outer.branch, peeled); |
| 186 ExpectNotPeeled(outer.if_true, peeled); |
| 187 ExpectNotPeeled(outer.exit, peeled); |
| 188 |
| 189 Node* bri = ExpectPeeled(inner.branch, peeled); |
| 190 Node* if_truei = ExpectPeeled(inner.if_true, peeled); |
| 191 Node* if_falsei = ExpectPeeled(inner.exit, peeled); |
| 192 |
| 193 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled))); |
| 194 EXPECT_THAT(if_truei, IsIfTrue(bri)); |
| 195 EXPECT_THAT(if_falsei, IsIfFalse(bri)); |
| 196 |
| 197 EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei))); |
| 198 ExpectNotPeeled(c.add, peeled); |
| 199 |
| 200 EXPECT_THAT(r, IsReturn(c.phi, start(), outer.exit)); |
| 201 } |
| 202 |
| 203 |
| 204 TEST_F(LoopPeelingTest, SimpleInnerCounter_peel_inner) { |
| 205 Node* p0 = Parameter(0); |
| 206 While outer(this, p0); |
| 207 While inner(this, p0); |
| 208 inner.Nest(&outer); |
| 209 Counter c(inner, machine(), 0, 1); |
| 210 Node* phi = outer.Phi(Int32Constant(11), c.phi); |
| 211 |
| 212 Node* r = InsertReturn(phi, start(), outer.exit); |
| 213 |
| 214 LoopTree* loop_tree = GetLoopTree(); |
| 215 LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop); |
| 216 EXPECT_NE(nullptr, loop); |
| 217 EXPECT_EQ(1, loop->depth()); |
| 218 |
| 219 PeeledIteration* peeled = Peel(loop_tree, loop); |
| 220 |
| 221 ExpectNotPeeled(outer.loop, peeled); |
| 222 ExpectNotPeeled(outer.branch, peeled); |
| 223 ExpectNotPeeled(outer.if_true, peeled); |
| 224 ExpectNotPeeled(outer.exit, peeled); |
| 225 |
| 226 Node* bri = ExpectPeeled(inner.branch, peeled); |
| 227 Node* if_truei = ExpectPeeled(inner.if_true, peeled); |
| 228 Node* if_falsei = ExpectPeeled(inner.exit, peeled); |
| 229 |
| 230 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled))); |
| 231 EXPECT_THAT(if_truei, IsIfTrue(bri)); |
| 232 EXPECT_THAT(if_falsei, IsIfFalse(bri)); |
| 233 |
| 234 EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei))); |
| 235 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc)); |
| 236 |
| 237 Node* back = phi->InputAt(1); |
| 238 EXPECT_THAT(back, IsPhi(kMachAnyTagged, c.phi, c.base, |
| 239 IsMerge(inner.exit, if_falsei))); |
| 240 |
| 241 EXPECT_THAT(phi, |
| 242 IsPhi(kMachAnyTagged, IsInt32Constant(11), back, outer.loop)); |
| 243 |
| 244 EXPECT_THAT(r, IsReturn(phi, start(), outer.exit)); |
| 245 } |
| 246 |
| 247 |
| 248 TEST_F(LoopPeelingTest, TwoBackedgeLoop) { |
| 249 Node* p0 = Parameter(0); |
| 250 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start()); |
| 251 Branch b1(this, p0, loop); |
| 252 Branch b2(this, p0, b1.if_true); |
| 253 |
| 254 loop->ReplaceInput(1, b2.if_true); |
| 255 loop->ReplaceInput(2, b2.if_false); |
| 256 |
| 257 Node* r = InsertReturn(p0, start(), b1.if_false); |
| 258 |
| 259 PeeledIteration* peeled = PeelOne(); |
| 260 |
| 261 Node* b1b = ExpectPeeled(b1.branch, peeled); |
| 262 Node* b1t = ExpectPeeled(b1.if_true, peeled); |
| 263 Node* b1f = ExpectPeeled(b1.if_false, peeled); |
| 264 |
| 265 EXPECT_THAT(b1b, IsBranch(p0, start())); |
| 266 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b)); |
| 267 EXPECT_THAT(b1f, IsIfFalse(b1b)); |
| 268 |
| 269 Node* b2b = ExpectPeeled(b2.branch, peeled); |
| 270 Node* b2t = ExpectPeeled(b2.if_true, peeled); |
| 271 Node* b2f = ExpectPeeled(b2.if_false, peeled); |
| 272 |
| 273 EXPECT_THAT(b2b, IsBranch(p0, b1t)); |
| 274 EXPECT_THAT(b2t, IsIfTrue(b2b)); |
| 275 EXPECT_THAT(b2f, IsIfFalse(b2b)); |
| 276 |
| 277 EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false)); |
| 278 EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(b1.if_false, b1f))); |
| 279 } |
| 280 |
| 281 |
| 282 TEST_F(LoopPeelingTest, TwoBackedgeLoopWithPhi) { |
| 283 Node* p0 = Parameter(0); |
| 284 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start()); |
| 285 Branch b1(this, p0, loop); |
| 286 Branch b2(this, p0, b1.if_true); |
| 287 Node* phi = |
| 288 graph()->NewNode(common()->Phi(kMachAnyTagged, 3), Int32Constant(0), |
| 289 Int32Constant(1), Int32Constant(2), loop); |
| 290 |
| 291 loop->ReplaceInput(1, b2.if_true); |
| 292 loop->ReplaceInput(2, b2.if_false); |
| 293 |
| 294 Node* r = InsertReturn(phi, start(), b1.if_false); |
| 295 |
| 296 PeeledIteration* peeled = PeelOne(); |
| 297 |
| 298 Node* b1b = ExpectPeeled(b1.branch, peeled); |
| 299 Node* b1t = ExpectPeeled(b1.if_true, peeled); |
| 300 Node* b1f = ExpectPeeled(b1.if_false, peeled); |
| 301 |
| 302 EXPECT_THAT(b1b, IsBranch(p0, start())); |
| 303 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b)); |
| 304 EXPECT_THAT(b1f, IsIfFalse(b1b)); |
| 305 |
| 306 Node* b2b = ExpectPeeled(b2.branch, peeled); |
| 307 Node* b2t = ExpectPeeled(b2.if_true, peeled); |
| 308 Node* b2f = ExpectPeeled(b2.if_false, peeled); |
| 309 |
| 310 EXPECT_THAT(b2b, IsBranch(p0, b1t)); |
| 311 EXPECT_THAT(b2t, IsIfTrue(b2b)); |
| 312 EXPECT_THAT(b2f, IsIfFalse(b2b)); |
| 313 |
| 314 EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false)); |
| 315 |
| 316 EXPECT_THAT( |
| 317 phi, IsPhi(kMachAnyTagged, IsPhi(kMachAnyTagged, IsInt32Constant(1), |
| 318 IsInt32Constant(2), IsMerge(b2t, b2f)), |
| 319 IsInt32Constant(1), IsInt32Constant(2), loop)); |
| 320 |
| 321 Capture<Node*> merge; |
| 322 EXPECT_THAT( |
| 323 r, IsReturn(IsPhi(kMachAnyTagged, phi, IsInt32Constant(0), |
| 324 AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))), |
| 325 start(), CaptureEq(&merge))); |
| 326 } |
| 327 |
| 328 |
| 329 TEST_F(LoopPeelingTest, TwoBackedgeLoopWithCounter) { |
| 330 Node* p0 = Parameter(0); |
| 331 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start()); |
| 332 Branch b1(this, p0, loop); |
| 333 Branch b2(this, p0, b1.if_true); |
| 334 Node* phi = |
| 335 graph()->NewNode(common()->Phi(kMachAnyTagged, 3), Int32Constant(0), |
| 336 Int32Constant(1), Int32Constant(2), loop); |
| 337 |
| 338 phi->ReplaceInput( |
| 339 1, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(1))); |
| 340 phi->ReplaceInput( |
| 341 2, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(2))); |
| 342 |
| 343 loop->ReplaceInput(1, b2.if_true); |
| 344 loop->ReplaceInput(2, b2.if_false); |
| 345 |
| 346 Node* r = InsertReturn(phi, start(), b1.if_false); |
| 347 |
| 348 PeeledIteration* peeled = PeelOne(); |
| 349 |
| 350 Node* b1b = ExpectPeeled(b1.branch, peeled); |
| 351 Node* b1t = ExpectPeeled(b1.if_true, peeled); |
| 352 Node* b1f = ExpectPeeled(b1.if_false, peeled); |
| 353 |
| 354 EXPECT_THAT(b1b, IsBranch(p0, start())); |
| 355 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b)); |
| 356 EXPECT_THAT(b1f, IsIfFalse(b1b)); |
| 357 |
| 358 Node* b2b = ExpectPeeled(b2.branch, peeled); |
| 359 Node* b2t = ExpectPeeled(b2.if_true, peeled); |
| 360 Node* b2f = ExpectPeeled(b2.if_false, peeled); |
| 361 |
| 362 EXPECT_THAT(b2b, IsBranch(p0, b1t)); |
| 363 EXPECT_THAT(b2t, IsIfTrue(b2b)); |
| 364 EXPECT_THAT(b2f, IsIfFalse(b2b)); |
| 365 |
| 366 Capture<Node*> entry; |
| 367 EXPECT_THAT(loop, IsLoop(AllOf(CaptureEq(&entry), IsMerge(b2t, b2f)), |
| 368 b2.if_true, b2.if_false)); |
| 369 |
| 370 Node* eval = phi->InputAt(0); |
| 371 |
| 372 EXPECT_THAT(eval, IsPhi(kMachAnyTagged, |
| 373 IsInt32Add(IsInt32Constant(0), IsInt32Constant(1)), |
| 374 IsInt32Add(IsInt32Constant(0), IsInt32Constant(2)), |
| 375 CaptureEq(&entry))); |
| 376 |
| 377 EXPECT_THAT(phi, |
| 378 IsPhi(kMachAnyTagged, eval, IsInt32Add(phi, IsInt32Constant(1)), |
| 379 IsInt32Add(phi, IsInt32Constant(2)), loop)); |
| 380 |
| 381 Capture<Node*> merge; |
| 382 EXPECT_THAT( |
| 383 r, IsReturn(IsPhi(kMachAnyTagged, phi, IsInt32Constant(0), |
| 384 AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))), |
| 385 start(), CaptureEq(&merge))); |
| 386 } |
| 387 |
| 388 |
| 389 } // namespace compiler |
| 390 } // namespace internal |
| 391 } // namespace v8 |
OLD | NEW |