Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- subzero/src/IceCfg.cpp - Control flow graph implementation ---------===// | 1 //===- subzero/src/IceCfg.cpp - Control flow graph implementation ---------===// |
| 2 // | 2 // |
| 3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
| 4 // | 4 // |
| 5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 /// | 9 /// |
| 10 /// \file | 10 /// \file |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 55 | 55 |
| 56 Cfg::~Cfg() { assert(ICE_TLS_GET_FIELD(CurrentCfg) == nullptr); } | 56 Cfg::~Cfg() { assert(ICE_TLS_GET_FIELD(CurrentCfg) == nullptr); } |
| 57 | 57 |
| 58 void Cfg::setError(const IceString &Message) { | 58 void Cfg::setError(const IceString &Message) { |
| 59 HasError = true; | 59 HasError = true; |
| 60 ErrorMessage = Message; | 60 ErrorMessage = Message; |
| 61 } | 61 } |
| 62 | 62 |
| 63 CfgNode *Cfg::makeNode() { | 63 CfgNode *Cfg::makeNode() { |
| 64 SizeT LabelIndex = Nodes.size(); | 64 SizeT LabelIndex = Nodes.size(); |
| 65 CfgNode *Node = CfgNode::create(this, LabelIndex); | 65 CfgNode *Node = CfgNode::create(this, LabelIndex); |
|
Jim Stichnoth
2015/09/01 22:17:37
May want to document here that LoopNestDepth may n
ascull
2015/09/03 19:52:37
Done.
| |
| 66 Nodes.push_back(Node); | 66 Nodes.push_back(Node); |
| 67 return Node; | 67 return Node; |
| 68 } | 68 } |
| 69 | 69 |
| 70 void Cfg::swapNodes(NodeList &NewNodes) { | 70 void Cfg::swapNodes(NodeList &NewNodes) { |
| 71 Nodes.swap(NewNodes); | 71 Nodes.swap(NewNodes); |
| 72 for (SizeT I = 0, NumNodes = getNumNodes(); I < NumNodes; ++I) | 72 for (SizeT I = 0, NumNodes = getNumNodes(); I < NumNodes; ++I) |
| 73 Nodes[I]->resetIndex(I); | 73 Nodes[I]->resetIndex(I); |
| 74 } | 74 } |
| 75 | 75 |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 455 | 455 |
| 456 // Compute the stack frame layout. | 456 // Compute the stack frame layout. |
| 457 void Cfg::genFrame() { | 457 void Cfg::genFrame() { |
| 458 TimerMarker T(TimerStack::TT_genFrame, this); | 458 TimerMarker T(TimerStack::TT_genFrame, this); |
| 459 getTarget()->addProlog(Entry); | 459 getTarget()->addProlog(Entry); |
| 460 for (CfgNode *Node : Nodes) | 460 for (CfgNode *Node : Nodes) |
| 461 if (Node->getHasReturn()) | 461 if (Node->getHasReturn()) |
| 462 getTarget()->addEpilog(Node); | 462 getTarget()->addEpilog(Node); |
| 463 } | 463 } |
| 464 | 464 |
| 465 // This is a lightweight version of live-range-end calculation. Marks | 465 // Use Tarjn's strongly connected components algorithm to identify outermost to |
|
jvoung (off chromium)
2015/09/01 18:56:18
Tarjan's
ascull
2015/09/03 19:52:37
Done.
| |
| 466 // the last use of only those variables whose definition and uses are | 466 // innermost loops. By deleting the head of the loop from the graph, inner |
| 467 // completely with a single block. It is a quick single pass and | 467 // loops can be found. This assumes that the head node is not shared between |
| 468 // doesn't need to iterate until convergence. | 468 // loops but instead all paths to the head come from 'continue' constructs. |
| 469 // | |
| 470 // This only computes the loop nest depth within the function and does not take | |
| 471 // into account whether the function was called from within a loop. | |
| 472 void Cfg::computeLoopNestDepth() { | |
|
Jim Stichnoth
2015/09/01 22:17:37
Add a TimerMarker here (and add a new marker type
ascull
2015/09/03 19:52:37
Done.
| |
| 473 using index_t = uint32_t; | |
|
Jim Stichnoth
2015/09/01 22:17:37
Type names should be capitalized per LLVM coding s
ascull
2015/09/03 19:52:37
Done.
| |
| 474 constexpr index_t UndefinedIndex = 0; | |
| 475 | |
| 476 struct LoopNode { | |
|
Jim Stichnoth
2015/09/01 22:17:37
I think if you are defining methods, you should ca
ascull
2015/09/03 19:52:37
Done.
| |
| 477 LoopNode() = delete; | |
| 478 LoopNode operator=(const LoopNode &) = delete; | |
| 479 | |
| 480 explicit LoopNode(CfgNode *BB) : BB(BB) {} | |
| 481 LoopNode(const LoopNode &) = default; | |
| 482 | |
| 483 void reset() { | |
| 484 Succ = BB->getOutEdges().begin(); | |
| 485 Index = LowLink = UndefinedIndex; | |
| 486 OnStack = false; | |
| 487 } | |
| 488 | |
| 489 void visit(index_t VisitIndex) { Index = LowLink = VisitIndex; } | |
| 490 bool isVisited() const { return Index != UndefinedIndex; } | |
| 491 | |
| 492 void setDeleted() { deleted = true; } | |
| 493 bool isDeleted() const { return deleted; } | |
| 494 | |
| 495 CfgNode *BB; | |
| 496 NodeList::const_iterator Succ; | |
|
Jim Stichnoth
2015/09/01 22:17:37
Initialize members in the ctor or in the same way
ascull
2015/09/03 19:52:37
Done.
| |
| 497 index_t Index; | |
| 498 index_t LowLink; | |
| 499 bool OnStack; | |
| 500 bool deleted = false; | |
|
jvoung (off chromium)
2015/09/01 18:56:17
capitalize deleted to follow code style
ascull
2015/09/03 19:52:37
Done.
| |
| 501 }; | |
| 502 | |
| 503 // Create a collection of decorated nodes in the same order as Nodes which | |
| 504 // means the node's index will be valid. | |
| 505 std::vector<LoopNode, CfgLocalAllocator<LoopNode>> AllNodes; | |
| 506 // This is used as a replacement for the call stack | |
| 507 std::vector<LoopNode *, CfgLocalAllocator<LoopNode *>> WorkStack; | |
| 508 // Track which loop a node belongs to | |
| 509 std::vector<LoopNode *, CfgLocalAllocator<LoopNode *>> LoopStack; | |
| 510 | |
| 511 // Allocate memory ahead of time. This is why a vector is used instead of a | |
| 512 // stack which doesn not support reserving (or bulk erasure used below). | |
|
jvoung (off chromium)
2015/09/01 18:56:18
doesn -> doesn't
ascull
2015/09/03 19:52:37
Done.
| |
| 513 AllNodes.reserve(Nodes.size()); | |
| 514 WorkStack.reserve(Nodes.size()); | |
| 515 LoopStack.reserve(Nodes.size()); | |
| 516 | |
| 517 // Load in initial data | |
| 518 for (CfgNode *Node : Nodes) | |
| 519 AllNodes.emplace_back(Node); | |
| 520 | |
| 521 size_t NumDeletedNodes = 0; | |
|
Jim Stichnoth
2015/09/01 22:17:37
SizeT
or, if you object to that,
std::vector<Loop
ascull
2015/09/03 19:52:37
Done.
| |
| 522 | |
| 523 while (NumDeletedNodes < AllNodes.size()) { | |
| 524 // Prepare to run Tarjan's | |
| 525 index_t NextIndex = 1; | |
|
jvoung (off chromium)
2015/09/01 18:56:18
nit: How about UndefinedIndex + 1, to ensure the i
ascull
2015/09/03 19:52:37
Done.
| |
| 526 for (LoopNode &Node : AllNodes) | |
| 527 Node.reset(); | |
|
jvoung (off chromium)
2015/09/01 18:56:18
Wonder if you should add an Ice timer for this who
ascull
2015/09/03 19:52:37
Done.
| |
| 528 | |
| 529 assert(WorkStack.empty()); | |
| 530 assert(LoopStack.empty()); | |
| 531 | |
| 532 for (LoopNode &Node : AllNodes) { | |
| 533 if (Node.isDeleted() || Node.isVisited()) | |
| 534 continue; | |
| 535 | |
| 536 WorkStack.push_back(&Node); | |
| 537 | |
| 538 continue_work_loop: | |
| 539 while (!WorkStack.empty()) { | |
| 540 LoopNode &WorkNode = *WorkStack.back(); | |
| 541 WorkStack.pop_back(); | |
| 542 | |
| 543 if (!WorkNode.isVisited()) { | |
| 544 WorkNode.visit(NextIndex++); | |
| 545 LoopStack.push_back(&WorkNode); | |
| 546 WorkNode.OnStack = true; | |
| 547 } else { | |
| 548 // Returning to a node after having recursed into Succ so continue | |
| 549 // iterating through successors after using the Succ.LowLink value | |
| 550 // that was computed in the recursion. | |
| 551 LoopNode &Succ = AllNodes[(*WorkNode.Succ)->getIndex()]; | |
| 552 WorkNode.LowLink = std::min(WorkNode.LowLink, Succ.LowLink); | |
| 553 ++WorkNode.Succ; | |
| 554 } | |
| 555 | |
| 556 // Visit the successors and recurse into unvisited nodes. The recursion | |
| 557 // could cause the iteration to be suspended but it will resume as the | |
| 558 // stack is unwound. | |
| 559 auto SuccEnd = WorkNode.BB->getOutEdges().end(); | |
| 560 for (; WorkNode.Succ != SuccEnd; ++WorkNode.Succ) { | |
| 561 LoopNode &Succ = AllNodes[(*WorkNode.Succ)->getIndex()]; | |
| 562 | |
| 563 if (Succ.isDeleted()) | |
| 564 continue; | |
| 565 | |
| 566 if (!Succ.isVisited()) { | |
| 567 WorkStack.insert(WorkStack.end(), {&WorkNode, &Succ}); | |
| 568 goto continue_work_loop; // continue outer loop | |
|
jvoung (off chromium)
2015/09/01 18:56:18
You might be able to get away without a goto, if y
ascull
2015/09/03 19:52:37
I refactored the loop stuff out into it's own clas
| |
| 569 } else if (Succ.OnStack) { | |
| 570 WorkNode.LowLink = std::min(WorkNode.LowLink, Succ.Index); | |
| 571 } | |
| 572 } | |
| 573 | |
| 574 // Check for a loop being closed | |
| 575 if (WorkNode.LowLink == WorkNode.Index) { | |
| 576 // Single node loops mean no loop in a pure CFG. After lowering there | |
| 577 // may be reentrancy but we ignore that here. | |
|
jvoung (off chromium)
2015/09/01 18:56:18
What do you mean by reentrancy here? Branches from
ascull
2015/09/03 19:52:37
That is what I meant, although it isn't a useful c
| |
| 578 if (LoopStack.back() == &WorkNode) { | |
| 579 LoopStack.back()->OnStack = false; | |
| 580 LoopStack.back()->setDeleted(); | |
| 581 ++NumDeletedNodes; | |
| 582 LoopStack.pop_back(); | |
| 583 continue; | |
| 584 } | |
| 585 | |
| 586 // Reaching here means a loop has been found! It consists of the | |
| 587 // nodes on the top of the stack, down until the current node being | |
| 588 // processed, WorkNode, is found. | |
| 589 for (auto It = LoopStack.rbegin(); It != LoopStack.rend(); ++It) { | |
| 590 (*It)->OnStack = false; | |
| 591 (*It)->BB->incrementLoopNestDepth(); | |
| 592 // Remove the loop from the stack and delete the head node | |
| 593 if (*It == &WorkNode) { | |
| 594 (*It)->setDeleted(); | |
| 595 ++NumDeletedNodes; | |
| 596 LoopStack.erase(It.base() - 1, LoopStack.end()); | |
| 597 break; | |
| 598 } | |
| 599 } | |
| 600 } | |
| 601 } | |
| 602 } | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 // This is a lightweight version of live-range-end calculation. Marks the last | |
| 607 // use of only those variables whose definition and uses are completely with a | |
| 608 // single block. It is a quick single pass and doesn't need to iterate until | |
| 609 // convergence. | |
| 469 void Cfg::livenessLightweight() { | 610 void Cfg::livenessLightweight() { |
| 470 TimerMarker T(TimerStack::TT_livenessLightweight, this); | 611 TimerMarker T(TimerStack::TT_livenessLightweight, this); |
| 471 getVMetadata()->init(VMK_Uses); | 612 getVMetadata()->init(VMK_Uses); |
| 472 for (CfgNode *Node : Nodes) | 613 for (CfgNode *Node : Nodes) |
| 473 Node->livenessLightweight(); | 614 Node->livenessLightweight(); |
| 474 } | 615 } |
| 475 | 616 |
| 476 void Cfg::liveness(LivenessMode Mode) { | 617 void Cfg::liveness(LivenessMode Mode) { |
| 477 TimerMarker T(TimerStack::TT_liveness, this); | 618 TimerMarker T(TimerStack::TT_liveness, this); |
| 478 Live.reset(new Liveness(this, Mode)); | 619 Live.reset(new Liveness(this, Mode)); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 599 Str << " live range " << Var->getLiveRange() << "\n"; | 740 Str << " live range " << Var->getLiveRange() << "\n"; |
| 600 } | 741 } |
| 601 } | 742 } |
| 602 } | 743 } |
| 603 } | 744 } |
| 604 } | 745 } |
| 605 return Valid; | 746 return Valid; |
| 606 } | 747 } |
| 607 | 748 |
| 608 void Cfg::contractEmptyNodes() { | 749 void Cfg::contractEmptyNodes() { |
| 609 // If we're decorating the asm output with register liveness info, | 750 // If we're decorating the asm output with register liveness info, this |
| 610 // this information may become corrupted or incorrect after | 751 // information may become corrupted or incorrect after contracting nodes that |
| 611 // contracting nodes that contain only redundant assignments. As | 752 // contain only redundant assignments. As such, we disable this pass when |
| 612 // such, we disable this pass when DecorateAsm is specified. This | 753 // DecorateAsm is specified. This may make the resulting code look more |
| 613 // may make the resulting code look more branchy, but it should have | 754 // branchy, but it should have no effect on the register assignments. |
| 614 // no effect on the register assignments. | |
| 615 if (Ctx->getFlags().getDecorateAsm()) | 755 if (Ctx->getFlags().getDecorateAsm()) |
| 616 return; | 756 return; |
| 617 for (CfgNode *Node : Nodes) { | 757 for (CfgNode *Node : Nodes) { |
| 618 Node->contractIfEmpty(); | 758 Node->contractIfEmpty(); |
| 619 } | 759 } |
| 620 } | 760 } |
| 621 | 761 |
| 622 void Cfg::doBranchOpt() { | 762 void Cfg::doBranchOpt() { |
| 623 TimerMarker T(TimerStack::TT_doBranchOpt, this); | 763 TimerMarker T(TimerStack::TT_doBranchOpt, this); |
| 624 for (auto I = Nodes.begin(), E = Nodes.end(); I != E; ++I) { | 764 for (auto I = Nodes.begin(), E = Nodes.end(); I != E; ++I) { |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 771 } | 911 } |
| 772 } | 912 } |
| 773 // Print each basic block | 913 // Print each basic block |
| 774 for (CfgNode *Node : Nodes) | 914 for (CfgNode *Node : Nodes) |
| 775 Node->dump(this); | 915 Node->dump(this); |
| 776 if (isVerbose(IceV_Instructions)) | 916 if (isVerbose(IceV_Instructions)) |
| 777 Str << "}\n"; | 917 Str << "}\n"; |
| 778 } | 918 } |
| 779 | 919 |
| 780 } // end of namespace Ice | 920 } // end of namespace Ice |
| OLD | NEW |