20#include <mlir/IR/BuiltinOps.h>
21#include <mlir/IR/SymbolTable.h>
23#include <llvm/ADT/SmallVector.h>
24#include <llvm/Support/Debug.h>
28#define GEN_PASS_DEF_UNUSEDDECLARATIONELIMINATIONPASS
36#define DEBUG_TYPE "llzk-unused-declaration-elim"
41 using Base = UnusedDeclarationEliminationPassBase<PassImpl>;
47 DenseMap<SymbolRefAttr, StructDefOp> symbolToStruct;
48 DenseMap<StructDefOp, SymbolRefAttr> structToSymbol;
50 const SymbolRefAttr &getSymbol(
StructDefOp s)
const {
return structToSymbol.at(s); }
51 StructDefOp getStruct(
const SymbolRefAttr &sym)
const {
return symbolToStruct.at(sym); }
53 static PassContext populate(ModuleOp modOp) {
56 modOp.walk<WalkOrder::PreOrder>([&ctx](
StructDefOp structDef) {
58 ensure(succeeded(structSymbolRes),
"failed to lookup struct symbol");
59 SymbolRefAttr structSym = *structSymbolRes;
60 ctx.symbolToStruct[structSym] = structDef;
61 ctx.structToSymbol[structDef] = structSym;
67 void runOnOperation()
override {
68 PassContext ctx = PassContext::populate(getOperation());
71 if (failed(removeUnusedMembers(ctx))) {
78 removeUnusedStructs(ctx);
87 LogicalResult removeUnusedMembers(PassContext &ctx) {
88 ModuleOp modOp = getOperation();
90 SymbolTableCollection symbolTables;
94 DenseSet<MemberDefOp> members;
95 for (
const auto &entry : ctx.structToSymbol) {
98 structDef.walk([notMain, &members](
MemberDefOp member) {
102 members.insert(member);
108 WalkResult readWalk = modOp.walk([&](
MemberReadOp readm) {
109 FailureOr<SymbolLookupResult<MemberDefOp>> memberDef = readm.
getMemberDefOp(symbolTables);
110 if (failed(memberDef)) {
111 return WalkResult::interrupt();
114 members.erase(memberDef->get());
115 return WalkResult::advance();
117 if (readWalk.wasInterrupted()) {
123 WalkResult writeWalk = modOp.walk([&](
MemberWriteOp writem) {
124 FailureOr<SymbolLookupResult<MemberDefOp>> memberDef = writem.
getMemberDefOp(symbolTables);
125 if (failed(memberDef)) {
126 return WalkResult::interrupt();
130 if (members.contains(writtenMember)) {
133 llvm::dbgs() <<
"Removing write " << writem <<
" to write-only member " << writtenMember
139 return WalkResult::advance();
141 if (writeWalk.wasInterrupted()) {
147 LLVM_DEBUG(llvm::dbgs() <<
"Removing member " << memberDef <<
'\n');
158 void removeUnusedStructs(PassContext &ctx) {
159 DenseMap<StructDefOp, DenseSet<StructDefOp>> uses;
160 DenseMap<StructDefOp, DenseSet<StructDefOp>> usedBy;
163 for (
auto &[structDef, _] : ctx.structToSymbol) {
164 uses[structDef] = {};
165 usedBy[structDef] = {};
168 getOperation().walk([&](Operation *op) {
169 auto structParent = op->getParentOfType<
StructDefOp>();
170 if (structParent ==
nullptr) {
171 return WalkResult::advance();
174 auto tryAddUse = [&](Type ty) {
175 if (
auto structTy = dyn_cast<StructType>(ty)) {
177 SymbolRefAttr sym = structTy.getNameRef();
179 if (refStruct != structParent) {
180 uses[structParent].insert(refStruct);
181 usedBy[refStruct].insert(structParent);
190 for (Value operand : op->getOperands()) {
191 tryAddUse(operand.getType());
195 for (Value result : op->getResults()) {
196 tryAddUse(result.getType());
200 for (Region ®ion : op->getRegions()) {
201 for (Block &block : region) {
202 for (BlockArgument arg : block.getArguments()) {
203 tryAddUse(arg.getType());
209 for (
const auto &namedAttr : op->getAttrs()) {
210 namedAttr.getValue().walk([&tryAddUse](TypeAttr typeAttr) {
211 tryAddUse(typeAttr.getValue());
215 return WalkResult::advance();
218 SmallVector<StructDefOp> unusedStructs;
220 auto updateUnusedStructs = [&usedBy, &unusedStructs]() {
221 for (
auto &[structDef, users] : usedBy) {
222 if (users.empty() && !structDef.isMainComponent()) {
223 unusedStructs.push_back(structDef);
228 updateUnusedStructs();
230 while (!unusedStructs.empty()) {
232 unusedStructs.pop_back();
235 for (
auto usedStruct : uses[unusedStruct]) {
237 usedBy[usedStruct].erase(unusedStruct);
241 usedBy.erase(unusedStruct);
242 uses.erase(unusedStruct);
243 unusedStruct->erase();
247 if (unusedStructs.empty()) {
248 updateUnusedStructs();
254 void removeEmptyModules() {
255 SmallVector<ModuleOp> emptyModules;
257 ModuleOp rootModOp = getOperation();
258 rootModOp.walk<WalkOrder::PostOrder>([&](ModuleOp modOp) {
259 if (modOp == rootModOp) {
262 Region ®ion = modOp.getBodyRegion();
263 if (region.empty() || region.front().empty()) {
264 emptyModules.push_back(modOp);
268 for (ModuleOp modOp : emptyModules) {
269 LLVM_DEBUG(llvm::dbgs() <<
"Removing empty module " << modOp.getName() <<
'\n');
This file defines methods symbol lookup across LLZK operations and included files.
bool hasPublicAttr()
Returns whether this member is a public output.
inline ::mlir::FailureOr< SymbolLookupResult< MemberDefOp > > getMemberDefOp(::mlir::SymbolTableCollection &tables)
Gets the definition for the member referenced in this op.
inline ::mlir::FailureOr< SymbolLookupResult< MemberDefOp > > getMemberDefOp(::mlir::SymbolTableCollection &tables)
Gets the definition for the member referenced in this op.
bool isMainComponent()
Return true iff this struct.def is the main struct. See llzk::MAIN_ATTR_NAME.
void ensure(bool condition, const llvm::Twine &errMsg)
FailureOr< SymbolRefAttr > getPathFromTopRoot(SymbolOpInterface to, ModuleOp *foundRoot)