24#include <mlir/Dialect/Func/IR/FuncOps.h>
25#include <mlir/IR/BuiltinOps.h>
26#include <mlir/IR/SymbolTable.h>
27#include <mlir/Transforms/Inliner.h>
28#include <mlir/Transforms/InliningUtils.h>
30#include <llvm/ADT/DenseSet.h>
31#include <llvm/ADT/SCCIterator.h>
32#include <llvm/ADT/STLExtras.h>
33#include <llvm/ADT/SmallVector.h>
34#include <llvm/Support/Debug.h>
37#define GEN_PASS_DEF_INLINEFREEFUNCTIONSPASS
41#define DEBUG_TYPE "llzk-inline-free-functions"
51static void walkTypeSymbolRefs(
52 Type type, function_ref<
void(SymbolRefAttr)> rootRef,
53 function_ref<
void(SymbolRefAttr)> unsupportedRef
58static void walkAttrSymbolRefs(
59 Attribute attr, function_ref<
void(SymbolRefAttr)> rootRef,
60 function_ref<
void(SymbolRefAttr)> unsupportedRef
62 attr.walk<WalkOrder::PreOrder>([rootRef, unsupportedRef](TypeAttr typeAttr) {
63 walkTypeSymbolRefs(typeAttr.getValue(), rootRef, unsupportedRef);
64 return WalkResult::skip();
65 }, [unsupportedRef](SymbolRefAttr ref) { unsupportedRef(ref); });
68static void walkTypeSymbolRefs(
69 Type type, function_ref<
void(SymbolRefAttr)> rootRef,
70 function_ref<
void(SymbolRefAttr)> unsupportedRef
73 rootRef(structType.getNameRef());
74 if (ArrayAttr params = structType.getParams()) {
75 walkAttrSymbolRefs(params, rootRef, unsupportedRef);
77 return WalkResult::skip();
78 }, [unsupportedRef](SymbolRefAttr ref) { unsupportedRef(ref); });
83static bool isInlinableFreeFunction(
FuncDefOp func, ModuleOp root, SymbolTableCollection &tables) {
84 Operation *parent = func->getParentOp();
89 bool hasFuncCall = walkContains<func::CallOp>(func);
94 <<
": body contains func.call whose callee could be captured after cloning\n";
103 if (!llvm::isa<ModuleOp, polymorphic::TemplateOp>(parent)) {
108 if (!root->isAncestor(func.getOperation())) {
111 << func.getLoc() <<
": function is defined outside the root module\n"
121 bool hasUnsupportedSymbolRef =
false;
122 func.walk([&](Operation *op) {
123 auto detectUnsupportedSymbolRef = [&](SymbolRefAttr ref) {
126 <<
": unsupported scope-sensitive symbol reference " << ref <<
" in "
127 << op->getName() <<
" at " << op->getLoc() <<
'\n'
129 hasUnsupportedSymbolRef =
true;
131 auto detectMissingRootSymbolRef = [&](SymbolRefAttr ref) {
132 if (!tables.lookupSymbolIn(root, ref)) {
135 <<
": symbol reference " << ref <<
" in " << op->getName() <<
" at "
136 << op->getLoc() <<
" does not resolve in the root module\n"
138 hasUnsupportedSymbolRef =
true;
142 op->getDiscardableAttrDictionary(), detectMissingRootSymbolRef, detectUnsupportedSymbolRef
144 if (Attribute properties = op->getPropertiesAsAttribute()) {
145 for (NamedAttribute property : llvm::cast<DictionaryAttr>(properties)) {
146 StringAttr name =
property.getName();
149 if (
auto memberRef = llvm::dyn_cast<component::MemberRefOpInterface>(op)) {
150 if (name == memberRef.getMemberNameAttrName()) {
157 bool usesRootLookup =
false;
158 if (
auto call = llvm::dyn_cast<CallOp>(op)) {
159 usesRootLookup = name == call.getCalleeAttrName();
160 }
else if (
auto globalRef = llvm::dyn_cast<global::GlobalRefOpInterface>(op)) {
161 usesRootLookup = name == globalRef.getNameRefAttrName();
163 if (usesRootLookup) {
166 detectMissingRootSymbolRef(llvm::cast<SymbolRefAttr>(property.getValue()));
169 property.getValue(), detectMissingRootSymbolRef, detectUnsupportedSymbolRef
174 for (Type type : llvm::concat<Type>(op->getOperandTypes(), op->getResultTypes())) {
175 walkTypeSymbolRefs(type, detectMissingRootSymbolRef, detectUnsupportedSymbolRef);
177 for (Region ®ion : op->getRegions()) {
178 for (Block &block : region) {
179 for (BlockArgument arg : block.getArguments()) {
180 walkTypeSymbolRefs(arg.getType(), detectMissingRootSymbolRef, detectUnsupportedSymbolRef);
185 return !hasUnsupportedSymbolRef;
191static bool isInlinableCallSite(
CallOp call, ModuleOp root) {
192 if (!call->getParentOfType<
FuncDefOp>()) {
195 << call.getLoc() <<
": call is outside a function.def body\n"
199 if (call->getParentOfType<ModuleOp>() != root) {
202 << call.getLoc() <<
": call is in a nested module\n"
213static llvm::DenseSet<Operation *> collectRecursiveFunctions(
const llzk::CallGraph &cg) {
214 llvm::DenseSet<Operation *> recursive;
215 for (
auto scc = llvm::scc_begin(&cg); !scc.isAtEnd(); ++scc) {
216 if (scc->size() == 1 && !scc.hasCycle()) {
220 if (!node->isExternal()) {
222 llvm::dbgs() <<
"[" DEBUG_TYPE "] cannot inline "
223 << SymbolTable::getSymbolName(node->getCalledFunction().getOperation())
224 <<
" at " << node->getCalledFunction().getLoc()
225 <<
": function participates in a call cycle\n"
227 recursive.insert(node->getCalledFunction().getOperation());
237 CallOp call, ModuleOp root, SymbolTableCollection &tables,
238 const llvm::DenseSet<Operation *> &skippedCallees
241 if (failed(tgtRes)) {
244 << call.getLoc() <<
": callee target resolution failed\n"
249 if (!isInlinableFreeFunction(callee, root, tables)) {
252 if (callee.isExternal()) {
255 << call.getLoc() <<
": callee is external and has no body\n"
259 if (skippedCallees.contains(callee)) {
262 << call.getLoc() <<
": callee was skipped due to a call cycle or an earlier "
263 <<
"inlining failure\n"
271struct FreeFunctionCall {
278static SmallVector<FreeFunctionCall> collectFreeFunctionCalls(
279 ModuleOp
mod, SymbolTableCollection &tables,
const llvm::DenseSet<Operation *> &skippedCallees
281 SmallVector<FreeFunctionCall> calls;
283 if (!isInlinableCallSite(call,
mod)) {
286 if (
FuncDefOp callee = resolveFreeCallee(call,
mod, tables, skippedCallees)) {
287 calls.push_back({call, callee});
295static SmallVector<FuncDefOp> collectUnusedHelpers(ModuleOp
mod) {
297 SmallVector<FuncDefOp> unusedFunctions;
299 if (func.isExternal() || !llvm::isa<ModuleOp, polymorphic::TemplateOp>(func->getParentOp())) {
304 unusedFunctions.push_back(func);
307 return unusedFunctions;
311 using Base = InlineFreeFunctionsPassBase<PassImpl>;
314 void runOnOperation()
override {
315 ModuleOp
mod = getOperation();
316 SymbolTableCollection tables;
317 InlinerInterface inliner(&getContext());
320 llvm::DenseSet<Operation *> skippedCallees =
321 collectRecursiveFunctions(getAnalysis<CallGraphAnalysis>().getCallGraph());
323 inlineCalls(
mod, tables, inliner, skippedCallees);
324 removeUnusedFunctions(
mod);
334 ModuleOp
mod, SymbolTableCollection &tables, InlinerInterface &inliner,
335 llvm::DenseSet<Operation *> &skippedCallees
337 InlinerConfig inlinerConfig;
338 SmallVector<FreeFunctionCall> callsToInline =
339 collectFreeFunctionCalls(
mod, tables, skippedCallees);
342 llvm::dbgs() <<
"[" DEBUG_TYPE "] round found " << callsToInline.size()
343 <<
" free-function call site(s) to inline\n";
346 if (callsToInline.empty()) {
350 for (
auto [call, callee] : callsToInline) {
351 if (skippedCallees.contains(callee)) {
354 <<
" at " << call.getLoc()
355 <<
": an earlier inlining attempt for this callee failed\n"
359 if (failed(inlineCall(
360 inliner, inlinerConfig.getCloneCallback(), call, callee, callee.getCallableRegion(),
365 <<
" at " << call.getLoc() <<
": MLIR inlineCall failed\n"
367 call.emitWarning(
"failed to inline free function call; skipping this callee");
368 skippedCallees.insert(callee);
374 callsToInline = collectFreeFunctionCalls(
mod, tables, skippedCallees);
380 void removeUnusedFunctions(ModuleOp
mod) {
381 SmallVector<FuncDefOp> toErase = collectUnusedHelpers(
mod);
382 while (!toErase.empty()) {
386 toErase = collectUnusedHelpers(
mod);
This is a simple port of the mlir::CallGraphNode with llzk::CallGraph as a friend class,...
This is a port of mlir::CallGraph that has been adapted to use the custom symbol lookup helpers (see ...
bool hasPredecessor() const
Return true if this node has any predecessors.
Builds a graph structure representing the relationships between symbols and their uses.
const SymbolUseGraphNode * lookupNode(mlir::ModuleOp pathRoot, mlir::SymbolRefAttr path) const
Return the existing node for the symbol reference relative to the given module, else nullptr.
::mlir::SymbolRefAttr getCallee()
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
::llvm::StringRef getSymName()
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)