//===- subzero/src/IceCfgNode.cpp - Basic block (node) implementation -----===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Implements the CfgNode class, including the complexities of /// instruction insertion and in-edge calculation. /// //===----------------------------------------------------------------------===// #include "IceCfgNode.h" #include "IceAssembler.h" #include "IceCfg.h" #include "IceGlobalInits.h" #include "IceInst.h" #include "IceInstVarIter.h" #include "IceLiveness.h" #include "IceOperand.h" #include "IceTargetLowering.h" namespace Ice { // Adds an instruction to either the Phi list or the regular instruction list. // Validates that all Phis are added before all regular instructions. void CfgNode::appendInst(Inst *Instr) { ++InstCountEstimate; if (BuildDefs::wasm()) { if (llvm::isa(Instr) || llvm::isa(Instr)) { for (auto *N : Instr->getTerminatorEdges()) { N->addInEdge(this); addOutEdge(N); } } } if (auto *Phi = llvm::dyn_cast(Instr)) { if (!Insts.empty()) { Func->setError("Phi instruction added to the middle of a block"); return; } Phis.push_back(Phi); } else { Insts.push_back(Instr); } } void CfgNode::replaceInEdge(CfgNode *Old, CfgNode *New) { for (SizeT i = 0; i < InEdges.size(); ++i) { if (InEdges[i] == Old) { InEdges[i] = New; } } for (auto &Inst : getPhis()) { auto &Phi = llvm::cast(Inst); for (SizeT i = 0; i < Phi.getSrcSize(); ++i) { if (Phi.getLabel(i) == Old) { Phi.setLabel(i, New); } } } } namespace { template void removeDeletedAndRenumber(List *L, Cfg *Func) { const bool DoDelete = BuildDefs::minimal() || !getFlags().getKeepDeletedInsts(); auto I = L->begin(), E = L->end(), Next = I; for (++Next; I != E; I = Next++) { if (DoDelete && I->isDeleted()) { L->remove(I); } else { I->renumber(Func); } } } } // end of anonymous namespace void CfgNode::renumberInstructions() { InstNumberT FirstNumber = Func->getNextInstNumber(); removeDeletedAndRenumber(&Phis, Func); removeDeletedAndRenumber(&Insts, Func); InstCountEstimate = Func->getNextInstNumber() - FirstNumber; } // When a node is created, the OutEdges are immediately known, but the InEdges // have to be built up incrementally. After the CFG has been constructed, the // computePredecessors() pass finalizes it by creating the InEdges list. void CfgNode::computePredecessors() { for (CfgNode *Succ : OutEdges) Succ->InEdges.push_back(this); } void CfgNode::computeSuccessors() { OutEdges.clear(); InEdges.clear(); assert(!Insts.empty()); OutEdges = Insts.rbegin()->getTerminatorEdges(); } // Ensure each Phi instruction in the node is consistent with respect to control // flow. For each predecessor, there must be a phi argument with that label. // If a phi argument's label doesn't appear in the predecessor list (which can // happen as a result of e.g. unreachable node elimination), its value is // modified to be zero, to maintain consistency in liveness analysis. This // allows us to remove some dead control flow without a major rework of the phi // instructions. We don't check that phi arguments with the same label have the // same value. void CfgNode::enforcePhiConsistency() { for (Inst &Instr : Phis) { auto *Phi = llvm::cast(&Instr); // We do a simple O(N^2) algorithm to check for consistency. Even so, it // shows up as only about 0.2% of the total translation time. But if // necessary, we could improve the complexity by using a hash table to // count how many times each node is referenced in the Phi instruction, and // how many times each node is referenced in the incoming edge list, and // compare the two for equality. for (SizeT i = 0; i < Phi->getSrcSize(); ++i) { CfgNode *Label = Phi->getLabel(i); bool Found = false; for (CfgNode *InNode : getInEdges()) { if (InNode == Label) { Found = true; break; } } if (!Found) { // Predecessor was unreachable, so if (impossibly) the control flow // enters from that predecessor, the value should be zero. Phi->clearOperandForTarget(Label); } } for (CfgNode *InNode : getInEdges()) { bool Found = false; for (SizeT i = 0; i < Phi->getSrcSize(); ++i) { CfgNode *Label = Phi->getLabel(i); if (InNode == Label) { Found = true; break; } } if (!Found) llvm::report_fatal_error("Phi error: missing label for incoming edge"); } } } // This does part 1 of Phi lowering, by creating a new dest variable for each // Phi instruction, replacing the Phi instruction's dest with that variable, // and adding an explicit assignment of the old dest to the new dest. For // example, // a=phi(...) // changes to // "a_phi=phi(...); a=a_phi". // // This is in preparation for part 2 which deletes the Phi instructions and // appends assignment instructions to predecessor blocks. Note that this // transformation preserves SSA form. void CfgNode::placePhiLoads() { for (Inst &I : Phis) { auto *Phi = llvm::dyn_cast(&I); Insts.insert(Insts.begin(), Phi->lower(Func)); } } // This does part 2 of Phi lowering. For each Phi instruction at each out-edge, // create a corresponding assignment instruction, and add all the assignments // near the end of this block. They need to be added before any branch // instruction, and also if the block ends with a compare instruction followed // by a branch instruction that we may want to fuse, it's better to insert the // new assignments before the compare instruction. The // tryOptimizedCmpxchgCmpBr() method assumes this ordering of instructions. // // Note that this transformation takes the Phi dest variables out of SSA form, // as there may be assignments to the dest variable in multiple blocks. void CfgNode::placePhiStores() { // Find the insertion point. InstList::iterator InsertionPoint = Insts.end(); // Every block must end in a terminator instruction, and therefore must have // at least one instruction, so it's valid to decrement InsertionPoint (but // assert just in case). assert(InsertionPoint != Insts.begin()); --InsertionPoint; // Confirm that InsertionPoint is a terminator instruction. Calling // getTerminatorEdges() on a non-terminator instruction will cause an // llvm_unreachable(). (void)InsertionPoint->getTerminatorEdges(); // SafeInsertionPoint is always immediately before the terminator // instruction. If the block ends in a compare and conditional branch, it's // better to place the Phi store before the compare so as not to interfere // with compare/branch fusing. However, if the compare instruction's dest // operand is the same as the new assignment statement's source operand, this // can't be done due to data dependences, so we need to fall back to the // SafeInsertionPoint. To illustrate: // ;