27#include <llvm/ADT/DenseMap.h>
28#include <llvm/ADT/STLExtras.h>
29#include <llvm/ADT/SmallVector.h>
30#include <llvm/Support/Debug.h>
32#define DEBUG_TYPE "poly-dialect-shared"
39constexpr char OPEN_SLOT_MARKER =
'\x1A';
46 TemplateOp parentTemplate, ArrayAttr callParams,
47 const DenseMap<Attribute, Attribute> ¶mNameToConcrete
49 MLIRContext *ctx = parentTemplate.getContext();
51 SmallVector<StringAttr> sourceChunks;
54 auto pattern = llvm::dyn_cast<ArrayAttr>(rawPattern);
56 return parentTemplate.emitOpError()
59 if (pattern.size() != paramNames.size() + 1) {
60 return parentTemplate.emitOpError()
62 << paramNames.size() + 1 <<
" literal chunk(s) for " << paramNames.size()
63 <<
" template parameter(s), but found " << pattern.size();
65 for (
auto [index, chunk] : llvm::enumerate(pattern)) {
66 auto stringChunk = llvm::dyn_cast<StringAttr>(chunk);
69 <<
"' element " << index <<
" to be a StringAttr";
71 sourceChunks.push_back(stringChunk);
74 std::string firstChunk = parentTemplate.
getSymName().str();
75 if (!paramNames.empty()) {
76 firstChunk.push_back(
'_');
78 sourceChunks.push_back(StringAttr::get(ctx, firstChunk));
79 for (
size_t i = 1; i < paramNames.size(); ++i) {
80 sourceChunks.push_back(StringAttr::get(ctx,
"_"));
82 if (!paramNames.empty()) {
83 sourceChunks.push_back(StringAttr::get(ctx,
""));
87 SmallVector<Attribute> remainingNames;
88 SmallVector<Attribute> concreteKeyEntries;
89 for (Attribute paramName : paramNames) {
90 auto concreteIt = paramNameToConcrete.find(paramName);
91 if (concreteIt == paramNameToConcrete.end() || !concreteIt->second) {
92 remainingNames.push_back(paramName);
96 concreteKeyEntries.push_back(paramName);
97 concreteKeyEntries.push_back(concreteIt->second);
100 ArrayAttr rewrittenCallParams =
nullptr;
102 assert(callParams.size() == paramNames.size() &&
"template parameter arity already verified");
103 SmallVector<Attribute> remainingCallParams;
104 for (
auto [paramName, attr] : llvm::zip_equal(paramNames, callParams.getValue())) {
105 auto concreteIt = paramNameToConcrete.find(paramName);
106 if (concreteIt == paramNameToConcrete.end() || !concreteIt->second) {
107 remainingCallParams.push_back(attr);
110 rewrittenCallParams = ArrayAttr::get(ctx, remainingCallParams);
115 SmallVector<std::string> refinedChunkValues;
116 refinedChunkValues.push_back(sourceChunks.front().getValue().str());
117 for (
size_t i = 0; i < paramNames.size(); ++i) {
118 auto concreteIt = paramNameToConcrete.find(paramNames[i]);
119 if (concreteIt != paramNameToConcrete.end() && concreteIt->second) {
120 Attribute binding = concreteIt->second;
122 refinedChunkValues.back() += sourceChunks[i + 1].getValue().str();
124 refinedChunkValues.push_back(sourceChunks[i + 1].getValue().str());
128 refinedChunkValues.size() == remainingNames.size() + 1 &&
129 "every unbound parameter creates one remaining gap"
132 std::string renderedName;
133 for (
auto [index, chunk] : llvm::enumerate(refinedChunkValues)) {
135 renderedName.push_back(OPEN_SLOT_MARKER);
137 renderedName += chunk;
140 SmallVector<Attribute> refinedPattern;
141 refinedPattern.reserve(refinedChunkValues.size());
142 for (
const std::string &chunk : refinedChunkValues) {
143 refinedPattern.push_back(StringAttr::get(ctx, chunk));
147 std::move(remainingNames), ArrayAttr::get(ctx, concreteKeyEntries), std::move(renderedName),
148 rewrittenCallParams, ArrayAttr::get(ctx, refinedPattern),
161 std::string result = baseName.str();
162 if (!concreteAttrs.empty()) {
163 result.push_back(
'_');
172 ConversionTarget target(*ctx);
173 target.addLegalDialect<
179 target.addLegalOp<ModuleOp>();
189 if (llvm::isa<llzk::component::StructDefOp>(op)) {
193 return !fdef.isInStruct();
200 DenseSet<SymbolRefAttr> &&tryToErasePaths
204 for (SymbolRefAttr path : tryToErasePaths) {
205 LLVM_DEBUG(llvm::dbgs() <<
"[FromEraseSet] path to erase: " << path <<
'\n';);
206 Operation *lookupFrom =
rootMod.getOperation();
208 assert(succeeded(res) &&
"inputs must be valid symbol references");
210 if (!res->viaInclude()) {
211 SymbolOpInterface op = llvm::cast<SymbolOpInterface>(res->get());
212 LLVM_DEBUG(llvm::dbgs() <<
"[FromEraseSet] added op to the erase set: " << op <<
'\n';);
213 tryToErase.insert(op);
216 llvm::dbgs() <<
"[FromEraseSet] ignored op because it comes from an include: "
217 << res->get() <<
'\n';
225 for (SymbolOpInterface sym : tryToErase) {
226 collectSafeToErase(sym);
230 for (
auto &it : llvm::make_early_inc_range(visitedPlusSafetyResult)) {
231 if (!it.second || !tryToErase.contains(it.first)) {
232 visitedPlusSafetyResult.erase(it.first);
235 for (
auto &[sym, _] : visitedPlusSafetyResult) {
236 LLVM_DEBUG(llvm::dbgs() <<
"[EraseIfUnused] removing: " << sym.getNameAttr() <<
'\n');
242bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(SymbolOpInterface
check) {
246 auto visited = visitedPlusSafetyResult.find(
check);
247 if (visited != visitedPlusSafetyResult.end()) {
248 return visited->second;
253 visitedPlusSafetyResult[
check] =
false;
259 visitedPlusSafetyResult[
check] =
true;
264 if (collectSafeToErase(defTree.lookupNode(
check))) {
265 const auto *useNode = useGraph.lookupNode(
check);
266 if (!useNode || collectSafeToErase(useNode)) {
272 visitedPlusSafetyResult[
check] =
false;
276bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(
277 const llzk::SymbolDefTreeNode *
check
280 if (
const llzk::SymbolDefTreeNode *p =
check->getParent()) {
281 if (SymbolOpInterface checkOp = p->getOp()) {
282 return collectSafeToErase(checkOp);
288bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(
289 const llzk::SymbolUseGraphNode *
check
292 for (
const llzk::SymbolUseGraphNode *p :
check->predecessorIter()) {
293 if (SymbolOpInterface checkOp = cachedLookup(p)) {
294 if (!collectSafeToErase(checkOp)) {
303llzk::polymorphic::detail::FromEraseSet::cachedLookup(
const llzk::SymbolUseGraphNode *node) {
304 assert(node &&
"must provide a node");
306 auto fromCache = lookupCache.find(node);
307 if (fromCache != lookupCache.end()) {
308 return fromCache->second;
312 assert(succeeded(lookupRes) &&
"graph contains node with invalid path");
313 assert(lookupRes->get() !=
nullptr &&
"lookup must return an Operation");
318 SymbolOpInterface actualRes =
319 lookupRes->viaInclude() ? nullptr : llvm::cast<SymbolOpInterface>(lookupRes->get());
321 lookupCache[node] = actualRes;
322 assert((!actualRes == lookupRes->viaInclude()) &&
"not found iff included");
327 llzk::array::ArrayType inputTy, Type convertedElemTy
330 while (
auto nestedArrTy = llvm::dyn_cast<llzk::array::ArrayType>(convertedElemTy)) {
331 llvm::append_range(mergedDims, nestedArrTy.getDimensionSizes());
332 convertedElemTy = nestedArrTy.getElementType();
Common private implementation for poly dialect passes.
This file defines methods symbol lookup across LLZK operations and included files.
static std::string from(mlir::Type type)
Return a brief string representation of the given LLZK type.
Builds a tree structure representing the symbol table structure.
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbol(mlir::SymbolTableCollection &tables, bool reportMissing=true) const
Builds a graph structure representing the relationships between symbols and their uses.
static ArrayType get(::mlir::Type elementType, ::llvm::ArrayRef<::mlir::Attribute > dimensionSizes)
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
::llvm::SmallVector<::mlir::Attribute > getConstNames()
Return the names of all ops of type OpT within the body region in the order they are defined.
::llvm::StringRef getSymName()
const SymbolDefTree & defTree
const SymbolUseGraph & useGraph
mlir::SymbolTableCollection tables
CleanupBase(mlir::ModuleOp root, const SymbolDefTree &symDefTree, const SymbolUseGraph &symUseGraph)
mlir::LogicalResult eraseUnusedDefinitions()
FromEraseSet(mlir::ModuleOp root, const SymbolDefTree &symDefTree, const SymbolUseGraph &symUseGraph, llvm::DenseSet< mlir::SymbolRefAttr > &&tryToErasePaths)
Note: paths in tryToErase should be relative to root.
FailureOr< InstantiationLayout > buildInstantiationLayout(TemplateOp parentTemplate, ArrayAttr callParams, const DenseMap< Attribute, Attribute > ¶mNameToConcrete)
array::ArrayType flattenInstantiatedArrayType(array::ArrayType inputTy, mlir::Type convertedElemTy)
Merge nested array dimensions produced by replacing an array element type.
void setInstantiationNamePattern(TemplateOp templateOp, ArrayAttr namePattern)
std::string buildOpaqueInstantiationName(StringRef baseName, ArrayRef< Attribute > concreteAttrs)
bool isErasableDefinition(mlir::Operation *op)
Return true iff op is a cleanup candidate.
mlir::ConversionTarget newBaseTarget(mlir::MLIRContext *ctx)
Return a new ConversionTarget allowing all LLZK-required dialects.
constexpr llvm::StringLiteral TEMPLATE_NAME_PATTERN_ATTR
Metadata carried across transformation passes preserving the literal chunks of a partially-instantiat...
bool isNullOrEmpty(mlir::ArrayAttr a)
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbolIn(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, Within &&lookupWithin, mlir::Operation *origin, bool reportMissing=true)
Groups the information needed after concrete parameters have been chosen to decide how to name a new ...