blob: 5b3074615503ae209c131c5a17ca27bc9e4b27a9 [file]
// Copyright 2024 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/compiler/Dialect/HAL/IR/HALDialect.h"
#include "iree/compiler/Dialect/HAL/IR/HALOps.h"
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/Pass/Pass.h"
namespace mlir::iree_compiler::IREE::HAL {
#define GEN_PASS_DEF_PRUNEEXECUTABLESPASS
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h.inc"
namespace {
struct SymbolReferences {
Operation *symbolOp;
unsigned count = 0;
};
using SymbolReferenceMap = DenseMap<Attribute, SymbolReferences>;
static void markReferenced(SymbolRefAttr symbolRefAttr,
SymbolReferenceMap &referenceMap) {
auto markReferencedNested = [&](StringAttr rootRefAttr,
ArrayRef<FlatSymbolRefAttr> nestedRefAttrs) {
auto nestedRefAttr = nestedRefAttrs.empty()
? SymbolRefAttr::get(rootRefAttr)
: SymbolRefAttr::get(rootRefAttr, nestedRefAttrs);
auto it = referenceMap.find(nestedRefAttr);
if (it != referenceMap.end())
++it->second.count;
};
auto rootRefAttr = symbolRefAttr.getRootReference();
auto nestedRefAttrs = symbolRefAttr.getNestedReferences();
for (size_t i = 0; i <= nestedRefAttrs.size(); ++i) {
markReferencedNested(rootRefAttr, nestedRefAttrs.slice(0, i));
}
}
static void processOp(Operation *op, SymbolReferenceMap &referenceMap) {
SmallVector<Attribute> worklist;
for (auto namedAttr : op->getAttrs())
worklist.push_back(namedAttr.getValue());
while (!worklist.empty()) {
auto attr = worklist.pop_back_val();
if (auto symbolRefAttr = dyn_cast<SymbolRefAttr>(attr)) {
markReferenced(symbolRefAttr, referenceMap);
} else {
attr.walkImmediateSubElements(
[&](Attribute attr) { worklist.push_back(attr); }, [](Type) {});
}
}
}
static void eraseOps(ArrayRef<Attribute> symbolRefAttrs,
SymbolReferenceMap &referenceMap) {
for (auto symbolRefAttr : symbolRefAttrs) {
auto &symbolRefs = referenceMap[symbolRefAttr];
if (symbolRefs.count == 0)
symbolRefs.symbolOp->erase();
}
}
//===----------------------------------------------------------------------===//
// --iree-hal-prune-executables
//===----------------------------------------------------------------------===//
struct PruneExecutablesPass
: public IREE::HAL::impl::PruneExecutablesPassBase<PruneExecutablesPass> {
void runOnOperation() override {
auto moduleOp = getOperation();
// Gather all executable op symbols into a map that we can quickly check
// while walking ops.
DenseSet<Operation *> ignoredOps;
SymbolReferenceMap referenceMap;
SmallVector<Attribute> executableRefAttrs;
SmallVector<Attribute> variantRefAttrs;
SmallVector<Attribute> exportRefAttrs;
for (auto executableOp : moduleOp.getOps<IREE::HAL::ExecutableOp>()) {
ignoredOps.insert(executableOp);
if (!executableOp.isPrivate())
continue;
auto executableRefAttr =
FlatSymbolRefAttr::get(executableOp.getSymNameAttr());
referenceMap[executableRefAttr].symbolOp = executableOp;
executableRefAttrs.push_back(executableRefAttr);
for (auto variantOp :
executableOp.getOps<IREE::HAL::ExecutableVariantOp>()) {
ignoredOps.insert(variantOp);
auto variantRefAttr = SymbolRefAttr::get(
executableOp.getSymNameAttr(),
{
FlatSymbolRefAttr::get(variantOp.getSymNameAttr()),
});
referenceMap[variantRefAttr].symbolOp = variantOp;
variantRefAttrs.push_back(variantRefAttr);
for (auto exportOp :
variantOp.getOps<IREE::HAL::ExecutableExportOp>()) {
ignoredOps.insert(exportOp);
auto exportRefAttr = SymbolRefAttr::get(
executableOp.getSymNameAttr(),
{
FlatSymbolRefAttr::get(variantOp.getSymNameAttr()),
FlatSymbolRefAttr::get(exportOp.getSymNameAttr()),
});
referenceMap[exportRefAttr].symbolOp = exportOp;
exportRefAttrs.push_back(exportRefAttr);
}
}
}
// Walk all ops in the module that can reference executable symbols and
// accumulate the usage counts.
SymbolTable symbolTable(moduleOp);
moduleOp.walk([&](Operation *op) -> WalkResult {
if (ignoredOps.contains(op))
return WalkResult::skip();
processOp(op, referenceMap);
return op->hasTrait<OpTrait::IsIsolatedFromAbove>()
? WalkResult::skip()
: WalkResult::advance();
});
// Erase any executable-related op with no references.
// We have to start with exports > variants > executables so that we don't
// erase a container before the nested ops.
eraseOps(exportRefAttrs, referenceMap);
eraseOps(variantRefAttrs, referenceMap);
eraseOps(executableRefAttrs, referenceMap);
}
};
} // namespace
} // namespace mlir::iree_compiler::IREE::HAL