109467b48Spatrick //===--- RDFDeadCode.cpp --------------------------------------------------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // RDF-based generic dead code elimination.
1009467b48Spatrick
1109467b48Spatrick #include "RDFDeadCode.h"
1209467b48Spatrick
1309467b48Spatrick #include "llvm/ADT/SetVector.h"
1409467b48Spatrick #include "llvm/CodeGen/MachineBasicBlock.h"
1509467b48Spatrick #include "llvm/CodeGen/MachineFunction.h"
1609467b48Spatrick #include "llvm/CodeGen/MachineRegisterInfo.h"
177299aa8dSpatrick #include "llvm/CodeGen/RDFGraph.h"
187299aa8dSpatrick #include "llvm/CodeGen/RDFLiveness.h"
1909467b48Spatrick #include "llvm/Support/Debug.h"
2009467b48Spatrick
2109467b48Spatrick #include <queue>
2209467b48Spatrick
2309467b48Spatrick using namespace llvm;
2409467b48Spatrick using namespace rdf;
2509467b48Spatrick
2609467b48Spatrick // This drastically improves execution time in "collect" over using
2709467b48Spatrick // SetVector as a work queue, and popping the first element from it.
2809467b48Spatrick template<typename T> struct DeadCodeElimination::SetQueue {
SetQueueDeadCodeElimination::SetQueue2909467b48Spatrick SetQueue() : Set(), Queue() {}
3009467b48Spatrick
emptyDeadCodeElimination::SetQueue3109467b48Spatrick bool empty() const {
3209467b48Spatrick return Queue.empty();
3309467b48Spatrick }
pop_frontDeadCodeElimination::SetQueue3409467b48Spatrick T pop_front() {
3509467b48Spatrick T V = Queue.front();
3609467b48Spatrick Queue.pop();
3709467b48Spatrick Set.erase(V);
3809467b48Spatrick return V;
3909467b48Spatrick }
push_backDeadCodeElimination::SetQueue4009467b48Spatrick void push_back(T V) {
4109467b48Spatrick if (Set.count(V))
4209467b48Spatrick return;
4309467b48Spatrick Queue.push(V);
4409467b48Spatrick Set.insert(V);
4509467b48Spatrick }
4609467b48Spatrick
4709467b48Spatrick private:
4809467b48Spatrick DenseSet<T> Set;
4909467b48Spatrick std::queue<T> Queue;
5009467b48Spatrick };
5109467b48Spatrick
5209467b48Spatrick
5309467b48Spatrick // Check if the given instruction has observable side-effects, i.e. if
5409467b48Spatrick // it should be considered "live". It is safe for this function to be
5509467b48Spatrick // overly conservative (i.e. return "true" for all instructions), but it
5609467b48Spatrick // is not safe to return "false" for an instruction that should not be
5709467b48Spatrick // considered removable.
isLiveInstr(const MachineInstr * MI) const5809467b48Spatrick bool DeadCodeElimination::isLiveInstr(const MachineInstr *MI) const {
5909467b48Spatrick if (MI->mayStore() || MI->isBranch() || MI->isCall() || MI->isReturn())
6009467b48Spatrick return true;
6109467b48Spatrick if (MI->hasOrderedMemoryRef() || MI->hasUnmodeledSideEffects() ||
6209467b48Spatrick MI->isPosition())
6309467b48Spatrick return true;
6409467b48Spatrick if (MI->isPHI())
6509467b48Spatrick return false;
6609467b48Spatrick for (auto &Op : MI->operands()) {
6709467b48Spatrick if (Op.isReg() && MRI.isReserved(Op.getReg()))
6809467b48Spatrick return true;
6909467b48Spatrick if (Op.isRegMask()) {
7009467b48Spatrick const uint32_t *BM = Op.getRegMask();
7109467b48Spatrick for (unsigned R = 0, RN = DFG.getTRI().getNumRegs(); R != RN; ++R) {
7209467b48Spatrick if (BM[R/32] & (1u << (R%32)))
7309467b48Spatrick continue;
7409467b48Spatrick if (MRI.isReserved(R))
7509467b48Spatrick return true;
7609467b48Spatrick }
7709467b48Spatrick }
7809467b48Spatrick }
7909467b48Spatrick return false;
8009467b48Spatrick }
8109467b48Spatrick
scanInstr(NodeAddr<InstrNode * > IA,SetQueue<NodeId> & WorkQ)8209467b48Spatrick void DeadCodeElimination::scanInstr(NodeAddr<InstrNode*> IA,
8309467b48Spatrick SetQueue<NodeId> &WorkQ) {
8409467b48Spatrick if (!DFG.IsCode<NodeAttrs::Stmt>(IA))
8509467b48Spatrick return;
8609467b48Spatrick if (!isLiveInstr(NodeAddr<StmtNode*>(IA).Addr->getCode()))
8709467b48Spatrick return;
8809467b48Spatrick for (NodeAddr<RefNode*> RA : IA.Addr->members(DFG)) {
8909467b48Spatrick if (!LiveNodes.count(RA.Id))
9009467b48Spatrick WorkQ.push_back(RA.Id);
9109467b48Spatrick }
9209467b48Spatrick }
9309467b48Spatrick
processDef(NodeAddr<DefNode * > DA,SetQueue<NodeId> & WorkQ)9409467b48Spatrick void DeadCodeElimination::processDef(NodeAddr<DefNode*> DA,
9509467b48Spatrick SetQueue<NodeId> &WorkQ) {
9609467b48Spatrick NodeAddr<InstrNode*> IA = DA.Addr->getOwner(DFG);
9709467b48Spatrick for (NodeAddr<UseNode*> UA : IA.Addr->members_if(DFG.IsUse, DFG)) {
9809467b48Spatrick if (!LiveNodes.count(UA.Id))
9909467b48Spatrick WorkQ.push_back(UA.Id);
10009467b48Spatrick }
10109467b48Spatrick for (NodeAddr<DefNode*> TA : DFG.getRelatedRefs(IA, DA))
10209467b48Spatrick LiveNodes.insert(TA.Id);
10309467b48Spatrick }
10409467b48Spatrick
processUse(NodeAddr<UseNode * > UA,SetQueue<NodeId> & WorkQ)10509467b48Spatrick void DeadCodeElimination::processUse(NodeAddr<UseNode*> UA,
10609467b48Spatrick SetQueue<NodeId> &WorkQ) {
10709467b48Spatrick for (NodeAddr<DefNode*> DA : LV.getAllReachingDefs(UA)) {
10809467b48Spatrick if (!LiveNodes.count(DA.Id))
10909467b48Spatrick WorkQ.push_back(DA.Id);
11009467b48Spatrick }
11109467b48Spatrick }
11209467b48Spatrick
11309467b48Spatrick // Traverse the DFG and collect the set dead RefNodes and the set of
11409467b48Spatrick // dead instructions. Return "true" if any of these sets is non-empty,
11509467b48Spatrick // "false" otherwise.
collect()11609467b48Spatrick bool DeadCodeElimination::collect() {
11709467b48Spatrick // This function works by first finding all live nodes. The dead nodes
11809467b48Spatrick // are then the complement of the set of live nodes.
11909467b48Spatrick //
12009467b48Spatrick // Assume that all nodes are dead. Identify instructions which must be
12109467b48Spatrick // considered live, i.e. instructions with observable side-effects, such
12209467b48Spatrick // as calls and stores. All arguments of such instructions are considered
12309467b48Spatrick // live. For each live def, all operands used in the corresponding
12409467b48Spatrick // instruction are considered live. For each live use, all its reaching
12509467b48Spatrick // defs are considered live.
12609467b48Spatrick LiveNodes.clear();
12709467b48Spatrick SetQueue<NodeId> WorkQ;
12809467b48Spatrick for (NodeAddr<BlockNode*> BA : DFG.getFunc().Addr->members(DFG))
12909467b48Spatrick for (NodeAddr<InstrNode*> IA : BA.Addr->members(DFG))
13009467b48Spatrick scanInstr(IA, WorkQ);
13109467b48Spatrick
13209467b48Spatrick while (!WorkQ.empty()) {
13309467b48Spatrick NodeId N = WorkQ.pop_front();
13409467b48Spatrick LiveNodes.insert(N);
13509467b48Spatrick auto RA = DFG.addr<RefNode*>(N);
13609467b48Spatrick if (DFG.IsDef(RA))
13709467b48Spatrick processDef(RA, WorkQ);
13809467b48Spatrick else
13909467b48Spatrick processUse(RA, WorkQ);
14009467b48Spatrick }
14109467b48Spatrick
14209467b48Spatrick if (trace()) {
14309467b48Spatrick dbgs() << "Live nodes:\n";
14409467b48Spatrick for (NodeId N : LiveNodes) {
14509467b48Spatrick auto RA = DFG.addr<RefNode*>(N);
14609467b48Spatrick dbgs() << PrintNode<RefNode*>(RA, DFG) << "\n";
14709467b48Spatrick }
14809467b48Spatrick }
14909467b48Spatrick
15009467b48Spatrick auto IsDead = [this] (NodeAddr<InstrNode*> IA) -> bool {
15109467b48Spatrick for (NodeAddr<DefNode*> DA : IA.Addr->members_if(DFG.IsDef, DFG))
15209467b48Spatrick if (LiveNodes.count(DA.Id))
15309467b48Spatrick return false;
15409467b48Spatrick return true;
15509467b48Spatrick };
15609467b48Spatrick
15709467b48Spatrick for (NodeAddr<BlockNode*> BA : DFG.getFunc().Addr->members(DFG)) {
15809467b48Spatrick for (NodeAddr<InstrNode*> IA : BA.Addr->members(DFG)) {
15909467b48Spatrick for (NodeAddr<RefNode*> RA : IA.Addr->members(DFG))
16009467b48Spatrick if (!LiveNodes.count(RA.Id))
16109467b48Spatrick DeadNodes.insert(RA.Id);
16209467b48Spatrick if (DFG.IsCode<NodeAttrs::Stmt>(IA))
16309467b48Spatrick if (isLiveInstr(NodeAddr<StmtNode*>(IA).Addr->getCode()))
16409467b48Spatrick continue;
16509467b48Spatrick if (IsDead(IA)) {
16609467b48Spatrick DeadInstrs.insert(IA.Id);
16709467b48Spatrick if (trace())
16809467b48Spatrick dbgs() << "Dead instr: " << PrintNode<InstrNode*>(IA, DFG) << "\n";
16909467b48Spatrick }
17009467b48Spatrick }
17109467b48Spatrick }
17209467b48Spatrick
17309467b48Spatrick return !DeadNodes.empty();
17409467b48Spatrick }
17509467b48Spatrick
17609467b48Spatrick // Erase the nodes given in the Nodes set from DFG. In addition to removing
17709467b48Spatrick // them from the DFG, if a node corresponds to a statement, the corresponding
17809467b48Spatrick // machine instruction is erased from the function.
erase(const SetVector<NodeId> & Nodes)17909467b48Spatrick bool DeadCodeElimination::erase(const SetVector<NodeId> &Nodes) {
18009467b48Spatrick if (Nodes.empty())
18109467b48Spatrick return false;
18209467b48Spatrick
18309467b48Spatrick // Prepare the actual set of ref nodes to remove: ref nodes from Nodes
18409467b48Spatrick // are included directly, for each InstrNode in Nodes, include the set
18509467b48Spatrick // of all RefNodes from it.
18609467b48Spatrick NodeList DRNs, DINs;
18709467b48Spatrick for (auto I : Nodes) {
18809467b48Spatrick auto BA = DFG.addr<NodeBase*>(I);
18909467b48Spatrick uint16_t Type = BA.Addr->getType();
19009467b48Spatrick if (Type == NodeAttrs::Ref) {
19109467b48Spatrick DRNs.push_back(DFG.addr<RefNode*>(I));
19209467b48Spatrick continue;
19309467b48Spatrick }
19409467b48Spatrick
19509467b48Spatrick // If it's a code node, add all ref nodes from it.
19609467b48Spatrick uint16_t Kind = BA.Addr->getKind();
19709467b48Spatrick if (Kind == NodeAttrs::Stmt || Kind == NodeAttrs::Phi) {
198*73471bf0Spatrick append_range(DRNs, NodeAddr<CodeNode*>(BA).Addr->members(DFG));
19909467b48Spatrick DINs.push_back(DFG.addr<InstrNode*>(I));
20009467b48Spatrick } else {
20109467b48Spatrick llvm_unreachable("Unexpected code node");
20209467b48Spatrick return false;
20309467b48Spatrick }
20409467b48Spatrick }
20509467b48Spatrick
20609467b48Spatrick // Sort the list so that use nodes are removed first. This makes the
20709467b48Spatrick // "unlink" functions a bit faster.
20809467b48Spatrick auto UsesFirst = [] (NodeAddr<RefNode*> A, NodeAddr<RefNode*> B) -> bool {
20909467b48Spatrick uint16_t KindA = A.Addr->getKind(), KindB = B.Addr->getKind();
21009467b48Spatrick if (KindA == NodeAttrs::Use && KindB == NodeAttrs::Def)
21109467b48Spatrick return true;
21209467b48Spatrick if (KindA == NodeAttrs::Def && KindB == NodeAttrs::Use)
21309467b48Spatrick return false;
21409467b48Spatrick return A.Id < B.Id;
21509467b48Spatrick };
21609467b48Spatrick llvm::sort(DRNs, UsesFirst);
21709467b48Spatrick
21809467b48Spatrick if (trace())
21909467b48Spatrick dbgs() << "Removing dead ref nodes:\n";
22009467b48Spatrick for (NodeAddr<RefNode*> RA : DRNs) {
22109467b48Spatrick if (trace())
22209467b48Spatrick dbgs() << " " << PrintNode<RefNode*>(RA, DFG) << '\n';
22309467b48Spatrick if (DFG.IsUse(RA))
22409467b48Spatrick DFG.unlinkUse(RA, true);
22509467b48Spatrick else if (DFG.IsDef(RA))
22609467b48Spatrick DFG.unlinkDef(RA, true);
22709467b48Spatrick }
22809467b48Spatrick
22909467b48Spatrick // Now, remove all dead instruction nodes.
23009467b48Spatrick for (NodeAddr<InstrNode*> IA : DINs) {
23109467b48Spatrick NodeAddr<BlockNode*> BA = IA.Addr->getOwner(DFG);
23209467b48Spatrick BA.Addr->removeMember(IA, DFG);
23309467b48Spatrick if (!DFG.IsCode<NodeAttrs::Stmt>(IA))
23409467b48Spatrick continue;
23509467b48Spatrick
23609467b48Spatrick MachineInstr *MI = NodeAddr<StmtNode*>(IA).Addr->getCode();
23709467b48Spatrick if (trace())
23809467b48Spatrick dbgs() << "erasing: " << *MI;
23909467b48Spatrick MI->eraseFromParent();
24009467b48Spatrick }
24109467b48Spatrick return true;
24209467b48Spatrick }
243