LLZK 0.1.0
Veridise's ZK Language IR
Loading...
Searching...
No Matches
AnalysisWrappers.h
Go to the documentation of this file.
1//===-- AnalysisWrappers.h --------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
21//===----------------------------------------------------------------------===//
22
23#pragma once
24
27#include "llzk/Util/Compare.h"
30
31#include <mlir/IR/BuiltinOps.h>
32#include <mlir/Pass/AnalysisManager.h>
33
34#include <map>
35
36namespace llzk {
37
42template <typename Result, typename Context> class StructAnalysis {
43public:
47 StructAnalysis(mlir::Operation *op) {
48 structDefOp = llvm::dyn_cast<component::StructDefOp>(op);
49 if (!structDefOp) {
50 const char *error_message = "StructAnalysis expects provided op to be a StructDefOp!";
51 op->emitError(error_message).report();
52 llvm::report_fatal_error(error_message);
53 }
54 auto maybeModOp = getRootModule(op);
55 if (mlir::failed(maybeModOp)) {
56 const char *error_message = "StructAnalysis could not find root module from StructDefOp!";
57 op->emitError(error_message).report();
58 llvm::report_fatal_error(error_message);
59 }
60 modOp = *maybeModOp;
61 }
62 virtual ~StructAnalysis() = default;
63
75 virtual mlir::LogicalResult runAnalysis(
76 mlir::DataFlowSolver &solver, mlir::AnalysisManager &moduleAnalysisManager, const Context &ctx
77 ) = 0;
78
80 bool constructed(const Context &ctx) const { return res.contains(ctx); }
81
83 const Result &getResult(const Context &ctx) const {
84 ensure(
85 constructed(ctx), mlir::Twine(__FUNCTION__) +
86 ": result has not been constructed for struct " +
87 mlir::Twine(getStruct().getName())
88 );
89 return *res.at(ctx);
90 }
91
92protected:
94 mlir::ModuleOp getModule() const { return modOp; }
95
97 component::StructDefOp getStruct() const { return structDefOp; }
98
100 void setResult(const Context &ctx, Result &&r) {
101 auto [_, inserted] = res.insert(std::make_pair(ctx, std::make_unique<Result>(r)));
102 ensure(inserted, "Result already initialized");
103 }
104
105private:
106 mlir::ModuleOp modOp;
107 component::StructDefOp structDefOp;
108 std::unordered_map<Context, std::unique_ptr<Result>> res;
109};
110
111template <typename Context>
112concept ContextType = requires(const Context &a, const Context &b) {
113 { a == b } -> std::convertible_to<bool>;
114 { std::hash<Context> {}(a) } -> std::convertible_to<std::size_t>;
115};
116
119struct NoContext {};
120
123template <typename Analysis, typename Result, typename Context>
124concept StructAnalysisType = requires {
125 requires std::is_base_of<StructAnalysis<Result, Context>, Analysis>::value;
126 requires ContextType<Context>;
127};
128
135template <typename Result, typename Context, StructAnalysisType<Result, Context> StructAnalysisTy>
137
139 using StructResults = std::map<
140 component::StructDefOp, std::reference_wrapper<const Result>,
142
147 using ResultMap = std::unordered_map<Context, StructResults>;
148
149public:
154 ModuleAnalysis(mlir::Operation *op) {
155 if (modOp = llvm::dyn_cast<mlir::ModuleOp>(op); !modOp) {
156 auto error_message = "ModuleAnalysis expects provided op to be an mlir::ModuleOp!";
157 op->emitError(error_message).report();
158 llvm::report_fatal_error(error_message);
159 }
160 }
161 virtual ~ModuleAnalysis() = default;
162
167 virtual void runAnalysis(mlir::AnalysisManager &am) { constructChildAnalyses(am); }
168
170 void ensureAnalysisRun(mlir::AnalysisManager &am) {
171 if (!constructed()) {
172 runAnalysis(am);
173 }
174 }
175
178 bool constructed() const { return results.contains(getContext()); }
179
182 return constructed() && results.at(getContext()).contains(op);
183 }
184
186 const Result &getResult(component::StructDefOp op) const {
187 ensureResultCreated(op);
188 return results.at(getContext()).at(op).get();
189 }
190
192 const StructResults &getCurrentResults() const {
193 ensure(constructed(), "results are not yet constructed for the current context");
194 return results.at(getContext());
195 }
196
197 mlir::DataFlowSolver &getSolver() { return solver; }
198
199protected:
200 mlir::DataFlowSolver solver;
201
205 virtual void initializeSolver() = 0;
206
209 virtual const Context &getContext() const = 0;
210
214 void constructChildAnalyses(mlir::AnalysisManager &am) {
216
217 // The analysis is run at the module level so that lattices are computed
218 // for global functions as well.
220 auto res = solver.initializeAndRun(modOp);
221 ensure(res.succeeded(), "solver failed to run on module!");
222
223 const Context &ctx = getContext();
224 modOp.walk([this, &am, &ctx](component::StructDefOp s) mutable {
225 auto &childAnalysis = am.getChildAnalysis<StructAnalysisTy>(s);
226 // Don't re-run the analysis if we already have the results.
227 // The analysis may have been run as part of a nested analysis.
228 if (!childAnalysis.constructed(ctx)) {
229 mlir::LogicalResult childAnalysisRes = childAnalysis.runAnalysis(solver, am, ctx);
230
231 if (mlir::failed(childAnalysisRes)) {
232 auto error_message = "StructAnalysis failed to run for " + mlir::Twine(s.getName());
233 s->emitError(error_message).report();
234 llvm::report_fatal_error(error_message);
235 }
236 }
237
238 auto [_, inserted] = results[ctx].insert(
239 std::make_pair(s, std::reference_wrapper(childAnalysis.getResult(ctx)))
240 );
241 ensure(inserted, "struct location conflict");
242 return mlir::WalkResult::skip();
243 });
244 }
245
246private:
247 mlir::ModuleOp modOp;
248 ResultMap results;
249
252 void ensureResultCreated(component::StructDefOp op) const {
253 ensure(hasResult(op), "Result does not exist for StructDefOp " + mlir::Twine(op.getName()));
254 }
255};
256
257} // namespace llzk
This file implements (LLZK-tailored) dense data-flow analysis using the data-flow analysis framework.
mlir::DataFlowSolver solver
ModuleAnalysis(mlir::Operation *op)
Asserts that the analysis is being run on a ModuleOp.
virtual ~ModuleAnalysis()=default
mlir::DataFlowSolver & getSolver()
void ensureAnalysisRun(mlir::AnalysisManager &am)
Runs the analysis if the results do not already exist.
bool constructed() const
Check if the results of this analysis have been created for the currently available context.
virtual void initializeSolver()=0
Initialize the shared dataflow solver with any common analyses required by the contained struct analy...
virtual void runAnalysis(mlir::AnalysisManager &am)
Run the StructAnalysisTy struct analysis on all child structs.
const StructResults & getCurrentResults() const
Get the results for the current context.
virtual const Context & getContext() const =0
Return the current Context object.
const Result & getResult(component::StructDefOp op) const
Asserts that op has a result and returns it.
void constructChildAnalyses(mlir::AnalysisManager &am)
Construct and run the StructAnalysisTy analyses on each StructDefOp contained in the ModuleOp that is...
bool hasResult(component::StructDefOp op) const
Checks if op has a result contained in the current result map.
StructAnalysis(mlir::Operation *op)
Assert that this analysis is being run on a StructDefOp and initializes the analysis with the current...
component::StructDefOp getStruct() const
Get the current StructDefOp that is under analysis.
virtual mlir::LogicalResult runAnalysis(mlir::DataFlowSolver &solver, mlir::AnalysisManager &moduleAnalysisManager, const Context &ctx)=0
Perform the analysis and construct the Result output.
void setResult(const Context &ctx, Result &&r)
Initialize the final Result object.
const Result & getResult(const Context &ctx) const
Access the result iff it has been created for the given Context object ctx.
mlir::ModuleOp getModule() const
Get the ModuleOp that is the parent of the StructDefOp that is under analysis.
bool constructed(const Context &ctx) const
Query if the analysis has constructed a Result object for the given Context.
virtual ~StructAnalysis()=default
Any type that is a subclass of StructAnalysis and provided a Context that matches ContextType.
void markAllOpsAsLive(DataFlowSolver &solver, Operation *top)
void ensure(bool condition, const llvm::Twine &errMsg)
FailureOr< ModuleOp > getRootModule(Operation *from)
An empty struct that is used for convenience for analyses that do not require any context.