Coverage Report

Created: 2025-06-24 06:43

/src/hermes/lib/Optimizer/Scalar/DCE.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#define DEBUG_TYPE "dce"
9
10
#include "hermes/Optimizer/Scalar/DCE.h"
11
12
#include "hermes/IR/Analysis.h"
13
#include "hermes/IR/CFG.h"
14
#include "hermes/IR/IRBuilder.h"
15
#include "hermes/IR/Instrs.h"
16
#include "hermes/Support/Statistic.h"
17
18
#include "llvh/ADT/DenseMap.h"
19
#include "llvh/ADT/DenseSet.h"
20
#include "llvh/Support/Debug.h"
21
22
using namespace hermes;
23
using llvh::dbgs;
24
25
STATISTIC(NumDCE, "Number of instructions DCE'd");
26
STATISTIC(NumFuncDCE, "Number of functions DCE'd");
27
28
0
static bool performFunctionDCE(Function *F) {
29
0
  bool changed = false;
30
0
  PostOrderAnalysis PO(F);
31
32
  // Scan the function in post order (from end to start). We want to visit the
33
  // uses of the instruction before we visit the instruction itself in order to
34
  // allow the optimization to delete long chains of dead code.
35
0
  for (auto *BB : PO) {
36
    // Scan the instructions in the block from end to start.
37
0
    for (auto it = BB->rbegin(), e = BB->rend(); it != e; /* nothing */) {
38
0
      Instruction *I = &*it;
39
      // Move the iterator to the next instruction in the block. This will
40
      // allow us to delete the current instruction.
41
0
      ++it;
42
43
      // If the instruction writes to memory then we can't remove it. Notice
44
      // that it is okay to delete instructions that only read memory and are
45
      // unused.
46
      //
47
      // Terminators don't have any uses but are never supposed to be removed
48
      // as dead code.
49
      //
50
      // CreateScopeInst may not have any users, but it is lowered to
51
      // HBCCreateEnvironmentInst which should always be emitted and DCE'd if
52
      // appropriate.
53
      //
54
      // HasRestrictedGlobalPropertyInst doesn't have a result but should never
55
      // be removed as they perform runtime validation.
56
0
      if (I->mayWriteMemory() || llvh::isa<TerminatorInst>(I) ||
57
0
          llvh::isa<CreateScopeInst>(I) ||
58
0
          llvh::isa<ThrowIfHasRestrictedGlobalPropertyInst>(I)) {
59
0
        continue;
60
0
      }
61
62
      // If some other instruction is using the result of this instruction then
63
      // we can't delete it.
64
0
      if (I->getNumUsers())
65
0
        continue;
66
67
0
      LLVM_DEBUG(
68
0
          dbgs() << "\t\tDCE: Erasing instruction \"" << I->getName()
69
0
                 << "\"\n");
70
0
      I->eraseFromParent();
71
0
      ++NumDCE;
72
0
      changed = true;
73
0
    }
74
0
  }
75
0
  return changed;
76
0
}
77
78
0
bool DCE::runOnModule(Module *M) {
79
0
  bool changed = false;
80
81
  // A list of unused functions to deallocate from memory.
82
  // We need to destroy the memory at the very end of this function because
83
  // a dead function may have a variable that is referrenced by an inner
84
  // function (which will also become dead once the outer function is removed).
85
  // However we cannot destroy the outer function right away until we destroy
86
  // the inner function.
87
0
  llvh::SmallVector<Function *, 16> toDestroy;
88
89
  // Perform per-function DCE.
90
0
  for (auto &F : *M) {
91
0
    LLVM_DEBUG(
92
0
        dbgs() << "\tDCE: running on function \"" << F.getInternalName()
93
0
               << "\"\n");
94
0
    changed |= performFunctionDCE(&F);
95
0
  }
96
97
0
  bool localChanged = false;
98
0
  do {
99
    // A list of unused functions to remove from the module without being
100
    // destroyed.
101
0
    llvh::SmallVector<Function *, 16> toRemove;
102
0
    localChanged = false;
103
0
    for (auto &F : *M) {
104
      // Try to remove unused functions. Notice that the top-level-function has
105
      // no external users so we must check for it explicitly.
106
0
      if (M->findCJSModule(&F)) {
107
        // If the function is a top-level module.
108
0
        continue;
109
0
      }
110
      // Don't delete the function if it is at global scope, or if it is the
111
      // entry point of a module.
112
0
      if (!F.isGlobalScope() && &F != M->getTopLevelFunction() &&
113
0
          !F.hasUsers()) {
114
0
        toRemove.push_back(&F);
115
0
        toDestroy.push_back(&F);
116
0
        changed = true;
117
0
        localChanged = true;
118
0
        NumFuncDCE++;
119
0
      }
120
0
    }
121
122
    // We erase the basic blocks and instructions from each function in
123
    // toRemove, and also remove the function from the module. However
124
    // the memory of the function remain alive.
125
0
    for (auto *F : toRemove) {
126
0
      LLVM_DEBUG(
127
0
          dbgs() << "\tDCE: Erasing function \"" << F->getInternalName()
128
0
                 << "\"\n");
129
0
      F->eraseFromParentNoDestroy();
130
0
    }
131
0
  } while (localChanged);
132
133
  // Now that all instructions have been destroyed from each dead function,
134
  // it's now safe to destroy them including the variables in them.
135
0
  for (auto *F : toDestroy) {
136
0
    assert(F->empty() && "All basic blocks should have been deleted.");
137
0
    Value::destroy(F);
138
0
  }
139
140
0
  return changed | localChanged;
141
0
}
142
143
0
std::unique_ptr<Pass> hermes::createDCE() {
144
0
  return std::make_unique<DCE>();
145
0
}
146
147
#undef DEBUG_TYPE