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 visitedPlusSafetyResult.remove_if([
this](
const auto &entry) {
231 return !entry.second || !tryToErase.contains(entry.first);
233 for (
auto &[sym, _] : visitedPlusSafetyResult) {
234 LLVM_DEBUG(llvm::dbgs() <<
"[EraseIfUnused] removing: " << sym.getNameAttr() <<
'\n');
240bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(SymbolOpInterface
check) {
244 auto visited = visitedPlusSafetyResult.find(
check);
245 if (visited != visitedPlusSafetyResult.end()) {
246 return visited->second;
251 visitedPlusSafetyResult[
check] =
false;
257 visitedPlusSafetyResult[
check] =
true;
262 if (collectSafeToErase(defTree.lookupNode(
check))) {
263 const auto *useNode = useGraph.lookupNode(
check);
264 if (!useNode || collectSafeToErase(useNode)) {
270 visitedPlusSafetyResult[
check] =
false;
274bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(
275 const llzk::SymbolDefTreeNode *
check
278 if (
const llzk::SymbolDefTreeNode *p =
check->getParent()) {
279 if (SymbolOpInterface checkOp = p->getOp()) {
280 return collectSafeToErase(checkOp);
286bool llzk::polymorphic::detail::FromEraseSet::collectSafeToErase(
287 const llzk::SymbolUseGraphNode *
check
290 for (
const llzk::SymbolUseGraphNode *p :
check->predecessorIter()) {
291 if (SymbolOpInterface checkOp = cachedLookup(p)) {
292 if (!collectSafeToErase(checkOp)) {
301llzk::polymorphic::detail::FromEraseSet::cachedLookup(
const llzk::SymbolUseGraphNode *node) {
302 assert(node &&
"must provide a node");
304 auto fromCache = lookupCache.find(node);
305 if (fromCache != lookupCache.end()) {
306 return fromCache->second;
310 assert(succeeded(lookupRes) &&
"graph contains node with invalid path");
311 assert(lookupRes->get() !=
nullptr &&
"lookup must return an Operation");
316 SymbolOpInterface actualRes =
317 lookupRes->viaInclude() ? nullptr : llvm::cast<SymbolOpInterface>(lookupRes->get());
319 lookupCache[node] = actualRes;
320 assert((!actualRes == lookupRes->viaInclude()) &&
"not found iff included");
325 llzk::array::ArrayType inputTy, Type convertedElemTy
328 while (
auto nestedArrTy = llvm::dyn_cast<llzk::array::ArrayType>(convertedElemTy)) {
329 llvm::append_range(mergedDims, nestedArrTy.getDimensionSizes());
330 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
LLZK type conversion operations.
LLZK component/struct operations.
LLZK constraint emission operations.
LLZK felt (Field ELemenT) type and operations.
LLZK function operations.
LLZK support for global values, defined outside of structs.
LLZK types and operations to support templated/parameterized structs.
::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.
LLZK support for random access memory during witness generation.
LLZK string type and operations.
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
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 ...