From 1ebfa02e6d74b39709f7b57f66e14763b8d348a7 Mon Sep 17 00:00:00 2001 From: Michele Scuttari Date: Wed, 27 Nov 2024 09:51:31 +0100 Subject: [PATCH] Add variables pruning pass --- .../Dialect/BaseModelica/Transforms/Passes.h | 1 + .../Dialect/BaseModelica/Transforms/Passes.td | 22 + .../Transforms/VariablesPruning.h | 14 + .../public/marco/Frontend/CodegenOptions.h | 1 + .../Modeling/ArrayEquationsDependencyGraph.h | 3 + .../marco/Modeling/SingleEntryDigraph.h | 156 +++++++ .../BaseModelica/Transforms/CMakeLists.txt | 1 + .../Transforms/VariablesPruning.cpp | 425 ++++++++++++++++++ lib/Frontend/CompilerInvocation.cpp | 2 + lib/Frontend/FrontendActions.cpp | 4 + lib/Modeling/SingleEntryDigraph.cpp | 1 + .../VariablesPruning/array-variable.mlir | 46 ++ .../VariablesPruning/derivative.mlir | 63 +++ .../initial-and-dynamic-equations.mlir | 61 +++ .../VariablesPruning/initial-equations.mlir | 55 +++ .../multiple-writing-equations.mlir | 151 +++++++ .../VariablesPruning/no-output-variables.mlir | 24 + .../VariablesPruning/unused-variable.mlir | 38 ++ 18 files changed, 1068 insertions(+) create mode 100644 include/public/marco/Dialect/BaseModelica/Transforms/VariablesPruning.h create mode 100644 include/public/marco/Modeling/SingleEntryDigraph.h create mode 100644 lib/Dialect/BaseModelica/Transforms/VariablesPruning.cpp create mode 100644 lib/Modeling/SingleEntryDigraph.cpp create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/array-variable.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/derivative.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-and-dynamic-equations.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-equations.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/multiple-writing-equations.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/no-output-variables.mlir create mode 100644 test/Dialect/BaseModelica/Transforms/VariablesPruning/unused-variable.mlir diff --git a/include/public/marco/Dialect/BaseModelica/Transforms/Passes.h b/include/public/marco/Dialect/BaseModelica/Transforms/Passes.h index 00f21e87b..14534955a 100644 --- a/include/public/marco/Dialect/BaseModelica/Transforms/Passes.h +++ b/include/public/marco/Dialect/BaseModelica/Transforms/Passes.h @@ -42,6 +42,7 @@ #include "marco/Dialect/BaseModelica/Transforms/Scheduling.h" #include "marco/Dialect/BaseModelica/Transforms/SingleValuedInductionElimination.h" #include "marco/Dialect/BaseModelica/Transforms/VariablesPromotion.h" +#include "marco/Dialect/BaseModelica/Transforms/VariablesPruning.h" #include "marco/Dialect/BaseModelica/Transforms/ViewAccessFolding.h" namespace mlir::bmodelica { diff --git a/include/public/marco/Dialect/BaseModelica/Transforms/Passes.td b/include/public/marco/Dialect/BaseModelica/Transforms/Passes.td index 9e0b1ddc3..4cf9045e8 100644 --- a/include/public/marco/Dialect/BaseModelica/Transforms/Passes.td +++ b/include/public/marco/Dialect/BaseModelica/Transforms/Passes.td @@ -400,6 +400,28 @@ def VariablesPromotionPass let constructor = "mlir::bmodelica::createVariablesPromotionPass()"; } +def VariablesPruningPass : Pass<"variables-pruning", "mlir::ModuleOp"> +{ + let summary = "Remove the unneeded variables."; + + let description = [{ + Remove the variables and the associated matched equations that are not + needed to compute the output variables. + }]; + + let dependentDialects = [ + "mlir::bmodelica::BaseModelicaDialect" + ]; + + let options = [ + Option<"outputVariables", + "output-variables", "std::string", "", + "Comma separated list of output variables"> + ]; + + let constructor = "mlir::bmodelica::createVariablesPruningPass()"; +} + //===---------------------------------------------------------------------===// // Model solving //===---------------------------------------------------------------------===// diff --git a/include/public/marco/Dialect/BaseModelica/Transforms/VariablesPruning.h b/include/public/marco/Dialect/BaseModelica/Transforms/VariablesPruning.h new file mode 100644 index 000000000..5d6f80146 --- /dev/null +++ b/include/public/marco/Dialect/BaseModelica/Transforms/VariablesPruning.h @@ -0,0 +1,14 @@ +#ifndef MARCO_DIALECT_BASEMODELICA_TRANSFORMS_VARIABLESPRUNING_H +#define MARCO_DIALECT_BASEMODELICA_TRANSFORMS_VARIABLESPRUNING_H + +#include "mlir/Pass/Pass.h" + +namespace mlir::bmodelica +{ +#define GEN_PASS_DECL_VARIABLESPRUNINGPASS +#include "marco/Dialect/BaseModelica/Transforms/Passes.h.inc" + +std::unique_ptr createVariablesPruningPass(); +} + +#endif // MARCO_DIALECT_BASEMODELICA_TRANSFORMS_VARIABLESPRUNING_H diff --git a/include/public/marco/Frontend/CodegenOptions.h b/include/public/marco/Frontend/CodegenOptions.h index 3751b120f..bf8f1f3f3 100644 --- a/include/public/marco/Frontend/CodegenOptions.h +++ b/include/public/marco/Frontend/CodegenOptions.h @@ -20,6 +20,7 @@ struct CodegenOptions : public clang::CodeGenOptions { bool outputArraysPromotion = false; bool heapToStackPromotion = false; bool readOnlyVariablesPropagation = false; + bool variablesPruning = false; bool variablesToParametersPromotion = false; int64_t sccSolvingBySubstitutionMaxIterations = 100; int64_t sccSolvingBySubstitutionMaxEquationsInSCC = 5; diff --git a/include/public/marco/Modeling/ArrayEquationsDependencyGraph.h b/include/public/marco/Modeling/ArrayEquationsDependencyGraph.h index cf4c2b7a1..223429f9b 100644 --- a/include/public/marco/Modeling/ArrayEquationsDependencyGraph.h +++ b/include/public/marco/Modeling/ArrayEquationsDependencyGraph.h @@ -124,6 +124,9 @@ class ArrayEquationsDependencyGraph { return result; } + /// Get the writes map of all the equations added to the graph. + const WritesMap &getWritesMap() const { return writesMap; } + /// Map each array variable to the equations that write into some of its /// scalar positions. /// diff --git a/include/public/marco/Modeling/SingleEntryDigraph.h b/include/public/marco/Modeling/SingleEntryDigraph.h new file mode 100644 index 000000000..f86944baf --- /dev/null +++ b/include/public/marco/Modeling/SingleEntryDigraph.h @@ -0,0 +1,156 @@ +#ifndef MARCO_MODELING_SINGLEENTRYDIGRAPH_H +#define MARCO_MODELING_SINGLEENTRYDIGRAPH_H + +#include "marco/Modeling/Graph.h" +#include "llvm/ADT/GraphTraits.h" + +namespace marco::modeling::dependency { +template +class SingleEntryDigraph { +public: + using VertexProperty = VP; + using EdgeProperty = EP; + +private: + using Graph = internal::DirectedGraph, + std::unique_ptr>; + +public: + using VertexDescriptor = typename Graph::VertexDescriptor; + using EdgeDescriptor = typename Graph::EdgeDescriptor; + + using VertexIterator = typename Graph::VertexIterator; + using IncidentEdgeIterator = typename Graph::IncidentEdgeIterator; + using LinkedVerticesIterator = typename Graph::LinkedVerticesIterator; + + SingleEntryDigraph() : entryNode(graph.addVertex(nullptr)) {} + + VertexProperty &operator[](VertexDescriptor vertex) { + assert(vertex != entryNode && "The entry node doesn't have a property"); + return *graph[vertex]; + } + + const VertexProperty &operator[](VertexDescriptor vertex) const { + assert(vertex != entryNode && "The entry node doesn't have a property"); + return *graph[vertex]; + } + + EdgeProperty &operator[](EdgeDescriptor edge) { return *graph[edge]; } + + const EdgeProperty &operator[](EdgeDescriptor edge) const { + return *graph[edge]; + } + + size_t size() const { return graph.verticesCount(); } + + VertexDescriptor getEntryNode() const { return entryNode; } + + VertexDescriptor addVertex(VertexProperty property) { + auto descriptor = + graph.addVertex(std::make_unique(std::move(property))); + + return descriptor; + } + + auto verticesBegin() const { + return graph.verticesBegin( + [](const typename Graph::VertexProperty &vertex) { + // Hide the entry point + return vertex != nullptr; + }); + } + + auto verticesEnd() const { + return graph.verticesEnd([](const typename Graph::VertexProperty &vertex) { + // Hide the entry point + return vertex != nullptr; + }); + } + + EdgeDescriptor addEdge(VertexDescriptor from, VertexDescriptor to, + EdgeProperty property = EdgeProperty()) { + return graph.addEdge(from, to, + std::make_unique(std::move(property))); + } + + auto getEdges() const { return graph.getEdges(); } + + auto outgoingEdgesBegin(VertexDescriptor vertex) const { + return graph.outgoingEdgesBegin(std::move(vertex)); + } + + auto outgoingEdgesEnd(VertexDescriptor vertex) const { + return graph.outgoingEdgesEnd(std::move(vertex)); + } + + auto linkedVerticesBegin(VertexDescriptor vertex) const { + return graph.linkedVerticesBegin(std::move(vertex)); + } + + auto linkedVerticesEnd(VertexDescriptor vertex) const { + return graph.linkedVerticesEnd(std::move(vertex)); + } + +private: + Graph graph; + VertexDescriptor entryNode; +}; +} // namespace marco::modeling::dependency + +namespace llvm { +// We specialize the LLVM's graph traits in order leverage the algorithms +// that are defined inside LLVM itself. This way we don't have to implement +// them from scratch. +template +struct GraphTraits *> { + // The LLVM traits require the class specified as Graph to be copyable. + // We use its address to overcome this limitation. + using Graph = + const marco::modeling::dependency::SingleEntryDigraph; + + using GraphPtr = Graph *; + + using NodeRef = typename Graph::VertexDescriptor; + using ChildIteratorType = typename Graph::LinkedVerticesIterator; + + static NodeRef getEntryNode(const GraphPtr &graph) { + return graph->getEntryNode(); + } + + static ChildIteratorType child_begin(NodeRef node) { + return node.graph->linkedVerticesBegin(node); + } + + static ChildIteratorType child_end(NodeRef node) { + return node.graph->linkedVerticesEnd(node); + } + + using nodes_iterator = typename Graph::VertexIterator; + + static nodes_iterator nodes_begin(GraphPtr *graph) { + return (*graph)->verticesBegin(); + } + + static nodes_iterator nodes_end(GraphPtr *graph) { + return (*graph)->verticesEnd(); + } + + using EdgeRef = typename Graph::EdgeDescriptor; + using ChildEdgeIteratorType = typename Graph::IncidentEdgeIterator; + + static ChildEdgeIteratorType child_edge_begin(NodeRef node) { + return node.graph->outgoingEdgesBegin(node); + } + + static ChildEdgeIteratorType child_edge_end(NodeRef node) { + return node.graph->outgoingEdgesEnd(node); + } + + static NodeRef edge_dest(EdgeRef edge) { return edge.to; } + + static size_t size(GraphPtr *graph) { return (*graph)->size(); } +}; +} // namespace llvm + +#endif // MARCO_MODELING_SINGLEENTRYDIGRAPH_H diff --git a/lib/Dialect/BaseModelica/Transforms/CMakeLists.txt b/lib/Dialect/BaseModelica/Transforms/CMakeLists.txt index 395dced5c..69c8f8b59 100644 --- a/lib/Dialect/BaseModelica/Transforms/CMakeLists.txt +++ b/lib/Dialect/BaseModelica/Transforms/CMakeLists.txt @@ -56,6 +56,7 @@ add_mlir_dialect_library(MLIRBaseModelicaTransforms Scheduling.cpp SingleValuedInductionElimination.cpp VariablesPromotion.cpp + VariablesPruning.cpp VectorizableOpInterfaceImpl.cpp ViewAccessFolding.cpp diff --git a/lib/Dialect/BaseModelica/Transforms/VariablesPruning.cpp b/lib/Dialect/BaseModelica/Transforms/VariablesPruning.cpp new file mode 100644 index 000000000..4e5c8c274 --- /dev/null +++ b/lib/Dialect/BaseModelica/Transforms/VariablesPruning.cpp @@ -0,0 +1,425 @@ +#include "marco/Dialect/BaseModelica/Transforms/VariablesPruning.h" +#include "marco/Dialect/BaseModelica/IR/BaseModelica.h" +#include "marco/Dialect/BaseModelica/Transforms/Modeling/Bridge.h" +#include "marco/Modeling/ArrayEquationsDependencyGraph.h" +#include "marco/Modeling/SingleEntryDigraph.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" + +namespace mlir::bmodelica { +#define GEN_PASS_DEF_VARIABLESPRUNINGPASS +#include "marco/Dialect/BaseModelica/Transforms/Passes.h.inc" +} // namespace mlir::bmodelica + +using namespace ::mlir::bmodelica; +using namespace ::mlir::bmodelica::bridge; + +namespace { +class VariablesPruningPass + : public mlir::bmodelica::impl::VariablesPruningPassBase< + VariablesPruningPass> { +public: + using VariablesPruningPassBase< + VariablesPruningPass>::VariablesPruningPassBase; + + void runOnOperation() override; + +private: + std::optional> + getVariableAccessAnalysis(EquationTemplateOp equationTemplate, + mlir::SymbolTableCollection &symbolTableCollection); + + mlir::LogicalResult processModelOp(ModelOp modelOp); + + mlir::LogicalResult + collectUsedVariables(llvm::DenseSet &usedVariables, + mlir::SymbolTableCollection &symbolTableCollection, + ModelOp modelOp, + const llvm::DenseSet& outputVariables, + llvm::ArrayRef equations); + + mlir::LogicalResult + removeIfUnused(mlir::RewriterBase &rewriter, + mlir::SymbolTableCollection &symbolTableCollection, + ModelOp modelOp, MatchedEquationInstanceOp equationOp, + const llvm::DenseSet &usedVariables); + + mlir::LogicalResult cleanModelOp(ModelOp modelOp); +}; +} // namespace + +void VariablesPruningPass::runOnOperation() { + llvm::SmallVector modelOps; + + walkClasses(getOperation(), [&](mlir::Operation *op) { + if (auto modelOp = mlir::dyn_cast(op)) { + modelOps.push_back(modelOp); + } + }); + + auto runFn = [&](mlir::Operation *op) { + auto modelOp = mlir::cast(op); + + if (mlir::failed(processModelOp(modelOp))) { + return mlir::failure(); + } + + if (mlir::failed(cleanModelOp(modelOp))) { + return mlir::failure(); + } + + return mlir::success(); + }; + + if (mlir::failed( + mlir::failableParallelForEach(&getContext(), modelOps, runFn))) { + return signalPassFailure(); + } +} + +std::optional> +VariablesPruningPass::getVariableAccessAnalysis( + EquationTemplateOp equationTemplate, + mlir::SymbolTableCollection &symbolTableCollection) { + mlir::ModuleOp moduleOp = getOperation(); + mlir::Operation *parentOp = equationTemplate->getParentOp(); + llvm::SmallVector parentOps; + + while (parentOp != moduleOp) { + parentOps.push_back(parentOp); + parentOp = parentOp->getParentOp(); + } + + mlir::AnalysisManager analysisManager = getAnalysisManager(); + + for (mlir::Operation *op : llvm::reverse(parentOps)) { + analysisManager = analysisManager.nest(op); + } + + if (auto analysis = + analysisManager.getCachedChildAnalysis( + equationTemplate)) { + return *analysis; + } + + auto &analysis = analysisManager.getChildAnalysis( + equationTemplate); + + if (mlir::failed(analysis.initialize(symbolTableCollection))) { + return std::nullopt; + } + + return std::reference_wrapper(analysis); +} + +mlir::LogicalResult VariablesPruningPass::processModelOp(ModelOp modelOp) { + mlir::SymbolTableCollection symbolTableCollection; + mlir::IRRewriter rewriter(&getContext()); + + llvm::SmallVector variableOps; + modelOp.collectVariables(variableOps); + + // Get the output variables. + llvm::DenseSet outputVariables; + + for (VariableOp variableOp : variableOps) { + if (variableOp.getVariableType().isOutput()) { + outputVariables.insert(variableOp); + } + } + + // Don't perform any optimization if no output variables have been found. + // The transformation would otherwise result in the elimination of all + // variables and equations. + + if (outputVariables.empty()) { + return mlir::success(); + } + + // Collect the equations. + llvm::SmallVector initialEquations; + llvm::SmallVector dynamicEquations; + llvm::SmallVector allEquations; + + modelOp.collectInitialEquations(initialEquations); + modelOp.collectMainEquations(dynamicEquations); + + allEquations.append(initialEquations); + allEquations.append(dynamicEquations); + + // Collect the used variables. + llvm::DenseSet usedVariables; + + /* + if (mlir::failed(collectUsedVariables(usedVariables, symbolTableCollection, + modelOp, outputVariables, + initialEquations))) { + return mlir::failure(); + } + + if (mlir::failed(collectUsedVariables(usedVariables, symbolTableCollection, + modelOp, outputVariables, + dynamicEquations))) { + return mlir::failure(); + } + */ + + if (mlir::failed(collectUsedVariables(usedVariables, symbolTableCollection, + modelOp, outputVariables, + allEquations))) { + return mlir::failure(); + } + + // Remove the unneeded equations. + for (MatchedEquationInstanceOp equationOp : initialEquations) { + if (mlir::failed(removeIfUnused(rewriter, symbolTableCollection, modelOp, + equationOp, usedVariables))) { + return mlir::failure(); + } + } + + for (MatchedEquationInstanceOp equationOp : dynamicEquations) { + if (mlir::failed(removeIfUnused(rewriter, symbolTableCollection, modelOp, + equationOp, usedVariables))) { + return mlir::failure(); + } + } + + // Remove the start operations. + for (StartOp startOp : + llvm::make_early_inc_range(modelOp.getOps())) { + auto variableOp = symbolTableCollection.lookupSymbolIn( + modelOp, startOp.getVariable()); + + if (!usedVariables.contains(variableOp)) { + rewriter.eraseOp(startOp); + } + } + + // Remove the binding equations. + for (BindingEquationOp bindingEquationOp : + llvm::make_early_inc_range(modelOp.getOps())) { + auto variableOp = symbolTableCollection.lookupSymbolIn( + modelOp, bindingEquationOp.getVariableAttr()); + + if (!usedVariables.contains(variableOp)) { + rewriter.eraseOp(bindingEquationOp); + } + } + + // Remove the unneeded variables. + for (VariableOp variableOp : + llvm::make_early_inc_range(modelOp.getVariables())) { + if (!usedVariables.contains(variableOp)) { + rewriter.eraseOp(variableOp); + } + } + + return mlir::success(); +} + +namespace { +void walkDerivedVariables(ModelOp modelOp, mlir::SymbolRefAttr variable, + std::function callbackFn) { + const auto &derivativesMap = modelOp.getProperties().derivativesMap; + auto derivedVar = derivativesMap.getDerivedVariable(variable); + + while (derivedVar) { + callbackFn(*derivedVar); + derivedVar = derivativesMap.getDerivedVariable(*derivedVar); + } +} + +void walkDerivativeVariables( + ModelOp modelOp, mlir::SymbolRefAttr variable, + std::function callbackFn) { + const auto &derivativesMap = modelOp.getProperties().derivativesMap; + auto derivativeVar = derivativesMap.getDerivative(variable); + + while (derivativeVar) { + callbackFn(*derivativeVar); + derivativeVar = derivativesMap.getDerivative(*derivativeVar); + } +} +} // namespace + +mlir::LogicalResult VariablesPruningPass::collectUsedVariables( + llvm::DenseSet &usedVariables, + mlir::SymbolTableCollection &symbolTableCollection, ModelOp modelOp, + const llvm::DenseSet& outputVariables, + llvm::ArrayRef equations) { + // Create the dependency graph. + using DependencyGraph = marco::modeling::ArrayEquationsDependencyGraph< + VariableBridge *, MatchedEquationBridge *, + marco::modeling::dependency::SingleEntryDigraph< + marco::modeling::internal::dependency::ArrayEquation< + MatchedEquationBridge *>>>; + + using DependencyGraphBase = typename DependencyGraph::Base; + + auto baseGraph = std::make_shared(); + DependencyGraph graph(&getContext(), baseGraph); + + llvm::SmallVector> variableBridges; + llvm::DenseMap variablesMap; + llvm::SmallVector> equationBridges; + llvm::SmallVector equationPtrs; + + for (VariableOp variableOp : modelOp.getVariables()) { + auto &bridge = + variableBridges.emplace_back(VariableBridge::build(variableOp)); + + auto symbolRefAttr = mlir::SymbolRefAttr::get(variableOp.getSymNameAttr()); + variablesMap[symbolRefAttr] = bridge.get(); + } + + for (MatchedEquationInstanceOp equation : equations) { + auto variableAccessAnalysis = getVariableAccessAnalysis( + equation.getTemplate(), symbolTableCollection); + + auto &bridge = equationBridges.emplace_back( + MatchedEquationBridge::build(equation, symbolTableCollection, + *variableAccessAnalysis, variablesMap)); + + equationPtrs.push_back(bridge.get()); + } + + graph.addEquations(equationPtrs); + + // Add the implicit relations introduced by derivatives. + for (VariableOp outputVariable : outputVariables) { + walkDerivedVariables( + modelOp, mlir::SymbolRefAttr::get(outputVariable.getSymNameAttr()), + [&](mlir::SymbolRefAttr derivedVarName) { + auto writingEquations = + graph.getWritesMap().equal_range(variablesMap[derivedVarName]); + + for (auto writeInfo : llvm::make_range(writingEquations)) { + baseGraph->addEdge(baseGraph->getEntryNode(), + writeInfo.second.getEquation()); + } + }); + + walkDerivativeVariables( + modelOp, mlir::SymbolRefAttr::get(outputVariable.getSymNameAttr()), + [&](mlir::SymbolRefAttr derivativeVarName) { + auto writingEquations = + graph.getWritesMap().equal_range(variablesMap[derivativeVarName]); + + for (auto writeInfo : llvm::make_range(writingEquations)) { + baseGraph->addEdge(baseGraph->getEntryNode(), + writeInfo.second.getEquation()); + } + }); + } + + for (auto equationDescriptor : + llvm::make_range(baseGraph->verticesBegin(), baseGraph->verticesEnd())) { + const auto &equationBridge = (*baseGraph)[equationDescriptor]; + + for (const auto &readAccess : equationBridge.getReads()) { + mlir::SymbolRefAttr readVariable = readAccess.getVariable()->name; + + walkDerivedVariables( + modelOp, readVariable, [&](mlir::SymbolRefAttr derivedVarName) { + auto writingEquations = + graph.getWritesMap().equal_range(variablesMap[derivedVarName]); + + for (auto writeInfo : llvm::make_range(writingEquations)) { + baseGraph->addEdge(equationDescriptor, + writeInfo.second.getEquation()); + } + }); + + walkDerivativeVariables( + modelOp, readVariable, [&](mlir::SymbolRefAttr derivativeVarName) { + auto writingEquations = graph.getWritesMap().equal_range( + variablesMap[derivativeVarName]); + + for (auto writeInfo : llvm::make_range(writingEquations)) { + baseGraph->addEdge(writeInfo.second.getEquation(), + equationDescriptor); + } + }); + } + } + + // Connect the entry node to the output variable. + for (VariableOp outputVariable : outputVariables) { + auto variableName = + mlir::SymbolRefAttr::get(outputVariable.getSymNameAttr()); + + auto writingEquations = + graph.getWritesMap().equal_range(variablesMap[variableName]); + + for (const auto& writeInfo : llvm::make_range(writingEquations)) { + baseGraph->addEdge(baseGraph->getEntryNode(), + writeInfo.second.getEquation()); + } + } + + // Collect the variables. + for (auto scc : graph.getSCCs()) { + for (auto equationDescriptor : scc) { + auto variableName = + scc[equationDescriptor].getWrite().getVariable()->name; + + auto variableOp = symbolTableCollection.lookupSymbolIn( + modelOp, variableName); + + usedVariables.insert(variableOp); + + walkDerivedVariables( + modelOp, variableName, [&](mlir::SymbolRefAttr derivedVarName) { + auto derivedVariableOp = + symbolTableCollection.lookupSymbolIn( + modelOp, derivedVarName); + + usedVariables.insert(derivedVariableOp); + }); + + walkDerivativeVariables( + modelOp, variableName, [&](mlir::SymbolRefAttr derivativeVarName) { + auto derivativeVariableOp = + symbolTableCollection.lookupSymbolIn( + modelOp, derivativeVarName); + + usedVariables.insert(derivativeVariableOp); + }); + } + } + + return mlir::success(); +} + +mlir::LogicalResult VariablesPruningPass::removeIfUnused( + mlir::RewriterBase &rewriter, + mlir::SymbolTableCollection &symbolTableCollection, ModelOp modelOp, + MatchedEquationInstanceOp equationOp, + const llvm::DenseSet &usedVariables) { + auto matchedAccess = equationOp.getMatchedAccess(symbolTableCollection); + + if (!matchedAccess) { + return mlir::failure(); + } + + auto variableOp = symbolTableCollection.lookupSymbolIn( + modelOp, matchedAccess->getVariable()); + + if (!usedVariables.contains(variableOp)) { + rewriter.eraseOp(equationOp); + } + + return mlir::success(); +} + +mlir::LogicalResult VariablesPruningPass::cleanModelOp(ModelOp modelOp) { + mlir::RewritePatternSet patterns(&getContext()); + ModelOp::getCleaningPatterns(patterns, &getContext()); + return mlir::applyPatternsAndFoldGreedily(modelOp, std::move(patterns)); +} + +namespace mlir::bmodelica { +std::unique_ptr createVariablesPruningPass() { + return std::make_unique(); +} +} // namespace mlir::bmodelica diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 0c3d2fad9..a0043f82a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -513,6 +513,7 @@ static void parseCodegenArgs(marco::frontend::CodegenOptions &options, options.outputArraysPromotion = true; options.heapToStackPromotion = true; options.readOnlyVariablesPropagation = true; + options.variablesPruning = true; options.variablesToParametersPromotion = true; options.inlining = true; options.cse = true; @@ -525,6 +526,7 @@ static void parseCodegenArgs(marco::frontend::CodegenOptions &options, options.debug = false; options.cse = true; options.singleValuedInductionElimination = true; + options.variablesPruning = true; options.loopFusion = true; options.loopCoalescing = true; } diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index cb55ddb72..d740d44fd 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -833,6 +833,10 @@ void CodeGenAction::buildMLIRLoweringPipeline(mlir::PassManager &pm) { pm.addPass(mlir::bmodelica::createSingleValuedInductionEliminationPass()); } + if (ci.getCodeGenOptions().variablesPruning) { + pm.addPass(mlir::bmodelica::createVariablesPruningPass()); + } + pm.addPass(mlir::bmodelica::createSCCDetectionPass()); if (ci.getCodeGenOptions().variablesToParametersPromotion) { diff --git a/lib/Modeling/SingleEntryDigraph.cpp b/lib/Modeling/SingleEntryDigraph.cpp new file mode 100644 index 000000000..00ff6188d --- /dev/null +++ b/lib/Modeling/SingleEntryDigraph.cpp @@ -0,0 +1 @@ +#include "marco/Modeling/SingleEntryDigraph.h" diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/array-variable.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/array-variable.mlir new file mode 100644 index 000000000..90f51eb34 --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/array-variable.mlir @@ -0,0 +1,46 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// Array dependency. + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable<5x!bmodelica.real> +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable + +// CHECK: %[[t0:.*]] = bmodelica.equation_template inductions = [%{{.*}}] attributes {id = "t0"} +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t0]] {indices = #modeling, path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test { + bmodelica.variable @x : !bmodelica.variable<5x!bmodelica.real> + bmodelica.variable @y : !bmodelica.variable + + // x[i] = 0 + %t0 = bmodelica.equation_template inductions = [%i] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : tensor<5x!bmodelica.real> + %1 = bmodelica.tensor_extract %0[%i] : tensor<5x!bmodelica.real> + %2 = bmodelica.constant #bmodelica + %3 = bmodelica.equation_side %1 : tuple + %4 = bmodelica.equation_side %2 : tuple + bmodelica.equation_sides %3, %4 : tuple, tuple + } + + // y = x[0] + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.variable_get @x : tensor<5x!bmodelica.real> + %2 = bmodelica.constant 0 : index + %3 = bmodelica.tensor_extract %1[%2] : tensor<5x!bmodelica.real> + %4 = bmodelica.equation_side %0 : tuple + %5 = bmodelica.equation_side %3 : tuple + bmodelica.equation_sides %4, %5 : tuple, tuple + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t0 {indices = #modeling, path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/derivative.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/derivative.mlir new file mode 100644 index 000000000..0c52aeb42 --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/derivative.mlir @@ -0,0 +1,63 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// Output attribute on derived variable. + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @der_x : !bmodelica.variable + +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test der = [<@x, @der_x>] { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @der_x : !bmodelica.variable + + // der_x = 1 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @der_x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + } +} + +// ----- + +// Output attribute on derivative variable. + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @der_x : !bmodelica.variable + +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test der = [<@x, @der_x>] { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @der_x : !bmodelica.variable + + // der_x = 1 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @der_x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-and-dynamic-equations.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-and-dynamic-equations.mlir new file mode 100644 index 000000000..06a55a5f9 --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-and-dynamic-equations.mlir @@ -0,0 +1,61 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @z : !bmodelica.variable + +// CHECK: %[[t0:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} +// CHECK: %[[t2:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t2"} + +// CHECK: bmodelica.initial { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t0]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: } + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t2]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @y : !bmodelica.variable + bmodelica.variable @z : !bmodelica.variable + + // x = 0 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // y = x + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.variable_get @x : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = y + %t2 = bmodelica.equation_template inductions = [] attributes {id = "t2"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.variable_get @y : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.initial { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t2 {path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-equations.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-equations.mlir new file mode 100644 index 000000000..faff4839b --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/initial-equations.mlir @@ -0,0 +1,55 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @z : !bmodelica.variable + +// CHECK: %[[t0:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} +// CHECK: %[[t2:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t2"} + +// CHECK: bmodelica.initial { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t0]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t2]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @y : !bmodelica.variable + bmodelica.variable @z : !bmodelica.variable + + // x = 0 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // y = x + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.variable_get @x : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = y + %t2 = bmodelica.equation_template inductions = [] attributes {id = "t2"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.variable_get @y : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.initial { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t2 {path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/multiple-writing-equations.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/multiple-writing-equations.mlir new file mode 100644 index 000000000..5fd0d8fb3 --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/multiple-writing-equations.mlir @@ -0,0 +1,151 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// Initial equation with dependency. + +// CHECK-LABEL: @InitialWithDependency +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @z : !bmodelica.variable + +// CHECK: %[[t0:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} +// CHECK: %[[t2:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t2"} +// CHECK: %[[t3:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t3"} + +// CHECK: bmodelica.initial { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t0]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t2]] {path = #bmodelica} +// CHECK-NEXT: } + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t3]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @InitialWithDependency { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @y : !bmodelica.variable + bmodelica.variable @z : !bmodelica.variable + + // x = 0 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // y = x + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.variable_get @x : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = y + %t2 = bmodelica.equation_template inductions = [] attributes {id = "t2"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.variable_get @y : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = 0 + %t3 = bmodelica.equation_template inductions = [] attributes {id = "t3"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.initial { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t2 {path = #bmodelica} : !bmodelica.equation + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t3 {path = #bmodelica} : !bmodelica.equation + } +} + +// ----- + +// Dyanmic equation with dependency. + +// CHECK-LABEL: @DynamicWithDependency +// CHECK-NEXT: bmodelica.variable @x : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable +// CHECK-NEXT: bmodelica.variable @z : !bmodelica.variable + +// CHECK: %[[t0:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t0"} +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} +// CHECK: %[[t2:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t2"} +// CHECK: %[[t3:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t3"} + +// CHECK: bmodelica.initial { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t0]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t2]] {path = #bmodelica} +// CHECK-NEXT: } + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t3]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @DynamicWithDependency { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @y : !bmodelica.variable + bmodelica.variable @z : !bmodelica.variable + + // x = 0 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // y = x + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.variable_get @x : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = 0 + %t2 = bmodelica.equation_template inductions = [] attributes {id = "t2"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // z = y + %t3 = bmodelica.equation_template inductions = [] attributes {id = "t3"} { + %0 = bmodelica.variable_get @z : !bmodelica.real + %1 = bmodelica.variable_get @y : !bmodelica.real + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.initial { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t2 {path = #bmodelica} : !bmodelica.equation + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t3 {path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/no-output-variables.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/no-output-variables.mlir new file mode 100644 index 000000000..5a59695bf --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/no-output-variables.mlir @@ -0,0 +1,24 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// CHECK-DAG: bmodelica.variable @x : !bmodelica.variable<3x!bmodelica.real> +// CHECK-DAG: %[[t0:.*]] = bmodelica.equation_template inductions = [%{{.*}}] attributes {id = "t0"} +// CHECK: bmodelica.dynamic +// CHECK: bmodelica.matched_equation_instance %[[t0]] + +bmodelica.model @Test { + bmodelica.variable @x : !bmodelica.variable<3x!bmodelica.real> + + // x = 0 + %t0 = bmodelica.equation_template inductions = [%i0] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : tensor<3x!bmodelica.real> + %1 = bmodelica.tensor_extract %0[%i0] : tensor<3x!bmodelica.real> + %2 = bmodelica.constant #bmodelica + %3 = bmodelica.equation_side %1 : tuple + %4 = bmodelica.equation_side %2 : tuple + bmodelica.equation_sides %3, %4 : tuple, tuple + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t0 {indices = #modeling, path = #bmodelica} : !bmodelica.equation + } +} diff --git a/test/Dialect/BaseModelica/Transforms/VariablesPruning/unused-variable.mlir b/test/Dialect/BaseModelica/Transforms/VariablesPruning/unused-variable.mlir new file mode 100644 index 000000000..5b324e61b --- /dev/null +++ b/test/Dialect/BaseModelica/Transforms/VariablesPruning/unused-variable.mlir @@ -0,0 +1,38 @@ +// RUN: modelica-opt %s --split-input-file --variables-pruning | FileCheck %s + +// CHECK-LABEL: @Test +// CHECK-NEXT: bmodelica.variable @y : !bmodelica.variable + +// CHECK: %[[t1:.*]] = bmodelica.equation_template inductions = [] attributes {id = "t1"} + +// CHECK: bmodelica.dynamic { +// CHECK-NEXT: bmodelica.matched_equation_instance %[[t1]] {path = #bmodelica} +// CHECK-NEXT: } + +bmodelica.model @Test { + bmodelica.variable @x : !bmodelica.variable + bmodelica.variable @y : !bmodelica.variable + + // x = 0 + %t0 = bmodelica.equation_template inductions = [] attributes {id = "t0"} { + %0 = bmodelica.variable_get @x : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + // y = 0 + %t1 = bmodelica.equation_template inductions = [] attributes {id = "t1"} { + %0 = bmodelica.variable_get @y : !bmodelica.real + %1 = bmodelica.constant #bmodelica + %2 = bmodelica.equation_side %0 : tuple + %3 = bmodelica.equation_side %1 : tuple + bmodelica.equation_sides %2, %3 : tuple, tuple + } + + bmodelica.dynamic { + bmodelica.matched_equation_instance %t0 {path = #bmodelica} : !bmodelica.equation + bmodelica.matched_equation_instance %t1 {path = #bmodelica} : !bmodelica.equation + } +}