From 6e592c9b082cb93395372189468392de4dc5ccd5 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sun, 28 Sep 2025 14:54:18 +0200 Subject: [PATCH 01/52] Baby steps. Writing out some graphs. --- jlm/hls/Makefile.sub | 2 + jlm/llvm/Makefile.sub | 2 + .../ir/operators/MemoryStateOperations.cpp | 18 +- jlm/llvm/opt/PartialRedundancyElimination.cpp | 579 ++++++++++++++++++ jlm/llvm/opt/PartialRedundancyElimination.hpp | 112 ++++ jlm/tooling/Command.cpp | 3 + jlm/tooling/CommandLine.cpp | 12 + jlm/tooling/CommandLine.hpp | 3 + jlm/util/Statistics.hpp | 1 + 9 files changed, 728 insertions(+), 4 deletions(-) create mode 100644 jlm/llvm/opt/PartialRedundancyElimination.cpp create mode 100644 jlm/llvm/opt/PartialRedundancyElimination.hpp diff --git a/jlm/hls/Makefile.sub b/jlm/hls/Makefile.sub index 137054364..abad3674b 100644 --- a/jlm/hls/Makefile.sub +++ b/jlm/hls/Makefile.sub @@ -18,6 +18,7 @@ libhls_SOURCES = \ jlm/hls/backend/rvsdg2rhls/alloca-conv.cpp \ jlm/hls/backend/rvsdg2rhls/check-rhls.cpp \ jlm/hls/backend/rvsdg2rhls/DeadNodeElimination.cpp \ + jlm/hls/backend/rvsdg2rhls/PartialRedundancyElimination.cpp \ jlm/hls/backend/rvsdg2rhls/decouple-mem-state.cpp \ jlm/hls/backend/rvsdg2rhls/distribute-constants.cpp \ jlm/hls/backend/rvsdg2rhls/GammaConversion.cpp \ @@ -60,6 +61,7 @@ libhls_HEADERS = \ jlm/hls/backend/rvsdg2rhls/alloca-conv.hpp \ jlm/hls/backend/rvsdg2rhls/check-rhls.hpp \ jlm/hls/backend/rvsdg2rhls/DeadNodeElimination.hpp \ + jlm/hls/backend/rvsdg2rhls/PartialRedundancyElimination.hpp \ jlm/hls/backend/rvsdg2rhls/decouple-mem-state.hpp \ jlm/hls/backend/rvsdg2rhls/distribute-constants.hpp \ jlm/hls/backend/rvsdg2rhls/GammaConversion.hpp \ diff --git a/jlm/llvm/Makefile.sub b/jlm/llvm/Makefile.sub index 1b118171a..8fa267fca 100644 --- a/jlm/llvm/Makefile.sub +++ b/jlm/llvm/Makefile.sub @@ -61,6 +61,7 @@ libllvm_SOURCES = \ jlm/llvm/opt/alias-analyses/TopDownModRefEliminator.cpp \ jlm/llvm/opt/cne.cpp \ jlm/llvm/opt/DeadNodeElimination.cpp \ + jlm/llvm/opt/PartialRedundancyElimination.cpp \ jlm/llvm/opt/IfConversion.cpp \ jlm/llvm/opt/inlining.cpp \ jlm/llvm/opt/InvariantValueRedirection.cpp \ @@ -79,6 +80,7 @@ libllvm_HEADERS = \ \ jlm/llvm/opt/unroll.hpp \ jlm/llvm/opt/DeadNodeElimination.hpp \ + jlm/llvm/opt/PartialRedundancyElimination.hpp \ jlm/llvm/opt/inlining.hpp \ jlm/llvm/opt/cne.hpp \ jlm/llvm/opt/push.hpp \ diff --git a/jlm/llvm/ir/operators/MemoryStateOperations.cpp b/jlm/llvm/ir/operators/MemoryStateOperations.cpp index 0102ac7d9..1bb19411f 100644 --- a/jlm/llvm/ir/operators/MemoryStateOperations.cpp +++ b/jlm/llvm/ir/operators/MemoryStateOperations.cpp @@ -39,8 +39,13 @@ MemoryStateMergeOperation::NormalizeSingleOperand( const MemoryStateMergeOperation &, const std::vector & operands) { - if (operands.size() == 1) - return operands; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wstringop-overflow" + if (operands.size() == 1){ + return {operands}; + } +#pragma GCC diagnostic pop return std::nullopt; } @@ -140,8 +145,13 @@ MemoryStateJoinOperation::NormalizeSingleOperand( const MemoryStateJoinOperation &, const std::vector & operands) { - if (operands.size() == 1) - return operands; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wstringop-overflow" + if (operands.size() == 1){ + return {operands}; + } +#pragma GCC diagnostic pop return std::nullopt; } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp new file mode 100644 index 000000000..3cf889964 --- /dev/null +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -0,0 +1,579 @@ +/* + * Copyright 2017 Nico Reißmann + * See COPYING for terms of redistribution. + */ + +#define TR_CMD "\033" +#define TR_RESET TR_CMD "[0m" + +#define TR_FG(r,g,b) TR_CMD "[38;2;" #r ";" #g ";" #b "m" +#define TR_RED TR_FG(255,64,64) +#define TR_GREEN TR_FG(64, 255, 64) +#define TR_YELLOW TR_FG(255, 255, 64) +#define TR_BLUE TR_FG(64, 64, 255) +#define TR_CYAN TR_FG(64, 255, 255) + +#include "../../rvsdg/structural-node.hpp" +#include "../../util/GraphWriter.hpp" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jlm::llvm +{ + + +void StrictHasher::populate_table(rvsdg::Region& reg) +{ + for (auto node : rvsdg::TopDownTraverser(®)){ + + std::cout << TR_RED << node << TR_RESET << std::endl; + std::cout << TR_RED << "==========================================" << std::endl; + rvsdg::MatchTypeWithDefault( + *node, + [](const rvsdg::LambdaNode & node) + { + std::cout<(output)) + { + SimpleNodes_.Insert(simpleNode); + return; + } + + Outputs_.Insert(&output); + }*/ + + /*bool + IsAlive(const jlm::rvsdg::Output & output) const noexcept + { + if (auto simpleNode = rvsdg::TryGetOwnerNode(output)) + { + return SimpleNodes_.Contains(simpleNode); + } + + return Outputs_.Contains(&output); + }*/ + + /*bool + IsAlive(const rvsdg::Node & node) const noexcept + { + if (auto simpleNode = dynamic_cast(&node)) + { + return SimpleNodes_.Contains(simpleNode); + } + + for (size_t n = 0; n < node.noutputs(); n++) + { + if (IsAlive(*node.output(n))) + { + return true; + } + } + + return false; + }*/ + + static std::unique_ptr + Create() + { + return std::make_unique(); + } + +private: + util::HashSet SimpleNodes_; + util::HashSet Outputs_; +}; + + + +/** \brief Dead Node Elimination statistics class + * + */ +class PartialRedundancyElimination::Statistics final : public util::Statistics +{ + const char * MarkTimerLabel_ = "MarkTime"; + const char * SweepTimerLabel_ = "SweepTime"; + +public: + ~Statistics() override = default; + + explicit Statistics(const util::FilePath & sourceFile) + : util::Statistics(Statistics::Id::PartialRedundancyElimination, sourceFile) + {} + + void + StartMarkStatistics(const rvsdg::Graph & graph) noexcept + { + AddMeasurement(Label::NumRvsdgNodesBefore, rvsdg::nnodes(&graph.GetRootRegion())); + AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion())); + AddTimer(MarkTimerLabel_).start(); + } + + void + StopMarkStatistics() noexcept + { + GetTimer(MarkTimerLabel_).stop(); + } + + static std::unique_ptr + Create(const util::FilePath & sourceFile) + { + return std::make_unique(sourceFile); + } +}; + +PartialRedundancyElimination::~PartialRedundancyElimination() noexcept = default; + +PartialRedundancyElimination::PartialRedundancyElimination() + : Transformation("PartialRedundancyElimination") +{} +/* +void +PartialRedundancyElimination::run(rvsdg::Region & region) +{ + std::cerr << TR_RED << "Not actually called?" << TR_RESET << std::endl; + Context_ = Context::Create(); + + MarkRegion(region); + SweepRegion(region); + + // Discard internal state to free up memory after we are done + Context_.reset(); +}*/ + +static int regions_dumped = 0; + +static void dump_region(jlm::rvsdg::Region& reg, std::string& name) +{ + { + auto my_graph_writer = new jlm::util::graph::Writer(); + + jlm::llvm::dot::LlvmDotWriter my_dot_writer; + my_dot_writer.WriteGraphs(*my_graph_writer , reg, false); + + std::string full_name = name+std::to_string(regions_dumped++)+".dot"; + std::cout<< TR_RED<OutputAllGraphs(my_dot_oss, jlm::util::graph::OutputFormat::Dot); + my_dot_oss.close(); + + delete my_graph_writer; + } +} + +void +PartialRedundancyElimination::Run( + rvsdg::RvsdgModule & module, + util::StatisticsCollector & statisticsCollector) +{ + std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; + + + Context_ = Context::Create(); + + auto & rvsdg = module.Rvsdg(); + auto statistics = Statistics::Create(module.SourceFilePath().value()); + + StrictHasher myhasher; + myhasher.populate_table(rvsdg.GetRootRegion()); + + for (auto& node : rvsdg.GetRootRegion().Nodes()) + { + rvsdg::StructuralNode* snode = dynamic_cast(&node); + if (snode) + { + for (auto& sub_reg : snode->Subregions()) + { + std::string tmp = snode->DebugString(); + dump_region(sub_reg, tmp); + } + } + } + + std::cout << TR_RESET; + +/* + util::graph::Graph & + WriteGraphs(util::graph::Writer & writer, Region & region, bool emitTypeGraph); +*/ + statistics->StartMarkStatistics(rvsdg); + //MarkRegion(rvsdg.GetRootRegion()); + statistics->StopMarkStatistics(); + + //statistics->StartSweepStatistics(); + //SweepRvsdg(rvsdg); + //statistics->StopSweepStatistics(rvsdg); + + statisticsCollector.CollectDemandedStatistics(std::move(statistics)); + + // Discard internal state to free up memory after we are done + Context_.reset(); +} +/* +void +PartialRedundancyElimination::MarkRegion(const rvsdg::Region & region) +{ + for (size_t n = 0; n < region.nresults(); n++) + { + MarkOutput(*region.result(n)->origin()); + } +}*/ +/* +void +PartialRedundancyElimination::MarkOutput(const jlm::rvsdg::Output & output) +{ + if (Context_->IsAlive(output)) + { + return; + } + + Context_->MarkAlive(output); + + if (is(&output)) + { + return; + } + + if (auto gamma = rvsdg::TryGetOwnerNode(output)) + { + MarkOutput(*gamma->predicate()->origin()); + for (const auto & result : gamma->MapOutputExitVar(output).branchResult) + { + MarkOutput(*result->origin()); + } + return; + } + + if (auto gamma = rvsdg::TryGetRegionParentNode(output)) + { + auto external_origin = std::visit( + [](const auto & rolevar) -> rvsdg::Output * + { + return rolevar.input->origin(); + }, + gamma->MapBranchArgument(output)); + MarkOutput(*external_origin); + return; + } + + if (auto theta = rvsdg::TryGetOwnerNode(output)) + { + auto loopvar = theta->MapOutputLoopVar(output); + MarkOutput(*theta->predicate()->origin()); + MarkOutput(*loopvar.post->origin()); + MarkOutput(*loopvar.input->origin()); + return; + } + + if (auto theta = rvsdg::TryGetRegionParentNode(output)) + { + auto loopvar = theta->MapPreLoopVar(output); + MarkOutput(*loopvar.output); + MarkOutput(*loopvar.input->origin()); + return; + } + + if (auto lambda = rvsdg::TryGetOwnerNode(output)) + { + for (auto & result : lambda->GetFunctionResults()) + { + MarkOutput(*result->origin()); + } + return; + } + + if (auto lambda = rvsdg::TryGetRegionParentNode(output)) + { + if (auto ctxvar = lambda->MapBinderContextVar(output)) + { + // Bound context variable. + MarkOutput(*ctxvar->input->origin()); + return; + } + else + { + // Function argument. + return; + } + } + + if (auto phi = rvsdg::TryGetOwnerNode(output)) + { + MarkOutput(*phi->MapOutputFixVar(output).result->origin()); + return; + } + + if (auto phi = rvsdg::TryGetRegionParentNode(output)) + { + auto var = phi->MapArgument(output); + if (auto fix = std::get_if(&var)) + { + // Recursion argument + MarkOutput(*fix->result->origin()); + return; + } + else if (auto ctx = std::get_if(&var)) + { + // Bound context variable. + MarkOutput(*ctx->input->origin()); + return; + } + else + { + JLM_UNREACHABLE("Phi argument must be either fixpoint or context variable"); + } + } + + if (const auto deltaNode = rvsdg::TryGetOwnerNode(output)) + { + const auto result = deltaNode->subregion()->result(0); + MarkOutput(*result->origin()); + return; + } + + if (rvsdg::TryGetRegionParentNode(output)) + { + const auto argument = util::AssertedCast(&output); + MarkOutput(*argument->input()->origin()); + return; + } + + if (const auto simpleNode = rvsdg::TryGetOwnerNode(output)) + { + for (size_t n = 0; n < simpleNode->ninputs(); n++) + { + MarkOutput(*simpleNode->input(n)->origin()); + } + return; + } + + JLM_UNREACHABLE("We should have never reached this statement."); +}*/ +/* +void +PartialRedundancyElimination::SweepRvsdg(rvsdg::Graph & rvsdg) const +{ + SweepRegion(rvsdg.GetRootRegion()); + + // Remove dead imports + for (size_t n = rvsdg.GetRootRegion().narguments() - 1; n != static_cast(-1); n--) + { + if (!Context_->IsAlive(*rvsdg.GetRootRegion().argument(n))) + { + rvsdg.GetRootRegion().RemoveArgument(n); + } + } +}*/ +/* +void +PartialRedundancyElimination::SweepRegion(rvsdg::Region & region) const +{ + region.prune(false); + + std::vector> nodesTopDown(region.nnodes()); + for (auto & node : region.Nodes()) + { + nodesTopDown[node.depth()].push_back(&node); + } + + for (auto it = nodesTopDown.rbegin(); it != nodesTopDown.rend(); it++) + { + for (auto node : *it) + { + if (!Context_->IsAlive(*node)) + { + remove(node); + continue; + } + + if (auto structuralNode = dynamic_cast(node)) + { + SweepStructuralNode(*structuralNode); + } + } + } + + JLM_ASSERT(region.NumBottomNodes() == 0); +}*/ +/* +void +PartialRedundancyElimination::SweepStructuralNode(rvsdg::StructuralNode & node) const +{ + rvsdg::MatchTypeOrFail( + node, + [this](rvsdg::GammaNode & node) + { + SweepGamma(node); + }, + [this](rvsdg::ThetaNode & node) + { + SweepTheta(node); + }, + [this](rvsdg::LambdaNode & node) + { + SweepLambda(node); + }, + [this](rvsdg::PhiNode & node) + { + SweepPhi(node); + }, + [](rvsdg::DeltaNode & node) + { + SweepDelta(node); + }); +}*/ +/* +void +PartialRedundancyElimination::SweepGamma(rvsdg::GammaNode & gammaNode) const +{ + // Remove dead exit vars. + std::vector deadExitVars; + for (const auto & exitvar : gammaNode.GetExitVars()) + { + if (!Context_->IsAlive(*exitvar.output)) + { + deadExitVars.push_back(exitvar); + } + } + gammaNode.RemoveExitVars(deadExitVars); + + // Sweep gamma subregions + for (size_t r = 0; r < gammaNode.nsubregions(); r++) + { + SweepRegion(*gammaNode.subregion(r)); + } + + // Remove dead entry vars. + std::vector deadEntryVars; + for (const auto & entryvar : gammaNode.GetEntryVars()) + { + bool alive = std::any_of( + entryvar.branchArgument.begin(), + entryvar.branchArgument.end(), + [this](const rvsdg::Output * arg) + { + return Context_->IsAlive(*arg); + }); + if (!alive) + { + deadEntryVars.push_back(entryvar); + } + } + gammaNode.RemoveEntryVars(deadEntryVars); +}*/ +/* +void +PartialRedundancyElimination::SweepTheta(rvsdg::ThetaNode & thetaNode) const +{ + // Determine loop variables to be removed. + std::vector loopvars; + for (const auto & loopvar : thetaNode.GetLoopVars()) + { + if (!Context_->IsAlive(*loopvar.pre) && !Context_->IsAlive(*loopvar.output)) + { + loopvar.post->divert_to(loopvar.pre); + loopvars.push_back(loopvar); + } + } + + // Now that the loop variables to be eliminated only point to + // their own pre-iteration values, any outputs within the subregion + // that only contributed to computing the post-iteration values + // of the variables are unlinked and can be removed as well. + SweepRegion(*thetaNode.subregion()); + + // There are now no other users of the pre-iteration values of the + // variables to be removed left in the subregion anymore. + // The variables have become "loop-invariant" and can simply + // be eliminated from the theta node. + thetaNode.RemoveLoopVars(std::move(loopvars)); +}*/ +/* +void +PartialRedundancyElimination::SweepLambda(rvsdg::LambdaNode & lambdaNode) const +{ + SweepRegion(*lambdaNode.subregion()); + lambdaNode.PruneLambdaInputs(); +}*/ +/* +void +PartialRedundancyElimination::SweepPhi(rvsdg::PhiNode & phiNode) const +{ + std::vector deadFixvars; + std::vector deadCtxvars; + + for (const auto & fixvar : phiNode.GetFixVars()) + { + bool isDead = !Context_->IsAlive(*fixvar.output) && !Context_->IsAlive(*fixvar.recref); + if (isDead) + { + deadFixvars.push_back(fixvar); + // Temporarily redirect the variable so it refers to itself + // (so the object is simply defined to be "itself"). + fixvar.result->divert_to(fixvar.recref); + } + }*/ +/* + SweepRegion(*phiNode.subregion()); + + for (const auto & ctxvar : phiNode.GetContextVars()) + { + if (ctxvar.inner->IsDead()) + { + deadCtxvars.push_back(ctxvar); + } + } + + phiNode.RemoveContextVars(std::move(deadCtxvars)); + phiNode.RemoveFixVars(std::move(deadFixvars)); +}*/ +/* +void +PartialRedundancyElimination::SweepDelta(rvsdg::DeltaNode & deltaNode) +{ + // A delta subregion can only contain simple nodes. Thus, a simple prune is sufficient. + deltaNode.subregion()->prune(false); + + deltaNode.PruneDeltaInputs(); +}*/ + +} diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp new file mode 100644 index 000000000..cf9e28aab --- /dev/null +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -0,0 +1,112 @@ +/* + * Copyright 2017 Nico Reißmann + * See COPYING for terms of redistribution. + */ + +#ifndef JLM_LLVM_OPT_PartialRedundancyElimination_HPP +#define JLM_LLVM_OPT_PartialRedundancyElimination_HPP + +#include +#include +#include +#include +#include + +namespace jlm::rvsdg +{ +class DeltaNode; +class GammaNode; +class Graph; +class LambdaNode; +class Output; +class StructuralNode; +class ThetaNode; +class Region; +} + +namespace jlm::llvm +{ + +class StrictHasher +{ + + /** \brief Partial Redundancy Elimination + * + * This hasher assumes all function arguments are different. + * + * + */ +public: + void populate_table(rvsdg::Region& reg); +private: + std::unordered_map hashes; + +}; + +/** \brief Partial Redundancy Elimination + * + * Todo: description here + * + * + */ +class PartialRedundancyElimination final : public rvsdg::Transformation +{ + class Context; + class Statistics; + +public: + ~PartialRedundancyElimination() noexcept override; + + PartialRedundancyElimination(); + + PartialRedundancyElimination(const PartialRedundancyElimination &) = delete; + PartialRedundancyElimination(PartialRedundancyElimination &&) = delete; + + PartialRedundancyElimination & + operator=(const PartialRedundancyElimination &) = delete; + PartialRedundancyElimination & + operator=(PartialRedundancyElimination &&) = delete; + + /*void + run(rvsdg::Region & region);*/ + + void + Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; + +private: + /* void + MarkRegion(const rvsdg::Region & region); + + void + MarkOutput(const jlm::rvsdg::Output & output); + + void + SweepRvsdg(rvsdg::Graph & rvsdg) const; + + void + SweepRegion(rvsdg::Region & region) const; + + void + SweepStructuralNode(rvsdg::StructuralNode & node) const; + + void + SweepGamma(rvsdg::GammaNode & gammaNode) const; + + void + SweepTheta(rvsdg::ThetaNode & thetaNode) const; + + void + SweepLambda(rvsdg::LambdaNode & lambdaNode) const; + + void + SweepPhi(rvsdg::PhiNode & phiNode) const; + + static void + SweepDelta(rvsdg::DeltaNode & deltaNode); +*/ + std::unique_ptr Context_; +}; + +} + +#endif diff --git a/jlm/tooling/Command.cpp b/jlm/tooling/Command.cpp index 214ad370d..2659379b0 100644 --- a/jlm/tooling/Command.cpp +++ b/jlm/tooling/Command.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,8 @@ JlmOptCommand::CreateTransformation( return std::make_unique(); case JlmOptCommandLineOptions::OptimizationId::DeadNodeElimination: return std::make_unique(); + case JlmOptCommandLineOptions::OptimizationId::PartialRedundancyElimination: + return std::make_unique(); case JlmOptCommandLineOptions::OptimizationId::FunctionInlining: return std::make_unique(); case JlmOptCommandLineOptions::OptimizationId::IfConversion: diff --git a/jlm/tooling/CommandLine.cpp b/jlm/tooling/CommandLine.cpp index e6d43f3b2..f04d0f187 100644 --- a/jlm/tooling/CommandLine.cpp +++ b/jlm/tooling/CommandLine.cpp @@ -109,6 +109,8 @@ JlmOptCommandLineOptions::FromCommandLineArgumentToOptimizationId( OptimizationId::CommonNodeElimination }, { OptimizationCommandLineArgument::DeadNodeElimination_, OptimizationId::DeadNodeElimination }, + { OptimizationCommandLineArgument::PartialRedundancyElimination_, + OptimizationId::PartialRedundancyElimination }, { OptimizationCommandLineArgument::FunctionInlining_, OptimizationId::FunctionInlining }, { OptimizationCommandLineArgument::IfConversion_, OptimizationId::IfConversion }, { OptimizationCommandLineArgument::InvariantValueRedirection_, @@ -145,6 +147,8 @@ JlmOptCommandLineOptions::ToCommandLineArgument(OptimizationId optimizationId) OptimizationCommandLineArgument::CommonNodeElimination_ }, { OptimizationId::DeadNodeElimination, OptimizationCommandLineArgument::DeadNodeElimination_ }, +{ OptimizationId::PartialRedundancyElimination, +OptimizationCommandLineArgument::PartialRedundancyElimination_ }, { OptimizationId::FunctionInlining, OptimizationCommandLineArgument::FunctionInlining_ }, { OptimizationId::IfConversion, OptimizationCommandLineArgument::IfConversion_ }, { OptimizationId::InvariantValueRedirection, @@ -503,6 +507,9 @@ JlcCommandLineParser::ParseCommandLineArguments(int argc, const char * const * a CreateStatisticsOption( util::Statistics::Id::DeadNodeElimination, "Collect dead node elimination pass statistics."), + CreateStatisticsOption( + util::Statistics::Id::PartialRedundancyElimination, + "Collect dead node elimination2 pass statistics."), CreateStatisticsOption( util::Statistics::Id::FunctionInlining, "Collect function inlining pass statistics."), @@ -821,6 +828,7 @@ JlmOptCommandLineParser::ParseCommandLineArguments(int argc, const char * const JlmOptCommandLineOptions::OptimizationId::AASteensgaardRegionAware; auto commonNodeElimination = JlmOptCommandLineOptions::OptimizationId::CommonNodeElimination; auto deadNodeElimination = JlmOptCommandLineOptions::OptimizationId::DeadNodeElimination; + auto PartialRedundancyElimination = JlmOptCommandLineOptions::OptimizationId::PartialRedundancyElimination; auto functionInlining = JlmOptCommandLineOptions::OptimizationId::FunctionInlining; auto ifConversion = JlmOptCommandLineOptions::OptimizationId::IfConversion; auto invariantValueRedirection = @@ -862,6 +870,10 @@ JlmOptCommandLineParser::ParseCommandLineArguments(int argc, const char * const deadNodeElimination, JlmOptCommandLineOptions::ToCommandLineArgument(deadNodeElimination), "Dead Node Elimination"), + ::clEnumValN( + PartialRedundancyElimination, + JlmOptCommandLineOptions::ToCommandLineArgument(PartialRedundancyElimination), + "Partial Redundancy Elimination. Eliminate computations found redundant along some execution paths"), ::clEnumValN( functionInlining, JlmOptCommandLineOptions::ToCommandLineArgument(functionInlining), diff --git a/jlm/tooling/CommandLine.hpp b/jlm/tooling/CommandLine.hpp index 1ab1740c4..da50cfb79 100644 --- a/jlm/tooling/CommandLine.hpp +++ b/jlm/tooling/CommandLine.hpp @@ -72,6 +72,7 @@ class JlmOptCommandLineOptions final : public CommandLineOptions AASteensgaardRegionAware, CommonNodeElimination, DeadNodeElimination, + PartialRedundancyElimination, FunctionInlining, IfConversion, InvariantValueRedirection, @@ -83,6 +84,7 @@ class JlmOptCommandLineOptions final : public CommandLineOptions ThetaGammaInversion, LastEnumValue // must always be the last enum value, used for iteration + , }; JlmOptCommandLineOptions( @@ -203,6 +205,7 @@ class JlmOptCommandLineOptions final : public CommandLineOptions inline static const char * AaSteensgaardRegionAware_ = "AASteensgaardRegionAware"; inline static const char * CommonNodeElimination_ = "CommonNodeElimination"; inline static const char * DeadNodeElimination_ = "DeadNodeElimination"; + inline static const char * PartialRedundancyElimination_ = "PartialRedundancyElimination"; inline static const char * FunctionInlining_ = "FunctionInlining"; inline static const char * IfConversion_ = "IfConversion"; inline static const char * InvariantValueRedirection_ = "InvariantValueRedirection"; diff --git a/jlm/util/Statistics.hpp b/jlm/util/Statistics.hpp index b31a6195a..6da6f0a1f 100644 --- a/jlm/util/Statistics.hpp +++ b/jlm/util/Statistics.hpp @@ -42,6 +42,7 @@ class Statistics ControlFlowRecovery, DataNodeToDelta, DeadNodeElimination, + PartialRedundancyElimination, FunctionInlining, IfConversion, InvariantValueRedirection, From 9e3600f518f4e9198146363f9eb9fa90b547d62e Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 30 Sep 2025 15:14:14 +0200 Subject: [PATCH 02/52] Implemented basic hashing. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 542 ++++++------------ jlm/llvm/opt/PartialRedundancyElimination.hpp | 94 +-- 2 files changed, 215 insertions(+), 421 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 3cf889964..816393200 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -9,14 +9,26 @@ #define TR_FG(r,g,b) TR_CMD "[38;2;" #r ";" #g ";" #b "m" #define TR_RED TR_FG(255,64,64) #define TR_GREEN TR_FG(64, 255, 64) + #define TR_YELLOW TR_FG(255, 255, 64) +#define TR_ORANGE TR_FG(255, 128, 0) #define TR_BLUE TR_FG(64, 64, 255) +#define TR_PINK TR_FG(255,128,128) #define TR_CYAN TR_FG(64, 255, 255) +#include "../../../tests/test-operation.hpp" +#include "../../rvsdg/lambda.hpp" +#include "../../rvsdg/MatchType.hpp" +#include "../../rvsdg/node.hpp" +#include "../../rvsdg/nullary.hpp" #include "../../rvsdg/structural-node.hpp" #include "../../util/GraphWriter.hpp" +#include "../ir/operators/call.hpp" +#include "PartialRedundancyElimination.hpp" #include +#include #include +#include #include #include @@ -27,44 +39,17 @@ #include #include #include +#include #include #include #include -namespace jlm::llvm -{ +#include +#include -void StrictHasher::populate_table(rvsdg::Region& reg) +namespace jlm::llvm { - for (auto node : rvsdg::TopDownTraverser(®)){ - - std::cout << TR_RED << node << TR_RESET << std::endl; - std::cout << TR_RED << "==========================================" << std::endl; - rvsdg::MatchTypeWithDefault( - *node, - [](const rvsdg::LambdaNode & node) - { - std::cout<OutputAllGraphs(my_dot_oss, jlm::util::graph::OutputFormat::Dot); - my_dot_oss.close(); + std::ofstream my_dot_oss (full_name); + my_graph_writer.OutputAllGraphs(my_dot_oss, jlm::util::graph::OutputFormat::Dot); + std::cout << TR_GREEN << "-------------------------------------" << TR_RESET << std::endl; + } + }); - delete my_graph_writer; - } } -void -PartialRedundancyElimination::Run( - rvsdg::RvsdgModule & module, - util::StatisticsCollector & statisticsCollector) -{ - std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; +static int indentation_level = 0; - Context_ = Context::Create(); - - auto & rvsdg = module.Rvsdg(); - auto statistics = Statistics::Create(module.SourceFilePath().value()); - - StrictHasher myhasher; - myhasher.populate_table(rvsdg.GetRootRegion()); - - for (auto& node : rvsdg.GetRootRegion().Nodes()) +inline std::string ind() +{ + std::string acc = ""; + for (int i = 0;i(&node); - if (snode) - { - for (auto& sub_reg : snode->Subregions()) - { - std::string tmp = snode->DebugString(); - dump_region(sub_reg, tmp); - } - } + acc += " "; } - - std::cout << TR_RESET; - -/* - util::graph::Graph & - WriteGraphs(util::graph::Writer & writer, Region & region, bool emitTypeGraph); -*/ - statistics->StartMarkStatistics(rvsdg); - //MarkRegion(rvsdg.GetRootRegion()); - statistics->StopMarkStatistics(); - - //statistics->StartSweepStatistics(); - //SweepRvsdg(rvsdg); - //statistics->StopSweepStatistics(rvsdg); - - statisticsCollector.CollectDemandedStatistics(std::move(statistics)); - - // Discard internal state to free up memory after we are done - Context_.reset(); + return acc; } -/* -void -PartialRedundancyElimination::MarkRegion(const rvsdg::Region & region) + +class IndentMan { - for (size_t n = 0; n < region.nresults(); n++) + +public: + IndentMan() { - MarkOutput(*region.result(n)->origin()); + indentation_level++; } -}*/ -/* -void -PartialRedundancyElimination::MarkOutput(const jlm::rvsdg::Output & output) -{ - if (Context_->IsAlive(output)) + ~IndentMan() { - return; + indentation_level--; } - Context_->MarkAlive(output); - - if (is(&output)) - { - return; - } +}; - if (auto gamma = rvsdg::TryGetOwnerNode(output)) +void PartialRedundancyElimination::TraverseSubRegions(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) +{ + IndentMan indenter = IndentMan(); + for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) { - MarkOutput(*gamma->predicate()->origin()); - for (const auto & result : gamma->MapOutputExitVar(output).branchResult) + cb(this, *node); + MatchType(*node, [this,cb](rvsdg::StructuralNode& sn) { - MarkOutput(*result->origin()); - } - return; - } - - if (auto gamma = rvsdg::TryGetRegionParentNode(output)) - { - auto external_origin = std::visit( - [](const auto & rolevar) -> rvsdg::Output * - { - return rolevar.input->origin(); - }, - gamma->MapBranchArgument(output)); - MarkOutput(*external_origin); - return; + for (auto& reg : sn.Subregions()) + { + this->TraverseSubRegions(reg, cb); + } + }); } +} - if (auto theta = rvsdg::TryGetOwnerNode(output)) - { - auto loopvar = theta->MapOutputLoopVar(output); - MarkOutput(*theta->predicate()->origin()); - MarkOutput(*loopvar.post->origin()); - MarkOutput(*loopvar.input->origin()); - return; - } - if (auto theta = rvsdg::TryGetRegionParentNode(output)) - { - auto loopvar = theta->MapPreLoopVar(output); - MarkOutput(*loopvar.output); - MarkOutput(*loopvar.input->origin()); - return; - } +/* +void TraverseSubTrees(rvsdg::StructuralNode& node, void(*cb)(PartialRedundancyElimination* pe, rvsdg::StructuralNode& node)); +*/ - if (auto lambda = rvsdg::TryGetOwnerNode(output)) +void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) +{ + std::cout << ind() << TR_BLUE << node.DebugString() << TR_CYAN<GetFunctionResults()) + auto k_present = pe->output_hashes.find(node.output(i)); + if (k_present != pe->output_hashes.end() ) { - MarkOutput(*result->origin()); - } - return; - } + size_t h = pe->output_hashes[node.output(i)]; + std::string color = (pe->hash_count(h) > 1 ? TR_GREEN : TR_YELLOW); - if (auto lambda = rvsdg::TryGetRegionParentNode(output)) - { - if (auto ctxvar = lambda->MapBinderContextVar(output)) - { - // Bound context variable. - MarkOutput(*ctxvar->input->origin()); - return; - } - else - { - // Function argument. - return; - } - } + MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_ORANGE;}); + MatchType(node, [&color, pe, h](rvsdg::LambdaNode& lm){color = pe->hash_count(h) > 1 ? TR_RED : TR_ORANGE;}); - if (auto phi = rvsdg::TryGetOwnerNode(output)) - { - MarkOutput(*phi->MapOutputFixVar(output).result->origin()); - return; + std::cout << " : " << color << h << TR_RESET; + } } - - if (auto phi = rvsdg::TryGetRegionParentNode(output)) + MatchType(node, [pe](rvsdg::LambdaNode& lm) { - auto var = phi->MapArgument(output); - if (auto fix = std::get_if(&var)) + for (auto& param : lm.GetFunctionArguments()) { - // Recursion argument - MarkOutput(*fix->result->origin()); - return; + if (pe->output_hashes.find(param) != pe->output_hashes.end()) + { + size_t h = pe->output_hashes[param]; + std::cout << ( pe->hash_count(h) > 1 ? TR_RED : TR_ORANGE) << " : " << h << TR_RESET; + } + } - else if (auto ctx = std::get_if(&var)) + }); + std::cout << std::endl; +} + +void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyElimination *pe, rvsdg::Node& node) +{ + MatchType(node.GetOperation(), + [pe, &node](const jlm::llvm::IntegerConstantOperation& iconst) { - // Bound context variable. - MarkOutput(*ctx->input->origin()); - return; + std::hash hasher; + size_t h = hasher(iconst.Representation().str()); + pe->register_hash(node.output(0), h); } - else + , + [pe, &node](const jlm::llvm::CallOperation& op) { - JLM_UNREACHABLE("Phi argument must be either fixpoint or context variable"); + auto s = node.DebugString() + std::to_string(node.GetNodeId() ); + for (size_t i = 0; i < node.noutputs(); i++){ + pe->register_hash_for_output(node.output(i), s, i); + } } - } - - if (const auto deltaNode = rvsdg::TryGetOwnerNode(output)) - { - const auto result = deltaNode->subregion()->result(0); - MarkOutput(*result->origin()); - return; - } - - if (rvsdg::TryGetRegionParentNode(output)) - { - const auto argument = util::AssertedCast(&output); - MarkOutput(*argument->input()->origin()); - return; - } + ); - if (const auto simpleNode = rvsdg::TryGetOwnerNode(output)) + /* Add each lambda parameter as a leaf hash for hashing within its body */ + MatchType(node, [pe, &node](rvsdg::LambdaNode& lm) { - for (size_t n = 0; n < simpleNode->ninputs(); n++) - { - MarkOutput(*simpleNode->input(n)->origin()); + auto fargs = lm.GetFunctionArguments(); + auto s = node.DebugString() + std::to_string(node.GetNodeId()); + for (size_t i = 0; i < fargs.size(); i++){ + pe->register_hash_for_output(fargs[i], node.DebugString(), i); } - return; - } + }); +} - JLM_UNREACHABLE("We should have never reached this statement."); -}*/ -/* -void -PartialRedundancyElimination::SweepRvsdg(rvsdg::Graph & rvsdg) const -{ - SweepRegion(rvsdg.GetRootRegion()); - // Remove dead imports - for (size_t n = rvsdg.GetRootRegion().narguments() - 1; n != static_cast(-1); n--) - { - if (!Context_->IsAlive(*rvsdg.GetRootRegion().argument(n))) - { - rvsdg.GetRootRegion().RemoveArgument(n); - } - } -}*/ -/* -void -PartialRedundancyElimination::SweepRegion(rvsdg::Region & region) const +void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rvsdg::Node& node) { - region.prune(false); - - std::vector> nodesTopDown(region.nnodes()); - for (auto & node : region.Nodes()) + MatchType(node.GetOperation(), [pe, &node](const rvsdg::BinaryOperation& op) { - nodesTopDown[node.depth()].push_back(&node); - } - - for (auto it = nodesTopDown.rbegin(); it != nodesTopDown.rend(); it++) - { - for (auto node : *it) + std::hash hasher; + size_t h = hasher(op.debug_string()); + bool was_hashable = true; + for (size_t i = 0 ; i < node.ninputs(); i++) { - if (!Context_->IsAlive(*node)) + if (pe->output_hashes.find(node.input(i)->origin()) != pe->output_hashes.end()) { - remove(node); - continue; - } - - if (auto structuralNode = dynamic_cast(node)) + size_t hash_in = pe->output_hashes[node.input(i)->origin()]; + if (op.is_commutative() && op.is_associative()) + { + h += hasher(std::to_string(hash_in)); + }else + { + h |= hasher(std::to_string(hash_in)) * (i+1); + } + }else { - SweepStructuralNode(*structuralNode); + std::cout << TR_RED << node.DebugString() << node.GetNodeId()<< "MISSING INPUT HASH" << TR_RESET << std::endl; + auto input_source = node.input(i)->origin()->GetOwner(); + try + { + auto src_node = std::get(input_source); + std::cout << TR_RED << "origin: " << src_node->DebugString() << src_node->GetNodeId() << TR_RESET<< std::endl; + }catch (std::bad_variant_access& ex) + { + std::cout< deadExitVars; - for (const auto & exitvar : gammaNode.GetExitVars()) - { - if (!Context_->IsAlive(*exitvar.output)) + if (was_hashable) { - deadExitVars.push_back(exitvar); + pe->output_hashes.insert( {node.output(0), h} ); + pe->register_hash(h); } - } - gammaNode.RemoveExitVars(deadExitVars); - // Sweep gamma subregions - for (size_t r = 0; r < gammaNode.nsubregions(); r++) - { - SweepRegion(*gammaNode.subregion(r)); - } + }); +} - // Remove dead entry vars. - std::vector deadEntryVars; - for (const auto & entryvar : gammaNode.GetEntryVars()) - { - bool alive = std::any_of( - entryvar.branchArgument.begin(), - entryvar.branchArgument.end(), - [this](const rvsdg::Output * arg) - { - return Context_->IsAlive(*arg); - }); - if (!alive) - { - deadEntryVars.push_back(entryvar); - } - } - gammaNode.RemoveEntryVars(deadEntryVars); -}*/ -/* void -PartialRedundancyElimination::SweepTheta(rvsdg::ThetaNode & thetaNode) const +PartialRedundancyElimination::Run( + rvsdg::RvsdgModule & module, + util::StatisticsCollector & statisticsCollector) { - // Determine loop variables to be removed. - std::vector loopvars; - for (const auto & loopvar : thetaNode.GetLoopVars()) - { - if (!Context_->IsAlive(*loopvar.pre) && !Context_->IsAlive(*loopvar.output)) - { - loopvar.post->divert_to(loopvar.pre); - loopvars.push_back(loopvar); - } - } + std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; - // Now that the loop variables to be eliminated only point to - // their own pre-iteration values, any outputs within the subregion - // that only contributed to computing the post-iteration values - // of the variables are unlinked and can be removed as well. - SweepRegion(*thetaNode.subregion()); - - // There are now no other users of the pre-iteration values of the - // variables to be removed left in the subregion anymore. - // The variables have become "loop-invariant" and can simply - // be eliminated from the theta node. - thetaNode.RemoveLoopVars(std::move(loopvars)); -}*/ -/* -void -PartialRedundancyElimination::SweepLambda(rvsdg::LambdaNode & lambdaNode) const -{ - SweepRegion(*lambdaNode.subregion()); - lambdaNode.PruneLambdaInputs(); -}*/ -/* -void -PartialRedundancyElimination::SweepPhi(rvsdg::PhiNode & phiNode) const -{ - std::vector deadFixvars; - std::vector deadCtxvars; - for (const auto & fixvar : phiNode.GetFixVars()) - { - bool isDead = !Context_->IsAlive(*fixvar.output) && !Context_->IsAlive(*fixvar.recref); - if (isDead) - { - deadFixvars.push_back(fixvar); - // Temporarily redirect the variable so it refers to itself - // (so the object is simply defined to be "itself"). - fixvar.result->divert_to(fixvar.recref); - } - }*/ -/* - SweepRegion(*phiNode.subregion()); - for (const auto & ctxvar : phiNode.GetContextVars()) - { - if (ctxvar.inner->IsDead()) - { - deadCtxvars.push_back(ctxvar); - } - } - phiNode.RemoveContextVars(std::move(deadCtxvars)); - phiNode.RemoveFixVars(std::move(deadFixvars)); -}*/ -/* -void -PartialRedundancyElimination::SweepDelta(rvsdg::DeltaNode & deltaNode) -{ - // A delta subregion can only contain simple nodes. Thus, a simple prune is sufficient. - deltaNode.subregion()->prune(false); - deltaNode.PruneDeltaInputs(); -}*/ + + auto & rvsdg = module.Rvsdg(); + auto statistics = Statistics::Create(module.SourceFilePath().value()); + + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_bin); + std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + + std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; +} } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index cf9e28aab..3c628c90f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -27,22 +27,6 @@ class Region; namespace jlm::llvm { -class StrictHasher -{ - - /** \brief Partial Redundancy Elimination - * - * This hasher assumes all function arguments are different. - * - * - */ -public: - void populate_table(rvsdg::Region& reg); -private: - std::unordered_map hashes; - -}; - /** \brief Partial Redundancy Elimination * * Todo: description here @@ -74,37 +58,59 @@ class PartialRedundancyElimination final : public rvsdg::Transformation Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; private: - /* void - MarkRegion(const rvsdg::Region & region); - void - MarkOutput(const jlm::rvsdg::Output & output); - void - SweepRvsdg(rvsdg::Graph & rvsdg) const; - - void - SweepRegion(rvsdg::Region & region) const; - - void - SweepStructuralNode(rvsdg::StructuralNode & node) const; - - void - SweepGamma(rvsdg::GammaNode & gammaNode) const; - - void - SweepTheta(rvsdg::ThetaNode & thetaNode) const; - - void - SweepLambda(rvsdg::LambdaNode & lambdaNode) const; - - void - SweepPhi(rvsdg::PhiNode & phiNode) const; + void TraverseSubRegions(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); + + static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void register_leaf_hash( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void hash_bin( PartialRedundancyElimination *pe, rvsdg::Node& node); + + inline void register_hash(jlm::rvsdg::Output* k, size_t h) + { + output_hashes.insert({k, h}); + register_hash(h); + } + + inline void register_hash_for_output(jlm::rvsdg::Output* out, std::string base, int index) + { + const std::hash hasher; + + size_t h = hasher(base); + h ^= index; + output_hashes.insert({out, h}); + register_hash(h); + } + + //todo rename to gvn_hashes + std::unordered_map output_hashes; + + /* Debug data */ + std::unordered_map hash_counts; + + inline void register_hash(size_t h) + { + if (hash_counts.find(h) == hash_counts.end()) + { + hash_counts.insert({h, 1}); + }else + { + hash_counts[h] = hash_counts[h] + 1; + } + } + + inline size_t hash_count(size_t h) + { + if (hash_counts.find(h) != hash_counts.end()) + { + return hash_counts[h]; + }else + { + return 0; + } + } - static void - SweepDelta(rvsdg::DeltaNode & deltaNode); -*/ - std::unique_ptr Context_; }; } From 1a88f78cfee43d6ff207817d10192b97acb5a6b9 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 30 Sep 2025 20:13:06 +0200 Subject: [PATCH 03/52] Rerouted hashes along gamme edges. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 37 ++++++++++++++++--- jlm/llvm/opt/PartialRedundancyElimination.hpp | 5 +++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 816393200..539002c12 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -17,6 +17,7 @@ #define TR_CYAN TR_FG(64, 255, 255) #include "../../../tests/test-operation.hpp" +#include "../../rvsdg/gamma.hpp" #include "../../rvsdg/lambda.hpp" #include "../../rvsdg/MatchType.hpp" #include "../../rvsdg/node.hpp" @@ -238,8 +239,8 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r size_t h = pe->output_hashes[node.output(i)]; std::string color = (pe->hash_count(h) > 1 ? TR_GREEN : TR_YELLOW); - MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_ORANGE;}); - MatchType(node, [&color, pe, h](rvsdg::LambdaNode& lm){color = pe->hash_count(h) > 1 ? TR_RED : TR_ORANGE;}); + MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_CYAN;}); + MatchType(node, [&color](rvsdg::LambdaNode& lm){color = TR_ORANGE;}); std::cout << " : " << color << h << TR_RESET; } @@ -289,6 +290,34 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati }); } +void PartialRedundancyElimination::hash_gamma(PartialRedundancyElimination* pe, rvsdg::Node& node) +{ + MatchType(node, [pe](rvsdg::GammaNode& node) + { + for (auto ev : node.GetEntryVars()) + { + rvsdg::Output* origin = ev.input->origin(); + if (pe->output_has_hash(origin)) + { + size_t h = pe->output_hashes[origin]; + for (rvsdg::Output* brarg : ev.branchArgument) + { + pe->register_hash(brarg, h); + } + } + } + }); +} + +void PartialRedundancyElimination::hash_node(PartialRedundancyElimination *pe, rvsdg::Node& node) +{ + /*Match by operation*/ + MatchType(node.GetOperation(), + [pe, &node](const rvsdg::BinaryOperation& op){hash_bin(pe, node);} + ); + /*Match by node type*/ + MatchType(node, [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);}); +} void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rvsdg::Node& node) { @@ -321,8 +350,6 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv { std::cout<TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_bin); + this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 3c628c90f..9e1c04675 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -66,6 +66,9 @@ class PartialRedundancyElimination final : public rvsdg::Transformation static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); static void register_leaf_hash( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_bin( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void hash_gamma( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); + //static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); inline void register_hash(jlm::rvsdg::Output* k, size_t h) { @@ -89,6 +92,8 @@ class PartialRedundancyElimination final : public rvsdg::Transformation /* Debug data */ std::unordered_map hash_counts; + inline bool output_has_hash(rvsdg::Output* out){return output_hashes.find(out) != output_hashes.end();} + inline void register_hash(size_t h) { if (hash_counts.find(h) == hash_counts.end()) From daeaa0af53d78b78e6ad378b39e68c5430a7b147 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Wed, 1 Oct 2025 11:54:53 +0200 Subject: [PATCH 04/52] Minor changes to coloring and added delims for sub-regions. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 33 ++++++++++++------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 539002c12..f37f3c4be 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -15,6 +15,7 @@ #define TR_BLUE TR_FG(64, 64, 255) #define TR_PINK TR_FG(255,128,128) #define TR_CYAN TR_FG(64, 255, 255) +#define TR_GRAY TR_FG(52,52,52) #include "../../../tests/test-operation.hpp" #include "../../rvsdg/gamma.hpp" @@ -25,6 +26,7 @@ #include "../../rvsdg/structural-node.hpp" #include "../../util/GraphWriter.hpp" #include "../ir/operators/call.hpp" +#include "../ir/operators/operators.hpp" #include "PartialRedundancyElimination.hpp" #include #include @@ -218,6 +220,7 @@ void PartialRedundancyElimination::TraverseSubRegions(rvsdg::Region& reg, void(* for (auto& reg : sn.Subregions()) { this->TraverseSubRegions(reg, cb); + std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; } }); } @@ -230,7 +233,7 @@ void TraverseSubTrees(rvsdg::StructuralNode& node, void(*cb)(PartialRedundancyEl void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) { - std::cout << ind() << TR_BLUE << node.DebugString() << TR_CYAN<"<< TR_RESET; for (size_t i = 0; i < node.noutputs(); i++) { auto k_present = pe->output_hashes.find(node.output(i)); @@ -252,7 +255,7 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r if (pe->output_hashes.find(param) != pe->output_hashes.end()) { size_t h = pe->output_hashes[param]; - std::cout << ( pe->hash_count(h) > 1 ? TR_RED : TR_ORANGE) << " : " << h << TR_RESET; + std::cout << TR_ORANGE << " : " << h << TR_RESET; } } @@ -269,14 +272,6 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati size_t h = hasher(iconst.Representation().str()); pe->register_hash(node.output(0), h); } - , - [pe, &node](const jlm::llvm::CallOperation& op) - { - auto s = node.DebugString() + std::to_string(node.GetNodeId() ); - for (size_t i = 0; i < node.noutputs(); i++){ - pe->register_hash_for_output(node.output(i), s, i); - } - } ); /* Add each lambda parameter as a leaf hash for hashing within its body */ @@ -287,6 +282,17 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati for (size_t i = 0; i < fargs.size(); i++){ pe->register_hash_for_output(fargs[i], node.DebugString(), i); } + pe->register_hash_for_output(node.output(0), node.DebugString() + "LM_NODE", 0); + }); +} + +void PartialRedundancyElimination::hash_call(PartialRedundancyElimination* pe, rvsdg::Node& node) +{ + MatchType(node.GetOperation(), [pe, &node](const jlm::llvm::CallOperation& op) + { + std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); + //std::cout << TR_PINK << s << TR_RESET << std::endl; + pe->register_hash_for_output(node.output(0), s, 0); }); } @@ -313,10 +319,13 @@ void PartialRedundancyElimination::hash_node(PartialRedundancyElimination *pe, r { /*Match by operation*/ MatchType(node.GetOperation(), - [pe, &node](const rvsdg::BinaryOperation& op){hash_bin(pe, node);} + [pe, &node](const rvsdg::BinaryOperation& op){hash_bin(pe, node);}, + [pe, &node](const jlm::llvm::CallOperation& op){hash_call(pe, node);} ); /*Match by node type*/ - MatchType(node, [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);}); + MatchType(node, + [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);} + ); } void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rvsdg::Node& node) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 9e1c04675..cd3ffd10a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -68,7 +68,7 @@ class PartialRedundancyElimination final : public rvsdg::Transformation static void hash_bin( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_gamma( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); - //static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); inline void register_hash(jlm::rvsdg::Output* k, size_t h) { From bad009fd5f068e18de1e81b4b2dbecbc80b5868d Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Fri, 3 Oct 2025 15:53:41 +0200 Subject: [PATCH 05/52] Renamed some methods and fields to follow naming convention. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 51 +++++++--------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 61 +++++++++++-------- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index f37f3c4be..78116202d 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -209,7 +209,7 @@ class IndentMan }; -void PartialRedundancyElimination::TraverseSubRegions(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) +void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) { IndentMan indenter = IndentMan(); for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) @@ -219,28 +219,23 @@ void PartialRedundancyElimination::TraverseSubRegions(rvsdg::Region& reg, void(* { for (auto& reg : sn.Subregions()) { - this->TraverseSubRegions(reg, cb); + this->TraverseTopDownRecursively(reg, cb); std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; } }); } } - -/* -void TraverseSubTrees(rvsdg::StructuralNode& node, void(*cb)(PartialRedundancyElimination* pe, rvsdg::StructuralNode& node)); -*/ - void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) { std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; for (size_t i = 0; i < node.noutputs(); i++) { - auto k_present = pe->output_hashes.find(node.output(i)); - if (k_present != pe->output_hashes.end() ) + auto k_present = pe->gvn_hashes_.find(node.output(i)); + if (k_present != pe->gvn_hashes_.end() ) { - size_t h = pe->output_hashes[node.output(i)]; - std::string color = (pe->hash_count(h) > 1 ? TR_GREEN : TR_YELLOW); + size_t h = pe->gvn_hashes_[node.output(i)]; + std::string color = (pe->DBG_HashCount(h) > 1 ? TR_GREEN : TR_YELLOW); MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_CYAN;}); MatchType(node, [&color](rvsdg::LambdaNode& lm){color = TR_ORANGE;}); @@ -252,9 +247,9 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r { for (auto& param : lm.GetFunctionArguments()) { - if (pe->output_hashes.find(param) != pe->output_hashes.end()) + if (pe->gvn_hashes_.find(param) != pe->gvn_hashes_.end()) { - size_t h = pe->output_hashes[param]; + size_t h = pe->gvn_hashes_[param]; std::cout << TR_ORANGE << " : " << h << TR_RESET; } @@ -280,9 +275,9 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati auto fargs = lm.GetFunctionArguments(); auto s = node.DebugString() + std::to_string(node.GetNodeId()); for (size_t i = 0; i < fargs.size(); i++){ - pe->register_hash_for_output(fargs[i], node.DebugString(), i); + pe->register_hash(fargs[i], node.DebugString(), i); } - pe->register_hash_for_output(node.output(0), node.DebugString() + "LM_NODE", 0); + pe->register_hash(node.output(0), node.DebugString() + "LM_NODE", 0); }); } @@ -292,7 +287,7 @@ void PartialRedundancyElimination::hash_call(PartialRedundancyElimination* pe, r { std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); //std::cout << TR_PINK << s << TR_RESET << std::endl; - pe->register_hash_for_output(node.output(0), s, 0); + pe->register_hash(node.output(0), s, 0); }); } @@ -303,9 +298,9 @@ void PartialRedundancyElimination::hash_gamma(PartialRedundancyElimination* pe, for (auto ev : node.GetEntryVars()) { rvsdg::Output* origin = ev.input->origin(); - if (pe->output_has_hash(origin)) + if (pe->OutputHasHash(origin)) { - size_t h = pe->output_hashes[origin]; + size_t h = pe->gvn_hashes_[origin]; for (rvsdg::Output* brarg : ev.branchArgument) { pe->register_hash(brarg, h); @@ -337,15 +332,15 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv bool was_hashable = true; for (size_t i = 0 ; i < node.ninputs(); i++) { - if (pe->output_hashes.find(node.input(i)->origin()) != pe->output_hashes.end()) + if (pe->gvn_hashes_.find(node.input(i)->origin()) != pe->gvn_hashes_.end()) { - size_t hash_in = pe->output_hashes[node.input(i)->origin()]; + size_t hash_in = pe->gvn_hashes_[node.input(i)->origin()]; if (op.is_commutative() && op.is_associative()) { h += hasher(std::to_string(hash_in)); }else { - h |= hasher(std::to_string(hash_in)) * (i+1); + h ^= hasher(std::to_string(hash_in)) * (i+1); } }else { @@ -364,7 +359,7 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv } if (was_hashable) { - pe->output_hashes.insert( {node.output(0), h} ); + pe->gvn_hashes_.insert( {node.output(0), h} ); pe->register_hash(h); } @@ -386,16 +381,16 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; - this->TraverseSubRegions(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index cd3ffd10a..a90740d3b 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -58,9 +58,12 @@ class PartialRedundancyElimination final : public rvsdg::Transformation Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; private: + std::unordered_map gvn_hashes_; + /* Debug data */ + std::unordered_map dbg_hash_counts_; - void TraverseSubRegions(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); + void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); @@ -70,46 +73,52 @@ class PartialRedundancyElimination final : public rvsdg::Transformation static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); - inline void register_hash(jlm::rvsdg::Output* k, size_t h) + /** + * Insert the hash into a map of {hash => count} for debugging purposes. + * \link dbg_hash_counts_ + */ + + inline void register_hash(size_t h) { - output_hashes.insert({k, h}); + if (dbg_hash_counts_.find(h) == dbg_hash_counts_.end()) + { + dbg_hash_counts_.insert({h, 1}); + }else + { + dbg_hash_counts_[h] = dbg_hash_counts_[h] + 1; + } + } + +inline void register_hash(jlm::rvsdg::Output* k, size_t h) + { + gvn_hashes_.insert({k, h}); register_hash(h); } - inline void register_hash_for_output(jlm::rvsdg::Output* out, std::string base, int index) + /** + * Convenience method for annotating outputs such as function arguments with hashes. + * + * @param out output to annotate with gvn value + * @param base a string to base the hash on + * @param index an index which is hashed together with the string hash + */ + inline void register_hash(jlm::rvsdg::Output* out, std::string base, int index) { const std::hash hasher; size_t h = hasher(base); h ^= index; - output_hashes.insert({out, h}); + gvn_hashes_.insert({out, h}); register_hash(h); } - //todo rename to gvn_hashes - std::unordered_map output_hashes; - - /* Debug data */ - std::unordered_map hash_counts; - - inline bool output_has_hash(rvsdg::Output* out){return output_hashes.find(out) != output_hashes.end();} - - inline void register_hash(size_t h) - { - if (hash_counts.find(h) == hash_counts.end()) - { - hash_counts.insert({h, 1}); - }else - { - hash_counts[h] = hash_counts[h] + 1; - } - } + inline bool OutputHasHash(rvsdg::Output* out){return gvn_hashes_.find(out) != gvn_hashes_.end();} - inline size_t hash_count(size_t h) + inline size_t DBG_HashCount(size_t h) { - if (hash_counts.find(h) != hash_counts.end()) + if (dbg_hash_counts_.find(h) != dbg_hash_counts_.end()) { - return hash_counts[h]; + return dbg_hash_counts_[h]; }else { return 0; From 5d915a161973369e2039ef3105a48b9c291ec86b Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sat, 4 Oct 2025 12:37:34 +0200 Subject: [PATCH 06/52] Refactored hash into a separate class for convenience. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 269 ++++++++---------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 87 ++++-- 2 files changed, 177 insertions(+), 179 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 24afd8a7b..ba05c265d 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017 Nico Reißmann + * Copyright 2025 Lars Astrup Sundt * See COPYING for terms of redistribution. */ @@ -24,6 +24,7 @@ #include "../../rvsdg/node.hpp" #include "../../rvsdg/nullary.hpp" #include "../../rvsdg/structural-node.hpp" +#include "../../rvsdg/theta.hpp" #include "../../util/GraphWriter.hpp" #include "../ir/operators/call.hpp" #include "../ir/operators/operators.hpp" @@ -51,68 +52,39 @@ #include -namespace jlm::llvm -{ +/** This might be moved to util if proven useful elsewhere **/ +static int indentation_level = 0; -class PartialRedundancyElimination::Context final +inline std::string ind() { -public: - /*void - MarkAlive(const jlm::rvsdg::Output & output) - { - if (auto simpleNode = rvsdg::TryGetOwnerNode(output)) - { - SimpleNodes_.Insert(simpleNode); - return; - } - - Outputs_.Insert(&output); - }*/ - - /*bool - IsAlive(const jlm::rvsdg::Output & output) const noexcept + std::string acc = ""; + for (int i = 0;i(output)) - { - return SimpleNodes_.Contains(simpleNode); - } + acc += " "; + } + return acc; +} - return Outputs_.Contains(&output); - }*/ +class IndentMan +{ - /*bool - IsAlive(const rvsdg::Node & node) const noexcept +public: + IndentMan() { - if (auto simpleNode = dynamic_cast(&node)) - { - return SimpleNodes_.Contains(simpleNode); - } - - for (size_t n = 0; n < node.noutputs(); n++) - { - if (IsAlive(*node.output(n))) - { - return true; - } - } - - return false; - }*/ - - static std::unique_ptr - Create() + indentation_level++; + } + ~IndentMan() { - return std::make_unique(); + indentation_level--; } - -private: - util::HashSet SimpleNodes_; - util::HashSet Outputs_; }; +/** -------------------------------------------------------------------------------------------- **/ +namespace jlm::llvm +{ -/** \brief Dead Node Elimination statistics class +/** \brief PRE statistics * */ class PartialRedundancyElimination::Statistics final : public util::Statistics @@ -148,13 +120,55 @@ class PartialRedundancyElimination::Statistics final : public util::Statistics } }; +/** -------------------------------------------------------------------------------------------- **/ + PartialRedundancyElimination::~PartialRedundancyElimination() noexcept = default; -PartialRedundancyElimination::PartialRedundancyElimination() - : Transformation("PartialRedundancyElimination") -{} +PartialRedundancyElimination::PartialRedundancyElimination(): Transformation("PartialRedundancyElimination"){} + +void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) +{ + IndentMan indenter = IndentMan(); + for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) + { + cb(this, *node); + MatchType(*node, [this,cb](rvsdg::StructuralNode& sn) + { + for (auto& reg : sn.Subregions()) + { + this->TraverseTopDownRecursively(reg, cb); + std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; + } + }); + } +} + +void +PartialRedundancyElimination::Run( + rvsdg::RvsdgModule & module, + util::StatisticsCollector & statisticsCollector) +{ + std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; + + auto & rvsdg = module.Rvsdg(); + auto statistics = Statistics::Create(module.SourceFilePath().value()); + + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); + std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; +} + +/** -------------------------------------------------------------------------------------------- **/ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, rvsdg::Node& node) { @@ -178,69 +192,23 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, std::cout << TR_GREEN << "-------------------------------------" << TR_RESET << std::endl; } }); - } -static int indentation_level = 0; - -inline std::string ind() -{ - std::string acc = ""; - for (int i = 0;iTraverseTopDownRecursively(reg, cb); - std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; - } - }); - } -} - void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) { std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; for (size_t i = 0; i < node.noutputs(); i++) { - auto k_present = pe->gvn_hashes_.find(node.output(i)); - if (k_present != pe->gvn_hashes_.end() ) + auto h = pe->GetHash(node.output(i)); + if (h.IsSome()) { - size_t h = pe->gvn_hashes_[node.output(i)]; std::string color = (pe->DBG_HashCount(h) > 1 ? TR_GREEN : TR_YELLOW); MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_CYAN;}); MatchType(node, [&color](rvsdg::LambdaNode& lm){color = TR_ORANGE;}); - std::cout << " : " << color << h << TR_RESET; + std::cout << " : " << color << std::to_string(h) << TR_RESET; } } MatchType(node, [pe](rvsdg::LambdaNode& lm) @@ -249,10 +217,9 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r { if (pe->gvn_hashes_.find(param) != pe->gvn_hashes_.end()) { - size_t h = pe->gvn_hashes_[param]; - std::cout << TR_ORANGE << " : " << h << TR_RESET; + GVN_Hash h = pe->gvn_hashes_[param]; + std::cout << TR_ORANGE << " : " << std::to_string(h) << TR_RESET; } - } }); std::cout << std::endl; @@ -265,7 +232,7 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati { std::hash hasher; size_t h = hasher(iconst.Representation().str()); - pe->register_hash(node.output(0), h); + pe->AssignGVN(node.output(0), GVN_Hash(h)); } ); @@ -275,35 +242,33 @@ void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyEliminati auto fargs = lm.GetFunctionArguments(); auto s = node.DebugString() + std::to_string(node.GetNodeId()); for (size_t i = 0; i < fargs.size(); i++){ - pe->register_hash(fargs[i], node.DebugString(), i); + pe->AssignGVN(fargs[i], node.DebugString(), i); } - pe->register_hash(node.output(0), node.DebugString() + "LM_NODE", 0); + pe->AssignGVN(node.output(0), node.DebugString() + "LM_NODE", 0); }); } void PartialRedundancyElimination::hash_call(PartialRedundancyElimination* pe, rvsdg::Node& node) { - MatchType(node.GetOperation(), [pe, &node](const jlm::llvm::CallOperation& op) + MatchTypeOrFail(node.GetOperation(), [pe, &node](const jlm::llvm::CallOperation& op) { std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); //std::cout << TR_PINK << s << TR_RESET << std::endl; - pe->register_hash(node.output(0), s, 0); + pe->AssignGVN(node.output(0), s, 0); }); } void PartialRedundancyElimination::hash_gamma(PartialRedundancyElimination* pe, rvsdg::Node& node) { - MatchType(node, [pe](rvsdg::GammaNode& node) + MatchTypeOrFail(node, [pe](rvsdg::GammaNode& node) { - for (auto ev : node.GetEntryVars()) - { + for (auto ev : node.GetEntryVars()){ rvsdg::Output* origin = ev.input->origin(); - if (pe->OutputHasHash(origin)) + GVN_Hash h = pe->GetHash(origin); + if (pe->GetHash(origin).IsSome()) { - size_t h = pe->gvn_hashes_[origin]; - for (rvsdg::Output* brarg : ev.branchArgument) - { - pe->register_hash(brarg, h); + for (rvsdg::Output* brarg : ev.branchArgument){ + pe->AssignGVN(brarg, h); } } } @@ -319,7 +284,29 @@ void PartialRedundancyElimination::hash_node(PartialRedundancyElimination *pe, r ); /*Match by node type*/ MatchType(node, - [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);} + [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);}, + [pe](rvsdg::ThetaNode& node){hash_theta_pre(pe, node);} + ); +} + +void PartialRedundancyElimination::hash_theta_pre(PartialRedundancyElimination *pe, rvsdg::Node& node) +{ + MatchTypeOrFail(node, + [pe, &node](rvsdg::ThetaNode& th) + { + std::cout << TR_RED << "Thefta here" << TR_RESET << std::endl; + for (size_t i = 0 ; i < node.ninputs() ; i++) + { + auto input = node.input(i); + if (input) + { + std::cout << TR_RED << "TODO: route loop variable." << TR_RESET << std::endl; + }else + { + std::cout << TR_RED << "WARN:pre: input to theta node is null" << TR_RESET << std::endl; + } + } + } ); } @@ -334,13 +321,13 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv { if (pe->gvn_hashes_.find(node.input(i)->origin()) != pe->gvn_hashes_.end()) { - size_t hash_in = pe->gvn_hashes_[node.input(i)->origin()]; + auto hash_in = pe->GetHash(node.input(i)->origin()); if (op.is_commutative() && op.is_associative()) { - h += hasher(std::to_string(hash_in)); + h += hasher(std::to_string(hash_in.value)); }else { - h ^= hasher(std::to_string(hash_in)) * (i+1); + h ^= hasher(std::to_string(hash_in.value)) * (i+1); } }else { @@ -357,42 +344,10 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv was_hashable = false; } } - if (was_hashable) - { - pe->gvn_hashes_.insert( {node.output(0), h} ); - pe->register_hash(h); + if (was_hashable){ + pe->AssignGVN(node.output(0), h); } - }); } -void -PartialRedundancyElimination::Run( - rvsdg::RvsdgModule & module, - util::StatisticsCollector & statisticsCollector) -{ - std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; - - - - - - - auto & rvsdg = module.Rvsdg(); - auto statistics = Statistics::Create(module.SourceFilePath().value()); - - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); - std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - - std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; -} - } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index a90740d3b..9ffb00c57 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2017 Nico Reißmann + * Copyright 2025 Lars Astrup Sundt * See COPYING for terms of redistribution. */ @@ -27,11 +27,31 @@ class Region; namespace jlm::llvm { +struct GVN_Hash +{ + size_t value; + inline GVN_Hash(){this->value = 0;} + inline GVN_Hash(size_t v){this->value = v;} + inline static GVN_Hash None() {return GVN_Hash(0);} + inline static GVN_Hash Tainted(){return GVN_Hash(1);} + inline bool IsValid(){return value >= 2;} + inline bool IsSome(){return value != 0;} +}; + +/** Boiler plate for making the struct compatible with std::unordered_map **/ +struct GVN_Map_Hash{ + size_t operator()(const GVN_Hash& v) const{return v.value;} +}; + +struct GVN_Map_Eq +{ + bool operator()(const GVN_Hash& a, const GVN_Hash& b) const{return a.value == b.value;} +}; + + /** \brief Partial Redundancy Elimination * - * Todo: description here - * - * + * A pass for doing partial redundancy analysis and elimination */ class PartialRedundancyElimination final : public rvsdg::Transformation { @@ -51,17 +71,15 @@ class PartialRedundancyElimination final : public rvsdg::Transformation PartialRedundancyElimination & operator=(PartialRedundancyElimination &&) = delete; - /*void - run(rvsdg::Region & region);*/ - void Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; private: - std::unordered_map gvn_hashes_; + /** \brief A mapping from Input and Output to gvn hashes **/ + std::unordered_map gvn_hashes_; /* Debug data */ - std::unordered_map dbg_hash_counts_; + std::unordered_map dbg_hash_counts_; void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); @@ -72,27 +90,33 @@ class PartialRedundancyElimination final : public rvsdg::Transformation static void hash_gamma( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void hash_theta_pre( PartialRedundancyElimination *pe, rvsdg::Node& node); + //static void hash_theta_post( PartialRedundancyElimination *pe, rvsdg::Node& ndoe); /** * Insert the hash into a map of {hash => count} for debugging purposes. - * \link dbg_hash_counts_ + * \ref dbg_hash_counts_ */ - inline void register_hash(size_t h) + inline void DBG_CountGVN_HashCounts(GVN_Hash h) { - if (dbg_hash_counts_.find(h) == dbg_hash_counts_.end()) - { + if (dbg_hash_counts_.find(h) == dbg_hash_counts_.end()){ dbg_hash_counts_.insert({h, 1}); - }else - { + }else{ dbg_hash_counts_[h] = dbg_hash_counts_[h] + 1; } } -inline void register_hash(jlm::rvsdg::Output* k, size_t h) +inline void AssignGVN(jlm::rvsdg::Output* k, GVN_Hash h) { gvn_hashes_.insert({k, h}); - register_hash(h); + DBG_CountGVN_HashCounts(h); + } + + inline void AssignGVN(jlm::rvsdg::Input* k, GVN_Hash h) + { + gvn_hashes_.insert({k, h}); + DBG_CountGVN_HashCounts(h); } /** @@ -102,19 +126,24 @@ inline void register_hash(jlm::rvsdg::Output* k, size_t h) * @param base a string to base the hash on * @param index an index which is hashed together with the string hash */ - inline void register_hash(jlm::rvsdg::Output* out, std::string base, int index) + inline void AssignGVN(jlm::rvsdg::Output* out, std::string base, int index) { const std::hash hasher; size_t h = hasher(base); h ^= index; - gvn_hashes_.insert({out, h}); - register_hash(h); + gvn_hashes_.insert({out, GVN_Hash(h)}); + DBG_CountGVN_HashCounts(h); } - inline bool OutputHasHash(rvsdg::Output* out){return gvn_hashes_.find(out) != gvn_hashes_.end();} + /** Safely returns a valid hash value or None **/ + GVN_Hash GetHash(void* input_or_output) + { + if (gvn_hashes_.find(input_or_output) != gvn_hashes_.end()){return gvn_hashes_[input_or_output];} + return GVN_Hash::None(); + } - inline size_t DBG_HashCount(size_t h) + inline size_t DBG_HashCount(GVN_Hash h) { if (dbg_hash_counts_.find(h) != dbg_hash_counts_.end()) { @@ -129,4 +158,18 @@ inline void register_hash(jlm::rvsdg::Output* k, size_t h) } +namespace std +{ + inline std::string to_string(jlm::llvm::GVN_Hash h) + { + switch (h.value) + { + case 0: return std::string("none"); + case 1: return std::string("tainted"); + default: return std::to_string(h.value); + } + } +} + + #endif From 5e98e1d9affb067cdad341a5a3ebdc1c407ad0ae Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sat, 4 Oct 2025 12:37:58 +0200 Subject: [PATCH 07/52] Refactored hash into a separate class for convenience. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index ba05c265d..87d0896af 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -294,7 +294,7 @@ void PartialRedundancyElimination::hash_theta_pre(PartialRedundancyElimination * MatchTypeOrFail(node, [pe, &node](rvsdg::ThetaNode& th) { - std::cout << TR_RED << "Thefta here" << TR_RESET << std::endl; + std::cout << TR_RED << "Theta here" << TR_RESET << std::endl; for (size_t i = 0 ; i < node.ninputs() ; i++) { auto input = node.input(i); From dfe5e4e2eaa12ab8b87eb6322ce5a91b159bb136 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sun, 5 Oct 2025 21:22:57 +0200 Subject: [PATCH 08/52] Save before delete flow draft --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 12 ++ jlm/llvm/opt/PartialRedundancyElimination.hpp | 202 +++++++++++++++++- 2 files changed, 205 insertions(+), 9 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 87d0896af..969681c1f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -166,6 +166,18 @@ PartialRedundancyElimination::Run( this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; + + + flows::AnalysisData my_int_tags; + + flows::RecurseTopDown(my_int_tags, + rvsdg.GetRootRegion(), + [](rvsdg::Node& node) + { + std::cout <<"."<< std::endl; + return flows::FlowValue(flows::FlowType::NODE, node.GetNodeId()); + } + ); } /** -------------------------------------------------------------------------------------------- **/ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 9ffb00c57..e48b54eb3 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -6,11 +6,13 @@ #ifndef JLM_LLVM_OPT_PartialRedundancyElimination_HPP #define JLM_LLVM_OPT_PartialRedundancyElimination_HPP +#include "jlm/rvsdg/MatchType.hpp" +#include "jlm/rvsdg/traverser.hpp" #include +#include #include #include #include -#include namespace jlm::rvsdg { @@ -24,9 +26,185 @@ class ThetaNode; class Region; } -namespace jlm::llvm + +namespace jlm::llvm::flows{ +const size_t PRE_PTR_TAG_OUTPUT = 0x1; +const size_t PRE_PTR_TAG_INPUT = 0x2; +const size_t PRE_PTR_TAG_NODE = 0x3; +const size_t PRE_PTR_TAG_MASK = 0x3; + +/** \brief A pointer to either rvsdg::Node*, rvsdg::Input or rvsdg::Output using the low bits as inline tags. **/ +union Flow +{ + Flow(rvsdg::Input* i){SetInput(i);} + Flow(rvsdg::Output* o){SetOutput(o);} + Flow(rvsdg::Node* n){SetNode(n);} + + inline void SetInput(rvsdg::Input* i) + { + this->input_ = reinterpret_cast(reinterpret_cast(i) | PRE_PTR_TAG_INPUT); + } + inline void SetOutput(rvsdg::Output* o) + { + this->output_ = reinterpret_cast(reinterpret_cast(o) | PRE_PTR_TAG_OUTPUT); + } + inline void SetNode(rvsdg::Node* n) + { + this->node_ = reinterpret_cast(reinterpret_cast(n) | PRE_PTR_TAG_NODE); + } + + inline rvsdg::Input* GetInput(){return (reinterpret_cast(input_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_INPUT ? input_ : NULL;} + inline rvsdg::Output* GetOutput(){return (reinterpret_cast(output_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_OUTPUT ? output_ : NULL;} + inline rvsdg::Node* GetNode(){return (reinterpret_cast(output_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_NODE ? node_ : NULL;} + inline void* UnsafeGetRaw() const {return static_cast(input_);} +private: + rvsdg::Input* input_; + rvsdg::Output* output_; + rvsdg::Node* node_; +}; + +struct Flow_Hash{void* operator()(const Flow& v) const{return v.UnsafeGetRaw();}}; +struct Flow_Eq{bool operator()(const Flow& a, const Flow& b) const{return a.UnsafeGetRaw() == b.UnsafeGetRaw();}}; + +template +struct AnalysisData +{ + std::unordered_map annot_flows; + D Get(Output o) + { + Flow f(o); + if (annot_flows.find(f) != annot_flows.end()) + { + return annot_flows[f]; + }else{ + return D(); /** Empty value **/ + } + } +}; + +/** \brief the type of flow. **/ +enum class FlowType { + //Optionally differentiate between single and multiple cases in order to catch logic errors + // where a single value is expected to flow from a node, but there are multiple exits + INPUT, + OUTPUT, + NODE, + PARAMETER, +}; + +template +struct FlowValue +{ + FlowType type; + D value; + size_t index; + FlowValue(FlowType type, D value) : type(type), value(value), index(0){} + FlowValue(D value, size_t index) : type(FlowType::PARAMETER),value(value), index(index){} +}; + +/** TODO: one data structure for storing annotations and another for storing reactive state **/ +/** TODO: no need for intermediate buffers. Project flows onto edges and update reactive state as needed **/ +/** Avoid duplicating data by storing maps elsewhere and provide mapping functions **/ + +/** \brief FlowsCtx a context used as a proxy inside graph traversers + * D must be comparable + * **/ + + + +/** \brief a buffer for the inputs and output flows from a node or region **/ +template +class FlowsBuffer +{ +public: + FlowsBuffer(){} + void Resize(size_t ninputs){ + values.resize(ninputs); + flow_direction.resize(ninputs); + } + void Clear(){values.clear(); values.clear();} + std::vector values; +private: + std::vector flow_direction; +}; + +} +namespace std +{ +inline std::string to_string(jlm::llvm::flows::FlowType ft) +{ + switch (ft) + { + case jlm::llvm::flows::FlowType::NODE: return std::string("NODE"); + case jlm::llvm::flows::FlowType::INPUT: return std::string("INPUT"); + case jlm::llvm::flows::FlowType::OUTPUT: return std::string("OUTPUT"); + case jlm::llvm::flows::FlowType::PARAMETER: return std::string("PARAMETER"); + default: return std::string("Invalid flow type"); + } +} +} + +namespace jlm::llvm::flows +{ +using namespace jlm; + +/** \brief reactively update annotated data for nodes and edges until a fixed point is reached **/ +template +void RecurseTopDownWithFlows(AnalysisData& analysis, rvsdg::Region& reg, Fn cb_producer) +{ + using namespace jlm::rvsdg; + + for (Node& node : reg.TopNodes()) + { + MatchType(node, + /** Split and merge values for each branch **/ + [&analysis](GammaNode& gn) + { + FlowsBuffer outputs; + outputs.Resize(gn.noutputs() ); + + FlowsBuffer inputs; + inputs.Resize(gn.ninputs()); + + for (auto i = 0; i ) + + for (size_t i = 0; i < gn.ninputs() ; i++) + { + if (gn.input(i)){inputs[i] = analysis.GetValue(gn.input(i)->origin());} + } + + + /* Split flows */ + size_t reg_count = gn.nsubregions(); + size_t input_count = gn.ninputs(); + size_t output_count = gn.noutputs(); + + + + } + ); + } +} + +template +void RecurseTopDown(AnalysisData& analysis, rvsdg::Region& reg, Fn cb_node) +{ + for ( rvsdg::Node* node : jlm::rvsdg::TopDownTraverser(®) ) + { + if (node){ + FlowValue fl = cb_node(*node); + std::cout << "The flow type is: " << std::to_string(fl.type) << std::endl; + std::cout << std::to_string(fl.value) << std::endl; + analysis.annot_node[node] = fl.value; + } + } +} +} + +namespace jlm::llvm +{ struct GVN_Hash { size_t value; @@ -36,6 +214,15 @@ struct GVN_Hash inline static GVN_Hash Tainted(){return GVN_Hash(1);} inline bool IsValid(){return value >= 2;} inline bool IsSome(){return value != 0;} + inline GVN_Hash Merge(GVN_Hash& other) + { + if (other.IsSome()) + { + return (this->value == other.value) ? *this : GVN_Hash::Tainted(); + }else{ + return this->IsSome() ? *this : other; + } + } }; /** Boiler plate for making the struct compatible with std::unordered_map **/ @@ -48,12 +235,11 @@ struct GVN_Map_Eq bool operator()(const GVN_Hash& a, const GVN_Hash& b) const{return a.value == b.value;} }; - /** \brief Partial Redundancy Elimination * * A pass for doing partial redundancy analysis and elimination */ -class PartialRedundancyElimination final : public rvsdg::Transformation +class PartialRedundancyElimination final : public jlm::rvsdg::Transformation { class Context; class Statistics; @@ -72,7 +258,7 @@ class PartialRedundancyElimination final : public rvsdg::Transformation operator=(PartialRedundancyElimination &&) = delete; void - Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; + Run(jlm::rvsdg::RvsdgModule & module, jlm::util::StatisticsCollector & statisticsCollector) override; private: /** \brief A mapping from Input and Output to gvn hashes **/ @@ -91,7 +277,7 @@ class PartialRedundancyElimination final : public rvsdg::Transformation static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_theta_pre( PartialRedundancyElimination *pe, rvsdg::Node& node); - //static void hash_theta_post( PartialRedundancyElimination *pe, rvsdg::Node& ndoe); + //static void hash_theta_post( PartialRedundancyElimination *pe, rvsdg::Node& node); /** * Insert the hash into a map of {hash => count} for debugging purposes. @@ -107,7 +293,7 @@ class PartialRedundancyElimination final : public rvsdg::Transformation } } -inline void AssignGVN(jlm::rvsdg::Output* k, GVN_Hash h) + inline void AssignGVN(jlm::rvsdg::Output* k, GVN_Hash h) { gvn_hashes_.insert({k, h}); DBG_CountGVN_HashCounts(h); @@ -153,7 +339,6 @@ inline void AssignGVN(jlm::rvsdg::Output* k, GVN_Hash h) return 0; } } - }; } @@ -171,5 +356,4 @@ namespace std } } - #endif From 8ac5ff97e86502d6d72197ebc2b13db3eca89399 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Mon, 6 Oct 2025 13:27:59 +0200 Subject: [PATCH 09/52] Reactive data flows top down. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 74 ++--- jlm/llvm/opt/PartialRedundancyElimination.hpp | 268 ++++++++++++------ 2 files changed, 216 insertions(+), 126 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 969681c1f..759610a7b 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -141,6 +141,7 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } }); } + } @@ -154,6 +155,44 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); + + flows::FlowData fd(&gvn_hashes_); + auto merge_gvn = [](GVN_Hash& a, GVN_Hash b){return GVN_Hash(0);}; + + + flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn, + [](rvsdg::Node& node, + flows::FlowData& fd, + std::vector>& flows_in, + std::vector>& flows_out + ) + { + + std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; + rvsdg::MatchType(node.GetOperation(), + [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ + std::hash hasher; + if (flows_out.size() == 0){return;} + flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); + } + ); + + rvsdg::MatchType(node, + [&flows_out](rvsdg::LambdaNode& lm){ + auto s = lm.DebugString(); + std::hash hasher; + for (size_t i = 0; i < flows_out.size(); i++){ + flows_out[i] = GVN_Hash( hasher(s + std::to_string(i)) ); + } + } + ); + } + ); + + std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; + + + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; @@ -168,16 +207,6 @@ PartialRedundancyElimination::Run( std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; - flows::AnalysisData my_int_tags; - - flows::RecurseTopDown(my_int_tags, - rvsdg.GetRootRegion(), - [](rvsdg::Node& node) - { - std::cout <<"."<< std::endl; - return flows::FlowValue(flows::FlowType::NODE, node.GetNodeId()); - } - ); } /** -------------------------------------------------------------------------------------------- **/ @@ -206,7 +235,6 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, }); } - void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) { std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; @@ -296,29 +324,7 @@ void PartialRedundancyElimination::hash_node(PartialRedundancyElimination *pe, r ); /*Match by node type*/ MatchType(node, - [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);}, - [pe](rvsdg::ThetaNode& node){hash_theta_pre(pe, node);} - ); -} - -void PartialRedundancyElimination::hash_theta_pre(PartialRedundancyElimination *pe, rvsdg::Node& node) -{ - MatchTypeOrFail(node, - [pe, &node](rvsdg::ThetaNode& th) - { - std::cout << TR_RED << "Theta here" << TR_RESET << std::endl; - for (size_t i = 0 ; i < node.ninputs() ; i++) - { - auto input = node.input(i); - if (input) - { - std::cout << TR_RED << "TODO: route loop variable." << TR_RESET << std::endl; - }else - { - std::cout << TR_RED << "WARN:pre: input to theta node is null" << TR_RESET << std::endl; - } - } - } + [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);} ); } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index e48b54eb3..15558907a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -9,6 +9,7 @@ #include "jlm/rvsdg/MatchType.hpp" #include "jlm/rvsdg/traverser.hpp" #include +#include #include #include #include @@ -28,13 +29,15 @@ class Region; namespace jlm::llvm::flows{ + +/* const size_t PRE_PTR_TAG_OUTPUT = 0x1; const size_t PRE_PTR_TAG_INPUT = 0x2; const size_t PRE_PTR_TAG_NODE = 0x3; const size_t PRE_PTR_TAG_MASK = 0x3; - +*/ /** \brief A pointer to either rvsdg::Node*, rvsdg::Input or rvsdg::Output using the low bits as inline tags. **/ -union Flow +/*union Flow { Flow(rvsdg::Input* i){SetInput(i);} Flow(rvsdg::Output* o){SetOutput(o);} @@ -63,26 +66,8 @@ union Flow rvsdg::Node* node_; }; -struct Flow_Hash{void* operator()(const Flow& v) const{return v.UnsafeGetRaw();}}; -struct Flow_Eq{bool operator()(const Flow& a, const Flow& b) const{return a.UnsafeGetRaw() == b.UnsafeGetRaw();}}; +*/ -template -struct AnalysisData -{ - std::unordered_map annot_flows; - D Get(Output o) - { - Flow f(o); - if (annot_flows.find(f) != annot_flows.end()) - { - return annot_flows[f]; - }else{ - return D(); /** Empty value **/ - } - } -}; - -/** \brief the type of flow. **/ enum class FlowType { //Optionally differentiate between single and multiple cases in order to catch logic errors @@ -93,15 +78,8 @@ enum class FlowType PARAMETER, }; -template -struct FlowValue -{ - FlowType type; - D value; - size_t index; - FlowValue(FlowType type, D value) : type(type), value(value), index(0){} - FlowValue(D value, size_t index) : type(FlowType::PARAMETER),value(value), index(index){} -}; + + /** TODO: one data structure for storing annotations and another for storing reactive state **/ /** TODO: no need for intermediate buffers. Project flows onto edges and update reactive state as needed **/ @@ -111,24 +89,6 @@ struct FlowValue * D must be comparable * **/ - - -/** \brief a buffer for the inputs and output flows from a node or region **/ -template -class FlowsBuffer -{ -public: - FlowsBuffer(){} - void Resize(size_t ninputs){ - values.resize(ninputs); - flow_direction.resize(ninputs); - } - void Clear(){values.clear(); values.clear();} - std::vector values; -private: - std::vector flow_direction; -}; - } namespace std @@ -150,57 +110,181 @@ namespace jlm::llvm::flows { using namespace jlm; -/** \brief reactively update annotated data for nodes and edges until a fixed point is reached **/ -template -void RecurseTopDownWithFlows(AnalysisData& analysis, rvsdg::Region& reg, Fn cb_producer) -{ - using namespace jlm::rvsdg; - - for (Node& node : reg.TopNodes()) + /** A view into the data to manipulate by flows **/ + template + class FlowData { - MatchType(node, - /** Split and merge values for each branch **/ - [&analysis](GammaNode& gn) - { - FlowsBuffer outputs; - outputs.Resize(gn.noutputs() ); - - FlowsBuffer inputs; - inputs.Resize(gn.ninputs()); + public: + FlowData(std::unordered_map * output_values) + { + JLM_ASSERT(output_values); //should be non-null + output_values_ = output_values; + } + std::optional Get(rvsdg::Output* k) + { + bool present = output_values_->find(k) == output_values_->end(); + return present ? std::optional((*output_values_)[k]) : std::nullopt; + } + void Set(rvsdg::Output* k, std::optional v) + { + if (v){ + output_values_->insert({k,*v}); + } + } + private: + std::unordered_map* output_values_; + }; - for (auto i = 0; i ) + enum class WorkItemType + { + REGION, + DELTA, + NODE, + GAMMA, + GAMMA_END, + THETA, + THETA_END, + LAMBDA, + }; + struct WorkItemValue + { + // implicit conversions are ok here + WorkItemValue(rvsdg::Region* r) {this->type = WorkItemType::REGION; this->region = r;} + WorkItemValue(WorkItemType type, rvsdg::Node* n){this->type = type; this->node = n;} + WorkItemValue(rvsdg::Node* n) + { + this->node = n; + this->type = WorkItemType::NODE; + rvsdg::MatchType(*n, + [this](jlm::rvsdg::GammaNode& gn){this->type = WorkItemType::GAMMA;}, + [this](jlm::rvsdg::ThetaNode& tn){this->type = WorkItemType::THETA;}, + [this](jlm::rvsdg::LambdaNode& lm) {this->type = WorkItemType::LAMBDA;}, + [this](jlm::rvsdg::DeltaNode& dl) {this->type = WorkItemType::DELTA;} + ); + } - for (size_t i = 0; i < gn.ninputs() ; i++) + WorkItemType type; + union{ + rvsdg::DeltaNode* dl; + rvsdg::Region* region; + rvsdg::Node* node; + rvsdg::GammaNode* gn; + rvsdg::ThetaNode* tn; + rvsdg::LambdaNode* lm; + }; + }; + + /** \brief abstracts away the walking of the rvsdg graph for data flows of a reactive nature + * Theta and Gamma nodes have special semantics as they must sometimes merge values. + * */ + template + void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, Merger mr, Prod cb){ + // A queue of nodes and regions to visit or equivalently a continuation + // of instruction to be executed by the interpreter below. + std::vector workItems; + // A buffer for flow values. + // The flows function handles lookup of values from the fd map. + std::vector< std::optional > flows_in; + std::vector< std::optional > flows_out; + + workItems.push_back(WorkItemValue(&scope)); + + while (workItems.size()) + { + auto w = workItems.back(); workItems.pop_back(); + switch (w.type) + { + case WorkItemType::DELTA:{ + for (auto& reg : w.lm->Subregions()){ + workItems.push_back(WorkItemValue(®)); + } + std::cout << "WL:DELTA"< 1000){std::cout<<"Stack overflow" << std::endl; return;} + + }break; + + case WorkItemType::REGION:{ + std::cout << "WL:REGION"< tmp; + for (auto node : rvsdg::TopDownTraverser(w.region)){tmp.push_back(WorkItemValue(node));} + while (tmp.size()){workItems.push_back(tmp.back()); tmp.pop_back();} + }break; + case WorkItemType::NODE:{ + std::cout << "WL:NODE:"<DebugString()<< std::endl; + // initialize input buffer + flows_in.clear(); + for (size_t i=0;i < w.node->ninputs() ; i++){ + auto ni = w.node->input(i); //check just in case Input is NULL + std::optional val = ni ? fd.Get(ni->origin()) : std::nullopt; + flows_in.push_back(std::optional(val)); + } + // initialize output buffer + flows_out.clear(); + flows_out.resize(w.node->noutputs(), std::nullopt); + + // visit node + cb(*(w.node), fd, flows_in, flows_out); + + // update map + for ( size_t i = 0; i < w.node->noutputs(); i++){ + fd.Set(w.node->output(i), flows_out[i]); + } + //DEBUG + for (size_t i = 0; i < flows_out.size(); i++){ + if (flows_out[i]){ + std::cout << "Flow out ["<GetFunctionArguments(); + flows_out.clear(); flows_out.resize(f_args.size(), std::nullopt); + + // visit node + cb(*(w.node), fd, flows_in, flows_out); + + // update map + for ( size_t i = 0; i < f_args.size(); i++){ + fd.Set(f_args[i], flows_out[i]); + } + //DEBUG + for (size_t i = 0; i < flows_out.size(); i++){ + if (flows_out[i]){ + std::cout << "Lambda: Flow out [" << i << "] : " << std::endl; + } + } + // Todododo : visit lambda after body has been visited once. + // Finally iterate over lambda body + workItems.push_back(w.lm->subregion()); + }break; + case WorkItemType::GAMMA: { - if (gn.input(i)){inputs[i] = analysis.GetValue(gn.input(i)->origin());} - } - - - /* Split flows */ - size_t reg_count = gn.nsubregions(); - size_t input_count = gn.ninputs(); - size_t output_count = gn.noutputs(); - - - + //Split flows + for (auto ev : w.gn->GetEntryVars()){ + auto tmp = ev.input; + auto flow_from = tmp ? fd.Get(tmp->origin()) : std::nullopt; + if (flow_from){ + for (rvsdg::Output* brarg : ev.branchArgument){ + fd.Set(brarg, *flow_from); + } + } + } + //Push todos in LIFO order + workItems.push_back(WorkItemValue(WorkItemType::GAMMA_END, w.gn)); + for (size_t i = 0; i < w.gn->nsubregions() ; i++){ + workItems.push_back(w.gn->subregion(i)); + } + }break; + default: std::cout << static_cast(w.type) <<"Ignoring work item..."< -void RecurseTopDown(AnalysisData& analysis, rvsdg::Region& reg, Fn cb_node) -{ - for ( rvsdg::Node* node : jlm::rvsdg::TopDownTraverser(®) ) - { - if (node){ - FlowValue fl = cb_node(*node); - std::cout << "The flow type is: " << std::to_string(fl.type) << std::endl; - std::cout << std::to_string(fl.value) << std::endl; - analysis.annot_node[node] = fl.value; } } -} + } namespace jlm::llvm From 7740b59a188e74256aec901f4783d301561cf72d Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Mon, 6 Oct 2025 20:04:20 +0200 Subject: [PATCH 10/52] Added gamma end --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 8 +++-- jlm/llvm/opt/PartialRedundancyElimination.hpp | 35 +++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 759610a7b..cd5c13345 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -157,10 +157,11 @@ PartialRedundancyElimination::Run( flows::FlowData fd(&gvn_hashes_); - auto merge_gvn = [](GVN_Hash& a, GVN_Hash b){return GVN_Hash(0);}; + auto merge_gvn_ga = [](std::optional& a, std::optional& b){return std::optional(0);}; + auto merge_gvn_th = [](std::optional& a, std::optional& b){return std::optional(0);}; - flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn, + flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, [](rvsdg::Node& node, flows::FlowData& fd, std::vector>& flows_in, @@ -168,6 +169,8 @@ PartialRedundancyElimination::Run( ) { + + std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; rvsdg::MatchType(node.GetOperation(), [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ @@ -189,6 +192,7 @@ PartialRedundancyElimination::Run( } ); + std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 15558907a..4cb5925b1 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -176,9 +176,14 @@ using namespace jlm; /** \brief abstracts away the walking of the rvsdg graph for data flows of a reactive nature * Theta and Gamma nodes have special semantics as they must sometimes merge values. + * Merges at gamma and theta nodes combine flows. + * The merge at theta nodes represent values with multiple alternatives from + * different loop iterations. + * Merges at gamma nodes represent the combined value from switch cases + * The merges may be different or the same * */ - template - void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, Merger mr, Prod cb){ + template + void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ // A queue of nodes and regions to visit or equivalently a continuation // of instruction to be executed by the interpreter below. std::vector workItems; @@ -262,6 +267,7 @@ using namespace jlm; // Finally iterate over lambda body workItems.push_back(w.lm->subregion()); }break; + case WorkItemType::GAMMA: { //Split flows @@ -274,12 +280,35 @@ using namespace jlm; } } } - //Push todos in LIFO order + //Push tasks in LIFO order workItems.push_back(WorkItemValue(WorkItemType::GAMMA_END, w.gn)); for (size_t i = 0; i < w.gn->nsubregions() ; i++){ workItems.push_back(w.gn->subregion(i)); } }break; + case WorkItemType::GAMMA_END:{ + // Reduce all outputs from exitVars with mrGa + auto ex_vars = w.gn->GetExitVars(); + JLM_ASSERT(ex_vars.size() == w.gn->noutputs()); + for (size_t v = 0; v < ex_vars.size(); v++){ + auto br_can_be_null = ex_vars[v].branchResult[0]; + if (br_can_be_null){flows_out[v] = fd.Get( br_can_be_null->origin() );} + auto merged_val = br_can_be_null ? fd.Get(br_can_be_null->origin()) : std::nullopt; + + for (size_t b = 1;b < ex_vars[v].branchResult.size();b++){ // !!! from 1 + auto next_br = ex_vars[v].branchResult[b]; + auto next_br_value = next_br ? fd.Get(next_br->origin()) : std::nullopt; + merged_val = mrGa(merged_val, next_br_value); + } + + fd.Set(ex_vars[v].output, merged_val); + JLM_ASSERT(ex_vars.size() == w.gn->noutputs()); + JLM_ASSERT(ex_vars[v].output == w.gn->output(v)); + } + }break; + + + default: std::cout << static_cast(w.type) <<"Ignoring work item..."< Date: Tue, 7 Oct 2025 11:20:30 +0200 Subject: [PATCH 11/52] Added theta node to flows --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 1 + jlm/llvm/opt/PartialRedundancyElimination.hpp | 53 ++++++++++++++++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index cd5c13345..96c76f172 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -172,6 +172,7 @@ PartialRedundancyElimination::Run( std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; + rvsdg::MatchType(node.GetOperation(), [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ std::hash hasher; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 4cb5925b1..61d4fcd3e 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -122,6 +122,7 @@ using namespace jlm; } std::optional Get(rvsdg::Output* k) { + if (k == NULL){return std::nullopt;} bool present = output_values_->find(k) == output_values_->end(); return present ? std::optional((*output_values_)[k]) : std::nullopt; } @@ -193,9 +194,10 @@ using namespace jlm; std::vector< std::optional > flows_out; workItems.push_back(WorkItemValue(&scope)); - - while (workItems.size()) + size_t max_iter = 500; + while (workItems.size() && max_iter) { + max_iter--; auto w = workItems.back(); workItems.pop_back(); switch (w.type) { @@ -264,6 +266,7 @@ using namespace jlm; } } // Todododo : visit lambda after body has been visited once. + // Finally iterate over lambda body workItems.push_back(w.lm->subregion()); }break; @@ -291,13 +294,12 @@ using namespace jlm; auto ex_vars = w.gn->GetExitVars(); JLM_ASSERT(ex_vars.size() == w.gn->noutputs()); for (size_t v = 0; v < ex_vars.size(); v++){ - auto br_can_be_null = ex_vars[v].branchResult[0]; - if (br_can_be_null){flows_out[v] = fd.Get( br_can_be_null->origin() );} - auto merged_val = br_can_be_null ? fd.Get(br_can_be_null->origin()) : std::nullopt; + auto br_fst = ex_vars[v].branchResult[0]; + auto merged_val = fd.Get( br_fst ? br_fst->origin() : NULL ); for (size_t b = 1;b < ex_vars[v].branchResult.size();b++){ // !!! from 1 auto next_br = ex_vars[v].branchResult[b]; - auto next_br_value = next_br ? fd.Get(next_br->origin()) : std::nullopt; + auto next_br_value = fd.Get( next_br ? next_br->origin() : NULL ); merged_val = mrGa(merged_val, next_br_value); } @@ -307,7 +309,44 @@ using namespace jlm; } }break; + case WorkItemType::THETA:{ + // At entry into a theta node check if the flow into each loop variable + // is compatible with the last output previous loop iterations + // This ensures theta bodies are only visited twice during GVN + // It is the responsibility of merge callbacks to ensure values + // reach a fixpoint. + auto loopvars = w.tn->GetLoopVars(); + bool fixed_point_reached = true; + + for (size_t i = 0;i < loopvars.size(); i++){ + auto lv = loopvars[i]; + auto lv_input = fd.Get( lv.input ? lv.input->origin() : NULL ); + + auto lv_post = fd.Get( lv.post ? lv.post->origin() : NULL ); + auto merged = mrTh( lv_input, lv_post ); + + auto lv_pre = fd.Get( lv.pre ); + if ( + (merged && !lv_pre) || (!merged && lv_pre) || (*merged != *lv_pre) + ){fixed_point_reached = false;} + + fd.Set( lv.pre, merged ); + } + + if (!fixed_point_reached){ + workItems.push_back( WorkItemValue(WorkItemType::THETA_END, w.tn) ); + workItems.push_back( w.tn->subregion() ); + } + }break; + case WorkItemType::THETA_END:{ + auto loopvars = w.tn->GetLoopVars(); + for (size_t i = 0;i < loopvars.size(); i++){ + auto lv = loopvars[i]; + fd.Set( lv.output, fd.Get( lv.post ? lv.post->origin() : NULL ) ); // Required for downstream nodes + } + workItems.push_back( WorkItemValue(w.tn) ); // Attempt another loop iteration + }break; default: std::cout << static_cast(w.type) <<"Ignoring work item..."<= 2;} inline bool IsSome(){return value != 0;} + inline bool operator==(const GVN_Hash &other){return this->value == other.value;} + inline bool operator!=(const GVN_Hash &other){return this->value != other.value;} inline GVN_Hash Merge(GVN_Hash& other) { if (other.IsSome()) From ea8e378bd0c04c103be34ec8d872ee31579357f9 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 11:57:32 +0200 Subject: [PATCH 12/52] Added binary op hasher back in --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 34 ++++++++++++++++--- jlm/llvm/opt/PartialRedundancyElimination.hpp | 3 ++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 96c76f172..caf7ca060 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -155,11 +155,21 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - flows::FlowData fd(&gvn_hashes_); - auto merge_gvn_ga = [](std::optional& a, std::optional& b){return std::optional(0);}; - auto merge_gvn_th = [](std::optional& a, std::optional& b){return std::optional(0);}; + auto merge_gvn_ga = [](std::optional& a, std::optional& b) + { + if (!a){return b;} if (!b){return a;} + if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } + size_t h = a->value ^ (b->value << 3); + return std::optional( GVN_Hash(h) ); + }; + auto merge_gvn_th = [](std::optional& a, std::optional& b) + { + if (!a){return b;} if (!b){return a;} + if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } + return a->value == b->value ? a : std::optional( GVN_Hash::Tainted() ); + }; flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, [](rvsdg::Node& node, @@ -169,8 +179,6 @@ PartialRedundancyElimination::Run( ) { - - std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; rvsdg::MatchType(node.GetOperation(), @@ -178,6 +186,22 @@ PartialRedundancyElimination::Run( std::hash hasher; if (flows_out.size() == 0){return;} flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); + }, + + [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ + JLM_ASSERT(flows_in.size() == 2); + if (!(flows_in[0]) || !(flows_in[1])){ + std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; + } + + std::hash hasher; + size_t h = hasher(op.debug_string() ); + + size_t a = hasher(std::to_string(flows_in[0]->value)); + size_t b = hasher(std::to_string(flows_in[1]->value)); + bool c_and_a = op.is_commutative() && op.is_associative(); + h ^= c_and_a ? (a + b) : (a ^ (b << 3)); + flows_out[0] = std::optional(h); } ); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 61d4fcd3e..18d873d75 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -185,6 +185,9 @@ using namespace jlm; * */ template void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ + // mrGa: represent the intersection of values from one data flow out from a gamma node + // mrTh: represent the merging of output of theta node with the input + // // A queue of nodes and regions to visit or equivalently a continuation // of instruction to be executed by the interpreter below. std::vector workItems; From 8ffe567a78bb5f0421edcc88912aa2ca6b1407e9 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 12:00:13 +0200 Subject: [PATCH 13/52] Added unary gvn op --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index caf7ca060..0a62a9eb1 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -202,6 +202,18 @@ PartialRedundancyElimination::Run( bool c_and_a = op.is_commutative() && op.is_associative(); h ^= c_and_a ? (a + b) : (a ^ (b << 3)); flows_out[0] = std::optional(h); + }, + [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ + JLM_ASSERT(flows_in.size() == 1); + if (!(flows_in[0])){ + std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; + } + + std::hash hasher; + size_t h = hasher(op.debug_string() ) << 3; + size_t a = hasher(std::to_string(flows_in[0]->value)); + h ^= a; + flows_out[0] = std::optional(h); } ); From 6ea5c1b6566fd8e123500e2e527bea2527cad2f7 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 12:06:34 +0200 Subject: [PATCH 14/52] Removed flow data from cb args. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 107 +++++++++--------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 4 +- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 0a62a9eb1..905118d99 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -172,62 +172,63 @@ PartialRedundancyElimination::Run( }; flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, - [](rvsdg::Node& node, - flows::FlowData& fd, - std::vector>& flows_in, - std::vector>& flows_out - ) - { + [](rvsdg::Node& node, + std::vector>& flows_in, + std::vector>& flows_out + ) + { + ///////////////////////////////// + std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; + + rvsdg::MatchType(node.GetOperation(), + [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ + std::hash hasher; + if (flows_out.size() == 0){return;} + flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); + }, + + [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ + JLM_ASSERT(flows_in.size() == 2); + if (!(flows_in[0]) || !(flows_in[1])){ + std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; + } - std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; - - rvsdg::MatchType(node.GetOperation(), - [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ - std::hash hasher; - if (flows_out.size() == 0){return;} - flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); - }, - - [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ - JLM_ASSERT(flows_in.size() == 2); - if (!(flows_in[0]) || !(flows_in[1])){ - std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - } - - std::hash hasher; - size_t h = hasher(op.debug_string() ); - - size_t a = hasher(std::to_string(flows_in[0]->value)); - size_t b = hasher(std::to_string(flows_in[1]->value)); - bool c_and_a = op.is_commutative() && op.is_associative(); - h ^= c_and_a ? (a + b) : (a ^ (b << 3)); - flows_out[0] = std::optional(h); - }, - [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ - JLM_ASSERT(flows_in.size() == 1); - if (!(flows_in[0])){ - std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - } - - std::hash hasher; - size_t h = hasher(op.debug_string() ) << 3; - size_t a = hasher(std::to_string(flows_in[0]->value)); - h ^= a; - flows_out[0] = std::optional(h); + std::hash hasher; + size_t h = hasher(op.debug_string() ); + + size_t a = hasher(std::to_string(flows_in[0]->value)); + size_t b = hasher(std::to_string(flows_in[1]->value)); + bool c_and_a = op.is_commutative() && op.is_associative(); + h ^= c_and_a ? (a + b) : (a ^ (b << 3)); + flows_out[0] = std::optional(h); + }, + [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ + JLM_ASSERT(flows_in.size() == 1); + if (!(flows_in[0])){ + std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; } - ); - - rvsdg::MatchType(node, - [&flows_out](rvsdg::LambdaNode& lm){ - auto s = lm.DebugString(); - std::hash hasher; - for (size_t i = 0; i < flows_out.size(); i++){ - flows_out[i] = GVN_Hash( hasher(s + std::to_string(i)) ); - } + + std::hash hasher; + size_t h = hasher(op.debug_string() ) << 3; + size_t a = hasher(std::to_string(flows_in[0]->value)); + h ^= a; + flows_out[0] = std::optional(h); + } + ); + + rvsdg::MatchType(node, + [&flows_out](rvsdg::LambdaNode& lm){ + auto s = lm.DebugString(); + std::hash hasher; + for (size_t i = 0; i < flows_out.size(); i++){ + flows_out[i] = GVN_Hash( hasher(s + std::to_string(i)) ); } - ); - } - ); + } + ); + + ////////////////////////////////////////// + } + ); std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 18d873d75..6ad579a02 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -234,7 +234,7 @@ using namespace jlm; flows_out.resize(w.node->noutputs(), std::nullopt); // visit node - cb(*(w.node), fd, flows_in, flows_out); + cb(*(w.node), flows_in, flows_out); // update map for ( size_t i = 0; i < w.node->noutputs(); i++){ @@ -256,7 +256,7 @@ using namespace jlm; flows_out.clear(); flows_out.resize(f_args.size(), std::nullopt); // visit node - cb(*(w.node), fd, flows_in, flows_out); + cb(*(w.node), flows_in, flows_out); // update map for ( size_t i = 0; i < f_args.size(); i++){ From e46d6b4d3df87a112c505c6e4e9be36259aab9e7 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 12:10:03 +0200 Subject: [PATCH 15/52] Improved code layout. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 905118d99..17e76b119 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -177,16 +177,17 @@ PartialRedundancyElimination::Run( std::vector>& flows_out ) { - ///////////////////////////////// + std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; rvsdg::MatchType(node.GetOperation(), + // ----------------------------------------------------------------------------------------- [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ std::hash hasher; if (flows_out.size() == 0){return;} flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); }, - + // ----------------------------------------------------------------------------------------- [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ JLM_ASSERT(flows_in.size() == 2); if (!(flows_in[0]) || !(flows_in[1])){ @@ -202,6 +203,7 @@ PartialRedundancyElimination::Run( h ^= c_and_a ? (a + b) : (a ^ (b << 3)); flows_out[0] = std::optional(h); }, + // ----------------------------------------------------------------------------------------- [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ JLM_ASSERT(flows_in.size() == 1); if (!(flows_in[0])){ @@ -217,6 +219,7 @@ PartialRedundancyElimination::Run( ); rvsdg::MatchType(node, + // ----------------------------------------------------------------------------------------- [&flows_out](rvsdg::LambdaNode& lm){ auto s = lm.DebugString(); std::hash hasher; @@ -225,8 +228,6 @@ PartialRedundancyElimination::Run( } } ); - - ////////////////////////////////////////// } ); From d503107e1d033a17e815a7d0b1f7bfbb1ef9565e Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 12:12:07 +0200 Subject: [PATCH 16/52] Trivial changes --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 17e76b119..f3f1d35dd 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -184,7 +184,7 @@ PartialRedundancyElimination::Run( // ----------------------------------------------------------------------------------------- [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ std::hash hasher; - if (flows_out.size() == 0){return;} + if(flows_out.size() != 1){return;} // Unused constant value. flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); }, // ----------------------------------------------------------------------------------------- From 048071ecfe01fa9f3937a6cd66ef3b8775f8e8d3 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 14:05:59 +0200 Subject: [PATCH 17/52] Added call op --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 22 +++++++++++-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 8 +++++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index f3f1d35dd..46729b710 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -184,7 +184,6 @@ PartialRedundancyElimination::Run( // ----------------------------------------------------------------------------------------- [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ std::hash hasher; - if(flows_out.size() != 1){return;} // Unused constant value. flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); }, // ----------------------------------------------------------------------------------------- @@ -205,16 +204,20 @@ PartialRedundancyElimination::Run( }, // ----------------------------------------------------------------------------------------- [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ - JLM_ASSERT(flows_in.size() == 1); - if (!(flows_in[0])){ + if (!(flows_in.size())){ std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; } - std::hash hasher; size_t h = hasher(op.debug_string() ) << 3; size_t a = hasher(std::to_string(flows_in[0]->value)); h ^= a; flows_out[0] = std::optional(h); + }, + // ----------------------------------------------------------------------------------------- + [&node, &flows_in, &flows_out](const jlm::llvm::CallOperation& op){ + std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); + std::hash hasher; + flows_out[0] = std::optional(hasher(s)); } ); @@ -223,8 +226,9 @@ PartialRedundancyElimination::Run( [&flows_out](rvsdg::LambdaNode& lm){ auto s = lm.DebugString(); std::hash hasher; + size_t h = hasher(s); for (size_t i = 0; i < flows_out.size(); i++){ - flows_out[i] = GVN_Hash( hasher(s + std::to_string(i)) ); + flows_out[i] = GVN_Hash( h+i ); } } ); @@ -237,13 +241,13 @@ PartialRedundancyElimination::Run( this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); + //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); + //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 6ad579a02..6b7c8cf43 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -234,7 +234,9 @@ using namespace jlm; flows_out.resize(w.node->noutputs(), std::nullopt); // visit node - cb(*(w.node), flows_in, flows_out); + if (flows_out.size()){ + cb(*(w.node), flows_in, flows_out); + } // update map for ( size_t i = 0; i < w.node->noutputs(); i++){ @@ -256,7 +258,9 @@ using namespace jlm; flows_out.clear(); flows_out.resize(f_args.size(), std::nullopt); // visit node - cb(*(w.node), flows_in, flows_out); + if (flows_out.size()){ + cb(*(w.node), flows_in, flows_out); + } // update map for ( size_t i = 0; i < f_args.size(); i++){ From 07b96a21def2e2e0d1b585dca9d81426477e8286 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 15:43:28 +0200 Subject: [PATCH 18/52] Fixed bug in FlowData --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 14 ++++- jlm/llvm/opt/PartialRedundancyElimination.hpp | 59 ++++++++++++------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 46729b710..5d315a87f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -126,6 +126,7 @@ PartialRedundancyElimination::~PartialRedundancyElimination() noexcept = default PartialRedundancyElimination::PartialRedundancyElimination(): Transformation("PartialRedundancyElimination"){} + void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) { IndentMan indenter = IndentMan(); @@ -155,6 +156,7 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); + flows::FlowData fd(&gvn_hashes_); auto merge_gvn_ga = [](std::optional& a, std::optional& b) { @@ -164,6 +166,7 @@ PartialRedundancyElimination::Run( return std::optional( GVN_Hash(h) ); }; + auto merge_gvn_th = [](std::optional& a, std::optional& b) { if (!a){return b;} if (!b){return a;} @@ -171,6 +174,7 @@ PartialRedundancyElimination::Run( return a->value == b->value ? a : std::optional( GVN_Hash::Tainted() ); }; + flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, [](rvsdg::Node& node, std::vector>& flows_in, @@ -186,16 +190,20 @@ PartialRedundancyElimination::Run( std::hash hasher; flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); }, + // ----------------------------------------------------------------------------------------- [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ JLM_ASSERT(flows_in.size() == 2); + if (!(flows_in[0]) || !(flows_in[1])){ + std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; } std::hash hasher; size_t h = hasher(op.debug_string() ); + size_t a = hasher(std::to_string(flows_in[0]->value)); size_t b = hasher(std::to_string(flows_in[1]->value)); bool c_and_a = op.is_commutative() && op.is_associative(); @@ -221,14 +229,16 @@ PartialRedundancyElimination::Run( } ); + rvsdg::MatchType(node, // ----------------------------------------------------------------------------------------- [&flows_out](rvsdg::LambdaNode& lm){ + //std::cout << TR_PINK << "LAMBDA PARAMS" << flows_out.size() << TR_RESET << std::endl; auto s = lm.DebugString(); std::hash hasher; size_t h = hasher(s); for (size_t i = 0; i < flows_out.size(); i++){ - flows_out[i] = GVN_Hash( h+i ); + flows_out[i] = std::optional( GVN_Hash( h+i ) ); } } ); @@ -379,6 +389,7 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv { MatchType(node.GetOperation(), [pe, &node](const rvsdg::BinaryOperation& op) { + std::hash hasher; size_t h = hasher(op.debug_string()); bool was_hashable = true; @@ -412,6 +423,7 @@ void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rv if (was_hashable){ pe->AssignGVN(node.output(0), h); } + }); } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 6b7c8cf43..88f8c8302 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -122,15 +122,16 @@ using namespace jlm; } std::optional Get(rvsdg::Output* k) { + //std::cout << "GET: " << k << std::endl; if (k == NULL){return std::nullopt;} - bool present = output_values_->find(k) == output_values_->end(); + bool present = output_values_->find(k) != output_values_->end(); return present ? std::optional((*output_values_)[k]) : std::nullopt; } void Set(rvsdg::Output* k, std::optional v) { - if (v){ - output_values_->insert({k,*v}); - } + //std::string opt_s = v ? "some" : "none"; + //std::cout << "SET: " << k << " = " << opt_s << std::endl; + output_values_->insert({k,*v}); } private: std::unordered_map* output_values_; @@ -202,32 +203,40 @@ using namespace jlm; { max_iter--; auto w = workItems.back(); workItems.pop_back(); + switch (w.type) { case WorkItemType::DELTA:{ for (auto& reg : w.lm->Subregions()){ workItems.push_back(WorkItemValue(®)); } - std::cout << "WL:DELTA"< 1000){std::cout<<"Stack overflow" << std::endl; return;} }break; case WorkItemType::REGION:{ - std::cout << "WL:REGION"< tmp; for (auto node : rvsdg::TopDownTraverser(w.region)){tmp.push_back(WorkItemValue(node));} - while (tmp.size()){workItems.push_back(tmp.back()); tmp.pop_back();} + while (tmp.size()){ + workItems.push_back(tmp.back()); + tmp.pop_back(); + } }break; case WorkItemType::NODE:{ - std::cout << "WL:NODE:"<DebugString()<< std::endl; + //std::cout << "WL:NODE:"<DebugString() << w.node->GetNodeId() << std::endl; // initialize input buffer - flows_in.clear(); + flows_in.resize(w.node->ninputs(), std::nullopt); + for (size_t i=0;i < w.node->ninputs() ; i++){ auto ni = w.node->input(i); //check just in case Input is NULL - std::optional val = ni ? fd.Get(ni->origin()) : std::nullopt; - flows_in.push_back(std::optional(val)); + auto v = fd.Get(ni ? ni->origin() : NULL); + flows_in[i] = v; + if (!v){ + //std::cout << w.node->DebugString() << "MISSING ARG from origin: " << ni->origin() << std::endl; + } } // initialize output buffer flows_out.clear(); @@ -236,17 +245,18 @@ using namespace jlm; // visit node if (flows_out.size()){ cb(*(w.node), flows_in, flows_out); + }else{ + //std::cout<< "WARN : ignored node " << w.node->DebugString() << std::endl; } // update map for ( size_t i = 0; i < w.node->noutputs(); i++){ fd.Set(w.node->output(i), flows_out[i]); - } - //DEBUG - for (size_t i = 0; i < flows_out.size(); i++){ - if (flows_out[i]){ - std::cout << "Flow out ["<DebugString()<<" : Flow out ["<output(i) << std::endl; + //} } }break; case WorkItemType::LAMBDA:{ @@ -259,21 +269,26 @@ using namespace jlm; // visit node if (flows_out.size()){ + //std::cout << "VISIT" << std::endl; cb(*(w.node), flows_in, flows_out); } // update map for ( size_t i = 0; i < f_args.size(); i++){ + //if (!flows_out[i]){std::cout << "LAM MISSING OUT" << std::endl;} + //std::cout << "lambda: Flow out ["<subregion(); + for (size_t i = 0 ; inarguments() ; i++){ + auto reg_arg = reg->argument(i); + fd.Set( reg_arg, fd.Get( reg_arg->input() ? reg_arg->input()->origin() : NULL) ); } + // Todododo : visit lambda after body has been visited once. + // Finally iterate over lambda body workItems.push_back(w.lm->subregion()); }break; From 041b7df10a6f6e3c8add0fa52fc876ffde4c889a Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 15:57:00 +0200 Subject: [PATCH 19/52] Removed commented out code and added comments. --- jlm/llvm/opt/PartialRedundancyElimination.hpp | 101 +++--------------- 1 file changed, 16 insertions(+), 85 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 88f8c8302..56bbc8dbe 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -27,90 +27,18 @@ class ThetaNode; class Region; } - -namespace jlm::llvm::flows{ - -/* -const size_t PRE_PTR_TAG_OUTPUT = 0x1; -const size_t PRE_PTR_TAG_INPUT = 0x2; -const size_t PRE_PTR_TAG_NODE = 0x3; -const size_t PRE_PTR_TAG_MASK = 0x3; -*/ -/** \brief A pointer to either rvsdg::Node*, rvsdg::Input or rvsdg::Output using the low bits as inline tags. **/ -/*union Flow -{ - Flow(rvsdg::Input* i){SetInput(i);} - Flow(rvsdg::Output* o){SetOutput(o);} - Flow(rvsdg::Node* n){SetNode(n);} - - inline void SetInput(rvsdg::Input* i) - { - this->input_ = reinterpret_cast(reinterpret_cast(i) | PRE_PTR_TAG_INPUT); - } - inline void SetOutput(rvsdg::Output* o) - { - this->output_ = reinterpret_cast(reinterpret_cast(o) | PRE_PTR_TAG_OUTPUT); - } - inline void SetNode(rvsdg::Node* n) - { - this->node_ = reinterpret_cast(reinterpret_cast(n) | PRE_PTR_TAG_NODE); - } - - inline rvsdg::Input* GetInput(){return (reinterpret_cast(input_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_INPUT ? input_ : NULL;} - inline rvsdg::Output* GetOutput(){return (reinterpret_cast(output_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_OUTPUT ? output_ : NULL;} - inline rvsdg::Node* GetNode(){return (reinterpret_cast(output_) & PRE_PTR_TAG_MASK) == PRE_PTR_TAG_NODE ? node_ : NULL;} - inline void* UnsafeGetRaw() const {return static_cast(input_);} -private: - rvsdg::Input* input_; - rvsdg::Output* output_; - rvsdg::Node* node_; -}; - -*/ - -enum class FlowType -{ - //Optionally differentiate between single and multiple cases in order to catch logic errors - // where a single value is expected to flow from a node, but there are multiple exits - INPUT, - OUTPUT, - NODE, - PARAMETER, -}; - - - - -/** TODO: one data structure for storing annotations and another for storing reactive state **/ -/** TODO: no need for intermediate buffers. Project flows onto edges and update reactive state as needed **/ -/** Avoid duplicating data by storing maps elsewhere and provide mapping functions **/ - -/** \brief FlowsCtx a context used as a proxy inside graph traversers - * D must be comparable - * **/ - -} - -namespace std -{ -inline std::string to_string(jlm::llvm::flows::FlowType ft) -{ - switch (ft) - { - case jlm::llvm::flows::FlowType::NODE: return std::string("NODE"); - case jlm::llvm::flows::FlowType::INPUT: return std::string("INPUT"); - case jlm::llvm::flows::FlowType::OUTPUT: return std::string("OUTPUT"); - case jlm::llvm::flows::FlowType::PARAMETER: return std::string("PARAMETER"); - default: return std::string("Invalid flow type"); - } -} -} - namespace jlm::llvm::flows { -using namespace jlm; + /** Generic functions for some data flow analyses. + * Nodes are treated as part of a reactive flow network. + * This makes it possible to write more generic analyses as some common tasks + * such as ensuring the data is passed to all downstream (or upstream) usage sites. + * Currently only a top-down flow is implemented. Usable for GVN. + */ + using namespace jlm; /** A view into the data to manipulate by flows **/ + /** This represents the value output from nodes **/ template class FlowData { @@ -137,6 +65,9 @@ using namespace jlm; std::unordered_map* output_values_; }; + /** \ref WorkItemType Union type tag. + * \ref WorkItemValue Used internally by the reactive interpreter to keep track of nodes and regions yet to be visited. + * **/ enum class WorkItemType { REGION, @@ -164,7 +95,7 @@ using namespace jlm; [this](jlm::rvsdg::DeltaNode& dl) {this->type = WorkItemType::DELTA;} ); } - + /* Fields should be made const */ WorkItemType type; union{ rvsdg::DeltaNode* dl; @@ -188,10 +119,11 @@ using namespace jlm; void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ // mrGa: represent the intersection of values from one data flow out from a gamma node // mrTh: represent the merging of output of theta node with the input - // - // A queue of nodes and regions to visit or equivalently a continuation + + // A FIFO queue of nodes and regions to visit or equivalently a continuation // of instruction to be executed by the interpreter below. std::vector workItems; + // A buffer for flow values. // The flows function handles lookup of values from the fd map. std::vector< std::optional > flows_in; @@ -204,8 +136,7 @@ using namespace jlm; max_iter--; auto w = workItems.back(); workItems.pop_back(); - switch (w.type) - { + switch (w.type){ case WorkItemType::DELTA:{ for (auto& reg : w.lm->Subregions()){ workItems.push_back(WorkItemValue(®)); From ccca080ab65e749aa38fd952fe7a252666e4b37e Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 16:27:12 +0200 Subject: [PATCH 20/52] Compile check --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 60 +++++++++++-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 8 ++- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 5d315a87f..df80f96d9 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -156,17 +156,27 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); + // NEWER CODE USING REACTIVE STYLE CALLBACKS + // SHOULD HANDLE THETA NODES, BUT HAVEN'T TESTED THIS YET flows::FlowData fd(&gvn_hashes_); + + //cb for handling two flows coming from different sub-regions of a gamma node. + // This reprents the GVN of expressions such as (a > b ? a : b) + // Thus, the branch order matters. + // The net hash of outputs from gamma nodes is computed by reducing each output across branches + // with this callback. auto merge_gvn_ga = [](std::optional& a, std::optional& b) { if (!a){return b;} if (!b){return a;} if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } - size_t h = a->value ^ (b->value << 3); + size_t h = a->value ^ (b->value << 3); //Hash branches differently. return std::optional( GVN_Hash(h) ); }; - + //cb for merging flows coming into a theta node for the first time and from previous iterations. + // on the second iteration all sub-nodes will have their values overwritten, except + // loop invariant nodes. auto merge_gvn_th = [](std::optional& a, std::optional& b) { if (!a){return b;} if (!b){return a;} @@ -174,15 +184,15 @@ PartialRedundancyElimination::Run( return a->value == b->value ? a : std::optional( GVN_Hash::Tainted() ); }; - flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, [](rvsdg::Node& node, std::vector>& flows_in, std::vector>& flows_out ) { - - std::cout << TR_GREEN << node.GetNodeId() << node.DebugString() << TR_RESET << std::endl; + // The gvn hashes are stored automatically by the argument fd, when the flows_out vector + // is written to. + std::cout << TR_GREEN << node.GetNodeId() << ":" << node.DebugString() << TR_RESET << std::endl; rvsdg::MatchType(node.GetOperation(), // ----------------------------------------------------------------------------------------- @@ -194,16 +204,13 @@ PartialRedundancyElimination::Run( // ----------------------------------------------------------------------------------------- [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ JLM_ASSERT(flows_in.size() == 2); - if (!(flows_in[0]) || !(flows_in[1])){ - std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; } std::hash hasher; size_t h = hasher(op.debug_string() ); - size_t a = hasher(std::to_string(flows_in[0]->value)); size_t b = hasher(std::to_string(flows_in[1]->value)); bool c_and_a = op.is_commutative() && op.is_associative(); @@ -216,20 +223,22 @@ PartialRedundancyElimination::Run( std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; } std::hash hasher; - size_t h = hasher(op.debug_string() ) << 3; + size_t h = hasher(op.debug_string() ); size_t a = hasher(std::to_string(flows_in[0]->value)); h ^= a; flows_out[0] = std::optional(h); }, // ----------------------------------------------------------------------------------------- [&node, &flows_in, &flows_out](const jlm::llvm::CallOperation& op){ - std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); + std::string s = node.DebugString() + "CALL"; std::hash hasher; - flows_out[0] = std::optional(hasher(s)); + size_t h = hasher(s); + for (size_t i = 0; i < flows_out.size(); i++){ + flows_out[i] = std::optional( h + i); + } } ); - rvsdg::MatchType(node, // ----------------------------------------------------------------------------------------- [&flows_out](rvsdg::LambdaNode& lm){ @@ -246,24 +255,27 @@ PartialRedundancyElimination::Run( ); - std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; - + std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; + //OLD CODE BELOW. this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - //this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); - std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; + /* + { + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); + std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; + } + */ + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; - - } /** -------------------------------------------------------------------------------------------- **/ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 56bbc8dbe..4717ea300 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -114,6 +114,8 @@ namespace jlm::llvm::flows * different loop iterations. * Merges at gamma nodes represent the combined value from switch cases * The merges may be different or the same + * + * The style of traversing the graph can be adapted to more complex cases. * */ template void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ @@ -192,7 +194,7 @@ namespace jlm::llvm::flows }break; case WorkItemType::LAMBDA:{ // This case only handles params out - // Add an enum for visit type later + // Add an enum for other visit types later // initialize input buffer flows_in.clear(); auto f_args = w.lm->GetFunctionArguments(); @@ -287,7 +289,7 @@ namespace jlm::llvm::flows } if (!fixed_point_reached){ - workItems.push_back( WorkItemValue(WorkItemType::THETA_END, w.tn) ); + workItems.push_back( WorkItemValue(WorkItemType::THETA_END, w.node) ); workItems.push_back( w.tn->subregion() ); } }break; @@ -298,7 +300,7 @@ namespace jlm::llvm::flows auto lv = loopvars[i]; fd.Set( lv.output, fd.Get( lv.post ? lv.post->origin() : NULL ) ); // Required for downstream nodes } - workItems.push_back( WorkItemValue(w.tn) ); // Attempt another loop iteration + workItems.push_back( WorkItemValue(w.node) ); // Attempt another loop iteration }break; default: std::cout << static_cast(w.type) <<"Ignoring work item..."< Date: Tue, 7 Oct 2025 16:30:45 +0200 Subject: [PATCH 21/52] Removed redundant loop. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 1 + jlm/llvm/opt/PartialRedundancyElimination.hpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index df80f96d9..9e21ca16a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -161,6 +161,7 @@ PartialRedundancyElimination::Run( flows::FlowData fd(&gvn_hashes_); + //cb for handling two flows coming from different sub-regions of a gamma node. // This reprents the GVN of expressions such as (a > b ? a : b) // Thus, the branch order matters. diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 4717ea300..efc10b438 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -212,12 +212,12 @@ namespace jlm::llvm::flows //std::cout << "lambda: Flow out ["<subregion(); + //This might replicate the above. + /*auto reg = w.lm->subregion(); for (size_t i = 0 ; inarguments() ; i++){ auto reg_arg = reg->argument(i); fd.Set( reg_arg, fd.Get( reg_arg->input() ? reg_arg->input()->origin() : NULL) ); - } + }*/ // Todododo : visit lambda after body has been visited once. From 3e5d5969b96bdfc27d13cfa7f385940e6dc0aa02 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 7 Oct 2025 22:07:05 +0200 Subject: [PATCH 22/52] First draft with Theta gvn possibly working. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 9 ++-- jlm/llvm/opt/PartialRedundancyElimination.hpp | 52 ++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 9e21ca16a..238c07e85 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -178,13 +178,16 @@ PartialRedundancyElimination::Run( //cb for merging flows coming into a theta node for the first time and from previous iterations. // on the second iteration all sub-nodes will have their values overwritten, except // loop invariant nodes. - auto merge_gvn_th = [](std::optional& a, std::optional& b) + auto merge_gvn_th = [](rvsdg::ThetaNode& tn, std::optional& a, std::optional& b) { - if (!a){return b;} if (!b){return a;} + if (!a){return b;} if (!b){std::cout<<"UNREACHABLE"; exit(-1);} if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } - return a->value == b->value ? a : std::optional( GVN_Hash::Tainted() ); + if (a->value == b->value){return a;} + if (a && a->IsLoopVar()){return a;} + return std::optional( GVN_Hash::LoopVar( a->value ^ b->value )); }; + flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, [](rvsdg::Node& node, std::vector>& flows_in, diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index efc10b438..b683a5d8f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -120,8 +120,12 @@ namespace jlm::llvm::flows template void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ // mrGa: represent the intersection of values from one data flow out from a gamma node - // mrTh: represent the merging of output of theta node with the input - + // mrTh: represent the merging of output of theta node with the input. Third arg must be non-null if second is something. + // args : theta node + // value from older iteration + // value from last iteration + // + // // A FIFO queue of nodes and regions to visit or equivalently a continuation // of instruction to be executed by the interpreter below. std::vector workItems; @@ -273,14 +277,16 @@ namespace jlm::llvm::flows auto loopvars = w.tn->GetLoopVars(); bool fixed_point_reached = true; + /* Try to push inputs into the loop */ + /* values from previous iterations stored on Output* of .pre field */ for (size_t i = 0;i < loopvars.size(); i++){ auto lv = loopvars[i]; - auto lv_input = fd.Get( lv.input ? lv.input->origin() : NULL ); + JLM_ASSERT(lv.input->origin() != lv.pre); - auto lv_post = fd.Get( lv.post ? lv.post->origin() : NULL ); - auto merged = mrTh( lv_input, lv_post ); + auto lv_input = fd.Get( lv.input ? lv.input->origin() : NULL ); + auto lv_pre = fd.Get( lv.pre ); + auto merged = mrTh( *(w.tn), lv_pre, lv_input ); - auto lv_pre = fd.Get( lv.pre ); if ( (merged && !lv_pre) || (!merged && lv_pre) || (*merged != *lv_pre) ){fixed_point_reached = false;} @@ -298,7 +304,12 @@ namespace jlm::llvm::flows auto loopvars = w.tn->GetLoopVars(); for (size_t i = 0;i < loopvars.size(); i++){ auto lv = loopvars[i]; - fd.Set( lv.output, fd.Get( lv.post ? lv.post->origin() : NULL ) ); // Required for downstream nodes + auto lv_pre = fd.Get( lv.pre ); + auto lv_post = fd.Get( lv.post ? lv.post->origin() : NULL ); + auto merged = mrTh(*(w.tn), lv_pre, lv_post ); + + fd.Set( lv.output, lv_post ); // Required for downstream nodes + fd.Set( lv.pre, merged); // Required for blocking new iterations } workItems.push_back( WorkItemValue(w.node) ); // Attempt another loop iteration }break; @@ -314,24 +325,22 @@ namespace jlm::llvm { struct GVN_Hash { +#define GVN_LV_BIT 0x1000 size_t value; inline GVN_Hash(){this->value = 0;} - inline GVN_Hash(size_t v){this->value = v;} + inline GVN_Hash(size_t v){this->value = v &(~GVN_LV_BIT);} + inline static GVN_Hash LoopVar(size_t v){auto h = GVN_Hash(v); h.value |= GVN_LV_BIT; + std::cout << "LP" << h.value << " !! " << h.IsLoopVar() << std::endl; + return h; + } + inline bool IsLoopVar() const {return value & GVN_LV_BIT;} inline static GVN_Hash None() {return GVN_Hash(0);} inline static GVN_Hash Tainted(){return GVN_Hash(1);} inline bool IsValid(){return value >= 2;} inline bool IsSome(){return value != 0;} inline bool operator==(const GVN_Hash &other){return this->value == other.value;} inline bool operator!=(const GVN_Hash &other){return this->value != other.value;} - inline GVN_Hash Merge(GVN_Hash& other) - { - if (other.IsSome()) - { - return (this->value == other.value) ? *this : GVN_Hash::Tainted(); - }else{ - return this->IsSome() ? *this : other; - } - } + #undef GVN_LV_BIT }; /** Boiler plate for making the struct compatible with std::unordered_map **/ @@ -458,9 +467,12 @@ namespace std { switch (h.value) { - case 0: return std::string("none"); - case 1: return std::string("tainted"); - default: return std::to_string(h.value); + case 0: return std::string("none"); + case 1: return std::string("tainted"); + default:{ + if (h.IsLoopVar()){ return std::string("lv") + std::to_string(h.value); } + return std::to_string(h.value); + } } } } From 637b702809899fc0b3f3bbbdabf17cea6e318fd2 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Wed, 8 Oct 2025 11:36:52 +0200 Subject: [PATCH 23/52] Minor changes. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 10 +++++++--- jlm/llvm/opt/PartialRedundancyElimination.hpp | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 238c07e85..85f25365f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -156,8 +156,8 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - // NEWER CODE USING REACTIVE STYLE CALLBACKS - // SHOULD HANDLE THETA NODES, BUT HAVEN'T TESTED THIS YET + // NEW CODE USING REACTIVE STYLE CALLBACKS + // SHOULD HANDLE THETA NODES, BUT MORE TESTING IS REQUIRED flows::FlowData fd(&gvn_hashes_); @@ -171,6 +171,7 @@ PartialRedundancyElimination::Run( { if (!a){return b;} if (!b){return a;} if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } + if ( a->value == b->value ){ return a; } size_t h = a->value ^ (b->value << 3); //Hash branches differently. return std::optional( GVN_Hash(h) ); }; @@ -183,7 +184,10 @@ PartialRedundancyElimination::Run( if (!a){return b;} if (!b){std::cout<<"UNREACHABLE"; exit(-1);} if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } if (a->value == b->value){return a;} - if (a && a->IsLoopVar()){return a;} + if (a && a->IsLoopVar()){return a;} // This is required for fixed points to be reached by gvn + // Values are identified as variant on exit of first iteration + // LoopVar hashes trickle through the loop body once more + // Subsequent iterations are blocked as loopvar hashes are never overwritten return std::optional( GVN_Hash::LoopVar( a->value ^ b->value )); }; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index b683a5d8f..194e39530 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -137,9 +137,9 @@ namespace jlm::llvm::flows workItems.push_back(WorkItemValue(&scope)); size_t max_iter = 500; - while (workItems.size() && max_iter) + while (workItems.size() ) { - max_iter--; + max_iter--; if (!max_iter){std::cout<<"ApplyDataFlowsTopDownMaximum iteration count reached"< Date: Thu, 16 Oct 2025 13:15:46 +0200 Subject: [PATCH 24/52] Deleted some cout --- jlm/llvm/opt/PartialRedundancyElimination.hpp | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 194e39530..3f2e4acd9 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -50,15 +50,12 @@ namespace jlm::llvm::flows } std::optional Get(rvsdg::Output* k) { - //std::cout << "GET: " << k << std::endl; if (k == NULL){return std::nullopt;} bool present = output_values_->find(k) != output_values_->end(); return present ? std::optional((*output_values_)[k]) : std::nullopt; } void Set(rvsdg::Output* k, std::optional v) { - //std::string opt_s = v ? "some" : "none"; - //std::cout << "SET: " << k << " = " << opt_s << std::endl; output_values_->insert({k,*v}); } private: @@ -147,13 +144,11 @@ namespace jlm::llvm::flows for (auto& reg : w.lm->Subregions()){ workItems.push_back(WorkItemValue(®)); } - //std::cout << "WL:DELTA"< 1000){std::cout<<"Stack overflow" << std::endl; return;} }break; case WorkItemType::REGION:{ - //std::cout << "WL:REGION"< tmp; for (auto node : rvsdg::TopDownTraverser(w.region)){tmp.push_back(WorkItemValue(node));} @@ -163,7 +158,6 @@ namespace jlm::llvm::flows } }break; case WorkItemType::NODE:{ - //std::cout << "WL:NODE:"<DebugString() << w.node->GetNodeId() << std::endl; // initialize input buffer flows_in.resize(w.node->ninputs(), std::nullopt); @@ -171,9 +165,6 @@ namespace jlm::llvm::flows auto ni = w.node->input(i); //check just in case Input is NULL auto v = fd.Get(ni ? ni->origin() : NULL); flows_in[i] = v; - if (!v){ - //std::cout << w.node->DebugString() << "MISSING ARG from origin: " << ni->origin() << std::endl; - } } // initialize output buffer flows_out.clear(); @@ -182,18 +173,11 @@ namespace jlm::llvm::flows // visit node if (flows_out.size()){ cb(*(w.node), flows_in, flows_out); - }else{ - //std::cout<< "WARN : ignored node " << w.node->DebugString() << std::endl; } // update map for ( size_t i = 0; i < w.node->noutputs(); i++){ fd.Set(w.node->output(i), flows_out[i]); - //if (flows_out[i]) - //{ - //auto s = std::to_string(flows_out[i].value().value); - //std::cout << "Node: "<DebugString()<<" : Flow out ["<output(i) << std::endl; - //} } }break; case WorkItemType::LAMBDA:{ @@ -205,10 +189,7 @@ namespace jlm::llvm::flows flows_out.clear(); flows_out.resize(f_args.size(), std::nullopt); // visit node - if (flows_out.size()){ - //std::cout << "VISIT" << std::endl; - cb(*(w.node), flows_in, flows_out); - } + if (flows_out.size()){ cb(*(w.node), flows_in, flows_out); } // update map for ( size_t i = 0; i < f_args.size(); i++){ @@ -216,16 +197,8 @@ namespace jlm::llvm::flows //std::cout << "lambda: Flow out ["<subregion(); - for (size_t i = 0 ; inarguments() ; i++){ - auto reg_arg = reg->argument(i); - fd.Set( reg_arg, fd.Get( reg_arg->input() ? reg_arg->input()->origin() : NULL) ); - }*/ - // Todododo : visit lambda after body has been visited once. - // Finally iterate over lambda body workItems.push_back(w.lm->subregion()); }break; From 90b7f725717e44c48ed51d40ddcf256261a8deb0 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sun, 19 Oct 2025 23:09:17 +0200 Subject: [PATCH 25/52] Refactoring making gvn computing gvn dependencies from output ptr --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 326 ++++++++++++------ jlm/llvm/opt/PartialRedundancyElimination.hpp | 84 ++++- 2 files changed, 300 insertions(+), 110 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 85f25365f..fe59a127c 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,45 @@ #include + +/** + * Partial redundancy elimination: + * -invariants: after each insertion of a new GVN all GVN shall have unique values + * -including op clusters + * -GVN source: + * -create a variant type for flows into an operator and gvn from constants + * -GVN map responsible for comparing constants + * -Collisions: + * -a GVN value is always generated when a new tuple of (op, flow0, flow1, ...) is inserted + * -on a collision generate a unique symbol + * -this prevents accidental matches downstream and keep the invariant of one gvn per + * value -it should be possible to recompute tuples from edges -outputs -operators: -keep around + * vectors of leaves with a count after performing the operation -vecs only required for internal + * nodes in op clusters -compare vectors rather than GVN arguments -in effect operator nodes whose + * args have the same op return a vector of leaf inputs to the operator dag -tracebacks from op + * nodes will thus bypasses non-leaf nodes -vectors of leaf counts stored per output edge -possible + * to discard for too large vectors, preventing excess memory usage -Theta nodes: -Complicated: + * -initial values passed into thetas must not match + * -the outputs might depend on other loop variables transitively + * -Solution: + * -dynamically switch between which table to insert values into + * -compute a GVN value by setting each loop input to a simple (OP-LOOP-PARAM, index) + * -these are placed in a separate table + * -this value unique identifies a loop + * -this scales well as each loop body is only scanned once for identifying structure + * and once afterwards to trickle down values from outer contexts + * -for a given set of loop inputs and loop hash, outputs are unique + * -that is treat theta nodes as operators from the outside + * -once such hashes have been calculated for all thetas proceed by passing + * -values into thetas hashed with loop hashes + * -two identical loops called with the same values give the same outputs + */ + +namespace jlm::rvsdg +{ +class Operation; +} + /** This might be moved to util if proven useful elsewhere **/ static int indentation_level = 0; @@ -145,6 +185,72 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } +void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg) +{ + IndentMan indenter = IndentMan(); + for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) + { + MatchType(*node, [this](rvsdg::LambdaNode& lm){ + for (auto& param : lm.GetFunctionArguments()) + { + auto deps = gvn_man_.DepsFromOutput(param); + std::cout << TR_RED << "IN LAMBDA" << TR_RESET; + std::cout << TR_GREEN << "deps:" << deps.inputs.size() << TR_RESET;; + } + }); + + MatchType(*node, [this](rvsdg::StructuralNode& sn) + { + for (auto& reg : sn.Subregions()) + { + this->TraverseTopDownRecursively(reg); + std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; + } + }); + } +} + +class LambdaParameterOperation : public jlm::rvsdg::Operation +{ +public: + LambdaParameterOperation(){} //trivial constructor + bool operator==(const Operation & other) const noexcept; + std::string debug_string() const override; + virtual ~LambdaParameterOperation() noexcept override; + [[nodiscard]] virtual std::unique_ptrcopy() const override; +}; + +LambdaParameterOperation::~LambdaParameterOperation() noexcept = default; + +bool LambdaParameterOperation::operator==(const Operation & other) const noexcept {return &other == this;} +std::string LambdaParameterOperation::debug_string() const { return "LambdaParameterOperation";} +std::unique_ptr LambdaParameterOperation::copy() const { std::cout<<"Attempt to copy singleton"; JLM_ASSERT(false); return nullptr; } +static LambdaParameterOperation lambdaParamOp; + +GVN_Deps GVN_Manager::DepsFromOutput(rvsdg::Output* output) +{ + auto deps = GVN_Deps(); + deps.op = NULL; + if (!output){return deps;} + auto owner = output->GetOwner(); + + if (std::holds_alternative(owner)){ + std::cout << "Owner is region" << std::endl; + } + if (std::holds_alternative(owner)){ + std::cout << "Owner is node" << std::endl; + rvsdg::Node* n = std::get(owner); + if (!n){return deps;} + jlm::rvsdg::MatchType(*n, [this, &deps, &output](rvsdg::LambdaNode& lm){ + std::cout << "Lambda node from variant." << std::endl; + deps.op = &lambdaParamOp; + auto gi = GVN_Input(this->FromIndex( output->index() )); + deps.inputs.push_back(gi); + }); + } + + return deps; +} void PartialRedundancyElimination::Run( @@ -155,122 +261,124 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - - // NEW CODE USING REACTIVE STYLE CALLBACKS - // SHOULD HANDLE THETA NODES, BUT MORE TESTING IS REQUIRED - - flows::FlowData fd(&gvn_hashes_); - - - //cb for handling two flows coming from different sub-regions of a gamma node. - // This reprents the GVN of expressions such as (a > b ? a : b) - // Thus, the branch order matters. - // The net hash of outputs from gamma nodes is computed by reducing each output across branches - // with this callback. - auto merge_gvn_ga = [](std::optional& a, std::optional& b) - { - if (!a){return b;} if (!b){return a;} - if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } - if ( a->value == b->value ){ return a; } - size_t h = a->value ^ (b->value << 3); //Hash branches differently. - return std::optional( GVN_Hash(h) ); - }; - - //cb for merging flows coming into a theta node for the first time and from previous iterations. - // on the second iteration all sub-nodes will have their values overwritten, except - // loop invariant nodes. - auto merge_gvn_th = [](rvsdg::ThetaNode& tn, std::optional& a, std::optional& b) - { - if (!a){return b;} if (!b){std::cout<<"UNREACHABLE"; exit(-1);} - if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } - if (a->value == b->value){return a;} - if (a && a->IsLoopVar()){return a;} // This is required for fixed points to be reached by gvn - // Values are identified as variant on exit of first iteration - // LoopVar hashes trickle through the loop body once more - // Subsequent iterations are blocked as loopvar hashes are never overwritten - return std::optional( GVN_Hash::LoopVar( a->value ^ b->value )); - }; - - - flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, - [](rvsdg::Node& node, - std::vector>& flows_in, - std::vector>& flows_out - ) - { - // The gvn hashes are stored automatically by the argument fd, when the flows_out vector - // is written to. - std::cout << TR_GREEN << node.GetNodeId() << ":" << node.DebugString() << TR_RESET << std::endl; - - rvsdg::MatchType(node.GetOperation(), - // ----------------------------------------------------------------------------------------- - [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ - std::hash hasher; - flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); - }, - - // ----------------------------------------------------------------------------------------- - [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ - JLM_ASSERT(flows_in.size() == 2); - if (!(flows_in[0]) || !(flows_in[1])){ - std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - } - - std::hash hasher; - size_t h = hasher(op.debug_string() ); - - size_t a = hasher(std::to_string(flows_in[0]->value)); - size_t b = hasher(std::to_string(flows_in[1]->value)); - bool c_and_a = op.is_commutative() && op.is_associative(); - h ^= c_and_a ? (a + b) : (a ^ (b << 3)); - flows_out[0] = std::optional(h); - }, - // ----------------------------------------------------------------------------------------- - [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ - if (!(flows_in.size())){ - std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - } - std::hash hasher; - size_t h = hasher(op.debug_string() ); - size_t a = hasher(std::to_string(flows_in[0]->value)); - h ^= a; - flows_out[0] = std::optional(h); - }, - // ----------------------------------------------------------------------------------------- - [&node, &flows_in, &flows_out](const jlm::llvm::CallOperation& op){ - std::string s = node.DebugString() + "CALL"; - std::hash hasher; - size_t h = hasher(s); - for (size_t i = 0; i < flows_out.size(); i++){ - flows_out[i] = std::optional( h + i); - } - } - ); - - rvsdg::MatchType(node, - // ----------------------------------------------------------------------------------------- - [&flows_out](rvsdg::LambdaNode& lm){ - //std::cout << TR_PINK << "LAMBDA PARAMS" << flows_out.size() << TR_RESET << std::endl; - auto s = lm.DebugString(); - std::hash hasher; - size_t h = hasher(s); - for (size_t i = 0; i < flows_out.size(); i++){ - flows_out[i] = std::optional( GVN_Hash( h+i ) ); - } - } - ); - } - ); - + // + // // NEW CODE USING REACTIVE STYLE CALLBACKS + // // SHOULD HANDLE THETA NODES, BUT MORE TESTING IS REQUIRED + // + // jlm::llvm::flows::FlowData fd(&gvn_hashes_); + // + // + // //cb for handling two flows coming from different sub-regions of a gamma node. + // // This reprents the GVN of expressions such as (a > b ? a : b) + // // Thus, the branch order matters. + // // The net hash of outputs from gamma nodes is computed by reducing each output across branches + // // with this callback. + // auto merge_gvn_ga = [](std::optional& a, std::optional& b) + // { + // if (!a){return b;} if (!b){return a;} + // if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } + // if ( a->value == b->value ){ return a; } + // size_t h = a->value ^ (b->value << 3); //Hash branches differently. + // return std::optional( GVN_Hash(h) ); + // }; + // + // //cb for merging flows coming into a theta node for the first time and from previous iterations. + // // on the second iteration all sub-nodes will have their values overwritten, except + // // loop invariant nodes. + // auto merge_gvn_th = [](rvsdg::ThetaNode& tn, std::optional& a, std::optional& b) + // { + // if (!a){return b;} if (!b){std::cout<<"UNREACHABLE"; exit(-1);} + // if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } + // if (a->value == b->value){return a;} + // if (a && a->IsLoopVar()){return a;} // This is required for fixed points to be reached by gvn + // // Values are identified as variant on exit of first iteration + // // LoopVar hashes trickle through the loop body once more + // // Subsequent iterations are blocked as loopvar hashes are never overwritten + // return std::optional( GVN_Hash::LoopVar( a->value ^ b->value )); + // }; + // + // + // jlm::llvm::flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, + // [](rvsdg::Node& node, + // std::vector>& flows_in, + // std::vector>& flows_out + // ) + // { + // // The gvn hashes are stored automatically by the argument fd, when the flows_out vector + // // is written to. + // std::cout << TR_GREEN << node.GetNodeId() << ":" << node.DebugString() << TR_RESET << std::endl; + // + // rvsdg::MatchType(node.GetOperation(), + // // ----------------------------------------------------------------------------------------- + // [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ + // std::hash hasher; + // flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); + // }, + // + // // ----------------------------------------------------------------------------------------- + // [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ + // JLM_ASSERT(flows_in.size() == 2); + // if (!(flows_in[0]) || !(flows_in[1])){ + // std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; + // } + // + // std::hash hasher; + // size_t h = hasher(op.debug_string() ); + // + // size_t a = hasher(std::to_string(flows_in[0]->value)); + // size_t b = hasher(std::to_string(flows_in[1]->value)); + // bool c_and_a = op.is_commutative() && op.is_associative(); + // h ^= c_and_a ? (a + b) : (a ^ (b << 3)); + // flows_out[0] = std::optional(h); + // }, + // // ----------------------------------------------------------------------------------------- + // [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ + // if (!(flows_in.size())){ + // std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; + // } + // std::hash hasher; + // size_t h = hasher(op.debug_string() ); + // size_t a = hasher(std::to_string(flows_in[0]->value)); + // h ^= a; + // flows_out[0] = std::optional(h); + // }, + // // ----------------------------------------------------------------------------------------- + // [&node, &flows_in, &flows_out](const jlm::llvm::CallOperation& op){ + // std::string s = node.DebugString() + "CALL"; + // std::hash hasher; + // size_t h = hasher(s); + // for (size_t i = 0; i < flows_out.size(); i++){ + // flows_out[i] = std::optional( h + i); + // } + // } + // ); + // + // rvsdg::MatchType(node, + // // ----------------------------------------------------------------------------------------- + // [&flows_out](rvsdg::LambdaNode& lm){ + // //std::cout << TR_PINK << "LAMBDA PARAMS" << flows_out.size() << TR_RESET << std::endl; + // auto s = lm.DebugString(); + // std::hash hasher; + // size_t h = hasher(s); + // for (size_t i = 0; i < flows_out.size(); i++){ + // flows_out[i] = std::optional( GVN_Hash( h+i ) ); + // } + // } + // ); + // } + // ); + // std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; - //OLD CODE BELOW. this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); + + this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + this->TraverseTopDownRecursively( rvsdg.GetRootRegion() ); /* { - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 3f2e4acd9..885a37d59 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -6,10 +6,11 @@ #ifndef JLM_LLVM_OPT_PartialRedundancyElimination_HPP #define JLM_LLVM_OPT_PartialRedundancyElimination_HPP +#include "jlm/llvm/ir/operators/IntegerOperations.hpp" #include "jlm/rvsdg/MatchType.hpp" #include "jlm/rvsdg/traverser.hpp" -#include #include +#include #include #include #include @@ -296,6 +297,83 @@ namespace jlm::llvm::flows namespace jlm::llvm { + +typedef size_t GVN_Val; + +class GVN_Manager; +union GVN_Input{ + static constexpr size_t FL_IS_VALUE = 0x1; + + bool IsValue() const {return value_ & FL_IS_VALUE;} + bool IsOutput() const {return !(value_ & FL_IS_VALUE);} + + GVN_Input(rvsdg::Output* o){this->output_ = o;} + GVN_Input(GVN_Val v){this->value_ = v;} + rvsdg::Output* AsOutput() const {return output_;} + GVN_Val AsValue() const {return value_;} +private: + rvsdg::Output* output_; + GVN_Val value_; +}; + +// A collection of all data required to compute a gvn value for an Output* +struct GVN_Deps{ + rvsdg::Operation* op; + std::vector inputs; +}; + + + +class GVN_Manager +{ + /** \brief Utility class for managing the symbol space created by GVN values. + * Responsibilities: keep track of which values are already in use and + * provide means of mapping simple literals to gvn values + * + */ + +public: + GVN_Deps DepsFromOutput(rvsdg::Output* output); + GVN_Val FromLit(std::string s) + { + if (lit_to_gvn_.find(s) == lit_to_gvn_.end()){lit_to_gvn_.insert({s, CreateUniqueGVN()});} + return lit_to_gvn_[s]; + } + GVN_Val FromIndex(std::size_t index) + { + if (index_to_gvn_.find(index) == index_to_gvn_.end()){index_to_gvn_.insert({index, CreateUniqueGVN()});} + return index_to_gvn_[index]; + } + GVN_Val FromOp(rvsdg::Operation* op) + { + if (op_to_gvn_.find(op) == op_to_gvn_.end()){op_to_gvn_.insert({op, CreateUniqueGVN()});} + return op_to_gvn_[op]; + } + +private: + GVN_Val CreateUniqueGVN() + { + GVN_Val v = random() | GVN_Input::FL_IS_VALUE; //always set 1 bit so the tagged ptr union above works + while (occurrences_.count(v) != 0){v = random();} + JLM_ASSERT(v & GVN_Input::FL_IS_VALUE); + occurrences_.insert(v); + return v; + } + // Multiple output edges may map to the same gvn + // No collisions here + std::unordered_map edges_to_gvn_; + std::unordered_map lit_to_gvn_; + std::unordered_map index_to_gvn_; + std::unordered_map op_to_gvn_; + + std::unordered_set occurrences_; +}; + + +}; + +namespace jlm::llvm{ + struct GVN_Hash { #define GVN_LV_BIT 0x1000 @@ -358,7 +436,10 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation /* Debug data */ std::unordered_map dbg_hash_counts_; + GVN_Manager gvn_man_; + void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); + void TraverseTopDownRecursively(rvsdg::Region& reg); static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); @@ -368,6 +449,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); static void hash_theta_pre( PartialRedundancyElimination *pe, rvsdg::Node& node); + //static void hash_theta_post( PartialRedundancyElimination *pe, rvsdg::Node& node); /** From 83d47cfdd870a51eb550d9282bf7a6f6b88ccf65 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 21 Oct 2025 11:12:44 +0200 Subject: [PATCH 26/52] New data flow style. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 72 +++------ jlm/llvm/opt/PartialRedundancyElimination.hpp | 142 ++++++++++++++---- 2 files changed, 135 insertions(+), 79 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index fe59a127c..4f78e3228 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -162,6 +162,26 @@ class PartialRedundancyElimination::Statistics final : public util::Statistics /** -------------------------------------------------------------------------------------------- **/ + + +class LambdaParameterOperation : public jlm::rvsdg::Operation +{ +public: + LambdaParameterOperation(){} //trivial constructor + bool operator==(const Operation & other) const noexcept; + std::string debug_string() const override; + virtual ~LambdaParameterOperation() noexcept override; + [[nodiscard]] virtual std::unique_ptrcopy() const override; +}; + +LambdaParameterOperation::~LambdaParameterOperation() noexcept = default; + +bool LambdaParameterOperation::operator==(const Operation & other) const noexcept {return &other == this;} +std::string LambdaParameterOperation::debug_string() const { return "LambdaParameterOperation";} +std::unique_ptr LambdaParameterOperation::copy() const { std::cout<<"Attempt to copy singleton"; JLM_ASSERT(false); return nullptr; } +static LambdaParameterOperation lambdaParamOp; + + PartialRedundancyElimination::~PartialRedundancyElimination() noexcept = default; PartialRedundancyElimination::PartialRedundancyElimination(): Transformation("PartialRedundancyElimination"){} @@ -190,12 +210,10 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg IndentMan indenter = IndentMan(); for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) { - MatchType(*node, [this](rvsdg::LambdaNode& lm){ - for (auto& param : lm.GetFunctionArguments()) - { - auto deps = gvn_man_.DepsFromOutput(param); - std::cout << TR_RED << "IN LAMBDA" << TR_RESET; - std::cout << TR_GREEN << "deps:" << deps.inputs.size() << TR_RESET;; + MatchType(*node, [this, node](rvsdg::LambdaNode& lm){ + auto params = lm.GetFunctionArguments(); + for (size_t i = 0; i < params.size() ; i++){ + gvn_man_.Start(params[i], lambdaParamOp, node).WithIndex(i).End(); } }); @@ -210,48 +228,6 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } } -class LambdaParameterOperation : public jlm::rvsdg::Operation -{ -public: - LambdaParameterOperation(){} //trivial constructor - bool operator==(const Operation & other) const noexcept; - std::string debug_string() const override; - virtual ~LambdaParameterOperation() noexcept override; - [[nodiscard]] virtual std::unique_ptrcopy() const override; -}; - -LambdaParameterOperation::~LambdaParameterOperation() noexcept = default; - -bool LambdaParameterOperation::operator==(const Operation & other) const noexcept {return &other == this;} -std::string LambdaParameterOperation::debug_string() const { return "LambdaParameterOperation";} -std::unique_ptr LambdaParameterOperation::copy() const { std::cout<<"Attempt to copy singleton"; JLM_ASSERT(false); return nullptr; } -static LambdaParameterOperation lambdaParamOp; - -GVN_Deps GVN_Manager::DepsFromOutput(rvsdg::Output* output) -{ - auto deps = GVN_Deps(); - deps.op = NULL; - if (!output){return deps;} - auto owner = output->GetOwner(); - - if (std::holds_alternative(owner)){ - std::cout << "Owner is region" << std::endl; - } - if (std::holds_alternative(owner)){ - std::cout << "Owner is node" << std::endl; - rvsdg::Node* n = std::get(owner); - if (!n){return deps;} - jlm::rvsdg::MatchType(*n, [this, &deps, &output](rvsdg::LambdaNode& lm){ - std::cout << "Lambda node from variant." << std::endl; - deps.op = &lambdaParamOp; - auto gi = GVN_Input(this->FromIndex( output->index() )); - deps.inputs.push_back(gi); - }); - } - - return deps; -} - void PartialRedundancyElimination::Run( rvsdg::RvsdgModule & module, diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 885a37d59..a2233559e 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -301,25 +301,24 @@ namespace jlm::llvm typedef size_t GVN_Val; class GVN_Manager; -union GVN_Input{ - static constexpr size_t FL_IS_VALUE = 0x1; - bool IsValue() const {return value_ & FL_IS_VALUE;} - bool IsOutput() const {return !(value_ & FL_IS_VALUE);} - - GVN_Input(rvsdg::Output* o){this->output_ = o;} - GVN_Input(GVN_Val v){this->value_ = v;} - rvsdg::Output* AsOutput() const {return output_;} - GVN_Val AsValue() const {return value_;} -private: - rvsdg::Output* output_; - GVN_Val value_; -}; - -// A collection of all data required to compute a gvn value for an Output* +/** \brief A collection of all data required to compute a gvn value for an Output* */ struct GVN_Deps{ - rvsdg::Operation* op; - std::vector inputs; + GVN_Deps() = default; + rvsdg::Output* output; + rvsdg::Operation* op; // Some edges have + rvsdg::Node* node; + std::vector producers; + GVN_Val literal; + bool has_literal; + void reset(){ + output = NULL; + op = NULL; + node = NULL; + producers.clear(); + literal = 0; + has_literal = false; + } }; @@ -333,29 +332,110 @@ class GVN_Manager */ public: - GVN_Deps DepsFromOutput(rvsdg::Output* output); - GVN_Val FromLit(std::string s) - { - if (lit_to_gvn_.find(s) == lit_to_gvn_.end()){lit_to_gvn_.insert({s, CreateUniqueGVN()});} - return lit_to_gvn_[s]; + GVN_Manager() = default; + inline GVN_Manager& Start(rvsdg::Output* output, rvsdg::Operation& op, rvsdg::Node* node){ + CheckDepsEmpty(); + if (!output){throw std::runtime_error("Output pointer was null");} + build_deps_.output = output; + build_deps_.op = &op; + build_deps_.node = node; + + if (op_to_gvn_.find(&op) == op_to_gvn_.end()){ + op_to_gvn_.insert({&op, CreateUniqueGVN()}); + } + + return *this; } - GVN_Val FromIndex(std::size_t index) + inline GVN_Manager& WithEdge(rvsdg::Output* source){ + build_deps_.producers.push_back(source); + return *this; + } + inline GVN_Manager& WithStr(std::string& str){ + CheckLitEmpty(); + build_deps_.has_literal = true; + bool not_created = lit_to_gvn_.find(str) == lit_to_gvn_.end(); + if (not_created){lit_to_gvn_.insert( {str, CreateUniqueGVN() });} + build_deps_.literal = lit_to_gvn_[str]; + return *this; + } + inline GVN_Manager& WithIndex(size_t index){ + CheckLitEmpty(); + build_deps_.has_literal = true; + bool not_created = index_to_gvn_.find(index) == index_to_gvn_.end(); + if (not_created){index_to_gvn_.insert( {index, CreateUniqueGVN() });} + build_deps_.literal = index_to_gvn_[index]; + return *this; + } + inline void End(){ + auto fresh_value = HashDeps(build_deps_); + + std::cout << "FLUSH GVN:" << std::endl; + std::cout << " op: " << build_deps_.op->debug_string() << std::endl; + if (build_deps_.node){std::cout << " node: " << build_deps_.node->DebugString() << std::endl;} + std::cout << " literal: " << build_deps_.literal << std::endl; + std::cout << ".........................." << std::endl; + + if (!fresh_value){ + fresh_value = std::optional(CreateUniqueGVN()); + } + //TODO: compare for structural equality here + edges_to_gvn_.insert({build_deps_.output, *fresh_value}); + occurrences_.insert(*fresh_value); + + build_deps_.reset(); + } + + + +private: + GVN_Deps build_deps_; + static bool CanHashAsAssociativeCumulativeOp(GVN_Deps& deps) { - if (index_to_gvn_.find(index) == index_to_gvn_.end()){index_to_gvn_.insert({index, CreateUniqueGVN()});} - return index_to_gvn_[index]; + if (!deps.node){return false;} + bool hash_as_ca = false; + MatchType(deps.node->GetOperation(), [&hash_as_ca](const rvsdg::BinaryOperation& bin_op){ + hash_as_ca = bin_op.is_associative() && bin_op.is_commutative(); + }); + return hash_as_ca; } - GVN_Val FromOp(rvsdg::Operation* op) + + std::optional HashDeps(GVN_Deps& deps) { - if (op_to_gvn_.find(op) == op_to_gvn_.end()){op_to_gvn_.insert({op, CreateUniqueGVN()});} - return op_to_gvn_[op]; + GVN_Val h = op_to_gvn_[deps.op]; + + if (CanHashAsAssociativeCumulativeOp(deps)){ + for (size_t i = 0 ; i < deps.producers.size() ; i++){ + auto g = FromEdge(deps.producers[i]); + if (!g){ return std::nullopt; } + size_t a = static_cast(*g); + h ^= a; // independent of order + } + return std::optional(h); + } + + // Default hashing, in order with each edge treated differently based on order + for (size_t i = 0 ; i < deps.producers.size() ; i++){ + auto g = FromEdge(deps.producers[i]); + if (!g){ return std::nullopt; } + size_t a = static_cast(*g); + h ^= a * (i+1) + a; // dependent on order + } + + return std::optional(h); } -private: + std::optional FromEdge(rvsdg::Output* producer){ + if (edges_to_gvn_.find(producer) == edges_to_gvn_.end()){return std::nullopt;} + return edges_to_gvn_[producer]; + } + + void CheckLitEmpty(){if (build_deps_.has_literal){throw std::runtime_error("Maximum one literal supported per Output*");}} + void CheckDepsEmpty(){if (build_deps_.op){throw std::runtime_error("Previous GVN value not flushed.");}} GVN_Val CreateUniqueGVN() { - GVN_Val v = random() | GVN_Input::FL_IS_VALUE; //always set 1 bit so the tagged ptr union above works + // All GVN values start here + GVN_Val v = random(); while (occurrences_.count(v) != 0){v = random();} - JLM_ASSERT(v & GVN_Input::FL_IS_VALUE); occurrences_.insert(v); return v; } From 9163d8047d8619091a4229c9c507451338158988 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 21 Oct 2025 15:37:57 +0200 Subject: [PATCH 27/52] Clean build --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 47 +++- jlm/llvm/opt/PartialRedundancyElimination.hpp | 223 +++++++++++++----- 2 files changed, 194 insertions(+), 76 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 4f78e3228..837d6c8ee 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -10,6 +10,7 @@ #define TR_RED TR_FG(255,64,64) #define TR_GREEN TR_FG(64, 255, 64) +#define TR_PURPLE TR_FG(128,0,128) #define TR_YELLOW TR_FG(255, 255, 64) #define TR_ORANGE TR_FG(255, 128, 0) #define TR_BLUE TR_FG(64, 64, 255) @@ -20,6 +21,7 @@ #include "../../../tests/test-operation.hpp" #include "../../rvsdg/gamma.hpp" #include "../../rvsdg/lambda.hpp" +#include "../../rvsdg/theta.hpp" #include "../../rvsdg/MatchType.hpp" #include "../../rvsdg/node.hpp" #include "../../rvsdg/nullary.hpp" @@ -182,9 +184,9 @@ std::unique_ptr LambdaParameterOperation::copy() const { static LambdaParameterOperation lambdaParamOp; -PartialRedundancyElimination::~PartialRedundancyElimination() noexcept = default; +PartialRedundancyElimination::~PartialRedundancyElimination() noexcept {} -PartialRedundancyElimination::PartialRedundancyElimination(): Transformation("PartialRedundancyElimination"){} +PartialRedundancyElimination::PartialRedundancyElimination(): jlm::rvsdg::Transformation("PartialRedundancyElimination"), gvn_man_(){} void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) @@ -205,23 +207,43 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } -void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg) +void PartialRedundancyElimination::GVN_Compute(rvsdg::Region& reg) { IndentMan indenter = IndentMan(); for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) { - MatchType(*node, [this, node](rvsdg::LambdaNode& lm){ - auto params = lm.GetFunctionArguments(); - for (size_t i = 0; i < params.size() ; i++){ - gvn_man_.Start(params[i], lambdaParamOp, node).WithIndex(i).End(); + /* setup flows into regions of structural nodes */ + MatchType(*node, + [this](const rvsdg::GammaNode& gn){ + for (auto v : gn.GetEntryVars()){ + for (auto ba : v.branchArgument){ + gvn_man_.ExtendFlow(v.input, ba); + } + } + }, + [this](const rvsdg::ThetaNode& tn){ + for (auto v : tn.GetLoopVars()){ + if (rvsdg::ThetaLoopVarIsInvariant(v)){ + gvn_man_.ExtendFlow(v.input, v.pre); + } + } + }, + [this](rvsdg::LambdaNode& lm){ + for ( auto cv : lm.GetContextVars() ){ + gvn_man_.ExtendFlow(cv.input, cv.inner ); + } + auto params = lm.GetFunctionArguments(); + for (size_t i = 0; i < params.size() ; i++){ + gvn_man_.Start(params[i], &lambdaParamOp)->WithIndex(i)->End(); + } } - }); + ); MatchType(*node, [this](rvsdg::StructuralNode& sn) { - for (auto& reg : sn.Subregions()) + for (auto& subreg : sn.Subregions()) { - this->TraverseTopDownRecursively(reg); + this->GVN_Compute(subreg ); std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; } }); @@ -351,7 +373,7 @@ PartialRedundancyElimination::Run( this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - this->TraverseTopDownRecursively( rvsdg.GetRootRegion() ); + this->GVN_Compute( rvsdg.GetRootRegion() ); /* { @@ -401,6 +423,9 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; for (size_t i = 0; i < node.noutputs(); i++) { + if (pe->gvn_man_.GetGVN(node.output(i))){ + std::cout << TR_PURPLE << "gvn" << *(pe->gvn_man_.GetGVN(node.output(i))) << TR_RESET; + } auto h = pe->GetHash(node.output(i)); if (h.IsSome()) { diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index a2233559e..5c0865cae 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -300,112 +300,217 @@ namespace jlm::llvm typedef size_t GVN_Val; -class GVN_Manager; - /** \brief A collection of all data required to compute a gvn value for an Output* */ struct GVN_Deps{ - GVN_Deps() = default; - rvsdg::Output* output; + GVN_Deps() : op(nullptr), producers(), literal(0), has_literal(false){} + GVN_Deps(const GVN_Deps& from) + { + std::cout << "CP called" << std::endl; + op = from.op; + producers = from.producers; + literal = from.literal; + has_literal = from.has_literal; + } rvsdg::Operation* op; // Some edges have - rvsdg::Node* node; std::vector producers; GVN_Val literal; bool has_literal; void reset(){ - output = NULL; - op = NULL; - node = NULL; + op = nullptr; producers.clear(); literal = 0; has_literal = false; } }; - - class GVN_Manager { - /** \brief Utility class for managing the symbol space created by GVN values. - * Responsibilities: keep track of which values are already in use and - * provide means of mapping simple literals to gvn values - * - */ + /** \brief A class for creating GVN hashes and keeping track of GVN values already in use. */ public: GVN_Manager() = default; - inline GVN_Manager& Start(rvsdg::Output* output, rvsdg::Operation& op, rvsdg::Node* node){ - CheckDepsEmpty(); + /** \brief GVN values are created using a simple builder pattern. Remember to call End().*/ + /** \param output : The output edge the GVN value applies to. */ + /** \param op : The operator which produced the value. */ + inline GVN_Manager* Start(rvsdg::Output* output, rvsdg::Operation* op){ + CheckDepsEmpty(); //was the last GVN value completed? if (!output){throw std::runtime_error("Output pointer was null");} - build_deps_.output = output; - build_deps_.op = &op; - build_deps_.node = node; + build_output_ = output; + build_deps_.op = op; - if (op_to_gvn_.find(&op) == op_to_gvn_.end()){ - op_to_gvn_.insert({&op, CreateUniqueGVN()}); + if (op_to_gvn_.find(op) == op_to_gvn_.end()){ + op_to_gvn_.insert({op, CreateUniqueGVN()}); } - return *this; + return this; } - inline GVN_Manager& WithEdge(rvsdg::Output* source){ + + inline GVN_Manager* WithEdge(rvsdg::Output* source){ build_deps_.producers.push_back(source); - return *this; + return this; } - inline GVN_Manager& WithStr(std::string& str){ + + inline GVN_Manager* WithStr(std::string& str){ CheckLitEmpty(); build_deps_.has_literal = true; bool not_created = lit_to_gvn_.find(str) == lit_to_gvn_.end(); if (not_created){lit_to_gvn_.insert( {str, CreateUniqueGVN() });} build_deps_.literal = lit_to_gvn_[str]; - return *this; + return this; } - inline GVN_Manager& WithIndex(size_t index){ + + inline GVN_Manager* WithIndex(size_t index){ CheckLitEmpty(); build_deps_.has_literal = true; bool not_created = index_to_gvn_.find(index) == index_to_gvn_.end(); if (not_created){index_to_gvn_.insert( {index, CreateUniqueGVN() });} build_deps_.literal = index_to_gvn_[index]; - return *this; + return this; } + inline void End(){ + // This method is the only place where non-literal nodes should allocate a new gvn value. auto fresh_value = HashDeps(build_deps_); + if (!fresh_value){ + // The value either has missing dependencies or has been explicitly marked as unique. + fresh_value = std::optional(CreateUniqueGVN()); + } + + build_deps_.reset(); + return; - std::cout << "FLUSH GVN:" << std::endl; + std::cout << "GVN:" << std::endl; std::cout << " op: " << build_deps_.op->debug_string() << std::endl; - if (build_deps_.node){std::cout << " node: " << build_deps_.node->DebugString() << std::endl;} std::cout << " literal: " << build_deps_.literal << std::endl; std::cout << ".........................." << std::endl; - - if (!fresh_value){ - fresh_value = std::optional(CreateUniqueGVN()); +/* + //Check if gvn value already exists + // if this is the case compare it with existing values + if (occurrences_.find(*fresh_value) != occurrences_.end()){ + if (!CompareDeps(gvn_to_deps_[*fresh_value], build_deps_)){ + std::cout << "COLLISION DETECTED" << std::endl; + fresh_value = CreateUniqueGVN(); + gvn_to_deps_.insert({*fresh_value, build_deps_}); + } + }else{ + std::cout << "CP?? [["; + gvn_to_deps_.insert({*fresh_value, build_deps_}); + std::cout << "]]"; + } +*/ + if (occurrences_.find(*fresh_value) == occurrences_.end()){ + occurrences_.insert(*fresh_value); } - //TODO: compare for structural equality here - edges_to_gvn_.insert({build_deps_.output, *fresh_value}); - occurrences_.insert(*fresh_value); + + edges_to_gvn_.insert({build_output_, *fresh_value}); build_deps_.reset(); } + /** \brief Extend the flow of a value into a structural node */ + inline void ExtendFlow(rvsdg::Output* from, rvsdg::Output* downto) + { + if (traceback_flows_.find(downto) != traceback_flows_.end()){throw std::runtime_error("Incorrect extension of flow. Cannot have two origins.");} + //This map can also be used when redirecting to available expressions in outer scopes. + traceback_flows_.insert({downto, from}); + } + inline void ExtendFlow(rvsdg::Input* from, rvsdg::Output* downto) + { + if (!from || !from->origin()){throw std::runtime_error("Input lacks source");} + ExtendFlow(from->origin(), downto); + } + std::optional GetGVN(rvsdg::Output* output) + { + if (edges_to_gvn_.find(output) != edges_to_gvn_.end()){return edges_to_gvn_[output];} + return std::nullopt; + } private: - GVN_Deps build_deps_; - static bool CanHashAsAssociativeCumulativeOp(GVN_Deps& deps) + // Multiple output edges may map to the same gvn + + std::unordered_map edges_to_gvn_; + std::unordered_map gvn_to_deps_; // For collision detection. + std::unordered_map lit_to_gvn_; + std::unordered_map index_to_gvn_; + std::unordered_map op_to_gvn_; + + std::unordered_map traceback_flows_; + + std::unordered_set occurrences_; + + /* ********************************************************************** */ + GVN_Val CreateUniqueGVN() + { + // All GVN values start here + GVN_Val v = random(); + while (occurrences_.count(v) != 0){v = random();} + occurrences_.insert(v); + return v; + } + + /* ********************************************************************** */ + + bool CompareDeps(GVN_Deps& a, GVN_Deps& b) + { + // Strict structural equality + bool m_lit = a.literal == b.literal && a.has_literal == b.has_literal; + bool m_op = a.op == b.op; + bool m_prods = false; + if (a.producers.size() == b.producers.size()){ + m_prods = true; + for (size_t i = 0; i < a.producers.size(); i++){ + auto ea = ResolveEdge(a.producers[i]); + auto eb = ResolveEdge(b.producers[i]); + if (!ea){m_prods = false;break;} + if (!eb){m_prods = false;break;} + if (*ea != *eb){m_prods = false;break;} + } + } + return m_lit && m_op && m_prods; + } + // GVN values are computed one at the time using + // the builder interface above. + // These fields hold the data required + GVN_Deps build_deps_; + rvsdg::Output* build_output_; + + static bool CanHashAsAssociativeCumulativeOp(const GVN_Deps& deps) { - if (!deps.node){return false;} bool hash_as_ca = false; - MatchType(deps.node->GetOperation(), [&hash_as_ca](const rvsdg::BinaryOperation& bin_op){ + MatchType(*(deps.op), [&hash_as_ca](const rvsdg::BinaryOperation& bin_op){ hash_as_ca = bin_op.is_associative() && bin_op.is_commutative(); }); return hash_as_ca; } - std::optional HashDeps(GVN_Deps& deps) + bool DepsAreEquivalent(const GVN_Deps& a, const GVN_Deps& b) { - GVN_Val h = op_to_gvn_[deps.op]; + // Expensive check for detecting collisions + if (a.op != b.op){return false;} + if (a.literal != b.literal){return false;} + + //TODO: handle ca and gamma here + if (a.producers.size() != b.producers.size()){return false;} + + for (size_t i = 0; i < a.producers.size(); i++){ + auto g_a = ResolveEdge(a.producers[i]); + auto g_b = ResolveEdge(b.producers[i]); + if (g_a != g_b){return false;} + } + + return true; + } + std::optional HashDeps(GVN_Deps& deps) + { + GVN_Val h = op_to_gvn_[deps.op] ^ deps.literal; + if (!deps.has_literal && !deps.producers.size()){ + throw std::runtime_error("Logic error: missing data sources for hashing."); + } if (CanHashAsAssociativeCumulativeOp(deps)){ for (size_t i = 0 ; i < deps.producers.size() ; i++){ - auto g = FromEdge(deps.producers[i]); + auto g = ResolveEdge(deps.producers[i]); if (!g){ return std::nullopt; } size_t a = static_cast(*g); h ^= a; // independent of order @@ -415,7 +520,7 @@ class GVN_Manager // Default hashing, in order with each edge treated differently based on order for (size_t i = 0 ; i < deps.producers.size() ; i++){ - auto g = FromEdge(deps.producers[i]); + auto g = ResolveEdge(deps.producers[i]); if (!g){ return std::nullopt; } size_t a = static_cast(*g); h ^= a * (i+1) + a; // dependent on order @@ -424,29 +529,17 @@ class GVN_Manager return std::optional(h); } - std::optional FromEdge(rvsdg::Output* producer){ + std::optional ResolveEdge(rvsdg::Output* producer){ + //trace the edge all the way back to the top, crossing into lambdas(context vars), gammas and theta nodes + while (traceback_flows_.find(producer) != traceback_flows_.end()){ + producer = traceback_flows_[producer]; + } if (edges_to_gvn_.find(producer) == edges_to_gvn_.end()){return std::nullopt;} return edges_to_gvn_[producer]; } - void CheckLitEmpty(){if (build_deps_.has_literal){throw std::runtime_error("Maximum one literal supported per Output*");}} - void CheckDepsEmpty(){if (build_deps_.op){throw std::runtime_error("Previous GVN value not flushed.");}} - GVN_Val CreateUniqueGVN() - { - // All GVN values start here - GVN_Val v = random(); - while (occurrences_.count(v) != 0){v = random();} - occurrences_.insert(v); - return v; - } - // Multiple output edges may map to the same gvn - // No collisions here - std::unordered_map edges_to_gvn_; - std::unordered_map lit_to_gvn_; - std::unordered_map index_to_gvn_; - std::unordered_map op_to_gvn_; - - std::unordered_set occurrences_; + void CheckLitEmpty() const {if (build_deps_.has_literal){throw std::runtime_error("Maximum one literal supported per Output*");}} + void CheckDepsEmpty() const {if (build_deps_.op){throw std::runtime_error("Previous GVN value not flushed.");}} }; @@ -519,7 +612,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation GVN_Manager gvn_man_; void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); - void TraverseTopDownRecursively(rvsdg::Region& reg); + void GVN_Compute(rvsdg::Region& reg); static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); From 431bb3701275737bffe5ede431c8c58830bbf0bd Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 21 Oct 2025 19:12:23 +0200 Subject: [PATCH 28/52] Added leaf counting --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 52 +++-- jlm/llvm/opt/PartialRedundancyElimination.hpp | 202 +++++++++++++----- 2 files changed, 187 insertions(+), 67 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 837d6c8ee..708c0c070 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -21,7 +21,6 @@ #include "../../../tests/test-operation.hpp" #include "../../rvsdg/gamma.hpp" #include "../../rvsdg/lambda.hpp" -#include "../../rvsdg/theta.hpp" #include "../../rvsdg/MatchType.hpp" #include "../../rvsdg/node.hpp" #include "../../rvsdg/nullary.hpp" @@ -29,6 +28,7 @@ #include "../../rvsdg/theta.hpp" #include "../../util/GraphWriter.hpp" #include "../ir/operators/call.hpp" +#include "../ir/operators/IntegerOperations.hpp" #include "../ir/operators/operators.hpp" #include "PartialRedundancyElimination.hpp" #include @@ -214,23 +214,23 @@ void PartialRedundancyElimination::GVN_Compute(rvsdg::Region& reg) { /* setup flows into regions of structural nodes */ MatchType(*node, - [this](const rvsdg::GammaNode& gn){ + [this](rvsdg::GammaNode& gn){ for (auto v : gn.GetEntryVars()){ for (auto ba : v.branchArgument){ - gvn_man_.ExtendFlow(v.input, ba); + gvn_man_.ExtendFlow(v.input, ba, &gn); } } }, - [this](const rvsdg::ThetaNode& tn){ + [this](rvsdg::ThetaNode& tn){ for (auto v : tn.GetLoopVars()){ if (rvsdg::ThetaLoopVarIsInvariant(v)){ - gvn_man_.ExtendFlow(v.input, v.pre); + gvn_man_.ExtendFlow(v.input, v.pre, &tn); } } }, [this](rvsdg::LambdaNode& lm){ for ( auto cv : lm.GetContextVars() ){ - gvn_man_.ExtendFlow(cv.input, cv.inner ); + gvn_man_.ExtendFlow(cv.input, cv.inner, &lm); } auto params = lm.GetFunctionArguments(); for (size_t i = 0; i < params.size() ; i++){ @@ -239,14 +239,35 @@ void PartialRedundancyElimination::GVN_Compute(rvsdg::Region& reg) } ); - MatchType(*node, [this](rvsdg::StructuralNode& sn) - { - for (auto& subreg : sn.Subregions()) - { - this->GVN_Compute(subreg ); - std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; + MatchType(node->GetOperation(), + [this, &node](const rvsdg::BinaryOperation& op){ + gvn_man_.Start(node->output(0), &op) + ->WithEdge(node->input(0)) + ->WithEdge(node->input(1)) + ->End(); + }, + [this, &node](const jlm::llvm::IntegerConstantOperation& iconst){ + gvn_man_.Start(node->output(0), &iconst) + ->WithStr(iconst.Representation().str()) + ->End(); + }, + [this, &node](const jlm::llvm::CallOperation& op){ + for (size_t i = 0; i < node->noutputs() ; i++){ + gvn_man_.Start(node->output(i), &op) + ->WithUnique() + ->End(); + } } - }); + ); + + MatchType(*node, + [this](rvsdg::StructuralNode& sn){ + for (auto& subreg : sn.Subregions()){ + this->GVN_Compute(subreg ); + std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; + } + } + ); } } @@ -424,7 +445,7 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r for (size_t i = 0; i < node.noutputs(); i++) { if (pe->gvn_man_.GetGVN(node.output(i))){ - std::cout << TR_PURPLE << "gvn" << *(pe->gvn_man_.GetGVN(node.output(i))) << TR_RESET; + std::cout << TR_PURPLE << " | " << *(pe->gvn_man_.GetGVN(node.output(i))) << TR_RESET; } auto h = pe->GetHash(node.output(i)); if (h.IsSome()) @@ -441,6 +462,9 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r { for (auto& param : lm.GetFunctionArguments()) { + if (pe->gvn_man_.GetGVN(param)){ + std::cout << TR_ORANGE << " | " << *(pe->gvn_man_.GetGVN(param)) << TR_RESET; + } if (pe->gvn_hashes_.find(param) != pe->gvn_hashes_.end()) { GVN_Hash h = pe->gvn_hashes_[param]; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 5c0865cae..bd44ef06a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -300,26 +300,78 @@ namespace jlm::llvm typedef size_t GVN_Val; -/** \brief A collection of all data required to compute a gvn value for an Output* */ -struct GVN_Deps{ - GVN_Deps() : op(nullptr), producers(), literal(0), has_literal(false){} - GVN_Deps(const GVN_Deps& from) +struct LeafCounts +{ + std::vector< std::pair > counts; + + void MakeSorted(){ + std::sort(counts.begin(), counts.end(), + [](const std::pair& a, std::pair& b){return a.first < b.first;} + ); + } + // Vector of leaf hashes for operator clusters + LeafCounts():counts(){} + explicit LeafCounts(std::vector< std::pair >& c) : counts(){ + counts = c; + MakeSorted(); + } + LeafCounts(GVN_Val a, GVN_Val b){ + if (a == b){ + counts.push_back(std::make_pair(a, 2)); + }else{ + counts.push_back(std::make_pair(a, 1)); + counts.push_back(std::make_pair(b, 1)); + MakeSorted(); + } + } + + inline bool operator==(const LeafCounts &other) const { + return counts == other.counts; + } + + inline static LeafCounts Add(const LeafCounts& a, const LeafCounts& b) { - std::cout << "CP called" << std::endl; - op = from.op; - producers = from.producers; - literal = from.literal; - has_literal = from.has_literal; + std::vector< std::pair > acc; + size_t pos_a = 0; + size_t pos_b = 0; + while (pos_a < a.counts.size() || pos_b < b.counts.size()){ + std::cout << "(" << pos_a << ", " << pos_b << ")" << std::endl; + if (pos_a >= a.counts.size()){ + acc.push_back(b.counts[pos_b]); + pos_b++; continue; + } + if (pos_b >= b.counts.size()){ + acc.push_back(a.counts[pos_a]); + pos_a++; continue; + } + if (a.counts[pos_a].first == b.counts[pos_b].first){ + auto ab = a.counts[pos_a].second + b.counts[pos_b].second; + acc.push_back(std::make_pair(a.counts[pos_a].first, ab)); + pos_a++; pos_b++; continue; + } + if (a.counts[pos_a].first < b.counts[pos_b].first){ + acc.push_back(a.counts[pos_a]); pos_a++; + }else{ + acc.push_back(b.counts[pos_b]); pos_b++; + } + } + return LeafCounts(acc); } - rvsdg::Operation* op; // Some edges have +}; + +/** \brief A collection of all data required to compute a gvn value for an Output* */ +struct GVN_Deps{ + const rvsdg::Operation* op; // Some edges have std::vector producers; GVN_Val literal; bool has_literal; + std::optional leaf_counts; // For ca operations void reset(){ op = nullptr; producers.clear(); literal = 0; has_literal = false; + leaf_counts = std::nullopt; } }; @@ -332,7 +384,7 @@ class GVN_Manager /** \brief GVN values are created using a simple builder pattern. Remember to call End().*/ /** \param output : The output edge the GVN value applies to. */ /** \param op : The operator which produced the value. */ - inline GVN_Manager* Start(rvsdg::Output* output, rvsdg::Operation* op){ + inline GVN_Manager* Start(rvsdg::Output* output, const rvsdg::Operation* op){ CheckDepsEmpty(); //was the last GVN value completed? if (!output){throw std::runtime_error("Output pointer was null");} build_output_ = output; @@ -349,8 +401,14 @@ class GVN_Manager build_deps_.producers.push_back(source); return this; } + inline GVN_Manager* WithEdge(rvsdg::Input* flow) + { + if (!flow || !flow->origin()){build_deps_.producers.push_back(nullptr);return this;} + build_deps_.producers.push_back(flow->origin()); + return this; + } - inline GVN_Manager* WithStr(std::string& str){ + inline GVN_Manager* WithStr(std::string str){ CheckLitEmpty(); build_deps_.has_literal = true; bool not_created = lit_to_gvn_.find(str) == lit_to_gvn_.end(); @@ -359,6 +417,11 @@ class GVN_Manager return this; } + inline GVN_Manager* WithUnique(){ + build_deps_.producers.push_back(nullptr); + return this; + } + inline GVN_Manager* WithIndex(size_t index){ CheckLitEmpty(); build_deps_.has_literal = true; @@ -373,32 +436,30 @@ class GVN_Manager auto fresh_value = HashDeps(build_deps_); if (!fresh_value){ // The value either has missing dependencies or has been explicitly marked as unique. - fresh_value = std::optional(CreateUniqueGVN()); + fresh_value = std::optional(CreateUniqueGVN()); //This prevents structural comparisons } - build_deps_.reset(); - return; - std::cout << "GVN:" << std::endl; std::cout << " op: " << build_deps_.op->debug_string() << std::endl; std::cout << " literal: " << build_deps_.literal << std::endl; std::cout << ".........................." << std::endl; -/* + + //ComputeCA_Leaves(build_deps_); + //Check if gvn value already exists // if this is the case compare it with existing values - if (occurrences_.find(*fresh_value) != occurrences_.end()){ + if (gvn_to_deps_.find(*fresh_value) != gvn_to_deps_.end()){ if (!CompareDeps(gvn_to_deps_[*fresh_value], build_deps_)){ - std::cout << "COLLISION DETECTED" << std::endl; + std::cout << *fresh_value << "------------------COLLISION DETECTED--------------------" << std::endl; + auto prev = gvn_to_deps_[*fresh_value]; + std::cout << build_deps_.op->debug_string() << " =?= " << prev.op->debug_string() << std::endl; + std::cout << build_deps_.producers.size() << " =?= " << prev.producers.size() << std::endl; fresh_value = CreateUniqueGVN(); - gvn_to_deps_.insert({*fresh_value, build_deps_}); } - }else{ - std::cout << "CP?? [["; - gvn_to_deps_.insert({*fresh_value, build_deps_}); - std::cout << "]]"; } -*/ + if (occurrences_.find(*fresh_value) == occurrences_.end()){ + gvn_to_deps_.insert({*fresh_value, build_deps_}); occurrences_.insert(*fresh_value); } @@ -408,16 +469,18 @@ class GVN_Manager } /** \brief Extend the flow of a value into a structural node */ - inline void ExtendFlow(rvsdg::Output* from, rvsdg::Output* downto) + inline void ExtendFlow(rvsdg::Output* from, rvsdg::Output* downto, rvsdg::Node* into) { + edges_to_gvn_.insert({downto, edges_to_gvn_[from]}); + if (traceback_flows_.find(downto) != traceback_flows_.end()){throw std::runtime_error("Incorrect extension of flow. Cannot have two origins.");} //This map can also be used when redirecting to available expressions in outer scopes. - traceback_flows_.insert({downto, from}); + traceback_flows_.insert({downto, std::make_pair(from, into)}); } - inline void ExtendFlow(rvsdg::Input* from, rvsdg::Output* downto) + inline void ExtendFlow(rvsdg::Input* from, rvsdg::Output* downto, rvsdg::Node* into) { if (!from || !from->origin()){throw std::runtime_error("Input lacks source");} - ExtendFlow(from->origin(), downto); + ExtendFlow(from->origin(), downto, into); } std::optional GetGVN(rvsdg::Output* output) @@ -433,9 +496,9 @@ class GVN_Manager std::unordered_map gvn_to_deps_; // For collision detection. std::unordered_map lit_to_gvn_; std::unordered_map index_to_gvn_; - std::unordered_map op_to_gvn_; + std::unordered_map op_to_gvn_; - std::unordered_map traceback_flows_; + std::unordered_map > traceback_flows_; std::unordered_set occurrences_; @@ -443,7 +506,7 @@ class GVN_Manager GVN_Val CreateUniqueGVN() { // All GVN values start here - GVN_Val v = random(); + GVN_Val v = random() & 0xFFFF; while (occurrences_.count(v) != 0){v = random();} occurrences_.insert(v); return v; @@ -451,8 +514,57 @@ class GVN_Manager /* ********************************************************************** */ + /** \brief Associative and commutative operations are treated such that (a+b) + c + c == c+(b+a)+c + * This is accomplished by symbolically comparing their sums of the bottom arguments + */ + void ComputeCA_Leaves(GVN_Deps& deps) + { + if (CanHashAsAssociativeCommutativeOp(deps) && deps.producers.size() == 2){ + auto g_a = ResolveEdge(deps.producers[0]); + auto g_b = ResolveEdge(deps.producers[1]); + if (!g_a || !g_b){return;} //no deps for args. This Output* will be assigned a unique gvn. + if (gvn_to_deps_.find(*g_a) == gvn_to_deps_.end()){throw std::runtime_error("no dep_a");} + if (gvn_to_deps_.find(*g_b) == gvn_to_deps_.end()){throw std::runtime_error("no dep_b");} + auto deps_a = gvn_to_deps_[*g_a]; + auto deps_b = gvn_to_deps_[*g_b]; + if (build_deps_.op == deps_a.op || build_deps_.op == deps_b.op){ + LeafCounts la; + LeafCounts lb; + + if (build_deps_.op != deps_a.op){ + la.counts.push_back(std::make_pair(*g_a, 1)); + }else{ + if (deps_a.leaf_counts){ + la = *deps_a.leaf_counts; + } + } + + if (build_deps_.op != deps_b.op){ + lb.counts.push_back(std::make_pair(*g_b, 1)); + }else{ + if (deps_b.leaf_counts){ + lb = *deps_b.leaf_counts; + } + } + if (la.counts.size() && lb.counts.size()){ + build_deps_.leaf_counts = LeafCounts::Add(la, lb); + return; + } + } + if (build_deps_.op != deps_a.op && build_deps_.op != deps_b.op){ + build_deps_.leaf_counts = LeafCounts(*g_a, *g_b); + return; + } + } + deps.leaf_counts = std::nullopt; + } bool CompareDeps(GVN_Deps& a, GVN_Deps& b) { + if (CanHashAsAssociativeCommutativeOp(a)){ + if (a.op == b.op && a.leaf_counts && b.leaf_counts){ + return a.leaf_counts == b.leaf_counts; + } + } // Strict structural equality bool m_lit = a.literal == b.literal && a.has_literal == b.has_literal; bool m_op = a.op == b.op; @@ -475,7 +587,7 @@ class GVN_Manager GVN_Deps build_deps_; rvsdg::Output* build_output_; - static bool CanHashAsAssociativeCumulativeOp(const GVN_Deps& deps) + static bool CanHashAsAssociativeCommutativeOp(const GVN_Deps& deps) { bool hash_as_ca = false; MatchType(*(deps.op), [&hash_as_ca](const rvsdg::BinaryOperation& bin_op){ @@ -484,31 +596,13 @@ class GVN_Manager return hash_as_ca; } - bool DepsAreEquivalent(const GVN_Deps& a, const GVN_Deps& b) - { - // Expensive check for detecting collisions - if (a.op != b.op){return false;} - if (a.literal != b.literal){return false;} - - //TODO: handle ca and gamma here - if (a.producers.size() != b.producers.size()){return false;} - - for (size_t i = 0; i < a.producers.size(); i++){ - auto g_a = ResolveEdge(a.producers[i]); - auto g_b = ResolveEdge(b.producers[i]); - if (g_a != g_b){return false;} - } - - return true; - } - std::optional HashDeps(GVN_Deps& deps) { GVN_Val h = op_to_gvn_[deps.op] ^ deps.literal; if (!deps.has_literal && !deps.producers.size()){ throw std::runtime_error("Logic error: missing data sources for hashing."); } - if (CanHashAsAssociativeCumulativeOp(deps)){ + if (CanHashAsAssociativeCommutativeOp(deps)){ for (size_t i = 0 ; i < deps.producers.size() ; i++){ auto g = ResolveEdge(deps.producers[i]); if (!g){ return std::nullopt; } @@ -530,9 +624,11 @@ class GVN_Manager } std::optional ResolveEdge(rvsdg::Output* producer){ + if (!producer){return std::nullopt;} //trace the edge all the way back to the top, crossing into lambdas(context vars), gammas and theta nodes + //might be easier traverse the rvsdg upwards instead while (traceback_flows_.find(producer) != traceback_flows_.end()){ - producer = traceback_flows_[producer]; + producer = traceback_flows_[producer].first; } if (edges_to_gvn_.find(producer) == edges_to_gvn_.end()){return std::nullopt;} return edges_to_gvn_[producer]; From c802a3dfb199b344326e4b85582d2f274d8cf8ff Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Mon, 27 Oct 2025 12:59:48 +0100 Subject: [PATCH 29/52] Removed old code. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 347 +-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 733 ------------------ 2 files changed, 1 insertion(+), 1079 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 708c0c070..cf129dceb 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -166,28 +166,8 @@ class PartialRedundancyElimination::Statistics final : public util::Statistics -class LambdaParameterOperation : public jlm::rvsdg::Operation -{ -public: - LambdaParameterOperation(){} //trivial constructor - bool operator==(const Operation & other) const noexcept; - std::string debug_string() const override; - virtual ~LambdaParameterOperation() noexcept override; - [[nodiscard]] virtual std::unique_ptrcopy() const override; -}; - -LambdaParameterOperation::~LambdaParameterOperation() noexcept = default; - -bool LambdaParameterOperation::operator==(const Operation & other) const noexcept {return &other == this;} -std::string LambdaParameterOperation::debug_string() const { return "LambdaParameterOperation";} -std::unique_ptr LambdaParameterOperation::copy() const { std::cout<<"Attempt to copy singleton"; JLM_ASSERT(false); return nullptr; } -static LambdaParameterOperation lambdaParamOp; - - PartialRedundancyElimination::~PartialRedundancyElimination() noexcept {} - -PartialRedundancyElimination::PartialRedundancyElimination(): jlm::rvsdg::Transformation("PartialRedundancyElimination"), gvn_man_(){} - +PartialRedundancyElimination::PartialRedundancyElimination(): jlm::rvsdg::Transformation("PartialRedundancyElimination"){} void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) { @@ -204,71 +184,6 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } }); } - -} - -void PartialRedundancyElimination::GVN_Compute(rvsdg::Region& reg) -{ - IndentMan indenter = IndentMan(); - for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) - { - /* setup flows into regions of structural nodes */ - MatchType(*node, - [this](rvsdg::GammaNode& gn){ - for (auto v : gn.GetEntryVars()){ - for (auto ba : v.branchArgument){ - gvn_man_.ExtendFlow(v.input, ba, &gn); - } - } - }, - [this](rvsdg::ThetaNode& tn){ - for (auto v : tn.GetLoopVars()){ - if (rvsdg::ThetaLoopVarIsInvariant(v)){ - gvn_man_.ExtendFlow(v.input, v.pre, &tn); - } - } - }, - [this](rvsdg::LambdaNode& lm){ - for ( auto cv : lm.GetContextVars() ){ - gvn_man_.ExtendFlow(cv.input, cv.inner, &lm); - } - auto params = lm.GetFunctionArguments(); - for (size_t i = 0; i < params.size() ; i++){ - gvn_man_.Start(params[i], &lambdaParamOp)->WithIndex(i)->End(); - } - } - ); - - MatchType(node->GetOperation(), - [this, &node](const rvsdg::BinaryOperation& op){ - gvn_man_.Start(node->output(0), &op) - ->WithEdge(node->input(0)) - ->WithEdge(node->input(1)) - ->End(); - }, - [this, &node](const jlm::llvm::IntegerConstantOperation& iconst){ - gvn_man_.Start(node->output(0), &iconst) - ->WithStr(iconst.Representation().str()) - ->End(); - }, - [this, &node](const jlm::llvm::CallOperation& op){ - for (size_t i = 0; i < node->noutputs() ; i++){ - gvn_man_.Start(node->output(i), &op) - ->WithUnique() - ->End(); - } - } - ); - - MatchType(*node, - [this](rvsdg::StructuralNode& sn){ - for (auto& subreg : sn.Subregions()){ - this->GVN_Compute(subreg ); - std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; - } - } - ); - } } void @@ -280,134 +195,8 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - // - // // NEW CODE USING REACTIVE STYLE CALLBACKS - // // SHOULD HANDLE THETA NODES, BUT MORE TESTING IS REQUIRED - // - // jlm::llvm::flows::FlowData fd(&gvn_hashes_); - // - // - // //cb for handling two flows coming from different sub-regions of a gamma node. - // // This reprents the GVN of expressions such as (a > b ? a : b) - // // Thus, the branch order matters. - // // The net hash of outputs from gamma nodes is computed by reducing each output across branches - // // with this callback. - // auto merge_gvn_ga = [](std::optional& a, std::optional& b) - // { - // if (!a){return b;} if (!b){return a;} - // if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted()); } - // if ( a->value == b->value ){ return a; } - // size_t h = a->value ^ (b->value << 3); //Hash branches differently. - // return std::optional( GVN_Hash(h) ); - // }; - // - // //cb for merging flows coming into a theta node for the first time and from previous iterations. - // // on the second iteration all sub-nodes will have their values overwritten, except - // // loop invariant nodes. - // auto merge_gvn_th = [](rvsdg::ThetaNode& tn, std::optional& a, std::optional& b) - // { - // if (!a){return b;} if (!b){std::cout<<"UNREACHABLE"; exit(-1);} - // if (*a == GVN_Hash::Tainted() || *b == GVN_Hash::Tainted()){ return std::optional(GVN_Hash::Tainted() ); } - // if (a->value == b->value){return a;} - // if (a && a->IsLoopVar()){return a;} // This is required for fixed points to be reached by gvn - // // Values are identified as variant on exit of first iteration - // // LoopVar hashes trickle through the loop body once more - // // Subsequent iterations are blocked as loopvar hashes are never overwritten - // return std::optional( GVN_Hash::LoopVar( a->value ^ b->value )); - // }; - // - // - // jlm::llvm::flows::ApplyDataFlowsTopDown(rvsdg.GetRootRegion(), fd, merge_gvn_ga, merge_gvn_th, - // [](rvsdg::Node& node, - // std::vector>& flows_in, - // std::vector>& flows_out - // ) - // { - // // The gvn hashes are stored automatically by the argument fd, when the flows_out vector - // // is written to. - // std::cout << TR_GREEN << node.GetNodeId() << ":" << node.DebugString() << TR_RESET << std::endl; - // - // rvsdg::MatchType(node.GetOperation(), - // // ----------------------------------------------------------------------------------------- - // [&flows_out](const jlm::llvm::IntegerConstantOperation& iconst){ - // std::hash hasher; - // flows_out[0] = GVN_Hash( hasher(iconst.Representation().str()) ); - // }, - // - // // ----------------------------------------------------------------------------------------- - // [&flows_in, &flows_out](const rvsdg::BinaryOperation& op){ - // JLM_ASSERT(flows_in.size() == 2); - // if (!(flows_in[0]) || !(flows_in[1])){ - // std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - // } - // - // std::hash hasher; - // size_t h = hasher(op.debug_string() ); - // - // size_t a = hasher(std::to_string(flows_in[0]->value)); - // size_t b = hasher(std::to_string(flows_in[1]->value)); - // bool c_and_a = op.is_commutative() && op.is_associative(); - // h ^= c_and_a ? (a + b) : (a ^ (b << 3)); - // flows_out[0] = std::optional(h); - // }, - // // ----------------------------------------------------------------------------------------- - // [&flows_in, &flows_out](const rvsdg::UnaryOperation& op){ - // if (!(flows_in.size())){ - // std::cout<< TR_RED << "Expected some input" << TR_RESET << std::endl;return; - // } - // std::hash hasher; - // size_t h = hasher(op.debug_string() ); - // size_t a = hasher(std::to_string(flows_in[0]->value)); - // h ^= a; - // flows_out[0] = std::optional(h); - // }, - // // ----------------------------------------------------------------------------------------- - // [&node, &flows_in, &flows_out](const jlm::llvm::CallOperation& op){ - // std::string s = node.DebugString() + "CALL"; - // std::hash hasher; - // size_t h = hasher(s); - // for (size_t i = 0; i < flows_out.size(); i++){ - // flows_out[i] = std::optional( h + i); - // } - // } - // ); - // - // rvsdg::MatchType(node, - // // ----------------------------------------------------------------------------------------- - // [&flows_out](rvsdg::LambdaNode& lm){ - // //std::cout << TR_PINK << "LAMBDA PARAMS" << flows_out.size() << TR_RESET << std::endl; - // auto s = lm.DebugString(); - // std::hash hasher; - // size_t h = hasher(s); - // for (size_t i = 0; i < flows_out.size(); i++){ - // flows_out[i] = std::optional( GVN_Hash( h+i ) ); - // } - // } - // ); - // } - // ); - // - - std::cout << TR_RED << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - this->GVN_Compute( rvsdg.GetRootRegion() ); - /* - { - - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::register_leaf_hash); - std::cout << TR_RED << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); - std::cout << TR_BLUE << "================================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::hash_node); - std::cout << TR_PINK << "================================================================" << TR_RESET << std::endl; - } - */ - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; @@ -442,142 +231,8 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) { std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; - for (size_t i = 0; i < node.noutputs(); i++) - { - if (pe->gvn_man_.GetGVN(node.output(i))){ - std::cout << TR_PURPLE << " | " << *(pe->gvn_man_.GetGVN(node.output(i))) << TR_RESET; - } - auto h = pe->GetHash(node.output(i)); - if (h.IsSome()) - { - std::string color = (pe->DBG_HashCount(h) > 1 ? TR_GREEN : TR_YELLOW); - - MatchType(node.GetOperation(),[&color](const jlm::llvm::CallOperation& op){color = TR_CYAN;}); - MatchType(node, [&color](rvsdg::LambdaNode& lm){color = TR_ORANGE;}); - - std::cout << " : " << color << std::to_string(h) << TR_RESET; - } - } - MatchType(node, [pe](rvsdg::LambdaNode& lm) - { - for (auto& param : lm.GetFunctionArguments()) - { - if (pe->gvn_man_.GetGVN(param)){ - std::cout << TR_ORANGE << " | " << *(pe->gvn_man_.GetGVN(param)) << TR_RESET; - } - if (pe->gvn_hashes_.find(param) != pe->gvn_hashes_.end()) - { - GVN_Hash h = pe->gvn_hashes_[param]; - std::cout << TR_ORANGE << " : " << std::to_string(h) << TR_RESET; - } - } - }); std::cout << std::endl; } -void PartialRedundancyElimination::register_leaf_hash(PartialRedundancyElimination *pe, rvsdg::Node& node) -{ - MatchType(node.GetOperation(), - [pe, &node](const jlm::llvm::IntegerConstantOperation& iconst) - { - std::hash hasher; - size_t h = hasher(iconst.Representation().str()); - pe->AssignGVN(node.output(0), GVN_Hash(h)); - } - ); - - /* Add each lambda parameter as a leaf hash for hashing within its body */ - MatchType(node, [pe, &node](rvsdg::LambdaNode& lm) - { - auto fargs = lm.GetFunctionArguments(); - auto s = node.DebugString() + std::to_string(node.GetNodeId()); - for (size_t i = 0; i < fargs.size(); i++){ - pe->AssignGVN(fargs[i], node.DebugString(), i); - } - pe->AssignGVN(node.output(0), node.DebugString() + "LM_NODE", 0); - }); -} - -void PartialRedundancyElimination::hash_call(PartialRedundancyElimination* pe, rvsdg::Node& node) -{ - MatchTypeOrFail(node.GetOperation(), [pe, &node](const jlm::llvm::CallOperation& op) - { - std::string s = node.DebugString() + std::to_string(node.GetNodeId()); // + op.GetLambdaOutput(); - //std::cout << TR_PINK << s << TR_RESET << std::endl; - pe->AssignGVN(node.output(0), s, 0); - }); } -void PartialRedundancyElimination::hash_gamma(PartialRedundancyElimination* pe, rvsdg::Node& node) -{ - MatchTypeOrFail(node, [pe](rvsdg::GammaNode& node) - { - for (auto ev : node.GetEntryVars()){ - rvsdg::Output* origin = ev.input->origin(); - GVN_Hash h = pe->GetHash(origin); - if (pe->GetHash(origin).IsSome()) - { - for (rvsdg::Output* brarg : ev.branchArgument){ - pe->AssignGVN(brarg, h); - } - } - } - }); -} - -void PartialRedundancyElimination::hash_node(PartialRedundancyElimination *pe, rvsdg::Node& node) -{ - /*Match by operation*/ - MatchType(node.GetOperation(), - [pe, &node](const rvsdg::BinaryOperation& op){hash_bin(pe, node);}, - [pe, &node](const jlm::llvm::CallOperation& op){hash_call(pe, node);} - ); - /*Match by node type*/ - MatchType(node, - [pe](rvsdg::GammaNode& node){hash_gamma(pe, node);} - ); -} - -void PartialRedundancyElimination::hash_bin(PartialRedundancyElimination *pe, rvsdg::Node& node) -{ - MatchType(node.GetOperation(), [pe, &node](const rvsdg::BinaryOperation& op) - { - - std::hash hasher; - size_t h = hasher(op.debug_string()); - bool was_hashable = true; - for (size_t i = 0 ; i < node.ninputs(); i++) - { - if (pe->gvn_hashes_.find(node.input(i)->origin()) != pe->gvn_hashes_.end()) - { - auto hash_in = pe->GetHash(node.input(i)->origin()); - if (op.is_commutative() && op.is_associative()) - { - h += hasher(std::to_string(hash_in.value)); - }else - { - h ^= hasher(std::to_string(hash_in.value)) * (i+1); - } - }else - { - std::cout << TR_RED << node.DebugString() << node.GetNodeId()<< "MISSING INPUT HASH" << TR_RESET << std::endl; - auto input_source = node.input(i)->origin()->GetOwner(); - try - { - auto src_node = std::get(input_source); - std::cout << TR_RED << "origin: " << src_node->DebugString() << src_node->GetNodeId() << TR_RESET<< std::endl; - }catch (std::bad_variant_access& ex) - { - std::cout<AssignGVN(node.output(0), h); - } - - }); -} - -} diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index bd44ef06a..2854b941f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -28,650 +28,8 @@ class ThetaNode; class Region; } -namespace jlm::llvm::flows -{ - /** Generic functions for some data flow analyses. - * Nodes are treated as part of a reactive flow network. - * This makes it possible to write more generic analyses as some common tasks - * such as ensuring the data is passed to all downstream (or upstream) usage sites. - * Currently only a top-down flow is implemented. Usable for GVN. - */ - using namespace jlm; - - /** A view into the data to manipulate by flows **/ - /** This represents the value output from nodes **/ - template - class FlowData - { - public: - FlowData(std::unordered_map * output_values) - { - JLM_ASSERT(output_values); //should be non-null - output_values_ = output_values; - } - std::optional Get(rvsdg::Output* k) - { - if (k == NULL){return std::nullopt;} - bool present = output_values_->find(k) != output_values_->end(); - return present ? std::optional((*output_values_)[k]) : std::nullopt; - } - void Set(rvsdg::Output* k, std::optional v) - { - output_values_->insert({k,*v}); - } - private: - std::unordered_map* output_values_; - }; - - /** \ref WorkItemType Union type tag. - * \ref WorkItemValue Used internally by the reactive interpreter to keep track of nodes and regions yet to be visited. - * **/ - enum class WorkItemType - { - REGION, - DELTA, - NODE, - GAMMA, - GAMMA_END, - THETA, - THETA_END, - LAMBDA, - }; - struct WorkItemValue - { - // implicit conversions are ok here - WorkItemValue(rvsdg::Region* r) {this->type = WorkItemType::REGION; this->region = r;} - WorkItemValue(WorkItemType type, rvsdg::Node* n){this->type = type; this->node = n;} - WorkItemValue(rvsdg::Node* n) - { - this->node = n; - this->type = WorkItemType::NODE; - rvsdg::MatchType(*n, - [this](jlm::rvsdg::GammaNode& gn){this->type = WorkItemType::GAMMA;}, - [this](jlm::rvsdg::ThetaNode& tn){this->type = WorkItemType::THETA;}, - [this](jlm::rvsdg::LambdaNode& lm) {this->type = WorkItemType::LAMBDA;}, - [this](jlm::rvsdg::DeltaNode& dl) {this->type = WorkItemType::DELTA;} - ); - } - /* Fields should be made const */ - WorkItemType type; - union{ - rvsdg::DeltaNode* dl; - rvsdg::Region* region; - rvsdg::Node* node; - rvsdg::GammaNode* gn; - rvsdg::ThetaNode* tn; - rvsdg::LambdaNode* lm; - }; - }; - - /** \brief abstracts away the walking of the rvsdg graph for data flows of a reactive nature - * Theta and Gamma nodes have special semantics as they must sometimes merge values. - * Merges at gamma and theta nodes combine flows. - * The merge at theta nodes represent values with multiple alternatives from - * different loop iterations. - * Merges at gamma nodes represent the combined value from switch cases - * The merges may be different or the same - * - * The style of traversing the graph can be adapted to more complex cases. - * */ - template - void ApplyDataFlowsTopDown(rvsdg::Region& scope, FlowData& fd, GaMerger mrGa, ThMerger mrTh, Prod cb){ - // mrGa: represent the intersection of values from one data flow out from a gamma node - // mrTh: represent the merging of output of theta node with the input. Third arg must be non-null if second is something. - // args : theta node - // value from older iteration - // value from last iteration - // - // - // A FIFO queue of nodes and regions to visit or equivalently a continuation - // of instruction to be executed by the interpreter below. - std::vector workItems; - - // A buffer for flow values. - // The flows function handles lookup of values from the fd map. - std::vector< std::optional > flows_in; - std::vector< std::optional > flows_out; - - workItems.push_back(WorkItemValue(&scope)); - size_t max_iter = 500; - while (workItems.size() ) - { - max_iter--; if (!max_iter){std::cout<<"ApplyDataFlowsTopDownMaximum iteration count reached"<Subregions()){ - workItems.push_back(WorkItemValue(®)); - } - if (workItems.size() > 1000){std::cout<<"Stack overflow" << std::endl; return;} - - }break; - - case WorkItemType::REGION:{ - // Push all nodes inside a region in topological order onto the queue - std::vector tmp; - for (auto node : rvsdg::TopDownTraverser(w.region)){tmp.push_back(WorkItemValue(node));} - while (tmp.size()){ - workItems.push_back(tmp.back()); - tmp.pop_back(); - } - }break; - case WorkItemType::NODE:{ - // initialize input buffer - flows_in.resize(w.node->ninputs(), std::nullopt); - - for (size_t i=0;i < w.node->ninputs() ; i++){ - auto ni = w.node->input(i); //check just in case Input is NULL - auto v = fd.Get(ni ? ni->origin() : NULL); - flows_in[i] = v; - } - // initialize output buffer - flows_out.clear(); - flows_out.resize(w.node->noutputs(), std::nullopt); - - // visit node - if (flows_out.size()){ - cb(*(w.node), flows_in, flows_out); - } - - // update map - for ( size_t i = 0; i < w.node->noutputs(); i++){ - fd.Set(w.node->output(i), flows_out[i]); - } - }break; - case WorkItemType::LAMBDA:{ - // This case only handles params out - // Add an enum for other visit types later - // initialize input buffer - flows_in.clear(); - auto f_args = w.lm->GetFunctionArguments(); - flows_out.clear(); flows_out.resize(f_args.size(), std::nullopt); - - // visit node - if (flows_out.size()){ cb(*(w.node), flows_in, flows_out); } - - // update map - for ( size_t i = 0; i < f_args.size(); i++){ - //if (!flows_out[i]){std::cout << "LAM MISSING OUT" << std::endl;} - //std::cout << "lambda: Flow out ["<subregion()); - }break; - - case WorkItemType::GAMMA: - { - //Split flows - for (auto ev : w.gn->GetEntryVars()){ - auto tmp = ev.input; - auto flow_from = tmp ? fd.Get(tmp->origin()) : std::nullopt; - if (flow_from){ - for (rvsdg::Output* brarg : ev.branchArgument){ - fd.Set(brarg, *flow_from); - } - } - } - //Push tasks in LIFO order - workItems.push_back(WorkItemValue(WorkItemType::GAMMA_END, w.gn)); - for (size_t i = 0; i < w.gn->nsubregions() ; i++){ - workItems.push_back(w.gn->subregion(i)); - } - }break; - case WorkItemType::GAMMA_END:{ - // Reduce all outputs from exitVars with mrGa - auto ex_vars = w.gn->GetExitVars(); - JLM_ASSERT(ex_vars.size() == w.gn->noutputs()); - for (size_t v = 0; v < ex_vars.size(); v++){ - auto br_fst = ex_vars[v].branchResult[0]; - auto merged_val = fd.Get( br_fst ? br_fst->origin() : NULL ); - - for (size_t b = 1;b < ex_vars[v].branchResult.size();b++){ // !!! from 1 - auto next_br = ex_vars[v].branchResult[b]; - auto next_br_value = fd.Get( next_br ? next_br->origin() : NULL ); - merged_val = mrGa(merged_val, next_br_value); - } - - fd.Set(ex_vars[v].output, merged_val); - JLM_ASSERT(ex_vars.size() == w.gn->noutputs()); - JLM_ASSERT(ex_vars[v].output == w.gn->output(v)); - } - }break; - - case WorkItemType::THETA:{ - // At entry into a theta node check if the flow into each loop variable - // is compatible with the last output previous loop iterations - // This ensures theta bodies are only visited twice during GVN - // It is the responsibility of merge callbacks to ensure values - // reach a fixpoint. - auto loopvars = w.tn->GetLoopVars(); - bool fixed_point_reached = true; - - /* Try to push inputs into the loop */ - /* values from previous iterations stored on Output* of .pre field */ - for (size_t i = 0;i < loopvars.size(); i++){ - auto lv = loopvars[i]; - JLM_ASSERT(lv.input->origin() != lv.pre); - - auto lv_input = fd.Get( lv.input ? lv.input->origin() : NULL ); - auto lv_pre = fd.Get( lv.pre ); - auto merged = mrTh( *(w.tn), lv_pre, lv_input ); - - if ( - (merged && !lv_pre) || (!merged && lv_pre) || (*merged != *lv_pre) - ){fixed_point_reached = false;} - - fd.Set( lv.pre, merged ); - } - - if (!fixed_point_reached){ - workItems.push_back( WorkItemValue(WorkItemType::THETA_END, w.node) ); - workItems.push_back( w.tn->subregion() ); - } - }break; - - case WorkItemType::THETA_END:{ - auto loopvars = w.tn->GetLoopVars(); - for (size_t i = 0;i < loopvars.size(); i++){ - auto lv = loopvars[i]; - auto lv_pre = fd.Get( lv.pre ); - auto lv_post = fd.Get( lv.post ? lv.post->origin() : NULL ); - auto merged = mrTh(*(w.tn), lv_pre, lv_post ); - - fd.Set( lv.output, lv_post ); // Required for downstream nodes - fd.Set( lv.pre, merged); // Required for blocking new iterations - } - workItems.push_back( WorkItemValue(w.node) ); // Attempt another loop iteration - }break; - - default: std::cout << static_cast(w.type) <<"Ignoring work item..."< > counts; - - void MakeSorted(){ - std::sort(counts.begin(), counts.end(), - [](const std::pair& a, std::pair& b){return a.first < b.first;} - ); - } - // Vector of leaf hashes for operator clusters - LeafCounts():counts(){} - explicit LeafCounts(std::vector< std::pair >& c) : counts(){ - counts = c; - MakeSorted(); - } - LeafCounts(GVN_Val a, GVN_Val b){ - if (a == b){ - counts.push_back(std::make_pair(a, 2)); - }else{ - counts.push_back(std::make_pair(a, 1)); - counts.push_back(std::make_pair(b, 1)); - MakeSorted(); - } - } - - inline bool operator==(const LeafCounts &other) const { - return counts == other.counts; - } - - inline static LeafCounts Add(const LeafCounts& a, const LeafCounts& b) - { - std::vector< std::pair > acc; - size_t pos_a = 0; - size_t pos_b = 0; - while (pos_a < a.counts.size() || pos_b < b.counts.size()){ - std::cout << "(" << pos_a << ", " << pos_b << ")" << std::endl; - if (pos_a >= a.counts.size()){ - acc.push_back(b.counts[pos_b]); - pos_b++; continue; - } - if (pos_b >= b.counts.size()){ - acc.push_back(a.counts[pos_a]); - pos_a++; continue; - } - if (a.counts[pos_a].first == b.counts[pos_b].first){ - auto ab = a.counts[pos_a].second + b.counts[pos_b].second; - acc.push_back(std::make_pair(a.counts[pos_a].first, ab)); - pos_a++; pos_b++; continue; - } - if (a.counts[pos_a].first < b.counts[pos_b].first){ - acc.push_back(a.counts[pos_a]); pos_a++; - }else{ - acc.push_back(b.counts[pos_b]); pos_b++; - } - } - return LeafCounts(acc); - } -}; - -/** \brief A collection of all data required to compute a gvn value for an Output* */ -struct GVN_Deps{ - const rvsdg::Operation* op; // Some edges have - std::vector producers; - GVN_Val literal; - bool has_literal; - std::optional leaf_counts; // For ca operations - void reset(){ - op = nullptr; - producers.clear(); - literal = 0; - has_literal = false; - leaf_counts = std::nullopt; - } -}; - -class GVN_Manager -{ - /** \brief A class for creating GVN hashes and keeping track of GVN values already in use. */ - -public: - GVN_Manager() = default; - /** \brief GVN values are created using a simple builder pattern. Remember to call End().*/ - /** \param output : The output edge the GVN value applies to. */ - /** \param op : The operator which produced the value. */ - inline GVN_Manager* Start(rvsdg::Output* output, const rvsdg::Operation* op){ - CheckDepsEmpty(); //was the last GVN value completed? - if (!output){throw std::runtime_error("Output pointer was null");} - build_output_ = output; - build_deps_.op = op; - - if (op_to_gvn_.find(op) == op_to_gvn_.end()){ - op_to_gvn_.insert({op, CreateUniqueGVN()}); - } - - return this; - } - - inline GVN_Manager* WithEdge(rvsdg::Output* source){ - build_deps_.producers.push_back(source); - return this; - } - inline GVN_Manager* WithEdge(rvsdg::Input* flow) - { - if (!flow || !flow->origin()){build_deps_.producers.push_back(nullptr);return this;} - build_deps_.producers.push_back(flow->origin()); - return this; - } - - inline GVN_Manager* WithStr(std::string str){ - CheckLitEmpty(); - build_deps_.has_literal = true; - bool not_created = lit_to_gvn_.find(str) == lit_to_gvn_.end(); - if (not_created){lit_to_gvn_.insert( {str, CreateUniqueGVN() });} - build_deps_.literal = lit_to_gvn_[str]; - return this; - } - - inline GVN_Manager* WithUnique(){ - build_deps_.producers.push_back(nullptr); - return this; - } - - inline GVN_Manager* WithIndex(size_t index){ - CheckLitEmpty(); - build_deps_.has_literal = true; - bool not_created = index_to_gvn_.find(index) == index_to_gvn_.end(); - if (not_created){index_to_gvn_.insert( {index, CreateUniqueGVN() });} - build_deps_.literal = index_to_gvn_[index]; - return this; - } - - inline void End(){ - // This method is the only place where non-literal nodes should allocate a new gvn value. - auto fresh_value = HashDeps(build_deps_); - if (!fresh_value){ - // The value either has missing dependencies or has been explicitly marked as unique. - fresh_value = std::optional(CreateUniqueGVN()); //This prevents structural comparisons - } - - std::cout << "GVN:" << std::endl; - std::cout << " op: " << build_deps_.op->debug_string() << std::endl; - std::cout << " literal: " << build_deps_.literal << std::endl; - std::cout << ".........................." << std::endl; - - //ComputeCA_Leaves(build_deps_); - - //Check if gvn value already exists - // if this is the case compare it with existing values - if (gvn_to_deps_.find(*fresh_value) != gvn_to_deps_.end()){ - if (!CompareDeps(gvn_to_deps_[*fresh_value], build_deps_)){ - std::cout << *fresh_value << "------------------COLLISION DETECTED--------------------" << std::endl; - auto prev = gvn_to_deps_[*fresh_value]; - std::cout << build_deps_.op->debug_string() << " =?= " << prev.op->debug_string() << std::endl; - std::cout << build_deps_.producers.size() << " =?= " << prev.producers.size() << std::endl; - fresh_value = CreateUniqueGVN(); - } - } - - if (occurrences_.find(*fresh_value) == occurrences_.end()){ - gvn_to_deps_.insert({*fresh_value, build_deps_}); - occurrences_.insert(*fresh_value); - } - - edges_to_gvn_.insert({build_output_, *fresh_value}); - - build_deps_.reset(); - } - - /** \brief Extend the flow of a value into a structural node */ - inline void ExtendFlow(rvsdg::Output* from, rvsdg::Output* downto, rvsdg::Node* into) - { - edges_to_gvn_.insert({downto, edges_to_gvn_[from]}); - - if (traceback_flows_.find(downto) != traceback_flows_.end()){throw std::runtime_error("Incorrect extension of flow. Cannot have two origins.");} - //This map can also be used when redirecting to available expressions in outer scopes. - traceback_flows_.insert({downto, std::make_pair(from, into)}); - } - inline void ExtendFlow(rvsdg::Input* from, rvsdg::Output* downto, rvsdg::Node* into) - { - if (!from || !from->origin()){throw std::runtime_error("Input lacks source");} - ExtendFlow(from->origin(), downto, into); - } - - std::optional GetGVN(rvsdg::Output* output) - { - if (edges_to_gvn_.find(output) != edges_to_gvn_.end()){return edges_to_gvn_[output];} - return std::nullopt; - } - -private: - // Multiple output edges may map to the same gvn - - std::unordered_map edges_to_gvn_; - std::unordered_map gvn_to_deps_; // For collision detection. - std::unordered_map lit_to_gvn_; - std::unordered_map index_to_gvn_; - std::unordered_map op_to_gvn_; - - std::unordered_map > traceback_flows_; - - std::unordered_set occurrences_; - - /* ********************************************************************** */ - GVN_Val CreateUniqueGVN() - { - // All GVN values start here - GVN_Val v = random() & 0xFFFF; - while (occurrences_.count(v) != 0){v = random();} - occurrences_.insert(v); - return v; - } - - /* ********************************************************************** */ - - /** \brief Associative and commutative operations are treated such that (a+b) + c + c == c+(b+a)+c - * This is accomplished by symbolically comparing their sums of the bottom arguments - */ - void ComputeCA_Leaves(GVN_Deps& deps) - { - if (CanHashAsAssociativeCommutativeOp(deps) && deps.producers.size() == 2){ - auto g_a = ResolveEdge(deps.producers[0]); - auto g_b = ResolveEdge(deps.producers[1]); - if (!g_a || !g_b){return;} //no deps for args. This Output* will be assigned a unique gvn. - if (gvn_to_deps_.find(*g_a) == gvn_to_deps_.end()){throw std::runtime_error("no dep_a");} - if (gvn_to_deps_.find(*g_b) == gvn_to_deps_.end()){throw std::runtime_error("no dep_b");} - auto deps_a = gvn_to_deps_[*g_a]; - auto deps_b = gvn_to_deps_[*g_b]; - if (build_deps_.op == deps_a.op || build_deps_.op == deps_b.op){ - LeafCounts la; - LeafCounts lb; - - if (build_deps_.op != deps_a.op){ - la.counts.push_back(std::make_pair(*g_a, 1)); - }else{ - if (deps_a.leaf_counts){ - la = *deps_a.leaf_counts; - } - } - - if (build_deps_.op != deps_b.op){ - lb.counts.push_back(std::make_pair(*g_b, 1)); - }else{ - if (deps_b.leaf_counts){ - lb = *deps_b.leaf_counts; - } - } - if (la.counts.size() && lb.counts.size()){ - build_deps_.leaf_counts = LeafCounts::Add(la, lb); - return; - } - } - if (build_deps_.op != deps_a.op && build_deps_.op != deps_b.op){ - build_deps_.leaf_counts = LeafCounts(*g_a, *g_b); - return; - } - } - deps.leaf_counts = std::nullopt; - } - bool CompareDeps(GVN_Deps& a, GVN_Deps& b) - { - if (CanHashAsAssociativeCommutativeOp(a)){ - if (a.op == b.op && a.leaf_counts && b.leaf_counts){ - return a.leaf_counts == b.leaf_counts; - } - } - // Strict structural equality - bool m_lit = a.literal == b.literal && a.has_literal == b.has_literal; - bool m_op = a.op == b.op; - bool m_prods = false; - if (a.producers.size() == b.producers.size()){ - m_prods = true; - for (size_t i = 0; i < a.producers.size(); i++){ - auto ea = ResolveEdge(a.producers[i]); - auto eb = ResolveEdge(b.producers[i]); - if (!ea){m_prods = false;break;} - if (!eb){m_prods = false;break;} - if (*ea != *eb){m_prods = false;break;} - } - } - return m_lit && m_op && m_prods; - } - // GVN values are computed one at the time using - // the builder interface above. - // These fields hold the data required - GVN_Deps build_deps_; - rvsdg::Output* build_output_; - - static bool CanHashAsAssociativeCommutativeOp(const GVN_Deps& deps) - { - bool hash_as_ca = false; - MatchType(*(deps.op), [&hash_as_ca](const rvsdg::BinaryOperation& bin_op){ - hash_as_ca = bin_op.is_associative() && bin_op.is_commutative(); - }); - return hash_as_ca; - } - - std::optional HashDeps(GVN_Deps& deps) - { - GVN_Val h = op_to_gvn_[deps.op] ^ deps.literal; - if (!deps.has_literal && !deps.producers.size()){ - throw std::runtime_error("Logic error: missing data sources for hashing."); - } - if (CanHashAsAssociativeCommutativeOp(deps)){ - for (size_t i = 0 ; i < deps.producers.size() ; i++){ - auto g = ResolveEdge(deps.producers[i]); - if (!g){ return std::nullopt; } - size_t a = static_cast(*g); - h ^= a; // independent of order - } - return std::optional(h); - } - - // Default hashing, in order with each edge treated differently based on order - for (size_t i = 0 ; i < deps.producers.size() ; i++){ - auto g = ResolveEdge(deps.producers[i]); - if (!g){ return std::nullopt; } - size_t a = static_cast(*g); - h ^= a * (i+1) + a; // dependent on order - } - - return std::optional(h); - } - - std::optional ResolveEdge(rvsdg::Output* producer){ - if (!producer){return std::nullopt;} - //trace the edge all the way back to the top, crossing into lambdas(context vars), gammas and theta nodes - //might be easier traverse the rvsdg upwards instead - while (traceback_flows_.find(producer) != traceback_flows_.end()){ - producer = traceback_flows_[producer].first; - } - if (edges_to_gvn_.find(producer) == edges_to_gvn_.end()){return std::nullopt;} - return edges_to_gvn_[producer]; - } - - void CheckLitEmpty() const {if (build_deps_.has_literal){throw std::runtime_error("Maximum one literal supported per Output*");}} - void CheckDepsEmpty() const {if (build_deps_.op){throw std::runtime_error("Previous GVN value not flushed.");}} -}; - - -}; - namespace jlm::llvm{ -struct GVN_Hash -{ -#define GVN_LV_BIT 0x1000 - size_t value; - inline GVN_Hash(){this->value = 0;} - inline GVN_Hash(size_t v){this->value = v &(~GVN_LV_BIT);} - inline static GVN_Hash LoopVar(size_t v){auto h = GVN_Hash(v); h.value |= GVN_LV_BIT; - std::cout << "LP" << h.value << " !! " << h.IsLoopVar() << std::endl; - return h; - } - inline bool IsLoopVar() const {return value & GVN_LV_BIT;} - inline static GVN_Hash None() {return GVN_Hash(0);} - inline static GVN_Hash Tainted(){return GVN_Hash(1);} - inline bool IsValid(){return value >= 2;} - inline bool IsSome(){return value != 0;} - inline bool operator==(const GVN_Hash &other){return this->value == other.value;} - inline bool operator!=(const GVN_Hash &other){return this->value != other.value;} - #undef GVN_LV_BIT -}; - -/** Boiler plate for making the struct compatible with std::unordered_map **/ -struct GVN_Map_Hash{ - size_t operator()(const GVN_Hash& v) const{return v.value;} -}; - -struct GVN_Map_Eq -{ - bool operator()(const GVN_Hash& a, const GVN_Hash& b) const{return a.value == b.value;} -}; /** \brief Partial Redundancy Elimination * @@ -699,106 +57,15 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation Run(jlm::rvsdg::RvsdgModule & module, jlm::util::StatisticsCollector & statisticsCollector) override; private: - /** \brief A mapping from Input and Output to gvn hashes **/ - std::unordered_map gvn_hashes_; - - /* Debug data */ - std::unordered_map dbg_hash_counts_; - - GVN_Manager gvn_man_; void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); - void GVN_Compute(rvsdg::Region& reg); static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void register_leaf_hash( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void hash_bin( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void hash_gamma( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void hash_node( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void hash_call( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void hash_theta_pre( PartialRedundancyElimination *pe, rvsdg::Node& node); - - //static void hash_theta_post( PartialRedundancyElimination *pe, rvsdg::Node& node); - - /** - * Insert the hash into a map of {hash => count} for debugging purposes. - * \ref dbg_hash_counts_ - */ - - inline void DBG_CountGVN_HashCounts(GVN_Hash h) - { - if (dbg_hash_counts_.find(h) == dbg_hash_counts_.end()){ - dbg_hash_counts_.insert({h, 1}); - }else{ - dbg_hash_counts_[h] = dbg_hash_counts_[h] + 1; - } - } - - inline void AssignGVN(jlm::rvsdg::Output* k, GVN_Hash h) - { - gvn_hashes_.insert({k, h}); - DBG_CountGVN_HashCounts(h); - } - inline void AssignGVN(jlm::rvsdg::Input* k, GVN_Hash h) - { - gvn_hashes_.insert({k, h}); - DBG_CountGVN_HashCounts(h); - } - - /** - * Convenience method for annotating outputs such as function arguments with hashes. - * - * @param out output to annotate with gvn value - * @param base a string to base the hash on - * @param index an index which is hashed together with the string hash - */ - inline void AssignGVN(jlm::rvsdg::Output* out, std::string base, int index) - { - const std::hash hasher; - - size_t h = hasher(base); - h ^= index; - gvn_hashes_.insert({out, GVN_Hash(h)}); - DBG_CountGVN_HashCounts(h); - } - - /** Safely returns a valid hash value or None **/ - GVN_Hash GetHash(void* input_or_output) - { - if (gvn_hashes_.find(input_or_output) != gvn_hashes_.end()){return gvn_hashes_[input_or_output];} - return GVN_Hash::None(); - } - - inline size_t DBG_HashCount(GVN_Hash h) - { - if (dbg_hash_counts_.find(h) != dbg_hash_counts_.end()) - { - return dbg_hash_counts_[h]; - }else - { - return 0; - } - } }; } -namespace std -{ - inline std::string to_string(jlm::llvm::GVN_Hash h) - { - switch (h.value) - { - case 0: return std::string("none"); - case 1: return std::string("tainted"); - default:{ - if (h.IsLoopVar()){ return std::string("lv") + std::to_string(h.value); } - return std::to_string(h.value); - } - } - } -} #endif From 7ed6f797549e52d73df99a88dcdbb042af9c53b6 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Mon, 27 Oct 2025 13:59:14 +0100 Subject: [PATCH 30/52] Clean build --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 57 +++++++++++++++---- jlm/llvm/opt/PartialRedundancyElimination.hpp | 14 ++++- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index cf129dceb..dfdeea15f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -167,14 +167,19 @@ class PartialRedundancyElimination::Statistics final : public util::Statistics PartialRedundancyElimination::~PartialRedundancyElimination() noexcept {} -PartialRedundancyElimination::PartialRedundancyElimination(): jlm::rvsdg::Transformation("PartialRedundancyElimination"){} +PartialRedundancyElimination::PartialRedundancyElimination() : + jlm::rvsdg::Transformation("PartialRedundancyElimination"), + stat_theta_count(0), + stat_gamma_count(0), + stat_interned_literals_count(0) + {} -void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)) +void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)) { IndentMan indenter = IndentMan(); for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)) { - cb(this, *node); + cb(this, node); MatchType(*node, [this,cb](rvsdg::StructuralNode& sn) { for (auto& reg : sn.Subregions()) @@ -196,20 +201,26 @@ PartialRedundancyElimination::Run( auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_region); - this->TraverseTopDownRecursively(rvsdg.GetRootRegion(), PartialRedundancyElimination::dump_node); + auto& root = rvsdg.GetRootRegion(); + this->TraverseTopDownRecursively(root, initialize_interned_and_stats); + this->TraverseTopDownRecursively(root, dump_region); + this->TraverseTopDownRecursively(root, dump_node); - std::cout << TR_GREEN << "=================================================" << TR_RESET << std::endl; + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; + std::cout <DebugString() + std::to_string(node->GetNodeId()); size_t reg_counter = 0; - MatchType(node, [&name, ®_counter](rvsdg::StructuralNode& sn) + MatchType(*node, [&name, ®_counter](rvsdg::StructuralNode& sn) { for (auto& reg : sn.Subregions()) { @@ -228,9 +239,33 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, }); } -void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node& node) +void PartialRedundancyElimination::initialize_interned_and_stats(PartialRedundancyElimination *pe, rvsdg::Node* node) +{ + MatchType(*node, [&pe, &node](rvsdg::ThetaNode& tn){ + pe->stat_theta_count++; + }); + + MatchType(*node, [&pe, &node](rvsdg::GammaNode& tn){ + pe->stat_gamma_count++; + }); + + MatchType(node->GetOperation(), [&pe, node](const jlm::llvm::IntegerConstantOperation& iconst) + { + auto s = iconst.Representation().str(); + if (pe->interned_literals_.find(s) == pe->interned_literals_.end()){ + pe->interned_literals_.insert({s, node}); + }else{ + pe->stat_interned_literals_count++; + } + rvsdg::Node* exemplar = pe->interned_literals_[s]; + pe->literals_nodes_.insert({node, exemplar}); + + }); +} + +void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node* node) { - std::cout << ind() << TR_BLUE << node.DebugString() << "<"<"<< TR_RESET; + std::cout << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; std::cout << std::endl; } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 2854b941f..f3c37685e 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -56,13 +56,21 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void Run(jlm::rvsdg::RvsdgModule & module, jlm::util::StatisticsCollector & statisticsCollector) override; + + private: - void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node& node)); + void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)); - static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node& node); - static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node& node); + static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void initialize_interned_and_stats(PartialRedundancyElimination *pe, rvsdg::Node* node); + size_t stat_theta_count; + size_t stat_gamma_count; + size_t stat_interned_literals_count; + std::unordered_map< std::string, rvsdg::Node* > interned_literals_; + std::unordered_map< rvsdg::Node*, rvsdg::Node* > literals_nodes_; }; } From b4f4bf29f5f0f230b8481897e68a0b3906bcd601 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Mon, 27 Oct 2025 17:45:43 +0100 Subject: [PATCH 31/52] Integrated new gvn manager --- jlm/llvm/Makefile.sub | 2 + jlm/llvm/opt/PartialRedundancyElimination.cpp | 126 +++- jlm/llvm/opt/PartialRedundancyElimination.hpp | 40 +- jlm/llvm/opt/gvn.cpp | 301 +++++++++ jlm/llvm/opt/gvn.hpp | 583 ++++++++++++++++++ 5 files changed, 1033 insertions(+), 19 deletions(-) create mode 100644 jlm/llvm/opt/gvn.cpp create mode 100644 jlm/llvm/opt/gvn.hpp diff --git a/jlm/llvm/Makefile.sub b/jlm/llvm/Makefile.sub index 8592bb720..addb06da8 100644 --- a/jlm/llvm/Makefile.sub +++ b/jlm/llvm/Makefile.sub @@ -63,6 +63,7 @@ libllvm_SOURCES = \ jlm/llvm/opt/cne.cpp \ jlm/llvm/opt/DeadNodeElimination.cpp \ jlm/llvm/opt/PartialRedundancyElimination.cpp \ + jlm/llvm/opt/gvn.cpp \ jlm/llvm/opt/IfConversion.cpp \ jlm/llvm/opt/inlining.cpp \ jlm/llvm/opt/InvariantValueRedirection.cpp \ @@ -84,6 +85,7 @@ libllvm_HEADERS = \ jlm/llvm/opt/unroll.hpp \ jlm/llvm/opt/DeadNodeElimination.hpp \ jlm/llvm/opt/PartialRedundancyElimination.hpp \ + jlm/llvm/opt/gvn.hpp \ jlm/llvm/opt/inlining.hpp \ jlm/llvm/opt/cne.hpp \ jlm/llvm/opt/push.hpp \ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index dfdeea15f..9567b3cc9 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -51,6 +51,9 @@ #include #include +#include "../../rvsdg/binary.hpp" +#include "../../util/common.hpp" +#include "gvn.hpp" #include #include @@ -170,9 +173,10 @@ PartialRedundancyElimination::~PartialRedundancyElimination() noexcept {} PartialRedundancyElimination::PartialRedundancyElimination() : jlm::rvsdg::Transformation("PartialRedundancyElimination"), stat_theta_count(0), - stat_gamma_count(0), - stat_interned_literals_count(0) - {} + stat_gamma_count(0) +{ + g_alternatives = gvn_.Leaf(rvsdg::gvn::GVN_OP_IS_SWITCH); +} void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)) { @@ -202,15 +206,25 @@ PartialRedundancyElimination::Run( auto statistics = Statistics::Create(module.SourceFilePath().value()); auto& root = rvsdg.GetRootRegion(); - this->TraverseTopDownRecursively(root, initialize_interned_and_stats); + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(root, dump_node); + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; + this->TraverseTopDownRecursively(root, initialize_stats); + this->TraverseTopDownRecursively(root, gvn_lambda_and_consts); + this->TraverseTopDownRecursively(root, gvn_compute); + this->TraverseTopDownRecursively(root, dump_region); this->TraverseTopDownRecursively(root, dump_node); std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; - std::cout <stat_theta_count++; @@ -248,24 +262,110 @@ void PartialRedundancyElimination::initialize_interned_and_stats(PartialRedundan MatchType(*node, [&pe, &node](rvsdg::GammaNode& tn){ pe->stat_gamma_count++; }); +} +void PartialRedundancyElimination::gvn_lambda_and_consts(PartialRedundancyElimination *pe, rvsdg::Node* node) +{ MatchType(node->GetOperation(), [&pe, node](const jlm::llvm::IntegerConstantOperation& iconst) { - auto s = iconst.Representation().str(); - if (pe->interned_literals_.find(s) == pe->interned_literals_.end()){ - pe->interned_literals_.insert({s, node}); - }else{ - pe->stat_interned_literals_count++; + JLM_ASSERT(node->noutputs() == 1); + pe->RegisterGVN(node->output(0), pe->gvn_.FromStr(iconst.Representation().str())); + }); + + MatchType(*node , [pe, node](jlm::rvsdg::LambdaNode& ln){ + for (auto arg : ln.GetFunctionArguments()){ + pe->RegisterGVN(arg, pe->gvn_.Leaf()); + } + for (auto arg : ln.GetContextVars()) + { + auto from = arg.input->origin(); + pe->RegisterGVN(arg.inner, pe->GVNOrFail(from, node)); } - rvsdg::Node* exemplar = pe->interned_literals_[s]; - pe->literals_nodes_.insert({node, exemplar}); }); } +void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, rvsdg::Node* node) +{ + MatchType(node->GetOperation(),[pe, node](const jlm::llvm::ConstantDataArray& data_array){ + auto op_data_array = pe->gvn_.FromStr("constant_data_array"); + pe->gvn_.Op(op_data_array); + for (size_t i = 0 ; i < node->noutputs() ; i++){ + pe->gvn_.Arg( pe->GVNOrFail(node->input(i)->origin(), node) ); + } + auto g = pe->gvn_.End(); + JLM_ASSERT(node->noutputs() == 1); + pe->RegisterGVN(node->output(0), g); + }); + MatchType(*node, [](rvsdg::ThetaNode& tn) + { + throw std::runtime_error("Not implemented"); + }); + + MatchType(node->GetOperation(), [pe, node](const jlm::llvm::CallOperation& call_op){ + auto op_call = pe->gvn_.FromStr("hash_call"); + auto hash_call_out = pe->gvn_.FromStr("hash_call_out"); + pe->gvn_.Op(op_call); + for (size_t i = 0 ; i < node->ninputs() ; i++){ + pe->gvn_.Arg( pe->GVNOrFail(node->input(i)->origin(), node) ); + } + auto hash_all_inputs = pe->gvn_.End(); + for (size_t i = 0 ; i < node->noutputs() ; i++){ + auto si = pe->gvn_.FromStr( std::to_string(i) ); + auto g_out = pe->gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(si).End(); + pe->RegisterGVN( node->output(i), g_out); + } + }); + + MatchType(*node, [pe, node](rvsdg::GammaNode& tn) + { + for (auto br : tn.GetEntryVars()){ + auto out = br.input->origin(); + for (auto into_branch : br.branchArgument){ + pe->RegisterGVN(into_branch, pe->GVNOrFail(out, node)); + } + + for () + } + }); + + MatchType(node->GetOperation(), [pe, node](const jlm::rvsdg::BinaryOperation& binop){ + if (binop.is_associative() && binop.is_commutative()){ + JLM_ASSERT(node->ninputs() >= 2); + auto op = pe->gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OP_IS_CA); + auto a = pe->GVNOrFail( node->input(0)->origin(), node); + auto b = pe->GVNOrFail( node->input(1)->origin(), node); + pe->RegisterGVN(node->output(0), pe->gvn_.Op(op).Arg(a).Arg(b).End()); + } + }); + +} + void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node* node) { std::cout << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; + + MatchType(*node, [&pe, &node](rvsdg::LambdaNode& ln){ + std::cout <GVNOrZero(arg); + } + std::cout << TR_RESET; + }); + + MatchType(node->GetOperation(), [&pe, node](const jlm::llvm::IntegerConstantOperation& iconst) + { + JLM_ASSERT(node->noutputs() == 1); + std::cout << TR_CYAN; + std::cout << pe->GVNOrZero( node->output(0) ); + std::cout << TR_RESET; + }); + + for (size_t i = 0; i < node->noutputs(); i++){ + std::cout << TR_GREEN; + std::cout << " : " << pe->GVNOrZero( node->output(i) ); + } + std::cout << std::endl; } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index f3c37685e..d32c9ff4a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -6,6 +6,7 @@ #ifndef JLM_LLVM_OPT_PartialRedundancyElimination_HPP #define JLM_LLVM_OPT_PartialRedundancyElimination_HPP +#include "gvn.hpp" #include "jlm/llvm/ir/operators/IntegerOperations.hpp" #include "jlm/rvsdg/MatchType.hpp" #include "jlm/rvsdg/traverser.hpp" @@ -62,15 +63,42 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)); - static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); - static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node* node); - static void initialize_interned_and_stats(PartialRedundancyElimination *pe, rvsdg::Node* node); + static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void initialize_stats( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void gvn_lambda_and_consts(PartialRedundancyElimination *pe, rvsdg::Node* node); + static void gvn_compute( PartialRedundancyElimination *pe, rvsdg::Node* node); size_t stat_theta_count; size_t stat_gamma_count; - size_t stat_interned_literals_count; - std::unordered_map< std::string, rvsdg::Node* > interned_literals_; - std::unordered_map< rvsdg::Node*, rvsdg::Node* > literals_nodes_; + + jlm::rvsdg::gvn::GVN_Val g_alternatives; + + + + std::unordered_map< rvsdg::Output *, rvsdg::gvn::GVN_Val> output_to_gvn_; + void RegisterGVN(rvsdg::Output * output, rvsdg::gvn::GVN_Val gvn){ + output_to_gvn_[output] = gvn; + } + + rvsdg::gvn::GVN_Manager gvn_; + inline rvsdg::gvn::GVN_Val GVNOrZero(rvsdg::Output* edge){ + if (output_to_gvn_.find(edge) != output_to_gvn_.end()){ + return output_to_gvn_[edge]; + } + return rvsdg::gvn::GVN_NULL; + } + + inline rvsdg::gvn::GVN_Val GVNOrFail(rvsdg::Output* edge, rvsdg::Node* ctx_node){ + if (output_to_gvn_.find(edge) != output_to_gvn_.end()){ + return output_to_gvn_[edge]; + } + + std::cout << "Logic error: missing input for edge" + ctx_node->DebugString() + std::to_string(ctx_node->GetNodeId()); + + return rvsdg::gvn::GVN_NULL; + } + }; } diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp new file mode 100644 index 000000000..f30a60262 --- /dev/null +++ b/jlm/llvm/opt/gvn.cpp @@ -0,0 +1,301 @@ +// +// Created by lars-astrup-sundt on 10/27/25. +// + +#include "gvn.hpp" + +// +// Created by lars-astrup-sundt on 10/26/25. +// + +bool jlm::rvsdg::gvn::gvn_verbose = false; +namespace jlm::rvsdg::gvn { + + + void GVN_Manager::Test0() { + GVN_Manager gm; + auto my_op = gm.Leaf(); + auto my_ca = gm.Leaf(GVN_OP_IS_CA); + + auto x = gm.Leaf(); + auto y = gm.Leaf(); + + if (x == y) { + throw std::runtime_error("Expected unique hashes"); + } + if (gvn_verbose) { + std::cout << "X: " << to_string(x) << std::endl; + std::cout << "Y: " << to_string(y) << std::endl; + + std::cout << "MyOp: " << to_string(my_op) << std::endl; + std::cout << "MyCa: " << to_string(my_ca) << std::endl; + } + { + GVN_Deps d; + d.op = my_op; + d.push(x); + d.push(y); + if (gvn_verbose){std::cout << "Hash of x - y is : " << to_string(gm.CalculateHash(d)) << std::endl;} + } + { + GVN_Deps d2; + d2.op = my_op; + d2.push(y); + d2.push(x); + if (gvn_verbose){std::cout << "Hash of y - x is : " << to_string(gm.CalculateHash(d2)) << std::endl;} + } + { + GVN_Deps d3; + d3.op = my_ca; + d3.push(y); + d3.push(x); + gm.gvn_.insert({gm.CalculateHash(d3), d3}); + + GVN_Deps d4; + d4.op = my_ca; + d4.push(y); + d4.push(gm.CalculateHash(d3)); + + GVN_Deps d5; + d5.op = my_ca; + d5.push(gm.CalculateHash(d3)); + d5.push(y); + + if (gm.CalculateHash(d4) != gm.CalculateHash(d5)) { + throw std::runtime_error("Test failed: expected equal hashes from associative and commutative op"); + } + if (gvn_verbose) { + std::cout << "y+x " << to_string(gm.CalculateHash(d3)) << std::endl; + std::cout << "y + (y+x): " << to_string(gm.CalculateHash(d4)) << std::endl; + std::cout << "(y+x) + y: " << to_string(gm.CalculateHash(d5)) << std::endl; + } + } + } + void GVN_Manager::Test1() + { + GVN_Manager gm; + const char* str = "foo"; + GVN_Val a = gm.FromPtr(str); + GVN_Val b = gm.FromPtr(str); + if (a != b) { + throw std::runtime_error("FromPtr failed"); + } + } + void GVN_Manager::Test2() + { + GVN_Manager gm; + GVN_Val x = gm.Leaf(); + GVN_Val y = gm.Leaf(); + + GVN_Val switchy_op = gm.FromPtr("switch", GVN_OP_IS_SWITCH); + + GVN_Val xx = gm.Op(switchy_op).Args({x,x}); + std::cout << "*************************"<< std::endl << std::endl; + GVN_Val xy = gm.Op(switchy_op).Args({x,y}); + + std::cout << "*************************"<< std::endl << std::endl; + GVN_Val xy_2 = gm.Op(switchy_op).Args({x,y}); + + if (xy != xy_2) { + std::cout << to_string(xy) << to_string(xy_2) << std::endl; + throw std::runtime_error("Values should be stable"); + } + + if (xy == xx) {throw std::runtime_error("Values should be different");} + if (xx != x) {throw std::runtime_error("Values should be same");} + } + + void GVN_Manager::Test3() + { + GVN_Manager gm; + auto a = gm.Leaf(); + auto b = gm.Leaf(); + auto c = gm.Leaf(); + auto d = c; + + auto op_add = gm.FromStr("+", GVN_OP_IS_CA); + auto op_alternatives = gm.FromStr("merge", GVN_OP_IS_SWITCH); + auto one = gm.FromStr("1"); + + auto reassign = [&gm, op_alternatives](BrittlePrismEle& ele) { + ele.partition = gm.Op(op_alternatives).Args({ele.disruptor, ele.partition}); + }; + + // loop ( + // a + 1 -> a + // b + a -> b + // c -> d + // d -> c + // ) + + GVN_Val last = 0; + for (size_t k = 0; k < 4; k++) { + std::optional< BrittlePrism > prevars = std::nullopt; + std::optional< BrittlePrism > postvars = std::nullopt; + + GVN_Val a_out = 0; + GVN_Val b_out = 0; + GVN_Val c_out = 0; + GVN_Val d_out = 0; + size_t max_iter = 100; + + std::cout << "INIT: " << a_out << " - " << b_out << " - " << c_out << " - " << d_out << std::endl; + + while (max_iter) { + max_iter--; + if (!prevars) { + prevars = BrittlePrism({a,b,c,d}); + }else { + prevars->elements[0].disruptor = a_out; + prevars->elements[1].disruptor = b_out; + prevars->elements[2].disruptor = c_out; + prevars->elements[3].disruptor = d_out; + + if (! BrittlePrism::Fracture(*prevars,reassign) ){break;} + } + a_out = gm.Op(op_add).Args({a, one}); + b_out = gm.Op(op_add).Args({b,a}); + c_out = d; + d_out = c; + if (!postvars) { + postvars = BrittlePrism( {a_out, b_out, c_out, d_out}); + }else { + postvars->elements[0].disruptor = a_out; + postvars->elements[1].disruptor = b_out; + postvars->elements[2].disruptor = c_out; + postvars->elements[3].disruptor = d_out; + if (! BrittlePrism::Fracture(*postvars,reassign) ){break;} + } + } + + std::cout << "after: " << a_out << " - " << b_out << " - " << c_out << " - " << d_out << std::endl; + + GVN_Val h = gm.Op(op_alternatives).Args({a_out,b_out,c_out,d_out}); + if (last && h != last && !(h & GVN_FROM_COLLISION)) { + std::cout << to_string(last) << " " << to_string(h) << std::endl; + throw std::runtime_error("Hashes where different across iterations"); + } + last = h; + } + } + + void BrittlePrism::Test0() + { + std::vector v = {77,128,128,77,77}; + BrittlePrism p0(v); + p0.dump(); + p0.OrderByPartition(); + if (gvn_verbose){std::cout << "----------partitions-------------"<" << ele.disruptor << std::endl;} + ele.partition = ele.disruptor; + }); + p0.OrderByPartition(); + if (gvn_verbose){std::cout << "----------after frac---------------"<" << ele.disruptor << std::endl;} + ele.partition = ele.disruptor; + }); + }catch (std::runtime_error& e) { + if (gvn_verbose){std::cout << "success : should throw : " << e.what() << std::endl;} + } + + BrittlePrism::EachPartition(p0, [](BrittlePrismEle& ele, size_t count) { + std::cout << ele.partition << " : " << count << std::endl; + }); + + + BrittlePrism p2 = p0; + p2 = p0; + p0.elements[0].disruptor = 10; + if (gvn_verbose){std::cout << "=======================================" << std::endl;} + p0.dump(); + if (gvn_verbose){std::cout << "=======================================" << std::endl;} + p2.dump(); + if (gvn_verbose){std::cout << "=======================================" << std::endl;} + + auto shatter_good = [](BrittlePrismEle& ele, size_t index) { + ele.partition = index; + }; + auto shatter_bad = [](BrittlePrismEle& ele, size_t index) { + ele.partition = 88; + }; + Shatter(p0, shatter_good); + try { + Shatter(p2, shatter_bad); + }catch (std::runtime_error& e) { + if (gvn_verbose){std::cout << "success : should throw : detected bad shatter : " << e.what() << std::endl;} + } + } + + void BrittlePrism::Test1() + { + auto br = BrittlePrism({1,1,1,4}); + br.elements[0].disruptor = 10; + br.elements[1].disruptor = 10; + br.elements[2].disruptor = 88; + br.elements[3].disruptor = 77; + BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { + ele.partition = ele.disruptor; + }); + br.OrderByOriginal(); + if (gvn_verbose){br.dump();} + if (br.elements[0].partition != 10){throw std::runtime_error("should be 10");} + if (br.elements[1].partition != 10){throw std::runtime_error("should be 10");} + if (br.elements[2].partition != 88){throw std::runtime_error("should be 88");} + if (br.elements[3].partition != 77){throw std::runtime_error("should be 77");} + //////////////////////////////////////////////////////////// + br.elements[0].disruptor = 100; + br.elements[1].disruptor = 100; + br.elements[2].disruptor = 888; + br.elements[3].disruptor = 123; + BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { + ele.partition = ele.disruptor; + }); + br.OrderByOriginal(); + if (gvn_verbose){br.dump();} + if (br.elements[0].partition != 10){throw std::runtime_error("should still be 10");} + if (br.elements[1].partition != 10){throw std::runtime_error("should still be 10");} + if (br.elements[2].partition != 88){throw std::runtime_error("should be 88");} + if (br.elements[3].partition != 77){throw std::runtime_error("should be 77");} + + //////////////////////////////////////////////////////////// + br.elements[0].disruptor = 1780; + br.elements[1].disruptor = 10; + br.elements[2].disruptor = 81488; + br.elements[3].disruptor = 12153; + BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { + ele.partition = ele.disruptor; + }); + br.OrderByOriginal(); + if (gvn_verbose){br.dump();} + if (br.elements[0].partition != 1780){throw std::runtime_error("should be 1780");} + if (br.elements[1].partition != 10){throw std::runtime_error("should still be 10");} + if (br.elements[2].partition != 88){throw std::runtime_error("should still be 88");} + if (br.elements[3].partition != 77){throw std::runtime_error("should still be 77");} + } +} + diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp new file mode 100644 index 000000000..88aafdb46 --- /dev/null +++ b/jlm/llvm/opt/gvn.hpp @@ -0,0 +1,583 @@ +// +// Created by lars-astrup-sundt on 10/27/25. +// + +#ifndef JLM_GVN_H +#define JLM_GVN_H + + +#ifndef GVN_H +#define GVN_H + +#include +#include +#include +#include +#include +#include +#include + +#include + + + +namespace jlm::rvsdg::gvn { + extern bool gvn_verbose; + typedef uint64_t GVN_Val; + + constexpr GVN_Val GVN_NULL = 0; + constexpr GVN_Val GVN_HAS_DEPS = 0x1; /* \brief : For making lambda values global */ + constexpr GVN_Val GVN_IS_LOCAL_VALUE = 0x2; /* \brief : This value depends on placeholders */ + constexpr GVN_Val GVN_FROM_COLLISION = 0x4; /* \brief : This gvn depends on value created by collision resolution */ + constexpr GVN_Val GVN_OP_IS_CA = 0x8; /* \brief : This gvn value represent a commutative and associative operation */ + constexpr GVN_Val GVN_OP_ADDS_PARAM_DEPENDENCE = 0x10; /* \brief : This operator creates a local value */ + constexpr GVN_Val GVN_OP_REMOVES_PARAM_DEPENDENCE = 0x20; /* \brief : For making lambda values global */ + constexpr GVN_Val GVN_OP_IS_SWITCH = 0x40; + + constexpr GVN_Val GVN_MASK_INHERIT = GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION; + constexpr GVN_Val GVN_MASK = GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION | GVN_OP_IS_CA | GVN_OP_ADDS_PARAM_DEPENDENCE | GVN_OP_REMOVES_PARAM_DEPENDENCE | GVN_HAS_DEPS | GVN_OP_IS_SWITCH; + + inline bool GVN_ValueIsGlobal(GVN_Val v) {return v & GVN_IS_LOCAL_VALUE;} + inline bool GVN_ValueIsTainted(GVN_Val v) {return v & GVN_FROM_COLLISION;} + inline bool GVN_ValueIsCA_Op(GVN_Val v) {return v & GVN_OP_IS_CA;} + inline bool GVN_ValueIsSwitch_Op(GVN_Val v) {return v & GVN_OP_IS_SWITCH;} + inline bool GVN_ValueParamDependent(GVN_Val v) {return v & GVN_OP_ADDS_PARAM_DEPENDENCE;} + inline bool GVN_ValueIsParamIndependent(GVN_Val v) {return v & GVN_OP_REMOVES_PARAM_DEPENDENCE;} + inline bool GVN_ValueHasDeps(GVN_Val v) {return v & GVN_HAS_DEPS;} + + inline std::string to_string(GVN_Val v) { + auto n = static_cast(v); + std::string s = "" + std::to_string(n); + if ((v & GVN_HAS_DEPS) == 0) {s += "";} + if (v & GVN_OP_IS_CA) {s+="";} + if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += ""; return s;} + if ((v & GVN_OP_ADDS_PARAM_DEPENDENCE)){s += "";} + if ((v & GVN_OP_REMOVES_PARAM_DEPENDENCE)){s += "";} + if (v & GVN_FROM_COLLISION) {s += ""; return s;} + return s; + } + + struct BrittlePrismEle { + GVN_Val partition; + GVN_Val disruptor; + GVN_Val original_partition; + size_t original_position; + }; + + constexpr const char* ORD_ORIGINAL = "order:original"; + constexpr const char* ORD_PARTITION = "order:partition"; + constexpr const char* ORD_PARTITION_DISRUPTOR = "order:partition>disruptor"; + constexpr const char* ORD_DISRUPTOR_PARTITION = "order:disruptor>partition"; + + class BrittlePrism { + private: + const char* current_ordering; + + public: + bool did_shatter; + size_t fracture_count; + std::vector elements; + + /// --------------------------- ORDERINGS ----------------------------------------------------------------- + explicit BrittlePrism(std::vector base) : did_shatter(false), fracture_count(0) + { + // set gvn values and original indices + // partition same as unique values + for (size_t i = 0; i < base.size(); i++) { + elements.emplace_back(BrittlePrismEle{base[i], base[i], base[i],i}); + } + current_ordering = ORD_ORIGINAL; + } + + void OrderByPartition() { + std::sort(elements.begin(), elements.end(), + [](BrittlePrismEle& a, BrittlePrismEle& b) { + return a.partition < b.partition; + } + ); + current_ordering = ORD_PARTITION; + } + + void OrderByOriginal() + { + std::sort(elements.begin(), elements.end(), + [](BrittlePrismEle& a, BrittlePrismEle& b) { + return a.original_position < b.original_position; + } + ); + current_ordering = ORD_ORIGINAL; + } + void OrderByPartitionThenDisruptor() + { + std::sort(elements.begin(), elements.end(), + [](BrittlePrismEle& a, BrittlePrismEle& b) { + if (a.partition == b.partition){return a.disruptor < b.disruptor;} + return a.partition < b.partition; + } + ); + current_ordering = ORD_PARTITION_DISRUPTOR; + } + void OrderByDisruptorThenPartition() + { + std::sort(elements.begin(), elements.end(), + [](BrittlePrismEle& a, BrittlePrismEle& b) { + if (a.disruptor == b.disruptor){return a.partition < b.partition;} + return a.disruptor < b.disruptor; + } + ); + current_ordering = ORD_DISRUPTOR_PARTITION; + } + /// --------------------------- SPANS ----------------------------------------------------------------- + size_t SpanPartition(size_t start) + { + size_t span = 1; + GVN_Val partition = elements[start].partition; + for (size_t i = start+1; i < elements.size(); i++) { + if (elements[i].partition == partition) {span++;}else{break;} + } + return span; + } + + size_t SpanDisruptor(size_t start) const + { + size_t span = 1; + GVN_Val disruptor = elements[start].disruptor; + for (size_t i = start+1; i < elements.size(); i++) { + if (elements[i].disruptor == disruptor) {span++;}else{break;} + } + return span; + } + + size_t SpanDisruptorAndPartition(size_t start) + { + size_t span = 1; + GVN_Val partition = elements[start].partition; + GVN_Val disruptor = elements[start].disruptor; + for (size_t i = start+1; i < elements.size(); i++) { + auto e = elements[i]; + if (e.partition == partition && e.disruptor == disruptor) {span++;}else{break;} + } + return span; + } + + /// --------------------------- INSPECT ----------------------------------------------------------------- + + void dump() const + { + std::cout << "gvn_partition -- " << current_ordering << std::endl; + + for (size_t i = 0; i < elements.size(); i++) { + auto e = elements[i]; + std::cout << "["< + static void EachPartition(BrittlePrism& p, Fn cb) + { + p.OrderByPartition(); + size_t it = 0; + while (it < p.elements.size()) { + size_t p_span = p.SpanPartition(it); + cb(p.elements[it], p_span); + it += p_span; + } + } + + template + static void Shatter(BrittlePrism& p, Fn reassign) { + if (p.did_shatter){return;} + // Call this when a loop body has been invoked too many times. + // This should assign unique partitions to all elements + // Provable loop invariant variables can be set to GVN_NULL + p.OrderByOriginal(); + for (size_t i = 0; i < p.elements.size(); i++) { + reassign(p.elements[i], i); + } + p.OrderByPartition(); + for (size_t i = 1; i < p.elements.size(); i++) { + if (p.elements[i].partition && (p.elements[i].partition == p.elements[i-1].partition) ) { + throw std::runtime_error("Shatter failed. All partitions must be unique."); + } + } + p.did_shatter = true; + } + + template + static bool Fracture(BrittlePrism& p, Fn reassign) + { + // Reassign take a single (BrittlePrismEle& ele) as argument + // reassign is called at most once per offending element + // reassign is NEVER called when the disruptor equals the current partition + bool did_fracture = false; + p.CheckDisruptorInvariants(); + p.OrderByPartitionThenDisruptor(); + + size_t it = 0; + while (it < p.elements.size()) { + size_t pspan = p.SpanPartition(it); + size_t gspan = p.SpanDisruptorAndPartition(it); + size_t p_end = it + pspan; + + if (pspan == gspan) { + // Check if value changes for first time + while (it < p_end) { + BrittlePrismEle& e = p.elements[it]; + if (e.original_partition == e.partition && e.disruptor != e.original_partition) { + reassign(e); + did_fracture = true; + } + it++; + } + }else { + // Partition was given multiple different disruptors + did_fracture = true; + while (it < p_end) { + BrittlePrismEle& e = p.elements[it]; + if (e.partition != e.disruptor) { + reassign(e); + } + it++; + } + } + + } + if (did_fracture) { + p.fracture_count++; + if (p.fracture_count > p.elements.size() * 2) { + throw std::runtime_error("Brittle prism invariant broken. Possible missing updates to by reassign callback."); + } + } + return did_fracture; + } + static void Test0(); + static void Test1(); + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////// + + class GVN_Manager{ + + struct GVN_Deps { + GVN_Val op; + std::vector > args; + void push(GVN_Val v, size_t count){args.emplace_back(v, count);} + void push(GVN_Val v) {args.emplace_back(v, 1);} + + void dump() { + std::cout << "op: " << op << std::endl; + for (size_t i = 0; i < args.size(); ++i) { + std::cout << " [" < builder_args_; + std::optional builder_op_; + std::optional builder_flags_; + + std::unordered_map< std::string, GVN_Val > str_to_gvn_; // Convenience map + std::unordered_map< const void*, GVN_Val > ptr_to_gvn_; // Convenience map + std::unordered_map< GVN_Val, std::optional > gvn_; + + public: + size_t stat_collisions; + size_t stat_leaf_collisions; + size_t stat_ca_too_big; + + size_t max_ca_size; + + GVN_Manager() : stat_collisions(0), stat_leaf_collisions(0), stat_ca_too_big(0), max_ca_size(32) { + gvn_.insert({0, std::nullopt}); + } + + GVN_Val Leaf() { + return Leaf(0); + } + GVN_Val Leaf(GVN_Val flags) { + auto g = FindUnique(flags); + gvn_.insert({g,std::nullopt}); + return g; + } + GVN_Manager& Op(GVN_Val op) { + if (builder_op_){throw std::runtime_error("Multiple calls to Op(...) or missing End()");} + builder_op_ = op; + builder_flags_ = 0; + return *this; + } + GVN_Manager& Arg(GVN_Val arg) { + builder_args_.emplace_back(arg); + return *this; + } + + GVN_Val Args(const std::vector& args) { + if (args.empty()){throw std::runtime_error("Args cannot be empty. Make empty argument lists explicit by passing a dummy leaf.");} + builder_args_ = args; + return End(); + } + + GVN_Val End() { + if (!builder_op_) {throw std::runtime_error("Operator not specified");} + if (builder_args_.empty()){throw std::runtime_error("Args not specified");} + GVN_Val g = Create(*builder_op_, builder_args_); + builder_flags_ = std::nullopt; + builder_args_.clear(); + builder_op_ = std::nullopt; + return g; + } + GVN_Val FromStr(const std::string& str) {return FromStr(str, 0);} + GVN_Val FromStr(const std::string s, uint64_t flags) + { + auto q = str_to_gvn_.find(s); + if (q == str_to_gvn_.end()) { + str_to_gvn_.insert({s,Leaf(flags)}); + q = str_to_gvn_.find(s); + } + if (q == str_to_gvn_.end()) {throw std::runtime_error("Expected some value");} + if ((q->second & GVN_MASK) != flags) { + throw std::runtime_error("Inconsistent flags for literal: " + s); + } + return str_to_gvn_[s]; + } + GVN_Val FromPtr(const void* p) {return FromPtr(p, 0);} + GVN_Val FromPtr(const void* p, uint64_t flags) + { + auto q = ptr_to_gvn_.find(p); + if (q == ptr_to_gvn_.end()) { + ptr_to_gvn_.insert({p,Leaf(flags)}); + q = ptr_to_gvn_.find(p); + } + if (q == ptr_to_gvn_.end()) {throw std::runtime_error("Expected some value");} + if ((q->second & GVN_MASK) != flags) { + throw std::runtime_error("Inconsistent flags for gvn generated from pointer: " + std::to_string(reinterpret_cast(p))); + } + return ptr_to_gvn_[p]; + } + + GVN_Val Prism(GVN_Val op, BrittlePrism& brittle) { + // It is sufficient to create a prism from the post array after taking the union + // with the pre array + Op(op); + brittle.OrderByPartition(); + size_t i = 0; + while (i < brittle.elements.size()) { + Arg(brittle.elements[i].partition); + i += brittle.SpanPartition(i); + } + return End(); + } + + private: + + GVN_Val Create(GVN_Val op, const std::vector& args) { + if (args.empty()){throw std::runtime_error("Logic error: GVN operator applied to zero args.");} + std::optional new_gvn = GVN_Deps(); + new_gvn->op = op; + // Initialize new_gvn.args + // Either a copy of args or count of operator cluster leaves + if (GVN_ValueIsCA_Op(new_gvn->op)) { + std::vector > acc; + for (auto arg : args) { + if (GVN_ValueHasDeps(arg)) { + auto ad = *(gvn_[arg]); + if (ad.op == new_gvn->op) { + for (auto leaf : ad.args) {acc.emplace_back(leaf);} + }else { + acc.emplace_back(arg, 1); + } + }else { + acc.emplace_back(arg, 1); + } + } + std::sort(acc.begin(), acc.end()); + if (!acc.empty()) { + GVN_Val last = GVN_NULL; + size_t acc_at = -1; + for (auto ele : acc) { + if (ele.first != last) { + acc_at = new_gvn->args.size(); + new_gvn->args.emplace_back(ele.first, 0); + } + new_gvn->args[acc_at].second += ele.second; + last = ele.first; + } + } + }else { + for (auto a : args) {new_gvn->push(a);} + } + // Handle the case where all args are the same + // such as in cond ? foo : foo + // foo + if (op & GVN_OP_IS_SWITCH) { + bool all_same = true; + if (args.size()) { + GVN_Val ele = args[0]; + for (auto a : args) { + if (a != ele) { + all_same=false; + break; + } + } + if (all_same) { + // Do not create a new gvn, use the same as each 'case' + return ele; + } + } + } + + GVN_Val v = CalculateHash(*new_gvn); + + // Check if gvn is already in use, compare with existing gvn if so + if (gvn_.find(v) != gvn_.end()) { + if ( gvn_[v] ) { + auto prev = *(gvn_[v]); + if (!DepsEqual(prev, *new_gvn)) { + // Collision with internal node + v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); + stat_collisions++; + } + }else { + // Collision between internal and leaf node + v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); + stat_leaf_collisions++; + } + } + + if (new_gvn->args.size() > max_ca_size) { + // Avoid excess memory usage by returning a unique gvn + // and discarding all args + v &= ~GVN_HAS_DEPS; + v = FindUnique(v); + new_gvn = std::nullopt; + stat_ca_too_big++; + } + + // ----------------- commit --------------------- + if (gvn_.find(v) == gvn_.end()) { + gvn_.insert({v,new_gvn}); + } + return v; + } + + private: + GVN_Val FindUnique() { + return FindUnique(0); + } + GVN_Val FindUnique(GVN_Val flags) { + GVN_Val g; + do{ + g = random() & ~GVN_MASK; + g |= flags; + }while(gvn_.find(g) != gvn_.end()); + + return g; + } + + GVN_Val CalculateHash(const GVN_Deps& deps) { + // The lower bits are used to store properties for operations and + // keep track of context dependence of values + GVN_Val flags = GVN_HAS_DEPS; + for (auto arg : deps.args) { + flags |= arg.first & GVN_MASK_INHERIT; + } + // Override GVN_IS_LOCAL_VALUE for operators introducing placeholders + if (GVN_ValueParamDependent(deps.op)) {flags |= GVN_IS_LOCAL_VALUE;} + if (GVN_ValueIsParamIndependent(deps.op)) {flags &= GVN_IS_LOCAL_VALUE;} + + GVN_Val v = 0; + if (GVN_ValueIsCA_Op(deps.op)) { + //std::cout << "--------------CA------------------"< Date: Mon, 27 Oct 2025 21:27:25 +0100 Subject: [PATCH 32/52] Changed visitors to methods. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 119 +++++++++++++++++- jlm/llvm/opt/PartialRedundancyElimination.hpp | 7 ++ jlm/llvm/opt/gvn.hpp | 15 +++ 3 files changed, 136 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 9567b3cc9..9b0d7b1fc 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -223,8 +223,10 @@ PartialRedundancyElimination::Run( std::cout <DebugString() << node->GetNodeId() << std::endl; + MatchTypeWithDefault(node->GetOperation(), + [this,node](const jlm::llvm::ConstantDataArray& data_array){ + auto op_data_array = gvn_.FromStr("constant_data_array"); + gvn_.Op(op_data_array); + for (size_t i = 0 ; i < node->noutputs() ; i++){ + gvn_.Arg( GVNOrFail(node->input(i)->origin(), node) ); + } + auto g = gvn_.End(); + JLM_ASSERT(node->noutputs() == 1); + RegisterGVN(node->output(0), g); + }, + // Treat nodes as opaque calls unless overridden. + [this, node](){ + if (node->ninputs() && node->noutputs()){ + auto op_opaque = gvn_.FromPtr(&(node->GetOperation()) ); + auto hash_call_out = gvn_.FromStr("hash_call_out"); + gvn_.Op(op_opaque); + for (size_t i = 0 ; i < node->ninputs() ; i++){ + gvn_.Arg( GVNOrFail(node->input(i)->origin(), node) ); + } + auto hash_all_inputs = gvn_.End(); + for (size_t i = 0 ; i < node->noutputs() ; i++){ + auto arg_pos = gvn_.FromWord(i); + auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); + RegisterGVN( node->output(i), g_out); + } + } + std::cout << ind() << "todo: handle default call" << std::endl; + } + ); +} + +void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) +{ + MatchTypeWithDefault(*node, + [this,node](rvsdg::DeltaNode& dn){ + std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(dn); + }, + [this,node](rvsdg::GammaNode& gn){ + std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(gn); + }, + [this,node](rvsdg::DeltaNode& tn){ + std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(tn); + }, + [this,node](rvsdg::LambdaNode& ln) + { + std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(ln); + }, + //DEFAULT + [this, node](){ + GVN_VisitLeafNode(node); + } + ); +} + void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, rvsdg::Node* node) { MatchType(node->GetOperation(),[pe, node](const jlm::llvm::ConstantDataArray& data_array){ @@ -311,24 +392,52 @@ void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, } auto hash_all_inputs = pe->gvn_.End(); for (size_t i = 0 ; i < node->noutputs() ; i++){ - auto si = pe->gvn_.FromStr( std::to_string(i) ); - auto g_out = pe->gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(si).End(); + auto arg_pos = pe->gvn_.FromWord(i); + auto g_out = pe->gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); pe->RegisterGVN( node->output(i), g_out); } }); - MatchType(*node, [pe, node](rvsdg::GammaNode& tn) + MatchTypeWithDefault(*node, [pe, node](rvsdg::GammaNode& tn) { for (auto br : tn.GetEntryVars()){ auto out = br.input->origin(); for (auto into_branch : br.branchArgument){ pe->RegisterGVN(into_branch, pe->GVNOrFail(out, node)); } + } + auto selector = tn.GetMatchVar().input->origin(); + auto match_var = pe->GVNOrFail(selector, node); + for (auto mv : tn.GetMatchVar().matchContent){ + pe->RegisterGVN( mv, pe->GVNOrFail(selector, node)); + } - for () + // Sub regions here + + for (auto ev : tn.GetExitVars()) + { + auto any_val = jlm::rvsdg::gvn::GVN_NULL; + pe->gvn_.Op(pe->g_alternatives); + for (auto leaving_branch : ev.branchResult){ + auto from_inner = pe->GVNOrZero(leaving_branch->origin()); + pe->gvn_.Arg( from_inner ); + any_val = from_inner; + } + auto branches_merged = pe->gvn_.End(); + if (any_val == branches_merged){ + pe->RegisterGVN(ev.output, branches_merged); // If all branches output the same value + }else{ + auto sel_op = pe->gvn_.FromStr("selector"); + auto hash_with_selector = pe->gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); + pe->RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. + } } + }, + [pe,node](){ + std::cout << TR_PINK << "Caught other structural node" << node->DebugString() << TR_RESET << std::endl; }); + MatchType(node->GetOperation(), [pe, node](const jlm::rvsdg::BinaryOperation& binop){ if (binop.is_associative() && binop.is_commutative()){ JLM_ASSERT(node->ninputs() >= 2); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index d32c9ff4a..4d9e47cf9 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -99,6 +99,13 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation return rvsdg::gvn::GVN_NULL; } + /// ----------------------------------------------------------- + + void GVN_VisitRegion(rvsdg::Region& reg); + void GVN_VisitAllSubRegions(rvsdg::Node& node); + void GVN_VisitNode(rvsdg::Node* node); + void GVN_VisitLeafNode(rvsdg::Node* node); + }; } diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 88aafdb46..064ff436a 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -316,6 +316,7 @@ namespace jlm::rvsdg::gvn { std::unordered_map< std::string, GVN_Val > str_to_gvn_; // Convenience map std::unordered_map< const void*, GVN_Val > ptr_to_gvn_; // Convenience map + std::unordered_map< size_t, GVN_Val > word_to_gvn_; // Convenience map std::unordered_map< GVN_Val, std::optional > gvn_; public: @@ -377,6 +378,20 @@ namespace jlm::rvsdg::gvn { } return str_to_gvn_[s]; } + GVN_Val FromWord(size_t w) {return FromWord(w, 0);} + GVN_Val FromWord(size_t w, uint64_t flags) + { + auto q = word_to_gvn_.find(w); + if (q == word_to_gvn_.end()) { + word_to_gvn_.insert({w,Leaf(flags)}); + q = word_to_gvn_.find(w); + } + if (q == word_to_gvn_.end()) {throw std::runtime_error("Expected some value");} + if ((q->second & GVN_MASK) != flags) { + throw std::runtime_error("Inconsistent flags for gvn generated from word: " + std::to_string(w)); + } + return word_to_gvn_[w]; + } GVN_Val FromPtr(const void* p) {return FromPtr(p, 0);} GVN_Val FromPtr(const void* p, uint64_t flags) { From 169cdf9eec9b5969413e0ba959d3e5dab7cd71dc Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 16:54:30 +0100 Subject: [PATCH 33/52] Save before code removal --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 107 +++++++++++++----- 1 file changed, 76 insertions(+), 31 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 9b0d7b1fc..110536f7a 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -212,9 +212,6 @@ PartialRedundancyElimination::Run( std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(root, initialize_stats); - this->TraverseTopDownRecursively(root, gvn_lambda_and_consts); - this->TraverseTopDownRecursively(root, gvn_compute); - this->TraverseTopDownRecursively(root, dump_region); this->TraverseTopDownRecursively(root, dump_node); @@ -306,37 +303,43 @@ void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node& node) void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) { + std::cout << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; - MatchTypeWithDefault(node->GetOperation(), - [this,node](const jlm::llvm::ConstantDataArray& data_array){ - auto op_data_array = gvn_.FromStr("constant_data_array"); - gvn_.Op(op_data_array); - for (size_t i = 0 ; i < node->noutputs() ; i++){ - gvn_.Arg( GVNOrFail(node->input(i)->origin(), node) ); + + MatchType(*node, [this](rvsdg::StructuralNode& sn){ + if (sn.ninputs() && sn.noutputs()){ + auto op_opaque = gvn_.FromPtr(&(sn.GetOperation()) ); + auto hash_call_out = gvn_.FromStr("hash_call_out"); + gvn_.Op(op_opaque); + for (size_t i = 0 ; i < sn.ninputs() ; i++){ + auto from = sn.input(i)->origin(); + gvn_.Arg( GVNOrFail(from, &sn) ); + } + auto hash_all_inputs = gvn_.End(); + for (size_t i = 0 ; i < sn.noutputs() ; i++){ + auto arg_pos = gvn_.FromWord(i); + auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); + RegisterGVN( sn.output(i), g_out ); } - auto g = gvn_.End(); - JLM_ASSERT(node->noutputs() == 1); - RegisterGVN(node->output(0), g); + } + }); + + MatchType(node->GetOperation(), + [this, node](const jlm::llvm::IntegerConstantOperation& iconst){ + auto v = gvn_.FromStr(iconst.Representation().str()); + RegisterGVN(node->output(0), v); }, - // Treat nodes as opaque calls unless overridden. - [this, node](){ - if (node->ninputs() && node->noutputs()){ - auto op_opaque = gvn_.FromPtr(&(node->GetOperation()) ); - auto hash_call_out = gvn_.FromStr("hash_call_out"); - gvn_.Op(op_opaque); - for (size_t i = 0 ; i < node->ninputs() ; i++){ - gvn_.Arg( GVNOrFail(node->input(i)->origin(), node) ); - } - auto hash_all_inputs = gvn_.End(); - for (size_t i = 0 ; i < node->noutputs() ; i++){ - auto arg_pos = gvn_.FromWord(i); - auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); - RegisterGVN( node->output(i), g_out); - } + [this, node](const jlm::rvsdg::BinaryOperation& binop){ + if (binop.is_associative() && binop.is_commutative()){ + JLM_ASSERT(node->ninputs() >= 2); + auto op = gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OP_IS_CA); + auto a = GVNOrFail( node->input(0)->origin(), node); + auto b = GVNOrFail( node->input(1)->origin(), node); + RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); } - std::cout << ind() << "todo: handle default call" << std::endl; } ); + } void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) @@ -348,14 +351,56 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) }, [this,node](rvsdg::GammaNode& gn){ std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + + for (auto br : gn.GetEntryVars()){ + auto out = br.input->origin(); + for (auto into_branch : br.branchArgument){ + RegisterGVN(into_branch, GVNOrFail(out, node)); + } + } + auto selector = gn.GetMatchVar().input->origin(); + auto match_var = GVNOrFail(selector, node); + for (auto mv : gn.GetMatchVar().matchContent){ + RegisterGVN( mv, GVNOrFail(selector, node)); + } + GVN_VisitAllSubRegions(gn); + + for (auto ev : gn.GetExitVars()) + { + auto any_val = jlm::rvsdg::gvn::GVN_NULL; + gvn_.Op(g_alternatives); + for (auto leaving_branch : ev.branchResult){ + auto from_inner = GVNOrZero(leaving_branch->origin()); + gvn_.Arg( from_inner ); + any_val = from_inner; + } + auto branches_merged = gvn_.End(); + if (any_val == branches_merged){ + RegisterGVN(ev.output, branches_merged); // If all branches output the same value + }else{ + auto sel_op = gvn_.FromStr("selector"); + auto hash_with_selector = gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); + RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. + } + } }, - [this,node](rvsdg::DeltaNode& tn){ + [this,node](rvsdg::ThetaNode& tn){ std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(tn); + throw std::runtime_error("Not implemented"); }, [this,node](rvsdg::LambdaNode& ln) { + for (auto arg : ln.GetFunctionArguments()){ + RegisterGVN(arg, gvn_.Leaf()); + } + for (auto arg : ln.GetContextVars()) + { + auto from = arg.input->origin(); + RegisterGVN(arg.inner, GVNOrFail(from, node)); + } + std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(ln); }, @@ -368,7 +413,7 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, rvsdg::Node* node) { - MatchType(node->GetOperation(),[pe, node](const jlm::llvm::ConstantDataArray& data_array){ + /*MatchType(node->GetOperation(),[pe, node](const jlm::llvm::ConstantDataArray& data_array){ auto op_data_array = pe->gvn_.FromStr("constant_data_array"); pe->gvn_.Op(op_data_array); for (size_t i = 0 ; i < node->noutputs() ; i++){ @@ -377,7 +422,7 @@ void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, auto g = pe->gvn_.End(); JLM_ASSERT(node->noutputs() == 1); pe->RegisterGVN(node->output(0), g); - }); + });*/ MatchType(*node, [](rvsdg::ThetaNode& tn) { throw std::runtime_error("Not implemented"); From fe4d91e5a4ddba78c89d5b9f2113086abc4d5aa0 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 18:26:59 +0100 Subject: [PATCH 34/52] Some refactoring. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 195 +++++------------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 9 +- 2 files changed, 50 insertions(+), 154 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 110536f7a..a85d503ba 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -263,27 +263,6 @@ void PartialRedundancyElimination::initialize_stats(PartialRedundancyElimination }); } -void PartialRedundancyElimination::gvn_lambda_and_consts(PartialRedundancyElimination *pe, rvsdg::Node* node) -{ - MatchType(node->GetOperation(), [&pe, node](const jlm::llvm::IntegerConstantOperation& iconst) - { - JLM_ASSERT(node->noutputs() == 1); - pe->RegisterGVN(node->output(0), pe->gvn_.FromStr(iconst.Representation().str())); - }); - - MatchType(*node , [pe, node](jlm::rvsdg::LambdaNode& ln){ - for (auto arg : ln.GetFunctionArguments()){ - pe->RegisterGVN(arg, pe->gvn_.Leaf()); - } - for (auto arg : ln.GetContextVars()) - { - auto from = arg.input->origin(); - pe->RegisterGVN(arg.inner, pe->GVNOrFail(from, node)); - } - - }); -} - void PartialRedundancyElimination::GVN_VisitRegion(rvsdg::Region& reg) { IndentMan indenter = IndentMan(); @@ -292,9 +271,9 @@ void PartialRedundancyElimination::GVN_VisitRegion(rvsdg::Region& reg) } } -void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node& node) +void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node* node) { - MatchType(node,[this](rvsdg::StructuralNode& sn){ + MatchType(*node,[this](rvsdg::StructuralNode& sn){ for (auto& reg : sn.Subregions()){ GVN_VisitRegion(reg); } @@ -303,9 +282,7 @@ void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node& node) void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) { - std::cout << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; - MatchType(*node, [this](rvsdg::StructuralNode& sn){ if (sn.ninputs() && sn.noutputs()){ auto op_opaque = gvn_.FromPtr(&(sn.GetOperation()) ); @@ -339,55 +316,61 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) } } ); - } -void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) +void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) { - MatchTypeWithDefault(*node, - [this,node](rvsdg::DeltaNode& dn){ - std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(dn); - }, - [this,node](rvsdg::GammaNode& gn){ - std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - - for (auto br : gn.GetEntryVars()){ - auto out = br.input->origin(); - for (auto into_branch : br.branchArgument){ - RegisterGVN(into_branch, GVNOrFail(out, node)); - } + MatchType(*node, [this,node](rvsdg::GammaNode& gn){ + std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + + for (auto br : gn.GetEntryVars()){ + auto out = br.input->origin(); + for (auto into_branch : br.branchArgument){ + RegisterGVN(into_branch, GVNOrFail(out, node)); } - auto selector = gn.GetMatchVar().input->origin(); - auto match_var = GVNOrFail(selector, node); - for (auto mv : gn.GetMatchVar().matchContent){ - RegisterGVN( mv, GVNOrFail(selector, node)); + } + auto selector = gn.GetMatchVar().input->origin(); + auto match_var = GVNOrFail(selector, node); + for (auto mv : gn.GetMatchVar().matchContent){ + RegisterGVN( mv, GVNOrFail(selector, node)); + } + + GVN_VisitAllSubRegions(node); + + for (auto ev : gn.GetExitVars()) + { + auto any_val = jlm::rvsdg::gvn::GVN_NULL; + gvn_.Op(g_alternatives); + for (auto leaving_branch : ev.branchResult){ + auto from_inner = GVNOrZero(leaving_branch->origin()); + gvn_.Arg( from_inner ); + any_val = from_inner; } - GVN_VisitAllSubRegions(gn); + auto branches_merged = gvn_.End(); - for (auto ev : gn.GetExitVars()) - { - auto any_val = jlm::rvsdg::gvn::GVN_NULL; - gvn_.Op(g_alternatives); - for (auto leaving_branch : ev.branchResult){ - auto from_inner = GVNOrZero(leaving_branch->origin()); - gvn_.Arg( from_inner ); - any_val = from_inner; - } - auto branches_merged = gvn_.End(); - if (any_val == branches_merged){ - RegisterGVN(ev.output, branches_merged); // If all branches output the same value - }else{ - auto sel_op = gvn_.FromStr("selector"); - auto hash_with_selector = gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); - RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. - } + if (any_val == branches_merged){ + RegisterGVN(ev.output, branches_merged); // If all branches output the same value + }else{ + auto sel_op = gvn_.FromStr("selector"); + auto hash_with_selector = gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); + RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. } + } + }); +} + + +void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) +{ + MatchTypeWithDefault(*node, + [this,node](rvsdg::DeltaNode& dn){ + std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(node); }, [this,node](rvsdg::ThetaNode& tn){ std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(tn); + GVN_VisitAllSubRegions(node); throw std::runtime_error("Not implemented"); }, [this,node](rvsdg::LambdaNode& ln) @@ -402,7 +385,7 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) } std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(ln); + GVN_VisitAllSubRegions(node); }, //DEFAULT [this, node](){ @@ -411,90 +394,6 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) ); } -void PartialRedundancyElimination::gvn_compute(PartialRedundancyElimination* pe, rvsdg::Node* node) -{ - /*MatchType(node->GetOperation(),[pe, node](const jlm::llvm::ConstantDataArray& data_array){ - auto op_data_array = pe->gvn_.FromStr("constant_data_array"); - pe->gvn_.Op(op_data_array); - for (size_t i = 0 ; i < node->noutputs() ; i++){ - pe->gvn_.Arg( pe->GVNOrFail(node->input(i)->origin(), node) ); - } - auto g = pe->gvn_.End(); - JLM_ASSERT(node->noutputs() == 1); - pe->RegisterGVN(node->output(0), g); - });*/ - MatchType(*node, [](rvsdg::ThetaNode& tn) - { - throw std::runtime_error("Not implemented"); - }); - - MatchType(node->GetOperation(), [pe, node](const jlm::llvm::CallOperation& call_op){ - auto op_call = pe->gvn_.FromStr("hash_call"); - auto hash_call_out = pe->gvn_.FromStr("hash_call_out"); - pe->gvn_.Op(op_call); - for (size_t i = 0 ; i < node->ninputs() ; i++){ - pe->gvn_.Arg( pe->GVNOrFail(node->input(i)->origin(), node) ); - } - auto hash_all_inputs = pe->gvn_.End(); - for (size_t i = 0 ; i < node->noutputs() ; i++){ - auto arg_pos = pe->gvn_.FromWord(i); - auto g_out = pe->gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); - pe->RegisterGVN( node->output(i), g_out); - } - }); - - MatchTypeWithDefault(*node, [pe, node](rvsdg::GammaNode& tn) - { - for (auto br : tn.GetEntryVars()){ - auto out = br.input->origin(); - for (auto into_branch : br.branchArgument){ - pe->RegisterGVN(into_branch, pe->GVNOrFail(out, node)); - } - } - auto selector = tn.GetMatchVar().input->origin(); - auto match_var = pe->GVNOrFail(selector, node); - for (auto mv : tn.GetMatchVar().matchContent){ - pe->RegisterGVN( mv, pe->GVNOrFail(selector, node)); - } - - // Sub regions here - - for (auto ev : tn.GetExitVars()) - { - auto any_val = jlm::rvsdg::gvn::GVN_NULL; - pe->gvn_.Op(pe->g_alternatives); - for (auto leaving_branch : ev.branchResult){ - auto from_inner = pe->GVNOrZero(leaving_branch->origin()); - pe->gvn_.Arg( from_inner ); - any_val = from_inner; - } - auto branches_merged = pe->gvn_.End(); - if (any_val == branches_merged){ - pe->RegisterGVN(ev.output, branches_merged); // If all branches output the same value - }else{ - auto sel_op = pe->gvn_.FromStr("selector"); - auto hash_with_selector = pe->gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); - pe->RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. - } - } - }, - [pe,node](){ - std::cout << TR_PINK << "Caught other structural node" << node->DebugString() << TR_RESET << std::endl; - }); - - - MatchType(node->GetOperation(), [pe, node](const jlm::rvsdg::BinaryOperation& binop){ - if (binop.is_associative() && binop.is_commutative()){ - JLM_ASSERT(node->ninputs() >= 2); - auto op = pe->gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OP_IS_CA); - auto a = pe->GVNOrFail( node->input(0)->origin(), node); - auto b = pe->GVNOrFail( node->input(1)->origin(), node); - pe->RegisterGVN(node->output(0), pe->gvn_.Op(op).Arg(a).Arg(b).End()); - } - }); - -} - void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node* node) { std::cout << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 4d9e47cf9..239db6ab5 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -66,16 +66,12 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node* node); static void initialize_stats( PartialRedundancyElimination *pe, rvsdg::Node* node); - static void gvn_lambda_and_consts(PartialRedundancyElimination *pe, rvsdg::Node* node); - static void gvn_compute( PartialRedundancyElimination *pe, rvsdg::Node* node); size_t stat_theta_count; size_t stat_gamma_count; jlm::rvsdg::gvn::GVN_Val g_alternatives; - - std::unordered_map< rvsdg::Output *, rvsdg::gvn::GVN_Val> output_to_gvn_; void RegisterGVN(rvsdg::Output * output, rvsdg::gvn::GVN_Val gvn){ output_to_gvn_[output] = gvn; @@ -102,10 +98,11 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation /// ----------------------------------------------------------- void GVN_VisitRegion(rvsdg::Region& reg); - void GVN_VisitAllSubRegions(rvsdg::Node& node); + void GVN_VisitAllSubRegions(rvsdg::Node* node); void GVN_VisitNode(rvsdg::Node* node); + void GVN_VisitGammaNode(rvsdg::Node* node); + void GVN_VisitThetaNode(rvsdg::ThetaNode* tn); void GVN_VisitLeafNode(rvsdg::Node* node); - }; } From 065281b1b757e6cebf8803f21aa2690631f24853 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 19:16:36 +0100 Subject: [PATCH 35/52] Clean build --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 137 ++++++-- jlm/llvm/opt/PartialRedundancyElimination.hpp | 17 +- jlm/llvm/opt/gvn.cpp | 115 ++++--- jlm/llvm/opt/gvn.hpp | 301 +++++++++++------- 4 files changed, 388 insertions(+), 182 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index a85d503ba..da548e987 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -58,7 +58,6 @@ #include - /** * Partial redundancy elimination: * -invariants: after each insertion of a new GVN all GVN shall have unique values @@ -175,7 +174,7 @@ PartialRedundancyElimination::PartialRedundancyElimination() : stat_theta_count(0), stat_gamma_count(0) { - g_alternatives = gvn_.Leaf(rvsdg::gvn::GVN_OP_IS_SWITCH); + } void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)) @@ -224,6 +223,7 @@ PartialRedundancyElimination::Run( jlm::rvsdg::gvn::RunAllTests(); GVN_VisitRegion(root); + PassDownRegion(root); } /** -------------------------------------------------------------------------------------------- **/ @@ -266,17 +266,13 @@ void PartialRedundancyElimination::initialize_stats(PartialRedundancyElimination void PartialRedundancyElimination::GVN_VisitRegion(rvsdg::Region& reg) { IndentMan indenter = IndentMan(); - for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)){ - GVN_VisitNode(node); - } + for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)){GVN_VisitNode(node);} } void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node* node) { MatchType(*node,[this](rvsdg::StructuralNode& sn){ - for (auto& reg : sn.Subregions()){ - GVN_VisitRegion(reg); - } + for (auto& reg : sn.Subregions()){GVN_VisitRegion(reg);} }); } @@ -309,7 +305,7 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) [this, node](const jlm::rvsdg::BinaryOperation& binop){ if (binop.is_associative() && binop.is_commutative()){ JLM_ASSERT(node->ninputs() >= 2); - auto op = gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OP_IS_CA); + auto op = gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OPERATOR_IS_CA); auto a = GVNOrFail( node->input(0)->origin(), node); auto b = GVNOrFail( node->input(1)->origin(), node); RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); @@ -323,6 +319,7 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) MatchType(*node, [this,node](rvsdg::GammaNode& gn){ std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + //Route gvn values into alternatives for (auto br : gn.GetEntryVars()){ auto out = br.input->origin(); for (auto into_branch : br.branchArgument){ @@ -337,16 +334,17 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) GVN_VisitAllSubRegions(node); + //route results out and handle cases where result is always the same for (auto ev : gn.GetExitVars()) { - auto any_val = jlm::rvsdg::gvn::GVN_NULL; - gvn_.Op(g_alternatives); + using namespace jlm::rvsdg::gvn; + auto any_val = GVN_NO_VALUE; + gvn_.Op(GVN_OP_ANY_ORDERED); for (auto leaving_branch : ev.branchResult){ auto from_inner = GVNOrZero(leaving_branch->origin()); gvn_.Arg( from_inner ); any_val = from_inner; } - auto branches_merged = gvn_.End(); if (any_val == branches_merged){ @@ -360,6 +358,32 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) }); } +void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) +{ + MatchType(*node, [this,node](rvsdg::ThetaNode& tn) + { + std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(node); + throw std::runtime_error("Not implemented"); + }); +} + +void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) +{ + MatchType(*node, [this,node](rvsdg::LambdaNode& ln){ + for (auto arg : ln.GetFunctionArguments()){ + RegisterGVN(arg, gvn_.Leaf()); + } + for (auto arg : ln.GetContextVars()) + { + auto from = arg.input->origin(); + RegisterGVN(arg.inner, GVNOrFail(from, node)); + } + + std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + GVN_VisitAllSubRegions(node); + }); +} void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) { @@ -369,23 +393,13 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) GVN_VisitAllSubRegions(node); }, [this,node](rvsdg::ThetaNode& tn){ - std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(node); - throw std::runtime_error("Not implemented"); + GVN_VisitThetaNode(node); }, - [this,node](rvsdg::LambdaNode& ln) - { - for (auto arg : ln.GetFunctionArguments()){ - RegisterGVN(arg, gvn_.Leaf()); - } - for (auto arg : ln.GetContextVars()) - { - auto from = arg.input->origin(); - RegisterGVN(arg.inner, GVNOrFail(from, node)); - } - - std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(node); + [this,node](rvsdg::ThetaNode& tn){ + GVN_VisitGammaNode(node); + }, + [this,node](rvsdg::LambdaNode& ln){ + GVN_VisitLambdaNode(node); }, //DEFAULT [this, node](){ @@ -418,9 +432,74 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r std::cout << TR_GREEN; std::cout << " : " << pe->GVNOrZero( node->output(i) ); } - std::cout << std::endl; } +/** ********************************************************************************************* */ +/** ********************************************************************************************* */ +/** ********************************************************************************************* */ + +void PartialRedundancyElimination::PassDownRegion(rvsdg::Region& reg) +{ + IndentMan indenter = IndentMan(); + + for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)){PassDownNode(node);} +} + +void PartialRedundancyElimination::PassDownAllSubRegions(rvsdg::Node* node) +{ + MatchType(*node,[this](rvsdg::StructuralNode& sn){ + for (auto& reg : sn.Subregions()){PassDownRegion(reg);} + }); +} + +void PartialRedundancyElimination::PassDownNode(rvsdg::Node* node) +{ + MatchTypeWithDefault(*node, + [this,node](rvsdg::DeltaNode& dn){ + std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + PassDownAllSubRegions(node); + }, + [this,node](rvsdg::ThetaNode& tn){ + PassDownThetaNode(node); + }, + [this, node](rvsdg::GammaNode& gn) + { + PassDownGammaNode(node); + }, + [this,node](rvsdg::LambdaNode& ln){ + PassDownLambdaNode(node); + }, + //DEFAULT + [this, node](){ + PassDownLeafNode(node); + } + ); +} + +void PartialRedundancyElimination::PassDownThetaNode(rvsdg::Node* node) +{ + PassDownAllSubRegions(node); +} + +void PartialRedundancyElimination::PassDownGammaNode(rvsdg::Node* node) +{ + PassDownAllSubRegions(node); +} + +void PartialRedundancyElimination::PassDownLeafNode(rvsdg::Node* node) +{ + std::cout << ind() <DebugString()<GetNodeId() << TR_RESET; +} + +void +PartialRedundancyElimination::PassDownLambdaNode(rvsdg::Node * ln) +{ + std::cout << ind() << TR_GREEN << ln->GetNodeId() << TR_RESET; + PassDownAllSubRegions(ln); } + +} + + diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 239db6ab5..b09f8c5df 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -70,8 +70,6 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation size_t stat_theta_count; size_t stat_gamma_count; - jlm::rvsdg::gvn::GVN_Val g_alternatives; - std::unordered_map< rvsdg::Output *, rvsdg::gvn::GVN_Val> output_to_gvn_; void RegisterGVN(rvsdg::Output * output, rvsdg::gvn::GVN_Val gvn){ output_to_gvn_[output] = gvn; @@ -82,7 +80,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation if (output_to_gvn_.find(edge) != output_to_gvn_.end()){ return output_to_gvn_[edge]; } - return rvsdg::gvn::GVN_NULL; + return rvsdg::gvn::GVN_NO_VALUE; } inline rvsdg::gvn::GVN_Val GVNOrFail(rvsdg::Output* edge, rvsdg::Node* ctx_node){ @@ -92,7 +90,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation std::cout << "Logic error: missing input for edge" + ctx_node->DebugString() + std::to_string(ctx_node->GetNodeId()); - return rvsdg::gvn::GVN_NULL; + return rvsdg::gvn::GVN_NO_VALUE; } /// ----------------------------------------------------------- @@ -101,8 +99,17 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void GVN_VisitAllSubRegions(rvsdg::Node* node); void GVN_VisitNode(rvsdg::Node* node); void GVN_VisitGammaNode(rvsdg::Node* node); - void GVN_VisitThetaNode(rvsdg::ThetaNode* tn); + void GVN_VisitThetaNode(rvsdg::Node* tn); + void GVN_VisitLambdaNode(rvsdg::Node* ln); void GVN_VisitLeafNode(rvsdg::Node* node); + + void PassDownRegion(rvsdg::Region& reg); + void PassDownAllSubRegions(rvsdg::Node* node); + void PassDownNode(rvsdg::Node* node); + void PassDownGammaNode(rvsdg::Node* gn); + void PassDownThetaNode(rvsdg::Node* tn); + void PassDownLambdaNode(rvsdg::Node* ln); + void PassDownLeafNode(rvsdg::Node* node); }; } diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp index f30a60262..1948a1db2 100644 --- a/jlm/llvm/opt/gvn.cpp +++ b/jlm/llvm/opt/gvn.cpp @@ -15,7 +15,7 @@ namespace jlm::rvsdg::gvn { void GVN_Manager::Test0() { GVN_Manager gm; auto my_op = gm.Leaf(); - auto my_ca = gm.Leaf(GVN_OP_IS_CA); + auto my_ca = gm.Leaf(GVN_OPERATOR_IS_CA); auto x = gm.Leaf(); auto y = gm.Leaf(); @@ -35,39 +35,39 @@ namespace jlm::rvsdg::gvn { d.op = my_op; d.push(x); d.push(y); - if (gvn_verbose){std::cout << "Hash of x - y is : " << to_string(gm.CalculateHash(d)) << std::endl;} + if (gvn_verbose){std::cout << "Hash of x - y is : " << to_string(gm.CalculateHash(d).first) << std::endl;} } { GVN_Deps d2; d2.op = my_op; d2.push(y); d2.push(x); - if (gvn_verbose){std::cout << "Hash of y - x is : " << to_string(gm.CalculateHash(d2)) << std::endl;} + if (gvn_verbose){std::cout << "Hash of y - x is : " << to_string(gm.CalculateHash(d2).first) << std::endl;} } { GVN_Deps d3; d3.op = my_ca; d3.push(y); d3.push(x); - gm.gvn_.insert({gm.CalculateHash(d3), d3}); + gm.gvn_.insert({gm.CalculateHash(d3).first, d3}); GVN_Deps d4; d4.op = my_ca; d4.push(y); - d4.push(gm.CalculateHash(d3)); + d4.push(gm.CalculateHash(d3).first); GVN_Deps d5; d5.op = my_ca; - d5.push(gm.CalculateHash(d3)); + d5.push(gm.CalculateHash(d3).first); d5.push(y); - if (gm.CalculateHash(d4) != gm.CalculateHash(d5)) { + if (gm.CalculateHash(d4).first != gm.CalculateHash(d5).first) { throw std::runtime_error("Test failed: expected equal hashes from associative and commutative op"); } if (gvn_verbose) { - std::cout << "y+x " << to_string(gm.CalculateHash(d3)) << std::endl; - std::cout << "y + (y+x): " << to_string(gm.CalculateHash(d4)) << std::endl; - std::cout << "(y+x) + y: " << to_string(gm.CalculateHash(d5)) << std::endl; + std::cout << "y+x " << to_string(gm.CalculateHash(d3).first) << std::endl; + std::cout << "y + (y+x): " << to_string(gm.CalculateHash(d4).first) << std::endl; + std::cout << "(y+x) + y: " << to_string(gm.CalculateHash(d5).first) << std::endl; } } } @@ -87,14 +87,12 @@ namespace jlm::rvsdg::gvn { GVN_Val x = gm.Leaf(); GVN_Val y = gm.Leaf(); - GVN_Val switchy_op = gm.FromPtr("switch", GVN_OP_IS_SWITCH); - - GVN_Val xx = gm.Op(switchy_op).Args({x,x}); + GVN_Val xx = gm.Op(GVN_OP_ANY_ORDERED).Args({x,x}); std::cout << "*************************"<< std::endl << std::endl; - GVN_Val xy = gm.Op(switchy_op).Args({x,y}); + GVN_Val xy = gm.Op(GVN_OP_ANY_ORDERED).Args({x,y}); std::cout << "*************************"<< std::endl << std::endl; - GVN_Val xy_2 = gm.Op(switchy_op).Args({x,y}); + GVN_Val xy_2 = gm.Op(GVN_OP_ANY_ORDERED).Args({x,y}); if (xy != xy_2) { std::cout << to_string(xy) << to_string(xy_2) << std::endl; @@ -113,14 +111,9 @@ namespace jlm::rvsdg::gvn { auto c = gm.Leaf(); auto d = c; - auto op_add = gm.FromStr("+", GVN_OP_IS_CA); - auto op_alternatives = gm.FromStr("merge", GVN_OP_IS_SWITCH); + auto op_add = gm.FromStr("+", GVN_OPERATOR_IS_CA); auto one = gm.FromStr("1"); - auto reassign = [&gm, op_alternatives](BrittlePrismEle& ele) { - ele.partition = gm.Op(op_alternatives).Args({ele.disruptor, ele.partition}); - }; - // loop ( // a + 1 -> a // b + a -> b @@ -151,7 +144,7 @@ namespace jlm::rvsdg::gvn { prevars->elements[2].disruptor = c_out; prevars->elements[3].disruptor = d_out; - if (! BrittlePrism::Fracture(*prevars,reassign) ){break;} + if (! prevars->Fracture() ){break;} } a_out = gm.Op(op_add).Args({a, one}); b_out = gm.Op(op_add).Args({b,a}); @@ -164,13 +157,13 @@ namespace jlm::rvsdg::gvn { postvars->elements[1].disruptor = b_out; postvars->elements[2].disruptor = c_out; postvars->elements[3].disruptor = d_out; - if (! BrittlePrism::Fracture(*postvars,reassign) ){break;} + if (! postvars->Fracture() ){break;} } } std::cout << "after: " << a_out << " - " << b_out << " - " << c_out << " - " << d_out << std::endl; - GVN_Val h = gm.Op(op_alternatives).Args({a_out,b_out,c_out,d_out}); + GVN_Val h = gm.Op(GVN_OP_ANY_ORDERED).Args({a_out,b_out,c_out,d_out}); if (last && h != last && !(h & GVN_FROM_COLLISION)) { std::cout << to_string(last) << " " << to_string(h) << std::endl; throw std::runtime_error("Hashes where different across iterations"); @@ -179,6 +172,19 @@ namespace jlm::rvsdg::gvn { } } + void GVN_Manager::Test4() + { + GVN_Manager gm; + GVN_Val four = gm.FromWord(4); + GVN_Val zero = gm.FromWord(0); + GVN_Val too_big = gm.FromWord(0xffffffffffffffull); + GVN_Val same_too_big = gm.FromWord(0xffffffffffffffull); + if (four != 4){throw std::runtime_error("Small value not non-symbolic: " + to_string(four));} + if (zero != 0){throw std::runtime_error("Small value not non-symbolic: " + to_string(zero));} + if (!(too_big & GVN_IS_SYMBOLIC)){throw std::runtime_error("Big constants should be symbols");} + if (too_big != same_too_big){throw std::runtime_error("FromWord should be referentially transparent.");} + } + void BrittlePrism::Test0() { std::vector v = {77,128,128,77,77}; @@ -200,10 +206,7 @@ namespace jlm::rvsdg::gvn { if (gvn_verbose){std::cout << "----------before fracture---------------"<" << ele.disruptor << std::endl;} - ele.partition = ele.disruptor; - }); + p0.Fracture(); p0.OrderByPartition(); if (gvn_verbose){std::cout << "----------after frac---------------"<" << ele.disruptor << std::endl;} - ele.partition = ele.disruptor; - }); + p0.Fracture(); }catch (std::runtime_error& e) { if (gvn_verbose){std::cout << "success : should throw : " << e.what() << std::endl;} } @@ -254,13 +254,14 @@ namespace jlm::rvsdg::gvn { void BrittlePrism::Test1() { auto br = BrittlePrism({1,1,1,4}); + br.dump(); + std::cout << "*****************************************" << std::endl; br.elements[0].disruptor = 10; br.elements[1].disruptor = 10; br.elements[2].disruptor = 88; br.elements[3].disruptor = 77; - BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { - ele.partition = ele.disruptor; - }); + br.Fracture(); + br.dump(); br.OrderByOriginal(); if (gvn_verbose){br.dump();} if (br.elements[0].partition != 10){throw std::runtime_error("should be 10");} @@ -272,9 +273,7 @@ namespace jlm::rvsdg::gvn { br.elements[1].disruptor = 100; br.elements[2].disruptor = 888; br.elements[3].disruptor = 123; - BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { - ele.partition = ele.disruptor; - }); + br.Fracture(); br.OrderByOriginal(); if (gvn_verbose){br.dump();} if (br.elements[0].partition != 10){throw std::runtime_error("should still be 10");} @@ -287,9 +286,7 @@ namespace jlm::rvsdg::gvn { br.elements[1].disruptor = 10; br.elements[2].disruptor = 81488; br.elements[3].disruptor = 12153; - BrittlePrism::Fracture(br, [](BrittlePrismEle& ele) { - ele.partition = ele.disruptor; - }); + br.Fracture(); br.OrderByOriginal(); if (gvn_verbose){br.dump();} if (br.elements[0].partition != 1780){throw std::runtime_error("should be 1780");} @@ -297,5 +294,41 @@ namespace jlm::rvsdg::gvn { if (br.elements[2].partition != 88){throw std::runtime_error("should still be 88");} if (br.elements[3].partition != 77){throw std::runtime_error("should still be 77");} } + + void GVN_Manager::Test5() + { + if (GVN_MASK & GVN_SMALL_VALUE) { + throw std::runtime_error("Flags of gvn value cannot overlap with field for integer constants."); + } + + GVN_Manager gm; + auto forty = gm.Op(GVN_OP_ADDITION).Arg(10).Arg(30).End(); + if (forty != 40){throw std::runtime_error("Bad addition" + to_string(forty));} + auto twelve = gm.Op(GVN_OP_MULTIPLY).Arg(4).Arg(3).End(); + if (twelve != 12){throw std::runtime_error("Bad multiplication");} + } + + void GVN_Manager::Test6() { + GVN_Manager gm; + if (gm.Op(GVN_OP_EQ).Arg(10).Arg(242).End() != GVN_FALSE) { + throw std::runtime_error("Should have been false"); + } + if (gm.Op(GVN_OP_EQ).Arg(20).Arg(20).End() != GVN_TRUE) { + throw std::runtime_error("Should have been true"); + } + auto l = gm.Leaf(); + if (gm.Op(GVN_OP_EQ).Arg(l).Arg(l).End() != GVN_TRUE) { + throw std::runtime_error("Equal symbols must be true"); + } + if (gm.Op(GVN_OP_NEQ).Arg(24).Arg(l).End() & GVN_CONST_SYMBOL) { + throw std::runtime_error("NEQ bad inference."); + } + if (gm.Op(GVN_OP_NEQ).Arg(24).Arg(24).End() != GVN_FALSE) { + throw std::runtime_error("NEQ bad inference."); + } + if (gm.Op(GVN_OP_NEQ).Arg(214).Arg(24).End() != GVN_TRUE) { + throw std::runtime_error("NEQ bad inference."); + } + } } diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 064ff436a..ed8dac4ef 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -5,10 +5,6 @@ #ifndef JLM_GVN_H #define JLM_GVN_H - -#ifndef GVN_H -#define GVN_H - #include #include #include @@ -19,41 +15,58 @@ #include - - namespace jlm::rvsdg::gvn { extern bool gvn_verbose; typedef uint64_t GVN_Val; - constexpr GVN_Val GVN_NULL = 0; - constexpr GVN_Val GVN_HAS_DEPS = 0x1; /* \brief : For making lambda values global */ - constexpr GVN_Val GVN_IS_LOCAL_VALUE = 0x2; /* \brief : This value depends on placeholders */ - constexpr GVN_Val GVN_FROM_COLLISION = 0x4; /* \brief : This gvn depends on value created by collision resolution */ - constexpr GVN_Val GVN_OP_IS_CA = 0x8; /* \brief : This gvn value represent a commutative and associative operation */ - constexpr GVN_Val GVN_OP_ADDS_PARAM_DEPENDENCE = 0x10; /* \brief : This operator creates a local value */ - constexpr GVN_Val GVN_OP_REMOVES_PARAM_DEPENDENCE = 0x20; /* \brief : For making lambda values global */ - constexpr GVN_Val GVN_OP_IS_SWITCH = 0x40; - - constexpr GVN_Val GVN_MASK_INHERIT = GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION; - constexpr GVN_Val GVN_MASK = GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION | GVN_OP_IS_CA | GVN_OP_ADDS_PARAM_DEPENDENCE | GVN_OP_REMOVES_PARAM_DEPENDENCE | GVN_HAS_DEPS | GVN_OP_IS_SWITCH; + constexpr GVN_Val GVN_SMALL_VALUE = 0xFFFFFFFF; // Must not collide with flags below. + inline bool GVN_IsSmallValue(GVN_Val v){return (v & GVN_SMALL_VALUE) == v;} + + /* Flags stored as part of GVN values */ + constexpr GVN_Val GVN_IS_SYMBOLIC = 1ull << 32; + constexpr GVN_Val GVN_HAS_DEPS = 1ull << 33; /* \brief : By setting a single bit for internal nodes it becomes impossible for leaf and internal nodes to have the same hash making code simpler. */ + constexpr GVN_Val GVN_IS_LOCAL_VALUE = 1ull << 34; /* \brief : This value depends on a lambda param. Cannot be moved into the global region. */ + constexpr GVN_Val GVN_FROM_COLLISION = 1ull << 35; /* \brief : This gvn depends on value created by collision resolution */ + constexpr GVN_Val GVN_OPERATOR_IS_CA = 1ull << 36; /* \brief : This gvn value represent a commutative and associative operation */ + constexpr GVN_Val GVN_CONST_SYMBOL = 1ull << 37; + + constexpr GVN_Val GVN_MASK_INHERIT = GVN_IS_LOCAL_VALUE | GVN_IS_SYMBOLIC; + constexpr GVN_Val GVN_MASK = GVN_IS_SYMBOLIC | GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION | GVN_OPERATOR_IS_CA | GVN_HAS_DEPS | GVN_CONST_SYMBOL; + + constexpr GVN_Val GVN_PREDEFS = GVN_CONST_SYMBOL | GVN_IS_SYMBOLIC; + + /* GLOBAL OPERATION */ + constexpr GVN_Val GVN_OP_ANY_ORDERED = GVN_PREDEFS | 1; + constexpr GVN_Val GVN_OP_BECOME_LOCAL = GVN_PREDEFS | 2; + constexpr GVN_Val GVN_OP_BECOME_GLOBAL = GVN_PREDEFS | 3; + constexpr GVN_Val GVN_OP_ADDITION = GVN_PREDEFS | GVN_OPERATOR_IS_CA | 4; + constexpr GVN_Val GVN_OP_MULTIPLY = GVN_PREDEFS | GVN_OPERATOR_IS_CA | 5; + constexpr GVN_Val GVN_OP_EQ = GVN_PREDEFS | 6; // N-ary checks if all values are the same + constexpr GVN_Val GVN_OP_NEQ = GVN_PREDEFS | 7; // N-ary checks is two values are distinct + + /* GLOBAL CONSTANTS */ + constexpr GVN_Val GVN_NO_VALUE = GVN_PREDEFS | GVN_CONST_SYMBOL | 100; + constexpr GVN_Val GVN_TRUE = GVN_PREDEFS | GVN_CONST_SYMBOL | 101; + constexpr GVN_Val GVN_FALSE = GVN_PREDEFS | GVN_CONST_SYMBOL | 102; + constexpr GVN_Val GVN_INVARIANT = GVN_PREDEFS | GVN_CONST_SYMBOL | 103; inline bool GVN_ValueIsGlobal(GVN_Val v) {return v & GVN_IS_LOCAL_VALUE;} - inline bool GVN_ValueIsTainted(GVN_Val v) {return v & GVN_FROM_COLLISION;} - inline bool GVN_ValueIsCA_Op(GVN_Val v) {return v & GVN_OP_IS_CA;} - inline bool GVN_ValueIsSwitch_Op(GVN_Val v) {return v & GVN_OP_IS_SWITCH;} - inline bool GVN_ValueParamDependent(GVN_Val v) {return v & GVN_OP_ADDS_PARAM_DEPENDENCE;} - inline bool GVN_ValueIsParamIndependent(GVN_Val v) {return v & GVN_OP_REMOVES_PARAM_DEPENDENCE;} + inline bool GVN_ValueIsFromCollision(GVN_Val v) {return v & GVN_FROM_COLLISION;} + inline bool GVN_ValueIsCA_Op(GVN_Val v) {return v & GVN_OPERATOR_IS_CA;} inline bool GVN_ValueHasDeps(GVN_Val v) {return v & GVN_HAS_DEPS;} inline std::string to_string(GVN_Val v) { auto n = static_cast(v); std::string s = "" + std::to_string(n); if ((v & GVN_HAS_DEPS) == 0) {s += "";} - if (v & GVN_OP_IS_CA) {s+="";} - if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += ""; return s;} - if ((v & GVN_OP_ADDS_PARAM_DEPENDENCE)){s += "";} - if ((v & GVN_OP_REMOVES_PARAM_DEPENDENCE)){s += "";} - if (v & GVN_FROM_COLLISION) {s += ""; return s;} + if (v & GVN_OPERATOR_IS_CA) {s+="";} + if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += "";} + if (v & GVN_FROM_COLLISION) {s += "";} + if (v & GVN_IS_SYMBOLIC){s += "";} + if (v == GVN_TRUE) {s += "GVN_TRUE";} + if (v == GVN_FALSE){s += "GVN_FALSE";} + if (v == GVN_NO_VALUE){s += "GVN_NO_VALUE";} + if (v == GVN_INVARIANT){s += "GVN_INVARIANT";} return s; } @@ -237,28 +250,27 @@ namespace jlm::rvsdg::gvn { p.did_shatter = true; } - template - static bool Fracture(BrittlePrism& p, Fn reassign) + bool Fracture() { - // Reassign take a single (BrittlePrismEle& ele) as argument - // reassign is called at most once per offending element - // reassign is NEVER called when the disruptor equals the current partition + // Partition again based on disruptors + // Either because a single partition faces distinct disruptors or when + // the disruptor does equal the original value. bool did_fracture = false; - p.CheckDisruptorInvariants(); - p.OrderByPartitionThenDisruptor(); + CheckDisruptorInvariants(); + OrderByPartitionThenDisruptor(); size_t it = 0; - while (it < p.elements.size()) { - size_t pspan = p.SpanPartition(it); - size_t gspan = p.SpanDisruptorAndPartition(it); + while (it < elements.size()) { + size_t pspan = SpanPartition(it); + size_t gspan = SpanDisruptorAndPartition(it); size_t p_end = it + pspan; if (pspan == gspan) { // Check if value changes for first time while (it < p_end) { - BrittlePrismEle& e = p.elements[it]; + BrittlePrismEle& e = elements[it]; if (e.original_partition == e.partition && e.disruptor != e.original_partition) { - reassign(e); + elements[it].partition = e.disruptor; did_fracture = true; } it++; @@ -267,23 +279,23 @@ namespace jlm::rvsdg::gvn { // Partition was given multiple different disruptors did_fracture = true; while (it < p_end) { - BrittlePrismEle& e = p.elements[it]; + BrittlePrismEle& e = elements[it]; if (e.partition != e.disruptor) { - reassign(e); + elements[it].partition = e.disruptor; } it++; } } - } if (did_fracture) { - p.fracture_count++; - if (p.fracture_count > p.elements.size() * 2) { + fracture_count++; + if (fracture_count > elements.size() * 2) { throw std::runtime_error("Brittle prism invariant broken. Possible missing updates to by reassign callback."); } } return did_fracture; } + static void Test0(); static void Test1(); }; @@ -327,7 +339,20 @@ namespace jlm::rvsdg::gvn { size_t max_ca_size; GVN_Manager() : stat_collisions(0), stat_leaf_collisions(0), stat_ca_too_big(0), max_ca_size(32) { - gvn_.insert({0, std::nullopt}); + // Add constant symbols to the table of all values such that + // values cannot collide. + DefineConst(GVN_OP_ANY_ORDERED); + DefineConst(GVN_OP_BECOME_LOCAL); + DefineConst(GVN_OP_BECOME_GLOBAL); + DefineConst(GVN_OP_ADDITION); + DefineConst(GVN_OP_MULTIPLY); + DefineConst(GVN_OP_EQ); + DefineConst(GVN_OP_NEQ); + + DefineConst(GVN_NO_VALUE); + DefineConst(GVN_TRUE); + DefineConst(GVN_FALSE); + DefineConst(GVN_INVARIANT); } GVN_Val Leaf() { @@ -367,6 +392,7 @@ namespace jlm::rvsdg::gvn { GVN_Val FromStr(const std::string& str) {return FromStr(str, 0);} GVN_Val FromStr(const std::string s, uint64_t flags) { + flags |= GVN_IS_SYMBOLIC; auto q = str_to_gvn_.find(s); if (q == str_to_gvn_.end()) { str_to_gvn_.insert({s,Leaf(flags)}); @@ -378,23 +404,21 @@ namespace jlm::rvsdg::gvn { } return str_to_gvn_[s]; } - GVN_Val FromWord(size_t w) {return FromWord(w, 0);} - GVN_Val FromWord(size_t w, uint64_t flags) + GVN_Val FromWord(size_t w) { + if (w <= GVN_SMALL_VALUE) {return w;} auto q = word_to_gvn_.find(w); if (q == word_to_gvn_.end()) { - word_to_gvn_.insert({w,Leaf(flags)}); + word_to_gvn_.insert({w,Leaf(0)}); q = word_to_gvn_.find(w); } if (q == word_to_gvn_.end()) {throw std::runtime_error("Expected some value");} - if ((q->second & GVN_MASK) != flags) { - throw std::runtime_error("Inconsistent flags for gvn generated from word: " + std::to_string(w)); - } return word_to_gvn_[w]; } GVN_Val FromPtr(const void* p) {return FromPtr(p, 0);} GVN_Val FromPtr(const void* p, uint64_t flags) { + flags |= GVN_IS_SYMBOLIC; auto q = ptr_to_gvn_.find(p); if (q == ptr_to_gvn_.end()) { ptr_to_gvn_.insert({p,Leaf(flags)}); @@ -407,7 +431,7 @@ namespace jlm::rvsdg::gvn { return ptr_to_gvn_[p]; } - GVN_Val Prism(GVN_Val op, BrittlePrism& brittle) { + GVN_Val FromPartitions(GVN_Val op, BrittlePrism& brittle) { // It is sufficient to create a prism from the post array after taking the union // with the pre array Op(op); @@ -421,14 +445,22 @@ namespace jlm::rvsdg::gvn { } private: - + void DefineConst(GVN_Val v) + { + if (gvn_.find(v) != gvn_.end()) { + throw std::runtime_error("Duplicate constant definition."); + } + gvn_[v] = std::nullopt; + } GVN_Val Create(GVN_Val op, const std::vector& args) { if (args.empty()){throw std::runtime_error("Logic error: GVN operator applied to zero args.");} std::optional new_gvn = GVN_Deps(); new_gvn->op = op; // Initialize new_gvn.args // Either a copy of args or count of operator cluster leaves - if (GVN_ValueIsCA_Op(new_gvn->op)) { + if (!GVN_ValueIsCA_Op(new_gvn->op)) { + for (auto a : args) {new_gvn->push(a);} + }else{ std::vector > acc; for (auto arg : args) { if (GVN_ValueHasDeps(arg)) { @@ -444,7 +476,7 @@ namespace jlm::rvsdg::gvn { } std::sort(acc.begin(), acc.end()); if (!acc.empty()) { - GVN_Val last = GVN_NULL; + GVN_Val last = GVN_NO_VALUE; size_t acc_at = -1; for (auto ele : acc) { if (ele.first != last) { @@ -455,59 +487,39 @@ namespace jlm::rvsdg::gvn { last = ele.first; } } - }else { - for (auto a : args) {new_gvn->push(a);} - } - // Handle the case where all args are the same - // such as in cond ? foo : foo - // foo - if (op & GVN_OP_IS_SWITCH) { - bool all_same = true; - if (args.size()) { - GVN_Val ele = args[0]; - for (auto a : args) { - if (a != ele) { - all_same=false; - break; - } - } - if (all_same) { - // Do not create a new gvn, use the same as each 'case' - return ele; - } - } } - GVN_Val v = CalculateHash(*new_gvn); + std::pair pr = CalculateHash(*new_gvn); - // Check if gvn is already in use, compare with existing gvn if so - if (gvn_.find(v) != gvn_.end()) { - if ( gvn_[v] ) { - auto prev = *(gvn_[v]); - if (!DepsEqual(prev, *new_gvn)) { - // Collision with internal node - v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); - stat_collisions++; - } - }else { - // Collision between internal and leaf node - v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); - stat_leaf_collisions++; - } - } + GVN_Val v = pr.first; + bool cannot_collide = pr.second; + if (cannot_collide) {return v;} //v was either a leaf or small number + // The memory usage for large dags of ca type operations might be too large if (new_gvn->args.size() > max_ca_size) { - // Avoid excess memory usage by returning a unique gvn - // and discarding all args - v &= ~GVN_HAS_DEPS; - v = FindUnique(v); - new_gvn = std::nullopt; stat_ca_too_big++; + return Leaf(); + } + + // Check if gvn is already in use, compare with existing gvn if so + if ( gvn_.find(v) != gvn_.end() ) { + if ( !gvn_[v] ) {throw std::runtime_error("Invariant violation.");} + + auto prev = *(gvn_[v]); + if (!DepsEqual(prev, *new_gvn)) { + // Collision with internal node + v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); + stat_collisions++; + } } // ----------------- commit --------------------- if (gvn_.find(v) == gvn_.end()) { - gvn_.insert({v,new_gvn}); + if ((v & GVN_IS_SYMBOLIC)) { + gvn_.insert({v,new_gvn}); + }else { + // gvn_.insert({v,std::nullopt}); + } } return v; } @@ -520,22 +532,25 @@ namespace jlm::rvsdg::gvn { GVN_Val g; do{ g = random() & ~GVN_MASK; - g |= flags; + g |= flags | GVN_IS_SYMBOLIC; }while(gvn_.find(g) != gvn_.end()); return g; } - GVN_Val CalculateHash(const GVN_Deps& deps) { + std::pair CalculateHash(const GVN_Deps& deps) { + // Return a gvn value based on operator and arguments + // The second element of the pair is true if the value cannot collide + // The lower bits are used to store properties for operations and // keep track of context dependence of values - GVN_Val flags = GVN_HAS_DEPS; + GVN_Val flags = 0; for (auto arg : deps.args) { flags |= arg.first & GVN_MASK_INHERIT; } - // Override GVN_IS_LOCAL_VALUE for operators introducing placeholders - if (GVN_ValueParamDependent(deps.op)) {flags |= GVN_IS_LOCAL_VALUE;} - if (GVN_ValueIsParamIndependent(deps.op)) {flags &= GVN_IS_LOCAL_VALUE;} + + if (deps.op == GVN_OP_BECOME_LOCAL) {flags |= GVN_IS_LOCAL_VALUE;} + if (deps.op == GVN_OP_BECOME_GLOBAL){flags &= ~GVN_IS_LOCAL_VALUE;} GVN_Val v = 0; if (GVN_ValueIsCA_Op(deps.op)) { @@ -562,8 +577,75 @@ namespace jlm::rvsdg::gvn { v ^= deps.args[i].first * (i+1); } } - v = (v & ~GVN_MASK) | flags; - return v; + + switch (deps.op) { + case GVN_OP_NEQ: { + // Note: NEQ cannot assume two symbol values are different. + bool must_be_different = false; + bool all_same = true; + for (size_t i = 1; i Date: Tue, 28 Oct 2025 20:16:29 +0100 Subject: [PATCH 36/52] Clean build --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 61 +++++++++++-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 2 +- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index da548e987..a8f9c90fc 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -21,6 +21,7 @@ #include "../../../tests/test-operation.hpp" #include "../../rvsdg/gamma.hpp" #include "../../rvsdg/lambda.hpp" +#include "../../rvsdg/delta.hpp" #include "../../rvsdg/MatchType.hpp" #include "../../rvsdg/node.hpp" #include "../../rvsdg/nullary.hpp" @@ -205,13 +206,15 @@ PartialRedundancyElimination::Run( auto statistics = Statistics::Create(module.SourceFilePath().value()); auto& root = rvsdg.GetRootRegion(); + this->TraverseTopDownRecursively(root, dump_region); + std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(root, dump_node); std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(root, initialize_stats); - this->TraverseTopDownRecursively(root, dump_region); + this->TraverseTopDownRecursively(root, dump_node); std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; @@ -219,11 +222,16 @@ PartialRedundancyElimination::Run( std::cout <DebugString() << node->GetNodeId() << std::endl; - MatchType(*node, [this](rvsdg::StructuralNode& sn){ - if (sn.ninputs() && sn.noutputs()){ - auto op_opaque = gvn_.FromPtr(&(sn.GetOperation()) ); - auto hash_call_out = gvn_.FromStr("hash_call_out"); - gvn_.Op(op_opaque); - for (size_t i = 0 ; i < sn.ninputs() ; i++){ - auto from = sn.input(i)->origin(); - gvn_.Arg( GVNOrFail(from, &sn) ); - } - auto hash_all_inputs = gvn_.End(); - for (size_t i = 0 ; i < sn.noutputs() ; i++){ - auto arg_pos = gvn_.FromWord(i); - auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); - RegisterGVN( sn.output(i), g_out ); - } + + if (node->ninputs() && node->noutputs()){ + auto op_opaque = gvn_.FromPtr(&(node->GetOperation()) ); + auto hash_call_out = gvn_.FromStr("hash_call_out"); + gvn_.Op(op_opaque); + for (size_t i = 0 ; i ninputs() ; i++){ + auto from = node->input(i)->origin(); + gvn_.Arg( GVNOrFail(from, node) ); } - }); + auto hash_all_inputs = gvn_.End(); + for (size_t i = 0 ; i < node->noutputs() ; i++){ + auto arg_pos = gvn_.FromWord(i); + auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); + RegisterGVN( node->output(i), g_out ); + } + } MatchType(node->GetOperation(), [this, node](const jlm::llvm::IntegerConstantOperation& iconst){ - auto v = gvn_.FromStr(iconst.Representation().str()); - RegisterGVN(node->output(0), v); + //std::cout << TR_RED << "FOUND: " << iconst.Representation().str() << TR_RESET << std::endl; + RegisterGVN( node->output(0), gvn_.FromStr(iconst.Representation().str()) ); }, [this, node](const jlm::rvsdg::BinaryOperation& binop){ if (binop.is_associative() && binop.is_commutative()){ @@ -312,6 +319,13 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) } } ); + + for (size_t i = 0; i < node->noutputs(); i++){ + if (output_to_gvn_.find(node->output(i)) == output_to_gvn_.end()){ + std::cout <DebugString() << node->GetNodeId() << std::endl; + RegisterGVN( node->output(i), gvn_.Leaf() ); + } + } } void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) @@ -379,7 +393,6 @@ void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) auto from = arg.input->origin(); RegisterGVN(arg.inner, GVNOrFail(from, node)); } - std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(node); }); @@ -395,7 +408,7 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) [this,node](rvsdg::ThetaNode& tn){ GVN_VisitThetaNode(node); }, - [this,node](rvsdg::ThetaNode& tn){ + [this,node](rvsdg::GammaNode& gn){ GVN_VisitGammaNode(node); }, [this,node](rvsdg::LambdaNode& ln){ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index b09f8c5df..e11d5fdcf 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -72,7 +72,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation std::unordered_map< rvsdg::Output *, rvsdg::gvn::GVN_Val> output_to_gvn_; void RegisterGVN(rvsdg::Output * output, rvsdg::gvn::GVN_Val gvn){ - output_to_gvn_[output] = gvn; + output_to_gvn_[output] = gvn; //overwrites old values } rvsdg::gvn::GVN_Manager gvn_; From ca3314c81922bafccd0068d36f0158de6060cc17 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 22:18:54 +0100 Subject: [PATCH 37/52] Theta gvn --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 95 ++++++++++++++++++- jlm/llvm/opt/PartialRedundancyElimination.hpp | 7 ++ jlm/llvm/opt/gvn.hpp | 17 ++++ 3 files changed, 114 insertions(+), 5 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index a8f9c90fc..7be496fc0 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -374,11 +374,96 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { - MatchType(*node, [this,node](rvsdg::ThetaNode& tn) - { - std::cout << ind() << TR_ORANGE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - GVN_VisitAllSubRegions(node); - throw std::runtime_error("Not implemented"); + MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ + using namespace jlm::rvsdg::gvn; + MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ + bool first_iter = false; + auto lv = tn.GetLoopVars(); + auto op_prism = gvn_.FromStr("prism"); + auto op_refract = gvn_.FromStr("refract"); + + /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ + + if (thetas_.find(node) == thetas_.end()){ + first_iter = true; + thetas_.insert({node, ThetaData()}); + thetas_[node].prism = 0; + + for (auto v : tn.GetLoopVars()){ + thetas_[node].pre.Add( GVNOrFail( v.input->origin() , node ) ); + thetas_[node].post.Add( 0 ); + } + }else{ + for (size_t i = 0; i < lv.size(); i++){ + thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); + } + } + + bool fracture = thetas_[node].pre.Fracture(); + + /** ----------------------------------- UPDATE PRISM -------------------------------------- */ + //DO LOOP TO UPDATE PRISM + //Always keep the most recent inputs in the pre buffer + while (fracture || first_iter){ + first_iter = false; + // ================= PERFORM GVN IN LOOP BODY ======================== + for (size_t i = 0; i < lv.size(); i++){ + RegisterGVN( lv[i].pre, thetas_[node].pre.elements[i].disruptor ); + } + + GVN_VisitAllSubRegions(node); + + for (size_t i = 0; i < lv.size(); i++){ + thetas_[node].post.elements[i].disruptor = GVNOrFail( lv[i].post->origin(), node ); + } + // ================= END LOOP BODY ==================================== + + thetas_[node].post.OrderByOriginal(); + for (size_t i = 0; i < lv.size(); i++){ + auto input = thetas_[node].pre.elements[i].disruptor; + auto output = thetas_[node].post.elements[i].disruptor; + auto merged_io = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(input).Arg(output).End(); + thetas_[node].post.elements[i].partition = merged_io; + } + + thetas_[node].prism = gvn_.FromPartitions(op_prism, thetas_[node].post); + thetas_[node].post.OrderByOriginal(); + + for (size_t i = 0; i < lv.size(); i++){ + auto pi = thetas_[node].post.elements[i].partition; + auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(thetas_[node].prism).End(); + + bool was_invariant = thetas_[node].pre.elements[i].disruptor == thetas_[node].post.elements[i].disruptor; + + thetas_[node].post.elements[i].partition = was_invariant ? GVN_INVARIANT : merged_with_prism; + if (! was_invariant ){ + thetas_[node].pre.elements[i].disruptor = merged_with_prism; + } + } + fracture = thetas_[node].pre.Fracture(); + if (!fracture){ + for (size_t i = 0; i < lv.size(); i++){ + thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); + } + } + } + + /** ----------------------------------- REFRACT INPUT -------------------------------------- */ + + GVN_Val input_hash = gvn_.FromDisruptors(op_prism, thetas_[node].pre); + thetas_[node].pre.OrderByOriginal(); + + for (size_t i = 0; i < lv.size(); i++){ + auto th_in = thetas_[node].pre.elements[i].disruptor; + auto th_out = gvn_.Op(op_refract) + .Arg(thetas_[node].prism) + .Arg(th_in) + .Arg(input_hash) + .End(); + bool invariance = thetas_[node].post.elements[i].partition == GVN_INVARIANT; + RegisterGVN(lv[i].output, invariance ? th_out : th_in); + } + }); }); } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index e11d5fdcf..60fca0f38 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -31,6 +31,12 @@ class Region; namespace jlm::llvm{ +struct ThetaData +{ + jlm::rvsdg::gvn::GVN_Val prism; + jlm::rvsdg::gvn::BrittlePrism pre; + jlm::rvsdg::gvn::BrittlePrism post; +}; /** \brief Partial Redundancy Elimination * @@ -69,6 +75,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation size_t stat_theta_count; size_t stat_gamma_count; + std::unordered_map thetas_; std::unordered_map< rvsdg::Output *, rvsdg::gvn::GVN_Val> output_to_gvn_; void RegisterGVN(rvsdg::Output * output, rvsdg::gvn::GVN_Val gvn){ diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index ed8dac4ef..37639d7e6 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -101,6 +101,12 @@ namespace jlm::rvsdg::gvn { } current_ordering = ORD_ORIGINAL; } + BrittlePrism() : did_shatter(false), fracture_count(0){ + current_ordering = ORD_ORIGINAL; + } + void Add(GVN_Val v){ + elements.emplace_back(BrittlePrismEle{v, v, v,elements.size()}); + } void OrderByPartition() { std::sort(elements.begin(), elements.end(), @@ -444,6 +450,17 @@ namespace jlm::rvsdg::gvn { return End(); } + GVN_Val FromDisruptors(GVN_Val op, BrittlePrism& brittle) { + Op(op); + brittle.OrderByDisruptorThenPartition(); + size_t i = 0; + while (i < brittle.elements.size()) { + Arg(brittle.elements[i].disruptor); + i += brittle.SpanDisruptor(i); + } + return End(); + } + private: void DefineConst(GVN_Val v) { From 2644386b4f7cc2a5352f5c850714b3c7583cbb35 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 22:21:03 +0100 Subject: [PATCH 38/52] Theta gvn --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 7be496fc0..6fbb2d9c7 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -379,8 +379,8 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ bool first_iter = false; auto lv = tn.GetLoopVars(); - auto op_prism = gvn_.FromStr("prism"); - auto op_refract = gvn_.FromStr("refract"); + auto OP_PRISM = gvn_.FromStr("prism"); + auto OP_REFRACT = gvn_.FromStr("refract"); /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ @@ -426,7 +426,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) thetas_[node].post.elements[i].partition = merged_io; } - thetas_[node].prism = gvn_.FromPartitions(op_prism, thetas_[node].post); + thetas_[node].prism = gvn_.FromPartitions(OP_PRISM, thetas_[node].post); thetas_[node].post.OrderByOriginal(); for (size_t i = 0; i < lv.size(); i++){ @@ -450,15 +450,15 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) /** ----------------------------------- REFRACT INPUT -------------------------------------- */ - GVN_Val input_hash = gvn_.FromDisruptors(op_prism, thetas_[node].pre); + GVN_Val hash_all_inputs = gvn_.FromDisruptors(OP_REFRACT, thetas_[node].pre); thetas_[node].pre.OrderByOriginal(); for (size_t i = 0; i < lv.size(); i++){ auto th_in = thetas_[node].pre.elements[i].disruptor; - auto th_out = gvn_.Op(op_refract) + auto th_out = gvn_.Op(OP_REFRACT) .Arg(thetas_[node].prism) .Arg(th_in) - .Arg(input_hash) + .Arg(hash_all_inputs) .End(); bool invariance = thetas_[node].post.elements[i].partition == GVN_INVARIANT; RegisterGVN(lv[i].output, invariance ? th_out : th_in); From aacd6d0f79b665c69dfd7042fb7c1e3787dc1b39 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 28 Oct 2025 22:28:54 +0100 Subject: [PATCH 39/52] Fixed bug where local should have been field. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 7 +++---- jlm/llvm/opt/PartialRedundancyElimination.hpp | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 6fbb2d9c7..1bf44edee 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -377,7 +377,6 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ using namespace jlm::rvsdg::gvn; MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ - bool first_iter = false; auto lv = tn.GetLoopVars(); auto OP_PRISM = gvn_.FromStr("prism"); auto OP_REFRACT = gvn_.FromStr("refract"); @@ -385,8 +384,8 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ if (thetas_.find(node) == thetas_.end()){ - first_iter = true; thetas_.insert({node, ThetaData()}); + thetas_[node].first_iteration = true; thetas_[node].prism = 0; for (auto v : tn.GetLoopVars()){ @@ -404,8 +403,8 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) /** ----------------------------------- UPDATE PRISM -------------------------------------- */ //DO LOOP TO UPDATE PRISM //Always keep the most recent inputs in the pre buffer - while (fracture || first_iter){ - first_iter = false; + while (fracture || thetas_[node].first_iteration){ + thetas_[node].first_iteration = false; // ================= PERFORM GVN IN LOOP BODY ======================== for (size_t i = 0; i < lv.size(); i++){ RegisterGVN( lv[i].pre, thetas_[node].pre.elements[i].disruptor ); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 60fca0f38..343cd47bc 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -33,6 +33,7 @@ namespace jlm::llvm{ struct ThetaData { + bool first_iteration; jlm::rvsdg::gvn::GVN_Val prism; jlm::rvsdg::gvn::BrittlePrism pre; jlm::rvsdg::gvn::BrittlePrism post; From a6a9ae6beb7d06627ba4ee7c5257b09f11f9e843 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Thu, 30 Oct 2025 10:19:58 +0100 Subject: [PATCH 40/52] Found bug in arithmetic --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 62 ++++++- jlm/llvm/opt/gvn.hpp | 153 ++++++++++-------- 2 files changed, 148 insertions(+), 67 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 1bf44edee..acb15358b 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -231,6 +231,9 @@ PartialRedundancyElimination::Run( TraverseTopDownRecursively(root, dump_node); std::cout << TR_PURPLE << "=================================================" << TR_RESET << std::endl; + if (gvn_.stat_collisions){ + throw std::runtime_error("gvn_.stat_collisions"); + } // PassDownRegion(root); } @@ -299,6 +302,8 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) auto hash_all_inputs = gvn_.End(); for (size_t i = 0 ; i < node->noutputs() ; i++){ auto arg_pos = gvn_.FromWord(i); + std::cout << TR_YELLOW << "hash_call_out: " << hash_call_out << std::endl; + std::cout << TR_YELLOW << "op_opaque: " << op_opaque << std::endl; auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); RegisterGVN( node->output(i), g_out ); } @@ -313,8 +318,12 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) if (binop.is_associative() && binop.is_commutative()){ JLM_ASSERT(node->ninputs() >= 2); auto op = gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OPERATOR_IS_CA); + auto a = GVNOrFail( node->input(0)->origin(), node); auto b = GVNOrFail( node->input(1)->origin(), node); + + std::cout << TR_RED << "BINOP" << op << "[" << a << " , " << b << "]" << TR_RESET << std::endl; + RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); } } @@ -322,8 +331,13 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) for (size_t i = 0; i < node->noutputs(); i++){ if (output_to_gvn_.find(node->output(i)) == output_to_gvn_.end()){ - std::cout <DebugString() << node->GetNodeId() << std::endl; - RegisterGVN( node->output(i), gvn_.Leaf() ); + if (node->DebugString() == std::string("undef")){ + //std::cout <DebugString() << node->GetNodeId() << std::endl; + RegisterGVN( node->output(i), gvn_.FromStr("undef") ); + }else{ + std::cout << std::endl << ind() << TR_RED << "Warning: Missing out from:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + RegisterGVN( node->output(i), gvn_.FromStr("missing") ); + } } } } @@ -381,10 +395,14 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) auto OP_PRISM = gvn_.FromStr("prism"); auto OP_REFRACT = gvn_.FromStr("refract"); + std::cout << TR_CYAN << "OPS" << OP_PRISM << "-----------" << OP_REFRACT << std::endl; + /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ if (thetas_.find(node) == thetas_.end()){ + std::cout << TR_PINK << "CREATED THETA DATA:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; thetas_.insert({node, ThetaData()}); + thetas_[node].first_iteration = true; thetas_[node].prism = 0; @@ -392,13 +410,26 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) thetas_[node].pre.Add( GVNOrFail( v.input->origin() , node ) ); thetas_[node].post.Add( 0 ); } + if (tn.GetLoopVars().size() != thetas_[node].pre.elements.size()){ + throw std::runtime_error("pre and lv mismatch"); + } + if (tn.GetLoopVars().size() != thetas_[node].post.elements.size()){ + throw std::runtime_error("post and lv mismatch"); + } + }else{ for (size_t i = 0; i < lv.size(); i++){ thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); } } + std::cout << TR_ORANGE << "Before loop body" << std::endl; + thetas_[node].pre.OrderByOriginal(); + thetas_[node].pre.dump(); + std::cout << TR_RESET << std::endl; + bool fracture = thetas_[node].pre.Fracture(); + thetas_[node].pre.OrderByOriginal(); /** ----------------------------------- UPDATE PRISM -------------------------------------- */ //DO LOOP TO UPDATE PRISM @@ -415,8 +446,16 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) for (size_t i = 0; i < lv.size(); i++){ thetas_[node].post.elements[i].disruptor = GVNOrFail( lv[i].post->origin(), node ); } + + std::cout << TR_GREEN << "Output from loop" << std::endl; + thetas_[node].post.OrderByOriginal(); + thetas_[node].post.dump(); + std::cout << TR_RESET << std::endl; + + // ================= END LOOP BODY ==================================== + thetas_[node].pre.OrderByOriginal(); thetas_[node].post.OrderByOriginal(); for (size_t i = 0; i < lv.size(); i++){ auto input = thetas_[node].pre.elements[i].disruptor; @@ -439,7 +478,22 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) thetas_[node].pre.elements[i].disruptor = merged_with_prism; } } + std::cout << TR_RED << "End prism update ------------------------------------" << std::endl; + thetas_[node].pre.dump_partitions(); + std::cout << TR_YELLOW << std::endl; + thetas_[node].post.dump(); + std::cout << TR_RESET << std::endl; + fracture = thetas_[node].pre.Fracture(); + + if (fracture) + { + std::cout << TR_PINK << "/*/*/*/*//*//**/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*//" << TR_RESET << std::endl; + } + + thetas_[node].pre.OrderByOriginal(); + + std::cout << TR_RESET << std::endl; if (!fracture){ for (size_t i = 0; i < lv.size(); i++){ thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); @@ -451,6 +505,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) GVN_Val hash_all_inputs = gvn_.FromDisruptors(OP_REFRACT, thetas_[node].pre); thetas_[node].pre.OrderByOriginal(); + thetas_[node].post.OrderByOriginal(); for (size_t i = 0; i < lv.size(); i++){ auto th_in = thetas_[node].pre.elements[i].disruptor; @@ -469,8 +524,9 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) { MatchType(*node, [this,node](rvsdg::LambdaNode& ln){ + size_t i = 0; for (auto arg : ln.GetFunctionArguments()){ - RegisterGVN(arg, gvn_.Leaf()); + RegisterGVN(arg, gvn_.FromStr("Param:" + std::to_string(i)) ); i++; } for (auto arg : ln.GetContextVars()) { diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 37639d7e6..f576c9bcc 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -190,25 +190,25 @@ namespace jlm::rvsdg::gvn { std::cout << "["< elements.size() * 2) { - throw std::runtime_error("Brittle prism invariant broken. Possible missing updates to by reassign callback."); - } - } - return did_fracture; + // Partition again based on disruptors + // Either because a single partition faces distinct disruptors or when + // the disruptor does equal the original value. + bool did_fracture = false; + CheckDisruptorInvariants(); + OrderByPartitionThenDisruptor(); + + size_t it = 0; + while (it < elements.size()) { + size_t pspan = SpanPartition(it); + size_t gspan = SpanDisruptorAndPartition(it); + size_t p_end = it + pspan; + + if (pspan == gspan) { + // Check if value changes for first time + while (it < p_end) { + BrittlePrismEle& e = elements[it]; + if (e.original_partition == e.partition && e.disruptor != e.original_partition) { + elements[it].partition = e.disruptor; + did_fracture = true; + } + it++; + } + }else { + // Partition was given multiple different disruptors + did_fracture = true; + while (it < p_end) { + BrittlePrismEle& e = elements[it]; + if (e.partition != e.disruptor) { + elements[it].partition = e.disruptor; + } + it++; + } + } + } + if (did_fracture) { + fracture_count++; + if (fracture_count > elements.size() * 2) { + throw std::runtime_error("Brittle prism invariant broken. Possible missing updates to by reassign callback."); + } + } + OrderByOriginal(); + return did_fracture; } static void Test0(); @@ -447,7 +448,8 @@ namespace jlm::rvsdg::gvn { Arg(brittle.elements[i].partition); i += brittle.SpanPartition(i); } - return End(); + brittle.OrderByOriginal(); + return End(); } GVN_Val FromDisruptors(GVN_Val op, BrittlePrism& brittle) { @@ -458,6 +460,7 @@ namespace jlm::rvsdg::gvn { Arg(brittle.elements[i].disruptor); i += brittle.SpanDisruptor(i); } + brittle.OrderByOriginal(); return End(); } @@ -484,8 +487,9 @@ namespace jlm::rvsdg::gvn { auto ad = *(gvn_[arg]); if (ad.op == new_gvn->op) { for (auto leaf : ad.args) {acc.emplace_back(leaf);} - }else { - acc.emplace_back(arg, 1); + } else { + GVN_Val h_with_op = arg * op; + acc.emplace_back(h_with_op, 1); } }else { acc.emplace_back(arg, 1); @@ -504,6 +508,16 @@ namespace jlm::rvsdg::gvn { last = ele.first; } } + + std::cout << "*******************************************************************" << std::endl; + std::cout << "*******************************************************************" << std::endl; + for (auto ele : new_gvn->args){ + std::cout << op << " LEAVES: " << ele.first << " " << ele.second << std::endl; + } + std::cout << "*******************************************************************" << std::endl; + std::cout << "*******************************************************************" << std::endl; + + } std::pair pr = CalculateHash(*new_gvn); @@ -526,7 +540,26 @@ namespace jlm::rvsdg::gvn { if (!DepsEqual(prev, *new_gvn)) { // Collision with internal node v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); + + std::cout << prev.op << " " << new_gvn->op << " " << std::endl; + if (new_gvn->args.size() == prev.args.size()){ + for (size_t i = 0; i < new_gvn->args.size(); i++){ + std::cout << new_gvn->args[i].first << " ?? " << prev.args[i].first << std::endl; + std::cout << new_gvn->args[i].second << " ?? " << prev.args[i].second << std::endl; + std::cout << "---------------------------------" << std::endl; + } + }else{ + std::cout << "Arg len mismatch..." << std::endl; + for (size_t i = 0; i < new_gvn->args.size(); i++){ + std::cout << new_gvn->args[i].first << " ?? " << new_gvn->args[i].second << std::endl; + } + for (size_t i = 0; i < new_gvn->args.size(); i++){ + std::cout << prev.args[i].first << " ?? " << prev.args[i].second << std::endl; + } + + } stat_collisions++; + throw std::runtime_error("Collision between:"); } } @@ -576,22 +609,14 @@ namespace jlm::rvsdg::gvn { * Take the sum of hashes for internal nodes. */ - for (size_t i = 0; i < deps.args.size(); ++i) { - if (GVN_ValueHasDeps(deps.args[i].first)) { - auto ad = *(gvn_[deps.args[i].first]); - if (ad.op == deps.op) { - v += deps.args[i].first; - }else { - v += deps.args[i].first * deps.op; - } - }else { - v += deps.args[i].first * deps.op; - } + for (size_t i = 0; i < deps.args.size(); i++) { + v += deps.args[i].first * deps.args[i].second; } }else { //std::cout << "+++++++++++++++STANDARD HASHER+++++++++++++++++"< Date: Fri, 31 Oct 2025 14:37:51 +0100 Subject: [PATCH 41/52] Working theta hash --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 287 ++++++++++-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 9 +- jlm/llvm/opt/gvn.cpp | 140 +-------- jlm/llvm/opt/gvn.hpp | 271 ++++++++--------- 4 files changed, 301 insertions(+), 406 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index acb15358b..6ef1c31ef 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -54,6 +54,7 @@ #include "../../rvsdg/binary.hpp" #include "../../util/common.hpp" +#include "../ir/types.hpp" #include "gvn.hpp" #include @@ -238,13 +239,12 @@ PartialRedundancyElimination::Run( } /** -------------------------------------------------------------------------------------------- **/ - +static size_t reg_counter = 0; void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, rvsdg::Node* node) { std::string name = node->DebugString() + std::to_string(node->GetNodeId()); - size_t reg_counter = 0; - MatchType(*node, [&name, ®_counter](rvsdg::StructuralNode& sn) + MatchType(*node, [&name](rvsdg::StructuralNode& sn) { for (auto& reg : sn.Subregions()) { @@ -253,7 +253,7 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, jlm::llvm::LlvmDotWriter my_dot_writer; my_dot_writer.WriteGraphs(my_graph_writer , reg, false); - std::string full_name = name+std::to_string(reg_counter++)+".dot"; + std::string full_name = "reg_dump/" + name+"__"+std::to_string(reg_counter)+"__.dot"; reg_counter++; std::cout<< TR_RED< > io_state; + std::optional< std::pair > mem_state; +}; +/* +static StateEdgesAt findStateIndices(rvsdg::Node* node) +{ + StateEdgesAt edge_indices; + edge_indices.io_state = std::make_pair(0,0); + edge_indices.mem_state = std::make_pair(0,0); + + bool found_io_state = false; + bool found_mem_state = false; + + for (size_t i = 0 ; i < node->ninputs() ; i++){ + if ( rvsdg::is(node->input(i)->Type()) ){ + edge_indices.mem_state->first = i; + found_io_state = true; + } + if ( rvsdg::is(node->input(i)->Type()) ){ + edge_indices.io_state->first = i; + found_mem_state = true; + } + } + + for (size_t o = 0 ; o < node->noutputs() ; o++){ + if ( rvsdg::is(node->output(o)->Type()) ){ + edge_indices.mem_state->second = o; + } + if ( rvsdg::is(node->input(o)->Type()) ){ + edge_indices.io_state->second = o; + } + } + + if (!found_io_state){edge_indices.io_state = std::nullopt;} + if (!found_mem_state){edge_indices.mem_state = std::nullopt;} + + return edge_indices; +} +*/ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) { std::cout << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; if (node->ninputs() && node->noutputs()){ - auto op_opaque = gvn_.FromPtr(&(node->GetOperation()) ); + auto op_opaque = gvn_.FromStr( node->DebugString() ); auto hash_call_out = gvn_.FromStr("hash_call_out"); gvn_.Op(op_opaque); for (size_t i = 0 ; i ninputs() ; i++){ auto from = node->input(i)->origin(); - gvn_.Arg( GVNOrFail(from, node) ); + gvn_.Arg( GVNOrWarn(from, node) ); } auto hash_all_inputs = gvn_.End(); for (size_t i = 0 ; i < node->noutputs() ; i++){ auto arg_pos = gvn_.FromWord(i); - std::cout << TR_YELLOW << "hash_call_out: " << hash_call_out << std::endl; - std::cout << TR_YELLOW << "op_opaque: " << op_opaque << std::endl; + //std::cout << TR_YELLOW << "hash_call_out: " << hash_call_out << std::endl; + //std::cout << TR_YELLOW << "op_opaque: " << op_opaque << std::endl; auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); RegisterGVN( node->output(i), g_out ); } @@ -312,20 +353,26 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) MatchType(node->GetOperation(), [this, node](const jlm::llvm::IntegerConstantOperation& iconst){ //std::cout << TR_RED << "FOUND: " << iconst.Representation().str() << TR_RESET << std::endl; - RegisterGVN( node->output(0), gvn_.FromStr(iconst.Representation().str()) ); + size_t value = 0; + auto istr = iconst.Representation().str(); + for (size_t i = 0 ; i < istr.length() ; i++){ + value += istr[i] == '1' ? 1 << i : 0; + } + RegisterGVN( node->output(0), gvn_.FromWord(value) ); }, [this, node](const jlm::rvsdg::BinaryOperation& binop){ - if (binop.is_associative() && binop.is_commutative()){ - JLM_ASSERT(node->ninputs() >= 2); - auto op = gvn_.FromStr(binop.debug_string(), rvsdg::gvn::GVN_OPERATOR_IS_CA); + using namespace jlm::rvsdg::gvn; + JLM_ASSERT(node->ninputs() >= 2); + auto op = gvn_.FromStr(binop.debug_string()); + if (binop.debug_string() == "IAdd"){op = GVN_OP_ADDITION; } + if (binop.debug_string() == "IMul"){op = GVN_OP_MULTIPLY; } - auto a = GVNOrFail( node->input(0)->origin(), node); - auto b = GVNOrFail( node->input(1)->origin(), node); + auto a = GVNOrWarn( node->input(0)->origin(), node); + auto b = GVNOrWarn( node->input(1)->origin(), node); - std::cout << TR_RED << "BINOP" << op << "[" << a << " , " << b << "]" << TR_RESET << std::endl; + //std::cout << TR_RED << "BINOP" << to_string(op) << "[" << to_string(a) << " , " << to_string(b) << "]" << TR_RESET << std::endl; - RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); - } + RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); } ); @@ -333,9 +380,9 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) if (output_to_gvn_.find(node->output(i)) == output_to_gvn_.end()){ if (node->DebugString() == std::string("undef")){ //std::cout <DebugString() << node->GetNodeId() << std::endl; - RegisterGVN( node->output(i), gvn_.FromStr("undef") ); + RegisterGVN( node->output(i), rvsdg::gvn::GVN_NO_VALUE ); }else{ - std::cout << std::endl << ind() << TR_RED << "Warning: Missing out from:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + //std::cout << std::endl << ind() << TR_RED << "Warning: Missing out from:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; RegisterGVN( node->output(i), gvn_.FromStr("missing") ); } } @@ -351,13 +398,13 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) for (auto br : gn.GetEntryVars()){ auto out = br.input->origin(); for (auto into_branch : br.branchArgument){ - RegisterGVN(into_branch, GVNOrFail(out, node)); + RegisterGVN(into_branch, GVNOrWarn(out, node)); } } auto selector = gn.GetMatchVar().input->origin(); - auto match_var = GVNOrFail(selector, node); + auto match_var = GVNOrWarn(selector, node); for (auto mv : gn.GetMatchVar().matchContent){ - RegisterGVN( mv, GVNOrFail(selector, node)); + RegisterGVN( mv, GVNOrWarn(selector, node)); } GVN_VisitAllSubRegions(node); @@ -388,136 +435,108 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { + MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ using namespace jlm::rvsdg::gvn; - MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ - auto lv = tn.GetLoopVars(); - auto OP_PRISM = gvn_.FromStr("prism"); - auto OP_REFRACT = gvn_.FromStr("refract"); - - std::cout << TR_CYAN << "OPS" << OP_PRISM << "-----------" << OP_REFRACT << std::endl; + auto GVN_INVARIANT = gvn_.FromStr("INVARIANT"); + auto LOOP_EXIT = gvn_.FromStr("LOOP_EXIT"); + auto lv = tn.GetLoopVars(); + auto OP_PRISM = gvn_.FromStr("prism"); - /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ + /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ - if (thetas_.find(node) == thetas_.end()){ - std::cout << TR_PINK << "CREATED THETA DATA:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - thetas_.insert({node, ThetaData()}); + if (thetas_.find(node) == thetas_.end()){ + thetas_.insert({node, ThetaData()}); - thetas_[node].first_iteration = true; - thetas_[node].prism = 0; + thetas_[node].prism = 0; // This value is unique for each non-isomorphic theta - for (auto v : tn.GetLoopVars()){ - thetas_[node].pre.Add( GVNOrFail( v.input->origin() , node ) ); - thetas_[node].post.Add( 0 ); - } - if (tn.GetLoopVars().size() != thetas_[node].pre.elements.size()){ - throw std::runtime_error("pre and lv mismatch"); - } - if (tn.GetLoopVars().size() != thetas_[node].post.elements.size()){ - throw std::runtime_error("post and lv mismatch"); - } - - }else{ - for (size_t i = 0; i < lv.size(); i++){ - thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); - } + for (auto v : tn.GetLoopVars()){ + thetas_[node].pre.Add( GVNOrWarn( v.input->origin() , node ) ); + thetas_[node].post.Add( 0 ); } - std::cout << TR_ORANGE << "Before loop body" << std::endl; - thetas_[node].pre.OrderByOriginal(); + std::cout << TR_YELLOW << "THETA INPUTS: " << node->GetNodeId() << std::endl; thetas_[node].pre.dump(); - std::cout << TR_RESET << std::endl; - - bool fracture = thetas_[node].pre.Fracture(); - thetas_[node].pre.OrderByOriginal(); - - /** ----------------------------------- UPDATE PRISM -------------------------------------- */ - //DO LOOP TO UPDATE PRISM - //Always keep the most recent inputs in the pre buffer - while (fracture || thetas_[node].first_iteration){ - thetas_[node].first_iteration = false; - // ================= PERFORM GVN IN LOOP BODY ======================== - for (size_t i = 0; i < lv.size(); i++){ - RegisterGVN( lv[i].pre, thetas_[node].pre.elements[i].disruptor ); - } - - GVN_VisitAllSubRegions(node); - - for (size_t i = 0; i < lv.size(); i++){ - thetas_[node].post.elements[i].disruptor = GVNOrFail( lv[i].post->origin(), node ); + std::cout << TR_RESET; + }else{ + for (size_t i = 0; i < lv.size(); i++){ + auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); + auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); + if ( thetas_[node].post.elements[i].disruptor == GVN_INVARIANT){ + thetas_[node].pre.elements[i].disruptor = from_outer; + }else{ + thetas_[node].pre.elements[i].disruptor = merged; } + } + } - std::cout << TR_GREEN << "Output from loop" << std::endl; - thetas_[node].post.OrderByOriginal(); - thetas_[node].post.dump(); - std::cout << TR_RESET << std::endl; - - - // ================= END LOOP BODY ==================================== + /** ----------------------------------- UPDATE PRISM -------------------------------------- */ + // The pre buffer contains either the most recent inputs for invariant values + // or the superposition of values across all evaluations. - thetas_[node].pre.OrderByOriginal(); - thetas_[node].post.OrderByOriginal(); - for (size_t i = 0; i < lv.size(); i++){ - auto input = thetas_[node].pre.elements[i].disruptor; - auto output = thetas_[node].post.elements[i].disruptor; - auto merged_io = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(input).Arg(output).End(); - thetas_[node].post.elements[i].partition = merged_io; - } + do{ + // ================= PERFORM GVN IN LOOP BODY ======================== - thetas_[node].prism = gvn_.FromPartitions(OP_PRISM, thetas_[node].post); - thetas_[node].post.OrderByOriginal(); - - for (size_t i = 0; i < lv.size(); i++){ - auto pi = thetas_[node].post.elements[i].partition; - auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(thetas_[node].prism).End(); + for (size_t i = 0; i < lv.size(); i++){ + RegisterGVN( lv[i].pre, thetas_[node].pre.elements[i].disruptor ); + } - bool was_invariant = thetas_[node].pre.elements[i].disruptor == thetas_[node].post.elements[i].disruptor; + GVN_VisitAllSubRegions(node); - thetas_[node].post.elements[i].partition = was_invariant ? GVN_INVARIANT : merged_with_prism; - if (! was_invariant ){ - thetas_[node].pre.elements[i].disruptor = merged_with_prism; - } - } - std::cout << TR_RED << "End prism update ------------------------------------" << std::endl; - thetas_[node].pre.dump_partitions(); - std::cout << TR_YELLOW << std::endl; - thetas_[node].post.dump(); - std::cout << TR_RESET << std::endl; + for (size_t i = 0; i < lv.size(); i++){ + thetas_[node].post.elements[i].disruptor = GVNOrWarn( lv[i].post->origin(), node ); + } - fracture = thetas_[node].pre.Fracture(); + // ================= END LOOP BODY ==================================== - if (fracture) - { - std::cout << TR_PINK << "/*/*/*/*//*//**/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*//" << TR_RESET << std::endl; - } + for (size_t i = 0; i < lv.size(); i++){ + auto input = thetas_[node].pre.elements[i].disruptor; + auto output = thetas_[node].post.elements[i].disruptor; + auto merged_io = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(input).Arg(output).End(); + thetas_[node].post.elements[i].partition = merged_io; + } - thetas_[node].pre.OrderByOriginal(); + GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); + /// This hash depends on the mapping between inputs to ouputs as well as the predicate + /// Does not depend on order of loop variables or duplicate loop variables + thetas_[node].prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(thetas_[node].post).End(); + thetas_[node].post.OrderByOriginal(); - std::cout << TR_RESET << std::endl; - if (!fracture){ - for (size_t i = 0; i < lv.size(); i++){ - thetas_[node].pre.elements[i].disruptor = GVNOrFail( lv[i].input->origin(), node ); - } + for (size_t i = 0; i < lv.size(); i++){ + auto pi = thetas_[node].post.elements[i].partition; + auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(thetas_[node].prism).End(); + bool was_invariant = thetas_[node].pre.elements[i].disruptor == thetas_[node].post.elements[i].disruptor; + + // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() + // state edges can be made invariant inside thetas only + // if they pass through referentially transparent nodes. + // Computing referential transparency + + if (was_invariant){ + thetas_[node].post.elements[i].partition = GVN_INVARIANT; + }else{ + thetas_[node].post.elements[i].partition = merged_with_prism; + auto loop_old = thetas_[node].pre.elements[i].disruptor; + thetas_[node].pre.elements[i].disruptor = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(merged_with_prism).Arg(loop_old).End(); } } - /** ----------------------------------- REFRACT INPUT -------------------------------------- */ - GVN_Val hash_all_inputs = gvn_.FromDisruptors(OP_REFRACT, thetas_[node].pre); - thetas_[node].pre.OrderByOriginal(); - thetas_[node].post.OrderByOriginal(); + } while (thetas_[node].pre.Fracture()); - for (size_t i = 0; i < lv.size(); i++){ - auto th_in = thetas_[node].pre.elements[i].disruptor; - auto th_out = gvn_.Op(OP_REFRACT) - .Arg(thetas_[node].prism) - .Arg(th_in) - .Arg(hash_all_inputs) - .End(); - bool invariance = thetas_[node].post.elements[i].partition == GVN_INVARIANT; - RegisterGVN(lv[i].output, invariance ? th_out : th_in); + /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ + + for (size_t i = 0; i < lv.size(); i++){ + auto inv = thetas_[node].post.elements[i].partition == GVN_INVARIANT; + if (inv){ + RegisterGVN(lv[i].output, GVNOrWarn(lv[i].input->origin(), node)); + }else{ + // The gvn for loop output variables must be different from the value exiting the loop. + auto superimposed_values_inside_loop = thetas_[node].pre.elements[i].disruptor; + auto g = gvn_.Op(LOOP_EXIT).Arg(superimposed_values_inside_loop).End(); + RegisterGVN(lv[i].output, g); } - }); + } }); } @@ -531,7 +550,7 @@ void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) for (auto arg : ln.GetContextVars()) { auto from = arg.input->origin(); - RegisterGVN(arg.inner, GVNOrFail(from, node)); + RegisterGVN(arg.inner, GVNOrWarn(from, node)); } std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(node); @@ -563,6 +582,7 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node* node) { + using namespace jlm::rvsdg::gvn; std::cout << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; MatchType(*node, [&pe, &node](rvsdg::LambdaNode& ln){ @@ -577,13 +597,18 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r { JLM_ASSERT(node->noutputs() == 1); std::cout << TR_CYAN; - std::cout << pe->GVNOrZero( node->output(0) ); + std::cout << to_string(pe->GVNOrZero( node->output(0) )); std::cout << TR_RESET; }); - for (size_t i = 0; i < node->noutputs(); i++){ + for (size_t i = 0; i < node->ninputs(); i++){ std::cout << TR_GREEN; - std::cout << " : " << pe->GVNOrZero( node->output(i) ); + std::cout << " : " << to_string(pe->GVNOrZero( node->input(i)->origin() )); + } + std::cout << TR_GRAY << " => "; + for (size_t i = 0; i < node->noutputs(); i++){ + std::cout << TR_RED; + std::cout << " : " << to_string(pe->GVNOrZero( node->output(i) )); } std::cout << std::endl; } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 343cd47bc..a861451fe 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -91,7 +91,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation return rvsdg::gvn::GVN_NO_VALUE; } - inline rvsdg::gvn::GVN_Val GVNOrFail(rvsdg::Output* edge, rvsdg::Node* ctx_node){ + inline rvsdg::gvn::GVN_Val GVNOrWarn(rvsdg::Output* edge, rvsdg::Node* ctx_node){ if (output_to_gvn_.find(edge) != output_to_gvn_.end()){ return output_to_gvn_[edge]; } @@ -101,6 +101,13 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation return rvsdg::gvn::GVN_NO_VALUE; } + inline rvsdg::gvn::GVN_Val GVNOrPanic(rvsdg::Output* edge, rvsdg::Node* ctx_node){ + if (output_to_gvn_.find(edge) != output_to_gvn_.end()){ + return output_to_gvn_[edge]; + } + throw std::runtime_error("Logic error: missing input for edge" + ctx_node->DebugString() + std::to_string(ctx_node->GetNodeId())); + } + /// ----------------------------------------------------------- void GVN_VisitRegion(rvsdg::Region& reg); diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp index 1948a1db2..bf8aa72f3 100644 --- a/jlm/llvm/opt/gvn.cpp +++ b/jlm/llvm/opt/gvn.cpp @@ -13,63 +13,7 @@ namespace jlm::rvsdg::gvn { void GVN_Manager::Test0() { - GVN_Manager gm; - auto my_op = gm.Leaf(); - auto my_ca = gm.Leaf(GVN_OPERATOR_IS_CA); - - auto x = gm.Leaf(); - auto y = gm.Leaf(); - - if (x == y) { - throw std::runtime_error("Expected unique hashes"); - } - if (gvn_verbose) { - std::cout << "X: " << to_string(x) << std::endl; - std::cout << "Y: " << to_string(y) << std::endl; - - std::cout << "MyOp: " << to_string(my_op) << std::endl; - std::cout << "MyCa: " << to_string(my_ca) << std::endl; - } - { - GVN_Deps d; - d.op = my_op; - d.push(x); - d.push(y); - if (gvn_verbose){std::cout << "Hash of x - y is : " << to_string(gm.CalculateHash(d).first) << std::endl;} - } - { - GVN_Deps d2; - d2.op = my_op; - d2.push(y); - d2.push(x); - if (gvn_verbose){std::cout << "Hash of y - x is : " << to_string(gm.CalculateHash(d2).first) << std::endl;} - } - { - GVN_Deps d3; - d3.op = my_ca; - d3.push(y); - d3.push(x); - gm.gvn_.insert({gm.CalculateHash(d3).first, d3}); - - GVN_Deps d4; - d4.op = my_ca; - d4.push(y); - d4.push(gm.CalculateHash(d3).first); - - GVN_Deps d5; - d5.op = my_ca; - d5.push(gm.CalculateHash(d3).first); - d5.push(y); - - if (gm.CalculateHash(d4).first != gm.CalculateHash(d5).first) { - throw std::runtime_error("Test failed: expected equal hashes from associative and commutative op"); - } - if (gvn_verbose) { - std::cout << "y+x " << to_string(gm.CalculateHash(d3).first) << std::endl; - std::cout << "y + (y+x): " << to_string(gm.CalculateHash(d4).first) << std::endl; - std::cout << "(y+x) + y: " << to_string(gm.CalculateHash(d5).first) << std::endl; - } - } + // TODO: arithmentic tests } void GVN_Manager::Test1() { @@ -106,69 +50,10 @@ namespace jlm::rvsdg::gvn { void GVN_Manager::Test3() { GVN_Manager gm; - auto a = gm.Leaf(); - auto b = gm.Leaf(); - auto c = gm.Leaf(); - auto d = c; - - auto op_add = gm.FromStr("+", GVN_OPERATOR_IS_CA); - auto one = gm.FromStr("1"); - - // loop ( - // a + 1 -> a - // b + a -> b - // c -> d - // d -> c - // ) - - GVN_Val last = 0; - for (size_t k = 0; k < 4; k++) { - std::optional< BrittlePrism > prevars = std::nullopt; - std::optional< BrittlePrism > postvars = std::nullopt; - - GVN_Val a_out = 0; - GVN_Val b_out = 0; - GVN_Val c_out = 0; - GVN_Val d_out = 0; - size_t max_iter = 100; - - std::cout << "INIT: " << a_out << " - " << b_out << " - " << c_out << " - " << d_out << std::endl; - - while (max_iter) { - max_iter--; - if (!prevars) { - prevars = BrittlePrism({a,b,c,d}); - }else { - prevars->elements[0].disruptor = a_out; - prevars->elements[1].disruptor = b_out; - prevars->elements[2].disruptor = c_out; - prevars->elements[3].disruptor = d_out; - - if (! prevars->Fracture() ){break;} - } - a_out = gm.Op(op_add).Args({a, one}); - b_out = gm.Op(op_add).Args({b,a}); - c_out = d; - d_out = c; - if (!postvars) { - postvars = BrittlePrism( {a_out, b_out, c_out, d_out}); - }else { - postvars->elements[0].disruptor = a_out; - postvars->elements[1].disruptor = b_out; - postvars->elements[2].disruptor = c_out; - postvars->elements[3].disruptor = d_out; - if (! postvars->Fracture() ){break;} - } - } - - std::cout << "after: " << a_out << " - " << b_out << " - " << c_out << " - " << d_out << std::endl; - - GVN_Val h = gm.Op(GVN_OP_ANY_ORDERED).Args({a_out,b_out,c_out,d_out}); - if (last && h != last && !(h & GVN_FROM_COLLISION)) { - std::cout << to_string(last) << " " << to_string(h) << std::endl; - throw std::runtime_error("Hashes where different across iterations"); - } - last = h; + auto a = gm.Leaf(); + auto b = gm.FromWord(88); + if ( gm.Op(GVN_OP_ADDITION).Args({a,b}) != gm.Op(GVN_OP_ADDITION).Args({b,GVN_IGNORE,a,GVN_IGNORE}) ) { + throw std::runtime_error("Should have ignored some args"); } } @@ -297,15 +182,22 @@ namespace jlm::rvsdg::gvn { void GVN_Manager::Test5() { - if (GVN_MASK & GVN_SMALL_VALUE) { - throw std::runtime_error("Flags of gvn value cannot overlap with field for integer constants."); - } - GVN_Manager gm; auto forty = gm.Op(GVN_OP_ADDITION).Arg(10).Arg(30).End(); if (forty != 40){throw std::runtime_error("Bad addition" + to_string(forty));} auto twelve = gm.Op(GVN_OP_MULTIPLY).Arg(4).Arg(3).End(); if (twelve != 12){throw std::runtime_error("Bad multiplication");} + + auto x = gm.Leaf(); + auto x_plus_zero = gm.Op(GVN_OP_ADDITION).Arg(0).Arg(x).End(); + auto x_times_zero = gm.Op(GVN_OP_MULTIPLY).Arg(0).Arg(x).End(); + auto x_times_one = gm.Op(GVN_OP_MULTIPLY).Arg(1).Arg(x).End(); + + auto x2 = gm.Op(GVN_OP_MULTIPLY).Arg(2).End(); + if (x2 == x){throw std::runtime_error("x == 2*x");} + if (x_plus_zero != x){throw std::runtime_error("Bad addition x + 0 != x");} + if (x_times_zero != 0){throw std::runtime_error("Bad multiplication x * 0 != 0");} + if (x_times_one != x){throw std::runtime_error("Bad multiplication x * 1 != x");} } void GVN_Manager::Test6() { diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index f576c9bcc..1929be4cf 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -20,27 +20,30 @@ namespace jlm::rvsdg::gvn { typedef uint64_t GVN_Val; constexpr GVN_Val GVN_SMALL_VALUE = 0xFFFFFFFF; // Must not collide with flags below. - inline bool GVN_IsSmallValue(GVN_Val v){return (v & GVN_SMALL_VALUE) == v;} + // ------------------------------------------------------------------------------- /* Flags stored as part of GVN values */ constexpr GVN_Val GVN_IS_SYMBOLIC = 1ull << 32; constexpr GVN_Val GVN_HAS_DEPS = 1ull << 33; /* \brief : By setting a single bit for internal nodes it becomes impossible for leaf and internal nodes to have the same hash making code simpler. */ constexpr GVN_Val GVN_IS_LOCAL_VALUE = 1ull << 34; /* \brief : This value depends on a lambda param. Cannot be moved into the global region. */ - constexpr GVN_Val GVN_FROM_COLLISION = 1ull << 35; /* \brief : This gvn depends on value created by collision resolution */ - constexpr GVN_Val GVN_OPERATOR_IS_CA = 1ull << 36; /* \brief : This gvn value represent a commutative and associative operation */ - constexpr GVN_Val GVN_CONST_SYMBOL = 1ull << 37; + constexpr GVN_Val GVN_CONST_SYMBOL = 1ull << 35; constexpr GVN_Val GVN_MASK_INHERIT = GVN_IS_LOCAL_VALUE | GVN_IS_SYMBOLIC; - constexpr GVN_Val GVN_MASK = GVN_IS_SYMBOLIC | GVN_IS_LOCAL_VALUE | GVN_FROM_COLLISION | GVN_OPERATOR_IS_CA | GVN_HAS_DEPS | GVN_CONST_SYMBOL; + constexpr GVN_Val GVN_MASK = GVN_IS_SYMBOLIC | GVN_IS_LOCAL_VALUE | GVN_HAS_DEPS | GVN_CONST_SYMBOL; + inline bool GVN_IsSmallValue(GVN_Val v) {return (v & GVN_SMALL_VALUE) == v;} + inline bool GVN_ValueIsGlobal(GVN_Val v) {return v & GVN_IS_LOCAL_VALUE;} + inline bool GVN_ValueHasDeps(GVN_Val v) {return v & GVN_HAS_DEPS;} + + // ------------------------------------------------------------------------------- constexpr GVN_Val GVN_PREDEFS = GVN_CONST_SYMBOL | GVN_IS_SYMBOLIC; /* GLOBAL OPERATION */ constexpr GVN_Val GVN_OP_ANY_ORDERED = GVN_PREDEFS | 1; constexpr GVN_Val GVN_OP_BECOME_LOCAL = GVN_PREDEFS | 2; constexpr GVN_Val GVN_OP_BECOME_GLOBAL = GVN_PREDEFS | 3; - constexpr GVN_Val GVN_OP_ADDITION = GVN_PREDEFS | GVN_OPERATOR_IS_CA | 4; - constexpr GVN_Val GVN_OP_MULTIPLY = GVN_PREDEFS | GVN_OPERATOR_IS_CA | 5; + constexpr GVN_Val GVN_OP_ADDITION = GVN_PREDEFS | 4; + constexpr GVN_Val GVN_OP_MULTIPLY = GVN_PREDEFS | 5; constexpr GVN_Val GVN_OP_EQ = GVN_PREDEFS | 6; // N-ary checks if all values are the same constexpr GVN_Val GVN_OP_NEQ = GVN_PREDEFS | 7; // N-ary checks is two values are distinct @@ -48,25 +51,28 @@ namespace jlm::rvsdg::gvn { constexpr GVN_Val GVN_NO_VALUE = GVN_PREDEFS | GVN_CONST_SYMBOL | 100; constexpr GVN_Val GVN_TRUE = GVN_PREDEFS | GVN_CONST_SYMBOL | 101; constexpr GVN_Val GVN_FALSE = GVN_PREDEFS | GVN_CONST_SYMBOL | 102; - constexpr GVN_Val GVN_INVARIANT = GVN_PREDEFS | GVN_CONST_SYMBOL | 103; - inline bool GVN_ValueIsGlobal(GVN_Val v) {return v & GVN_IS_LOCAL_VALUE;} - inline bool GVN_ValueIsFromCollision(GVN_Val v) {return v & GVN_FROM_COLLISION;} - inline bool GVN_ValueIsCA_Op(GVN_Val v) {return v & GVN_OPERATOR_IS_CA;} - inline bool GVN_ValueHasDeps(GVN_Val v) {return v & GVN_HAS_DEPS;} + /* SPECIAL VALUES */ + constexpr GVN_Val GVN_IGNORE = GVN_PREDEFS | GVN_CONST_SYMBOL | 103; inline std::string to_string(GVN_Val v) { auto n = static_cast(v); + std::string s = "" + std::to_string(n); - if ((v & GVN_HAS_DEPS) == 0) {s += "";} - if (v & GVN_OPERATOR_IS_CA) {s+="";} - if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += "";} - if (v & GVN_FROM_COLLISION) {s += "";} - if (v & GVN_IS_SYMBOLIC){s += "";} - if (v == GVN_TRUE) {s += "GVN_TRUE";} - if (v == GVN_FALSE){s += "GVN_FALSE";} - if (v == GVN_NO_VALUE){s += "GVN_NO_VALUE";} - if (v == GVN_INVARIANT){s += "GVN_INVARIANT";} + if (s.length() > 5) + { + s = s.substr(0,4) + "..."; + } + + //if ((v & GVN_HAS_DEPS) == 0) {s += "";} + //if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += "";} + if (v & GVN_IS_SYMBOLIC){s += "$";} + // Constant predefined symbols + if (v == GVN_TRUE) {s = "TRUE";} + if (v == GVN_FALSE) {s = "FALSE";} + if (v == GVN_NO_VALUE) {s = "NO_VALUE";} + if (v == GVN_OP_ADDITION) {s = "+";} + if (v == GVN_OP_MULTIPLY) {s = "*";} return s; } @@ -79,6 +85,7 @@ namespace jlm::rvsdg::gvn { constexpr const char* ORD_ORIGINAL = "order:original"; constexpr const char* ORD_PARTITION = "order:partition"; + constexpr const char* ORD_DISRUPTOR = "order:disruptor"; constexpr const char* ORD_PARTITION_DISRUPTOR = "order:partition>disruptor"; constexpr const char* ORD_DISRUPTOR_PARTITION = "order:disruptor>partition"; @@ -117,6 +124,15 @@ namespace jlm::rvsdg::gvn { current_ordering = ORD_PARTITION; } + void OrderByDisruptor() { + std::sort(elements.begin(), elements.end(), + [](BrittlePrismEle& a, BrittlePrismEle& b) { + return a.disruptor < b.disruptor; + } + ); + current_ordering = ORD_DISRUPTOR; + } + void OrderByOriginal() { std::sort(elements.begin(), elements.end(), @@ -190,8 +206,8 @@ namespace jlm::rvsdg::gvn { std::cout << "["< static void EachPartition(BrittlePrism& p, Fn cb) @@ -262,7 +265,6 @@ namespace jlm::rvsdg::gvn { // Either because a single partition faces distinct disruptors or when // the disruptor does equal the original value. bool did_fracture = false; - CheckDisruptorInvariants(); OrderByPartitionThenDisruptor(); size_t it = 0; @@ -286,7 +288,7 @@ namespace jlm::rvsdg::gvn { did_fracture = true; while (it < p_end) { BrittlePrismEle& e = elements[it]; - if (e.partition != e.disruptor) { + if (e.partition != e.disruptor && e.disruptor != e.original_partition) { elements[it].partition = e.disruptor; } it++; @@ -305,6 +307,8 @@ namespace jlm::rvsdg::gvn { static void Test0(); static void Test1(); + static void Test2(); + static void Test3(); }; ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -359,14 +363,17 @@ namespace jlm::rvsdg::gvn { DefineConst(GVN_NO_VALUE); DefineConst(GVN_TRUE); DefineConst(GVN_FALSE); - DefineConst(GVN_INVARIANT); + + DefineConst(GVN_IGNORE); + + DefineConst(~0ull); } GVN_Val Leaf() { return Leaf(0); } GVN_Val Leaf(GVN_Val flags) { - auto g = FindUnique(flags); + auto g = SymGen(flags); gvn_.insert({g,std::nullopt}); return g; } @@ -438,21 +445,25 @@ namespace jlm::rvsdg::gvn { return ptr_to_gvn_[p]; } - GVN_Val FromPartitions(GVN_Val op, BrittlePrism& brittle) { - // It is sufficient to create a prism from the post array after taking the union - // with the pre array - Op(op); + GVN_Manager& FromPartitions(BrittlePrism& brittle) + { + // It is sufficient to create a prism from the post array after taking the union + // with the pre array + + { brittle.OrderByPartition(); size_t i = 0; - while (i < brittle.elements.size()) { - Arg(brittle.elements[i].partition); - i += brittle.SpanPartition(i); + while (i < brittle.elements.size()) + { + Arg(brittle.elements[i].partition); + i += brittle.SpanPartition(i); } - brittle.OrderByOriginal(); - return End(); + brittle.OrderByOriginal(); + } + return *this; } - GVN_Val FromDisruptors(GVN_Val op, BrittlePrism& brittle) { + /*GVN_Val FromDisruptors(GVN_Val op, BrittlePrism& brittle) { Op(op); brittle.OrderByDisruptorThenPartition(); size_t i = 0; @@ -462,7 +473,7 @@ namespace jlm::rvsdg::gvn { } brittle.OrderByOriginal(); return End(); - } + }*/ private: void DefineConst(GVN_Val v) @@ -474,121 +485,60 @@ namespace jlm::rvsdg::gvn { } GVN_Val Create(GVN_Val op, const std::vector& args) { if (args.empty()){throw std::runtime_error("Logic error: GVN operator applied to zero args.");} - std::optional new_gvn = GVN_Deps(); - new_gvn->op = op; + GVN_Deps new_gvn = GVN_Deps(); + new_gvn.op = op; // Initialize new_gvn.args // Either a copy of args or count of operator cluster leaves - if (!GVN_ValueIsCA_Op(new_gvn->op)) { - for (auto a : args) {new_gvn->push(a);} - }else{ - std::vector > acc; - for (auto arg : args) { - if (GVN_ValueHasDeps(arg)) { - auto ad = *(gvn_[arg]); - if (ad.op == new_gvn->op) { - for (auto leaf : ad.args) {acc.emplace_back(leaf);} - } else { - GVN_Val h_with_op = arg * op; - acc.emplace_back(h_with_op, 1); - } - }else { - acc.emplace_back(arg, 1); - } - } - std::sort(acc.begin(), acc.end()); - if (!acc.empty()) { - GVN_Val last = GVN_NO_VALUE; - size_t acc_at = -1; - for (auto ele : acc) { - if (ele.first != last) { - acc_at = new_gvn->args.size(); - new_gvn->args.emplace_back(ele.first, 0); - } - new_gvn->args[acc_at].second += ele.second; - last = ele.first; - } - } - std::cout << "*******************************************************************" << std::endl; - std::cout << "*******************************************************************" << std::endl; - for (auto ele : new_gvn->args){ - std::cout << op << " LEAVES: " << ele.first << " " << ele.second << std::endl; + for (auto a : args) { + if (a != GVN_IGNORE) { + new_gvn.push(a); } - std::cout << "*******************************************************************" << std::endl; - std::cout << "*******************************************************************" << std::endl; - - } - std::pair pr = CalculateHash(*new_gvn); + std::pair pr = CalculateHash(new_gvn); GVN_Val v = pr.first; - bool cannot_collide = pr.second; - if (cannot_collide) {return v;} //v was either a leaf or small number + bool is_older_value = pr.second; + if (is_older_value) {return v;} //Note: if the hash is a small number return here. // The memory usage for large dags of ca type operations might be too large - if (new_gvn->args.size() > max_ca_size) { + if (new_gvn.args.size() > max_ca_size) { stat_ca_too_big++; return Leaf(); } + bool did_linear_probe = false; // Check if gvn is already in use, compare with existing gvn if so - if ( gvn_.find(v) != gvn_.end() ) { - if ( !gvn_[v] ) {throw std::runtime_error("Invariant violation.");} - - auto prev = *(gvn_[v]); - if (!DepsEqual(prev, *new_gvn)) { - // Collision with internal node - v = FindUnique((v & GVN_MASK) | GVN_FROM_COLLISION ); - - std::cout << prev.op << " " << new_gvn->op << " " << std::endl; - if (new_gvn->args.size() == prev.args.size()){ - for (size_t i = 0; i < new_gvn->args.size(); i++){ - std::cout << new_gvn->args[i].first << " ?? " << prev.args[i].first << std::endl; - std::cout << new_gvn->args[i].second << " ?? " << prev.args[i].second << std::endl; - std::cout << "---------------------------------" << std::endl; - } - }else{ - std::cout << "Arg len mismatch..." << std::endl; - for (size_t i = 0; i < new_gvn->args.size(); i++){ - std::cout << new_gvn->args[i].first << " ?? " << new_gvn->args[i].second << std::endl; - } - for (size_t i = 0; i < new_gvn->args.size(); i++){ - std::cout << prev.args[i].first << " ?? " << prev.args[i].second << std::endl; - } - - } - stat_collisions++; - throw std::runtime_error("Collision between:"); - } + while ( gvn_.find(v) != gvn_.end() ) { + if (DepsEqual(*gvn_[v] , new_gvn)){break;} + v = (v & ~GVN_SMALL_VALUE) | ((v + 1) & GVN_SMALL_VALUE); + did_linear_probe = true; + throw std::runtime_error("True hash collision detected."); } // ----------------- commit --------------------- if (gvn_.find(v) == gvn_.end()) { - if ((v & GVN_IS_SYMBOLIC)) { - gvn_.insert({v,new_gvn}); - }else { - // gvn_.insert({v,std::nullopt}); - } + if (did_linear_probe) { stat_collisions++; } + gvn_.insert({v,new_gvn}); } return v; } private: - GVN_Val FindUnique() { - return FindUnique(0); + GVN_Val SymGen() { + return SymGen(0); } - GVN_Val FindUnique(GVN_Val flags) { + GVN_Val SymGen(GVN_Val flags) { GVN_Val g; do{ g = random() & ~GVN_MASK; g |= flags | GVN_IS_SYMBOLIC; }while(gvn_.find(g) != gvn_.end()); - return g; } - std::pair CalculateHash(const GVN_Deps& deps) { + std::pair CalculateHash(GVN_Deps& deps) { // Return a gvn value based on operator and arguments // The second element of the pair is true if the value cannot collide @@ -603,23 +553,7 @@ namespace jlm::rvsdg::gvn { if (deps.op == GVN_OP_BECOME_GLOBAL){flags &= ~GVN_IS_LOCAL_VALUE;} GVN_Val v = 0; - if (GVN_ValueIsCA_Op(deps.op)) { - //std::cout << "--------------CA------------------"< Date: Fri, 31 Oct 2025 15:07:08 +0100 Subject: [PATCH 42/52] Removed some debugging printing. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 67 +++++++------------ jlm/llvm/opt/PartialRedundancyElimination.hpp | 2 +- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 6ef1c31ef..b1818b3da 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -207,31 +207,22 @@ PartialRedundancyElimination::Run( auto statistics = Statistics::Create(module.SourceFilePath().value()); auto& root = rvsdg.GetRootRegion(); - this->TraverseTopDownRecursively(root, dump_region); - - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; - this->TraverseTopDownRecursively(root, dump_node); - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; this->TraverseTopDownRecursively(root, initialize_stats); - this->TraverseTopDownRecursively(root, dump_node); - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; std::cout <DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << TR_RESET << std::endl; + } + if (gvn_.stat_collisions){ throw std::runtime_error("gvn_.stat_collisions"); } @@ -331,7 +322,7 @@ static StateEdgesAt findStateIndices(rvsdg::Node* node) void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) { std::cout << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; - + // Treats state edges just like any other value if (node->ninputs() && node->noutputs()){ auto op_opaque = gvn_.FromStr( node->DebugString() ); auto hash_call_out = gvn_.FromStr("hash_call_out"); @@ -343,8 +334,6 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) auto hash_all_inputs = gvn_.End(); for (size_t i = 0 ; i < node->noutputs() ; i++){ auto arg_pos = gvn_.FromWord(i); - //std::cout << TR_YELLOW << "hash_call_out: " << hash_call_out << std::endl; - //std::cout << TR_YELLOW << "op_opaque: " << op_opaque << std::endl; auto g_out = gvn_.Op(hash_call_out).Arg(hash_all_inputs).Arg(arg_pos).End(); RegisterGVN( node->output(i), g_out ); } @@ -379,11 +368,7 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) for (size_t i = 0; i < node->noutputs(); i++){ if (output_to_gvn_.find(node->output(i)) == output_to_gvn_.end()){ if (node->DebugString() == std::string("undef")){ - //std::cout <DebugString() << node->GetNodeId() << std::endl; RegisterGVN( node->output(i), rvsdg::gvn::GVN_NO_VALUE ); - }else{ - //std::cout << std::endl << ind() << TR_RED << "Warning: Missing out from:" << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - RegisterGVN( node->output(i), gvn_.FromStr("missing") ); } } } @@ -448,16 +433,16 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) if (thetas_.find(node) == thetas_.end()){ thetas_.insert({node, ThetaData()}); - thetas_[node].prism = 0; // This value is unique for each non-isomorphic theta - + thetas_[node].prism = 0; // This value capture the behavior of thetas given their context + thetas_[node].stat_iteration_count = 0; for (auto v : tn.GetLoopVars()){ thetas_[node].pre.Add( GVNOrWarn( v.input->origin() , node ) ); thetas_[node].post.Add( 0 ); } - +/* std::cout << TR_YELLOW << "THETA INPUTS: " << node->GetNodeId() << std::endl; thetas_[node].pre.dump(); - std::cout << TR_RESET; + std::cout << TR_RESET;*/ }else{ for (size_t i = 0; i < lv.size(); i++){ auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); @@ -469,7 +454,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) } } } - + auto& td = thetas_[node]; /** ----------------------------------- UPDATE PRISM -------------------------------------- */ // The pre buffer contains either the most recent inputs for invariant values // or the superposition of values across all evaluations. @@ -478,34 +463,34 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) // ================= PERFORM GVN IN LOOP BODY ======================== for (size_t i = 0; i < lv.size(); i++){ - RegisterGVN( lv[i].pre, thetas_[node].pre.elements[i].disruptor ); + RegisterGVN( lv[i].pre, td.pre.elements[i].disruptor ); } GVN_VisitAllSubRegions(node); for (size_t i = 0; i < lv.size(); i++){ - thetas_[node].post.elements[i].disruptor = GVNOrWarn( lv[i].post->origin(), node ); + td.post.elements[i].disruptor = GVNOrWarn( lv[i].post->origin(), node ); } // ================= END LOOP BODY ==================================== for (size_t i = 0; i < lv.size(); i++){ - auto input = thetas_[node].pre.elements[i].disruptor; - auto output = thetas_[node].post.elements[i].disruptor; + auto input = td.pre.elements[i].disruptor; + auto output = td.post.elements[i].disruptor; auto merged_io = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(input).Arg(output).End(); - thetas_[node].post.elements[i].partition = merged_io; + td.post.elements[i].partition = merged_io; } GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); /// This hash depends on the mapping between inputs to ouputs as well as the predicate /// Does not depend on order of loop variables or duplicate loop variables - thetas_[node].prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(thetas_[node].post).End(); - thetas_[node].post.OrderByOriginal(); + td.prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(td.post).End(); + td.post.OrderByOriginal(); for (size_t i = 0; i < lv.size(); i++){ - auto pi = thetas_[node].post.elements[i].partition; - auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(thetas_[node].prism).End(); - bool was_invariant = thetas_[node].pre.elements[i].disruptor == thetas_[node].post.elements[i].disruptor; + auto pi = td.post.elements[i].partition; + auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(td.prism).End(); + bool was_invariant = td.pre.elements[i].disruptor == td.post.elements[i].disruptor; // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() // state edges can be made invariant inside thetas only @@ -513,15 +498,15 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) // Computing referential transparency if (was_invariant){ - thetas_[node].post.elements[i].partition = GVN_INVARIANT; + td.post.elements[i].partition = GVN_INVARIANT; }else{ - thetas_[node].post.elements[i].partition = merged_with_prism; - auto loop_old = thetas_[node].pre.elements[i].disruptor; - thetas_[node].pre.elements[i].disruptor = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(merged_with_prism).Arg(loop_old).End(); + td.post.elements[i].partition = merged_with_prism; + auto loop_old = td.pre.elements[i].disruptor; + td.pre.elements[i].disruptor = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(merged_with_prism).Arg(loop_old).End(); } } - + td.stat_iteration_count++; } while (thetas_[node].pre.Fracture()); /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index a861451fe..ab4a950ad 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -33,7 +33,7 @@ namespace jlm::llvm{ struct ThetaData { - bool first_iteration; + size_t stat_iteration_count; jlm::rvsdg::gvn::GVN_Val prism; jlm::rvsdg::gvn::BrittlePrism pre; jlm::rvsdg::gvn::BrittlePrism post; From 38808ca2694cc5f9629383f7959e2baebc39a5db Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Fri, 31 Oct 2025 15:10:50 +0100 Subject: [PATCH 43/52] Cleaned up some code --- jlm/llvm/opt/gvn.hpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 1929be4cf..9ff1b4613 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -447,9 +447,7 @@ namespace jlm::rvsdg::gvn { GVN_Manager& FromPartitions(BrittlePrism& brittle) { - // It is sufficient to create a prism from the post array after taking the union - // with the pre array - + // Call Arg( ) once for each partition { brittle.OrderByPartition(); size_t i = 0; @@ -463,18 +461,6 @@ namespace jlm::rvsdg::gvn { return *this; } - /*GVN_Val FromDisruptors(GVN_Val op, BrittlePrism& brittle) { - Op(op); - brittle.OrderByDisruptorThenPartition(); - size_t i = 0; - while (i < brittle.elements.size()) { - Arg(brittle.elements[i].disruptor); - i += brittle.SpanDisruptor(i); - } - brittle.OrderByOriginal(); - return End(); - }*/ - private: void DefineConst(GVN_Val v) { From 378d9a99a9e2cd936dcc4effdfb496e4ba8c1ad9 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Fri, 31 Oct 2025 16:16:54 +0100 Subject: [PATCH 44/52] Checksum for inputs to prevent theta body reevaluation unless inputs change. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 48 ++++++++++++------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 3 ++ jlm/llvm/opt/gvn.cpp | 3 -- jlm/llvm/opt/gvn.hpp | 5 +- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index b1818b3da..5c4ab6626 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -220,7 +220,7 @@ PartialRedundancyElimination::Run( for (auto kv : thetas_){ auto ic = kv.second.stat_iteration_count; - std::cout << TR_ORANGE << kv.first->DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << TR_RESET << std::endl; + std::cout << TR_ORANGE << kv.first->DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << " Checksum inputs: " << kv.second.checksum_inputs << TR_RESET << std::endl; } if (gvn_.stat_collisions){ @@ -418,6 +418,17 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) }); } +jlm::rvsdg::gvn::GVN_Val PartialRedundancyElimination::ComputeInputCheckSumForTheta(rvsdg::ThetaNode& tn) +{ + auto CHECKSUM = gvn_.FromStr("CHECKSUM"); + gvn_.Op(CHECKSUM); + for (auto v : tn.GetLoopVars()){ + auto from_outer = GVNOrZero( v.input->origin() ); + gvn_.Arg( from_outer ); + } + return gvn_.End(); +} + void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { @@ -432,36 +443,36 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) if (thetas_.find(node) == thetas_.end()){ thetas_.insert({node, ThetaData()}); - thetas_[node].prism = 0; // This value capture the behavior of thetas given their context thetas_[node].stat_iteration_count = 0; + thetas_[node].checksum_inputs = ComputeInputCheckSumForTheta(tn); for (auto v : tn.GetLoopVars()){ thetas_[node].pre.Add( GVNOrWarn( v.input->origin() , node ) ); thetas_[node].post.Add( 0 ); } -/* - std::cout << TR_YELLOW << "THETA INPUTS: " << node->GetNodeId() << std::endl; - thetas_[node].pre.dump(); - std::cout << TR_RESET;*/ }else{ - for (size_t i = 0; i < lv.size(); i++){ - auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); - auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); - if ( thetas_[node].post.elements[i].disruptor == GVN_INVARIANT){ - thetas_[node].pre.elements[i].disruptor = from_outer; - }else{ - thetas_[node].pre.elements[i].disruptor = merged; + // Only evaluate loop body once per unique set of inputs + auto check_new = ComputeInputCheckSumForTheta(tn); + if (thetas_[node].checksum_inputs == check_new){return;} + thetas_[node].checksum_inputs = check_new; + + ///// Fill buffers from input edges + { + for (size_t i = 0; i < lv.size(); i++){ + auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); + auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); + if ( thetas_[node].post.elements[i].disruptor == GVN_INVARIANT){ + thetas_[node].pre.elements[i].disruptor = from_outer; + }else{ + thetas_[node].pre.elements[i].disruptor = merged; + } } } } auto& td = thetas_[node]; - /** ----------------------------------- UPDATE PRISM -------------------------------------- */ - // The pre buffer contains either the most recent inputs for invariant values - // or the superposition of values across all evaluations. do{ // ================= PERFORM GVN IN LOOP BODY ======================== - for (size_t i = 0; i < lv.size(); i++){ RegisterGVN( lv[i].pre, td.pre.elements[i].disruptor ); } @@ -471,7 +482,6 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) for (size_t i = 0; i < lv.size(); i++){ td.post.elements[i].disruptor = GVNOrWarn( lv[i].post->origin(), node ); } - // ================= END LOOP BODY ==================================== for (size_t i = 0; i < lv.size(); i++){ @@ -508,6 +518,8 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) td.stat_iteration_count++; } while (thetas_[node].pre.Fracture()); + //Loop when either a loop variable changed from its initial value or loop variables + // which were the same at the start of the iteration diverged (partition splits or fractures). /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index ab4a950ad..8b8c25394 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -33,6 +33,7 @@ namespace jlm::llvm{ struct ThetaData { + jlm::rvsdg::gvn::GVN_Val checksum_inputs; size_t stat_iteration_count; jlm::rvsdg::gvn::GVN_Val prism; jlm::rvsdg::gvn::BrittlePrism pre; @@ -125,6 +126,8 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void PassDownThetaNode(rvsdg::Node* tn); void PassDownLambdaNode(rvsdg::Node* ln); void PassDownLeafNode(rvsdg::Node* node); + + jlm::rvsdg::gvn::GVN_Val ComputeInputCheckSumForTheta(rvsdg::ThetaNode& tn); }; } diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp index bf8aa72f3..2cff6b55b 100644 --- a/jlm/llvm/opt/gvn.cpp +++ b/jlm/llvm/opt/gvn.cpp @@ -12,9 +12,6 @@ bool jlm::rvsdg::gvn::gvn_verbose = false; namespace jlm::rvsdg::gvn { - void GVN_Manager::Test0() { - // TODO: arithmentic tests - } void GVN_Manager::Test1() { GVN_Manager gm; diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 9ff1b4613..63bb5500c 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -307,8 +307,6 @@ namespace jlm::rvsdg::gvn { static void Test0(); static void Test1(); - static void Test2(); - static void Test3(); }; ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -657,7 +655,7 @@ namespace jlm::rvsdg::gvn { } public: - static void Test0(); + static void Test1(); static void Test2(); static void Test3(); @@ -670,7 +668,6 @@ namespace jlm::rvsdg::gvn { { BrittlePrism::Test0(); BrittlePrism::Test1(); - GVN_Manager::Test0(); GVN_Manager::Test1(); GVN_Manager::Test2(); GVN_Manager::Test3(); From b4dc2b7e12621a5d48ae5d5605db779b366b0dc3 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Fri, 31 Oct 2025 20:41:46 +0100 Subject: [PATCH 45/52] Invariants treated as initial values when from outer loop. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 5c4ab6626..e20d8f3bc 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -456,13 +456,15 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) if (thetas_[node].checksum_inputs == check_new){return;} thetas_[node].checksum_inputs = check_new; - ///// Fill buffers from input edges + ///// Only called for nested loops { for (size_t i = 0; i < lv.size(); i++){ auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); if ( thetas_[node].post.elements[i].disruptor == GVN_INVARIANT){ thetas_[node].pre.elements[i].disruptor = from_outer; + thetas_[node].pre.elements[i].partition = from_outer; + thetas_[node].pre.elements[i].original_partition = from_outer; }else{ thetas_[node].pre.elements[i].disruptor = merged; } From 1d381da16ef8e230fcb5dcd3d571b8178340fae9 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Sat, 1 Nov 2025 20:26:30 +0100 Subject: [PATCH 46/52] Fixed minor bug. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 85 ++++++++++++++----- jlm/llvm/opt/gvn.hpp | 22 +++++ 2 files changed, 88 insertions(+), 19 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index e20d8f3bc..d81c1a665 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -32,6 +32,7 @@ #include "../ir/operators/IntegerOperations.hpp" #include "../ir/operators/operators.hpp" #include "PartialRedundancyElimination.hpp" +#include "../../rvsdg/control.hpp" #include #include #include @@ -216,6 +217,7 @@ PartialRedundancyElimination::Run( GVN_VisitRegion(root); TraverseTopDownRecursively(root, dump_node); + TraverseTopDownRecursively(root, dump_region); std::cout << TR_PURPLE << "=================================================" << TR_RESET << std::endl; for (auto kv : thetas_){ @@ -355,6 +357,8 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) auto op = gvn_.FromStr(binop.debug_string()); if (binop.debug_string() == "IAdd"){op = GVN_OP_ADDITION; } if (binop.debug_string() == "IMul"){op = GVN_OP_MULTIPLY; } + if (binop.debug_string() == "INe"){op = GVN_OP_NEQ; } + if (binop.debug_string() == "IEq"){op = GVN_OP_EQ; } auto a = GVNOrWarn( node->input(0)->origin(), node); auto b = GVNOrWarn( node->input(1)->origin(), node); @@ -370,12 +374,30 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) if (node->DebugString() == std::string("undef")){ RegisterGVN( node->output(i), rvsdg::gvn::GVN_NO_VALUE ); } + auto& op = node->GetOperation(); + if ( rvsdg::is_ctlconstant_op( op ) ){ + RegisterGVN( node->output(i), rvsdg::gvn::GVN_NO_VALUE ); + } } } + + MatchType(node->GetOperation(), [this, node](const rvsdg::MatchOperation& mop){ + using namespace jlm::rvsdg::gvn; + auto pred = GVNOrPanic( node->input(0)->origin(), node ); + if (pred == GVN_TRUE) {pred = 1;} + if (pred == GVN_FALSE){pred = 0;} + if (jlm::rvsdg::gvn::GVN_IsSmallValue(pred)){ + RegisterGVN( node->output(0), mop.alternative(static_cast(pred)) ); + } + //std::cout << "MAPPED MATCH" << std::endl; + //dump_node(this, node); + }); } void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) { + using namespace jlm::rvsdg::gvn; + MatchType(*node, [this,node](rvsdg::GammaNode& gn){ std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; @@ -386,35 +408,60 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) RegisterGVN(into_branch, GVNOrWarn(out, node)); } } - auto selector = gn.GetMatchVar().input->origin(); - auto match_var = GVNOrWarn(selector, node); + + auto mv_edge = gn.GetMatchVar().input->origin(); + auto mv_val = GVNOrWarn( mv_edge, node ); for (auto mv : gn.GetMatchVar().matchContent){ - RegisterGVN( mv, GVNOrWarn(selector, node)); + RegisterGVN(mv, mv_val); } + // -------------------------------------------------------------------------------------------- GVN_VisitAllSubRegions(node); - - //route results out and handle cases where result is always the same - for (auto ev : gn.GetExitVars()) - { - using namespace jlm::rvsdg::gvn; - auto any_val = GVN_NO_VALUE; - gvn_.Op(GVN_OP_ANY_ORDERED); - for (auto leaving_branch : ev.branchResult){ + // -------------------------------------------------------------------------------------------- + + auto GAMMA_VARIABLE_OUT = gvn_.FromStr("GAMMA_VARIABLE_OUT"); + auto BRANCHES_CONDITIONALLY = gvn_.FromStr("CONDITIONAL_BRANCHING"); + + auto entry_vars = gn.GetEntryVars(); + auto exit_vars = gn.GetExitVars(); + auto match_var = gn.GetMatchVar(); + auto predicate = GVNOrPanic(gn.predicate()->origin(), node); + + for (auto ev : exit_vars){ + auto any_branch_value = GVN_NO_VALUE; + auto value_from_branch_always_taken = BRANCHES_CONDITIONALLY; + gvn_.Op(GVN_OP_ANY_ORDERED); // Returns input if all inputs are the same. + for (size_t b = 0; b < ev.branchResult.size(); b++){ + auto leaving_branch = ev.branchResult[b]; auto from_inner = GVNOrZero(leaving_branch->origin()); gvn_.Arg( from_inner ); - any_val = from_inner; + any_branch_value = from_inner; + + // --------------------- check if predicate is known and maps to this branch ------------- + auto match_node = rvsdg::TryGetOwnerNode( *(match_var.input->origin()) ); + if (match_node){ + MatchType(match_node->GetOperation(), [&value_from_branch_always_taken, predicate, b, from_inner](const rvsdg::MatchOperation& mop){ + if (jlm::rvsdg::gvn::GVN_IsSmallValue(predicate) && mop.alternative(static_cast(predicate)) == b){ + value_from_branch_always_taken = from_inner; + } + }); + } + // --------------------- } auto branches_merged = gvn_.End(); - if (any_val == branches_merged){ - RegisterGVN(ev.output, branches_merged); // If all branches output the same value - }else{ - auto sel_op = gvn_.FromStr("selector"); - auto hash_with_selector = gvn_.Op(sel_op).Arg(match_var).Arg(branches_merged).End(); - RegisterGVN( ev.output, hash_with_selector); // Typical case. Note: branch order matters. - } + RegisterGVN( ev.output, + gvn_.Op(GAMMA_VARIABLE_OUT).Arg(predicate).Arg(branches_merged).End() + ); + + // If all branches output the same value + if (any_branch_value == branches_merged){ RegisterGVN(ev.output, any_branch_value); } + // If the match variable has a known value + if (value_from_branch_always_taken != BRANCHES_CONDITIONALLY){ RegisterGVN(ev.output, value_from_branch_always_taken); } } + + + }); } diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index 63bb5500c..8e126d84a 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -567,6 +567,7 @@ namespace jlm::rvsdg::gvn { if (must_be_different) {return {GVN_FALSE, true};} }break; case GVN_OP_ADDITION: { + //flatten (a + (2*a) + b + a + b) => a + 2*a + a + b for (size_t i = 0; i < deps.args.size(); i++) { auto leaf = deps.args[i].first; if (leaf & GVN_HAS_DEPS) { @@ -579,6 +580,16 @@ namespace jlm::rvsdg::gvn { if (deps.args[i].first == 0) {deps.args[i].first = TO_BE_DELETED;} //delete zeroes } std::sort(deps.args.begin(), deps.args.end()); + // Coalesce a + 2*a + a + b => 4*a + b + for (size_t i = 1; i < deps.args.size(); i++) + { + if (deps.args[i ].first == deps.args[i-1].first){ + deps.args[i].second += deps.args[i-1].second; + deps.args[i-1].first = TO_BE_DELETED; + } + } + std::sort(deps.args.begin(), deps.args.end()); + // Collect while (deps.args[ deps.args.size() - 1 ].first == TO_BE_DELETED) {deps.args.pop_back();} if (deps.args.size() == 0){return {0, true};} if (deps.args.size() == 1 && deps.args[0].second == 1){return {deps.args[0].first, true};} // x + 0 == x @@ -594,6 +605,7 @@ namespace jlm::rvsdg::gvn { } }break; case GVN_OP_MULTIPLY: { + // Flatten for (size_t i = 0; i < deps.args.size(); i++) { auto leaf = deps.args[i].first; if (leaf & GVN_HAS_DEPS) { @@ -607,6 +619,16 @@ namespace jlm::rvsdg::gvn { } for (auto ele : deps.args){if (ele.first == 0){return {0, true};}} //x * 0 == 0 std::sort(deps.args.begin(), deps.args.end()); + // Coalesce + for (size_t i = 1; i < deps.args.size(); i++) + { + if (deps.args[i ].first == deps.args[i-1].first){ + deps.args[i].second += deps.args[i-1].second; + deps.args[i-1].first = TO_BE_DELETED; + } + } + std::sort(deps.args.begin(), deps.args.end()); + // Collect while (deps.args[ deps.args.size() - 1 ].first == TO_BE_DELETED) {deps.args.pop_back();} if (deps.args.size() == 0){return {1, true};} // all ones were removed above. if (deps.args.size() == 1 && deps.args[0].second == 1){return {deps.args[0].first, true};} // x * 1 == x From 96efc34091da9ec69af984e6e38e68fb128bf3f7 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 4 Nov 2025 18:05:36 +0100 Subject: [PATCH 47/52] Save before changes. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 7 +- jlm/llvm/opt/gvn.cpp | 71 ++++++++--- jlm/llvm/opt/gvn.hpp | 114 +++++++----------- 3 files changed, 101 insertions(+), 91 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index d81c1a665..4e3acdb2c 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -203,7 +203,7 @@ PartialRedundancyElimination::Run( util::StatisticsCollector & statisticsCollector) { std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; - + jlm::rvsdg::gvn::RunAllTests(); auto & rvsdg = module.Rvsdg(); auto statistics = Statistics::Create(module.SourceFilePath().value()); @@ -478,7 +478,6 @@ jlm::rvsdg::gvn::GVN_Val PartialRedundancyElimination::ComputeInputCheckSumForTh void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { - MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ using namespace jlm::rvsdg::gvn; auto GVN_INVARIANT = gvn_.FromStr("INVARIANT"); @@ -572,6 +571,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ + for (size_t i = 0; i < lv.size(); i++){ auto inv = thetas_[node].post.elements[i].partition == GVN_INVARIANT; if (inv){ @@ -591,7 +591,8 @@ void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) MatchType(*node, [this,node](rvsdg::LambdaNode& ln){ size_t i = 0; for (auto arg : ln.GetFunctionArguments()){ - RegisterGVN(arg, gvn_.FromStr("Param:" + std::to_string(i)) ); i++; + auto g = gvn_.FromStr("Param:" + std::to_string(i)); + RegisterGVN(arg, g ); i++; } for (auto arg : ln.GetContextVars()) { diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp index 2cff6b55b..dca965d66 100644 --- a/jlm/llvm/opt/gvn.cpp +++ b/jlm/llvm/opt/gvn.cpp @@ -11,7 +11,48 @@ bool jlm::rvsdg::gvn::gvn_verbose = false; namespace jlm::rvsdg::gvn { - + void GVN_Manager::Test0() + { + GVN_Manager gm; + auto x = gm.Leaf(); + auto y = gm.Leaf(); + { + if (gm.Op(GVN_OP_ADDITION).Arg(x).Arg(0).End() != x) { + throw std::runtime_error("Addition error, x expected"); + } + if (gm.Op(GVN_OP_ADDITION).Arg(3).Arg(4).Arg(2).Arg(3).End() != 12) { + throw std::runtime_error("Addition error, 12"); + } + if (gm.Op(GVN_OP_MULTIPLY).Arg(3).Arg(4).Arg(2).Arg(3).End() != 72) { + throw std::runtime_error("Addition error, 72"); + } + } + { + auto xyx = gm.Op(GVN_OP_ADDITION).Arg(x).Arg(y).Arg(x).End(); + auto xxyy = gm.Op(GVN_OP_ADDITION).Arg(x).Arg(y).Arg(x).Arg(y).End(); + if (gm.Op(GVN_OP_ADDITION).Arg(xyx).Arg(y).End() != xxyy){ + throw std::runtime_error("Addition error x+y+x + y != x+y+x+y"); + } + } + { + auto xx = gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(x).End(); + auto xxyy = gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(y).Arg(x).Arg(y).End(); + if (gm.Op(GVN_OP_MULTIPLY).Arg(y).Arg(xx).Arg(y).End() != xxyy){ + throw std::runtime_error("Multiplication error, yxxy != xxyy"); + } + } + { + auto x0 = gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(0).End(); + auto x1 = gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(1).End(); + //check identities + if (gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(0).End() != x0 || x0 != 0){ + throw std::runtime_error("Multiplication error: x*0"); + } + if (gm.Op(GVN_OP_MULTIPLY).Arg(x).Arg(1).End() != x1){ + throw std::runtime_error("Multiplication error: x*1"); + } + } + } void GVN_Manager::Test1() { GVN_Manager gm; @@ -29,10 +70,7 @@ namespace jlm::rvsdg::gvn { GVN_Val y = gm.Leaf(); GVN_Val xx = gm.Op(GVN_OP_ANY_ORDERED).Args({x,x}); - std::cout << "*************************"<< std::endl << std::endl; GVN_Val xy = gm.Op(GVN_OP_ANY_ORDERED).Args({x,y}); - - std::cout << "*************************"<< std::endl << std::endl; GVN_Val xy_2 = gm.Op(GVN_OP_ANY_ORDERED).Args({x,y}); if (xy != xy_2) { @@ -71,13 +109,13 @@ namespace jlm::rvsdg::gvn { { std::vector v = {77,128,128,77,77}; BrittlePrism p0(v); - p0.dump(); + if (gvn_verbose){p0.dump();} p0.OrderByPartition(); if (gvn_verbose){std::cout << "----------partitions-------------"<(v); std::string s = "" + std::to_string(n); - if (s.length() > 5) - { + if (s.length() > 5){ s = s.substr(0,4) + "..."; } //if ((v & GVN_HAS_DEPS) == 0) {s += "";} - //if ((v & GVN_IS_LOCAL_VALUE) == 0) {s += "";} if (v & GVN_IS_SYMBOLIC){s += "$";} // Constant predefined symbols if (v == GVN_TRUE) {s = "TRUE";} @@ -351,8 +346,6 @@ namespace jlm::rvsdg::gvn { // Add constant symbols to the table of all values such that // values cannot collide. DefineConst(GVN_OP_ANY_ORDERED); - DefineConst(GVN_OP_BECOME_LOCAL); - DefineConst(GVN_OP_BECOME_GLOBAL); DefineConst(GVN_OP_ADDITION); DefineConst(GVN_OP_MULTIPLY); DefineConst(GVN_OP_EQ); @@ -521,10 +514,39 @@ namespace jlm::rvsdg::gvn { }while(gvn_.find(g) != gvn_.end()); return g; } + void NormalizeCa(GVN_Deps& deps) + { + // Flatten + for (size_t i = 0; i < deps.args.size(); i++) { + auto leaf = deps.args[i].first; + if (leaf & GVN_HAS_DEPS) { + auto leaf_deps = *gvn_[leaf]; + if (leaf_deps.op == deps.op) { + for (auto lf : leaf_deps.args) {deps.args.emplace_back(lf);} + deps.args[i].first = GVN_TOMBSTONE; //mark for deletion + } + } + if (deps.op == GVN_OP_ADDITION && deps.args[i].first == 0) {deps.args[i].first = GVN_TOMBSTONE;} + if (deps.op == GVN_OP_MULTIPLY && deps.args[i].first == 1) {deps.args[i].first = GVN_TOMBSTONE;} //delete zeroes + } + std::sort(deps.args.begin(), deps.args.end()); + // Coalesce + for (size_t i = 1; i < deps.args.size(); i++) + { + if (deps.args[i ].first == deps.args[i-1].first){ + deps.args[i].second += deps.args[i-1].second; + deps.args[i-1].first = GVN_TOMBSTONE; + } + } + std::sort(deps.args.begin(), deps.args.end()); + // Collect + while (deps.args.size() && deps.args[ deps.args.size() - 1 ].first == GVN_TOMBSTONE) {deps.args.pop_back();} + } std::pair CalculateHash(GVN_Deps& deps) { // Return a gvn value based on operator and arguments // The second element of the pair is true if the value cannot collide + if (deps.op == GVN_OP_ADDITION || deps.op == GVN_OP_MULTIPLY){NormalizeCa(deps);} // The lower bits are used to store properties for operations and // keep track of context dependence of values @@ -533,11 +555,8 @@ namespace jlm::rvsdg::gvn { flags |= arg.first & GVN_MASK_INHERIT; } - if (deps.op == GVN_OP_BECOME_LOCAL) {flags |= GVN_IS_LOCAL_VALUE;} - if (deps.op == GVN_OP_BECOME_GLOBAL){flags &= ~GVN_IS_LOCAL_VALUE;} - GVN_Val v = 0; - constexpr GVN_Val TO_BE_DELETED = ~0ull; + switch (deps.op) { case GVN_OP_NEQ: { // Note: NEQ cannot assume two symbol values are different. @@ -567,30 +586,6 @@ namespace jlm::rvsdg::gvn { if (must_be_different) {return {GVN_FALSE, true};} }break; case GVN_OP_ADDITION: { - //flatten (a + (2*a) + b + a + b) => a + 2*a + a + b - for (size_t i = 0; i < deps.args.size(); i++) { - auto leaf = deps.args[i].first; - if (leaf & GVN_HAS_DEPS) { - auto leaf_deps = *gvn_[leaf]; - if (leaf_deps.op == GVN_OP_ADDITION) { - for (auto lf : leaf_deps.args) {deps.args.emplace_back(lf);} - deps.args[i].first = TO_BE_DELETED; //mark for deletion - } - } - if (deps.args[i].first == 0) {deps.args[i].first = TO_BE_DELETED;} //delete zeroes - } - std::sort(deps.args.begin(), deps.args.end()); - // Coalesce a + 2*a + a + b => 4*a + b - for (size_t i = 1; i < deps.args.size(); i++) - { - if (deps.args[i ].first == deps.args[i-1].first){ - deps.args[i].second += deps.args[i-1].second; - deps.args[i-1].first = TO_BE_DELETED; - } - } - std::sort(deps.args.begin(), deps.args.end()); - // Collect - while (deps.args[ deps.args.size() - 1 ].first == TO_BE_DELETED) {deps.args.pop_back();} if (deps.args.size() == 0){return {0, true};} if (deps.args.size() == 1 && deps.args[0].second == 1){return {deps.args[0].first, true};} // x + 0 == x // ------------------------------------------------------------------------------------------------- @@ -605,31 +600,7 @@ namespace jlm::rvsdg::gvn { } }break; case GVN_OP_MULTIPLY: { - // Flatten - for (size_t i = 0; i < deps.args.size(); i++) { - auto leaf = deps.args[i].first; - if (leaf & GVN_HAS_DEPS) { - auto leaf_deps = *gvn_[leaf]; - if (leaf_deps.op == GVN_OP_MULTIPLY) { - for (auto lf : leaf_deps.args) {deps.args.emplace_back(lf);} - deps.args[i].first = TO_BE_DELETED; //mark for deletion - } - } - if (deps.args[i].first == 1) {deps.args[i].first = TO_BE_DELETED;} //delete zeroes - } - for (auto ele : deps.args){if (ele.first == 0){return {0, true};}} //x * 0 == 0 - std::sort(deps.args.begin(), deps.args.end()); - // Coalesce - for (size_t i = 1; i < deps.args.size(); i++) - { - if (deps.args[i ].first == deps.args[i-1].first){ - deps.args[i].second += deps.args[i-1].second; - deps.args[i-1].first = TO_BE_DELETED; - } - } - std::sort(deps.args.begin(), deps.args.end()); - // Collect - while (deps.args[ deps.args.size() - 1 ].first == TO_BE_DELETED) {deps.args.pop_back();} + if (deps.args.size() && deps.args[0].first == 0){return {0, true};} //x * 0 == 0 if (deps.args.size() == 0){return {1, true};} // all ones were removed above. if (deps.args.size() == 1 && deps.args[0].second == 1){return {deps.args[0].first, true};} // x * 1 == x if (!(flags & GVN_IS_SYMBOLIC)) { @@ -677,7 +648,7 @@ namespace jlm::rvsdg::gvn { } public: - + static void Test0(); static void Test1(); static void Test2(); static void Test3(); @@ -690,6 +661,7 @@ namespace jlm::rvsdg::gvn { { BrittlePrism::Test0(); BrittlePrism::Test1(); + GVN_Manager::Test0(); GVN_Manager::Test1(); GVN_Manager::Test2(); GVN_Manager::Test3(); From 93147c67c7d4e7b0be83e7fe50b181c903c050cb Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 4 Nov 2025 19:29:40 +0100 Subject: [PATCH 48/52] Added finalization pass for thetas' --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 140 ++++++++++-------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 11 +- jlm/llvm/opt/gvn.hpp | 2 + 3 files changed, 91 insertions(+), 62 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 4e3acdb2c..b0bae615f 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -197,6 +197,14 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg } } +void PartialRedundancyElimination::GVN(rvsdg::Region& root) +{ + gvn_mode_thetas_ = ThetaMode::GVN_FIND_FIXED_POINT; + GVN_VisitRegion(root); + gvn_mode_thetas_ = ThetaMode::GVN_FINALIZE; + GVN_VisitRegion(root); +} + void PartialRedundancyElimination::Run( rvsdg::RvsdgModule & module, @@ -215,14 +223,15 @@ PartialRedundancyElimination::Run( std::cout <DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << " Checksum inputs: " << kv.second.checksum_inputs << TR_RESET << std::endl; + std::cout << TR_ORANGE << kv.first->DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << TR_RESET << std::endl; } if (gvn_.stat_collisions){ @@ -435,8 +444,10 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) auto leaving_branch = ev.branchResult[b]; auto from_inner = GVNOrZero(leaving_branch->origin()); gvn_.Arg( from_inner ); - any_branch_value = from_inner; + // Special case all branches return the same value + any_branch_value = from_inner; + // Special case. Match variable was known. // --------------------- check if predicate is known and maps to this branch ------------- auto match_node = rvsdg::TryGetOwnerNode( *(match_var.input->origin()) ); if (match_node){ @@ -459,67 +470,46 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) // If the match variable has a known value if (value_from_branch_always_taken != BRANCHES_CONDITIONALLY){ RegisterGVN(ev.output, value_from_branch_always_taken); } } - - - }); } -jlm::rvsdg::gvn::GVN_Val PartialRedundancyElimination::ComputeInputCheckSumForTheta(rvsdg::ThetaNode& tn) -{ - auto CHECKSUM = gvn_.FromStr("CHECKSUM"); - gvn_.Op(CHECKSUM); - for (auto v : tn.GetLoopVars()){ - auto from_outer = GVNOrZero( v.input->origin() ); - gvn_.Arg( from_outer ); - } - return gvn_.End(); -} - void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ using namespace jlm::rvsdg::gvn; - auto GVN_INVARIANT = gvn_.FromStr("INVARIANT"); - auto LOOP_EXIT = gvn_.FromStr("LOOP_EXIT"); - auto lv = tn.GetLoopVars(); + auto LOOP_EXIT = gvn_.FromStr("LOOP_EXIT"); + auto LOOP_BACK = gvn_.FromStr("LOOP_BACK"); auto OP_PRISM = gvn_.FromStr("prism"); + auto lv = tn.GetLoopVars(); + /** ----------------------------------- LOAD INPUTS INTO PRE.disruptors ------------------- */ if (thetas_.find(node) == thetas_.end()){ thetas_.insert({node, ThetaData()}); thetas_[node].prism = 0; // This value capture the behavior of thetas given their context thetas_[node].stat_iteration_count = 0; - thetas_[node].checksum_inputs = ComputeInputCheckSumForTheta(tn); for (auto v : tn.GetLoopVars()){ thetas_[node].pre.Add( GVNOrWarn( v.input->origin() , node ) ); thetas_[node].post.Add( 0 ); } }else{ - // Only evaluate loop body once per unique set of inputs - auto check_new = ComputeInputCheckSumForTheta(tn); - if (thetas_[node].checksum_inputs == check_new){return;} - thetas_[node].checksum_inputs = check_new; - - ///// Only called for nested loops + ///// Only called for nested loops. Not tested yet. { + throw std::runtime_error("Code not tested yet. Beware."); + // Similar to loop back at the end of simple loops. for (size_t i = 0; i < lv.size(); i++){ auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); - if ( thetas_[node].post.elements[i].disruptor == GVN_INVARIANT){ - thetas_[node].pre.elements[i].disruptor = from_outer; - thetas_[node].pre.elements[i].partition = from_outer; - thetas_[node].pre.elements[i].original_partition = from_outer; - }else{ - thetas_[node].pre.elements[i].disruptor = merged; - } + thetas_[node].pre.elements[i].disruptor = merged; } } } - auto& td = thetas_[node]; - do{ + // Inputs from upstreams have now been placed in pre .disruptor fields + + auto& td = thetas_[node]; + while (thetas_[node].pre.Fracture() || td.stat_iteration_count == 0){ // ================= PERFORM GVN IN LOOP BODY ======================== for (size_t i = 0; i < lv.size(); i++){ RegisterGVN( lv[i].pre, td.pre.elements[i].disruptor ); @@ -532,6 +522,8 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) } // ================= END LOOP BODY ==================================== + // Merge input and outputs for each loop variable in order to identify + // which variables to move into which partitions for (size_t i = 0; i < lv.size(); i++){ auto input = td.pre.elements[i].disruptor; auto output = td.post.elements[i].disruptor; @@ -542,50 +534,79 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); /// This hash depends on the mapping between inputs to ouputs as well as the predicate /// Does not depend on order of loop variables or duplicate loop variables + td.prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(td.post).End(); - td.post.OrderByOriginal(); + // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() + // state edges can be made invariant inside thetas only + // if they pass through referentially transparent nodes. + // There might be some instances of referential transparency + // such as x+0 -> x not detected by other passes. + // This also detects invariant values from crossed edges. for (size_t i = 0; i < lv.size(); i++){ - auto pi = td.post.elements[i].partition; - auto merged_with_prism = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(pi).Arg(td.prism).End(); + // input and output for one loop body visit are stored in pre and post, .disruptor bool was_invariant = td.pre.elements[i].disruptor == td.post.elements[i].disruptor; - // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() - // state edges can be made invariant inside thetas only - // if they pass through referentially transparent nodes. - // Computing referential transparency - if (was_invariant){ + // no need to update the input for the next loop iteration + // , however store away whether the value is invariant for later use td.post.elements[i].partition = GVN_INVARIANT; }else{ - td.post.elements[i].partition = merged_with_prism; - auto loop_old = td.pre.elements[i].disruptor; - td.pre.elements[i].disruptor = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(merged_with_prism).Arg(loop_old).End(); + auto lv_old = td.pre.elements[i].disruptor; + auto lv_newer = td.post.elements[i].disruptor; + td.pre.elements[i].disruptor = gvn_.Op(LOOP_BACK).Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); + td.post.elements[i].partition = gvn_.Op(LOOP_EXIT).Arg(td.prism).Arg(td.post.elements[i].partition).End(); + // hashing with prism here prevents accidental capture of values from outer loops. } } - td.stat_iteration_count++; - } while (thetas_[node].pre.Fracture()); - //Loop when either a loop variable changed from its initial value or loop variables - // which were the same at the start of the iteration diverged (partition splits or fractures). + } - /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ + // After the loop body has been evaluated until partitions reach a fixed point + // post.partition either contains contents compatible with the most recent partitions + // or a token indicating invariance. + // Note: inner loops are only re-evaluated when the outer loop passes in new data which + // either causes a re-partitioning or a initial values changes for the first time (fracture). + /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ for (size_t i = 0; i < lv.size(); i++){ auto inv = thetas_[node].post.elements[i].partition == GVN_INVARIANT; + auto input_for_lv = GVNOrWarn(lv[i].input->origin(), node); if (inv){ - RegisterGVN(lv[i].output, GVNOrWarn(lv[i].input->origin(), node)); + RegisterGVN(lv[i].output, input_for_lv); }else{ - // The gvn for loop output variables must be different from the value exiting the loop. - auto superimposed_values_inside_loop = thetas_[node].pre.elements[i].disruptor; - auto g = gvn_.Op(LOOP_EXIT).Arg(superimposed_values_inside_loop).End(); - RegisterGVN(lv[i].output, g); + RegisterGVN( + lv[i].output, + gvn_.Op(LOOP_EXIT).Arg(input_for_lv).Arg(thetas_[node].post.elements[i].partition).End() + ); } } }); } +void PartialRedundancyElimination::GVN_FinalizeThetaNode(rvsdg::Node * node) +{ + MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ + using namespace jlm::rvsdg::gvn; + auto lv = tn.GetLoopVars(); + + // Visit each loop body one more time, but let the most recent values for invariant values from + // outer loops inside. + for (size_t i = 0; i < lv.size(); i++){ + auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); + auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); + if (thetas_[node].post.elements[i].partition == GVN_INVARIANT){ + RegisterGVN( lv[i].pre, from_outer ); + }else{ + RegisterGVN( lv[i].pre, merged ); + } + } + GVN_VisitAllSubRegions(node); + // Do not update output of thetas, just the gvn values in the loop body. + }); +} + void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) { MatchType(*node, [this,node](rvsdg::LambdaNode& ln){ @@ -612,7 +633,10 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) GVN_VisitAllSubRegions(node); }, [this,node](rvsdg::ThetaNode& tn){ - GVN_VisitThetaNode(node); + switch (gvn_mode_thetas_){ + case ThetaMode::GVN_FIND_FIXED_POINT: GVN_VisitThetaNode(node); break; + case ThetaMode::GVN_FINALIZE: GVN_FinalizeThetaNode(node); break; + }; }, [this,node](rvsdg::GammaNode& gn){ GVN_VisitGammaNode(node); diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 8b8c25394..18e7cfcbb 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -33,7 +33,6 @@ namespace jlm::llvm{ struct ThetaData { - jlm::rvsdg::gvn::GVN_Val checksum_inputs; size_t stat_iteration_count; jlm::rvsdg::gvn::GVN_Val prism; jlm::rvsdg::gvn::BrittlePrism pre; @@ -68,7 +67,11 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation private: - + enum class ThetaMode{ + GVN_FIND_FIXED_POINT, + GVN_FINALIZE, + }; + ThetaMode gvn_mode_thetas_ = ThetaMode::GVN_FIND_FIXED_POINT; void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)); static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); @@ -111,11 +114,13 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation /// ----------------------------------------------------------- + void GVN(rvsdg::Region& root); void GVN_VisitRegion(rvsdg::Region& reg); void GVN_VisitAllSubRegions(rvsdg::Node* node); void GVN_VisitNode(rvsdg::Node* node); void GVN_VisitGammaNode(rvsdg::Node* node); void GVN_VisitThetaNode(rvsdg::Node* tn); + void GVN_FinalizeThetaNode(rvsdg::Node * node); void GVN_VisitLambdaNode(rvsdg::Node* ln); void GVN_VisitLeafNode(rvsdg::Node* node); @@ -126,8 +131,6 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void PassDownThetaNode(rvsdg::Node* tn); void PassDownLambdaNode(rvsdg::Node* ln); void PassDownLeafNode(rvsdg::Node* node); - - jlm::rvsdg::gvn::GVN_Val ComputeInputCheckSumForTheta(rvsdg::ThetaNode& tn); }; } diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index bc1970e8b..ac4c99bed 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -42,6 +42,7 @@ namespace jlm::rvsdg::gvn { constexpr GVN_Val GVN_OP_MULTIPLY = GVN_PREDEFS | 3; constexpr GVN_Val GVN_OP_EQ = GVN_PREDEFS | 4; // N-ary checks if all values are the same constexpr GVN_Val GVN_OP_NEQ = GVN_PREDEFS | 5; // N-ary checks is two values are distinct + constexpr GVN_Val GVN_INVARIANT = GVN_PREDEFS | 6; constexpr GVN_Val GVN_TOMBSTONE = (~0ull & ~GVN_MASK) | GVN_PREDEFS; // Largest possible value /* GLOBAL CONSTANTS */ @@ -356,6 +357,7 @@ namespace jlm::rvsdg::gvn { DefineConst(GVN_FALSE); DefineConst(GVN_IGNORE); + DefineConst(GVN_INVARIANT); DefineConst(~0ull); } From 71969f5b7f274cc05fed30c6bad78d2fba53a121 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Wed, 5 Nov 2025 12:43:33 +0100 Subject: [PATCH 49/52] Support for nested loops. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 52 ++++++++++++------- jlm/llvm/opt/gvn.hpp | 21 ++++++-- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index b0bae615f..d6705f61b 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -479,6 +479,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) using namespace jlm::rvsdg::gvn; auto LOOP_EXIT = gvn_.FromStr("LOOP_EXIT"); auto LOOP_BACK = gvn_.FromStr("LOOP_BACK"); + auto OUTPUT_PARTITION = gvn_.FromStr("OUTPUT_PARTITION"); auto OP_PRISM = gvn_.FromStr("prism"); auto lv = tn.GetLoopVars(); @@ -496,19 +497,19 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) }else{ ///// Only called for nested loops. Not tested yet. { - throw std::runtime_error("Code not tested yet. Beware."); + std::cout << TR_RED << "Warning nested loops not tested yet." << TR_RESET << std::endl; // Similar to loop back at the end of simple loops. for (size_t i = 0; i < lv.size(); i++){ auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); - auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].disruptor).End(); + auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].partition).End(); thetas_[node].pre.elements[i].disruptor = merged; } } } - // Inputs from upstreams have now been placed in pre .disruptor fields - auto& td = thetas_[node]; + + // Inputs from upstreams have now been placed in pre .disruptor fields while (thetas_[node].pre.Fracture() || td.stat_iteration_count == 0){ // ================= PERFORM GVN IN LOOP BODY ======================== for (size_t i = 0; i < lv.size(); i++){ @@ -523,39 +524,37 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) // ================= END LOOP BODY ==================================== // Merge input and outputs for each loop variable in order to identify - // which variables to move into which partitions + // the most updated partitions for (size_t i = 0; i < lv.size(); i++){ auto input = td.pre.elements[i].disruptor; auto output = td.post.elements[i].disruptor; auto merged_io = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(input).Arg(output).End(); td.post.elements[i].partition = merged_io; } - - GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); /// This hash depends on the mapping between inputs to ouputs as well as the predicate /// Does not depend on order of loop variables or duplicate loop variables - + GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); td.prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(td.post).End(); // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() - // state edges can be made invariant inside thetas only - // if they pass through referentially transparent nodes. - // There might be some instances of referential transparency - // such as x+0 -> x not detected by other passes. - // This also detects invariant values from crossed edges. + // Because we pass in data from outside the theta it might be possible + // to detect more invariance such as from + // gammas inside the loop with constant match variables + // However, using rvsdg::ThetaLoopVarIsInvariant() will work as well + for (size_t i = 0; i < lv.size(); i++){ // input and output for one loop body visit are stored in pre and post, .disruptor bool was_invariant = td.pre.elements[i].disruptor == td.post.elements[i].disruptor; if (was_invariant){ // no need to update the input for the next loop iteration - // , however store away whether the value is invariant for later use + // , however store away whether the value is invariant for when computing outputs from the theta td.post.elements[i].partition = GVN_INVARIANT; }else{ auto lv_old = td.pre.elements[i].disruptor; auto lv_newer = td.post.elements[i].disruptor; - td.pre.elements[i].disruptor = gvn_.Op(LOOP_BACK).Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); - td.post.elements[i].partition = gvn_.Op(LOOP_EXIT).Arg(td.prism).Arg(td.post.elements[i].partition).End(); + td.pre.elements[i].disruptor = gvn_.Op(LOOP_BACK) .Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); + td.post.elements[i].partition = gvn_.Op(OUTPUT_PARTITION).Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); // hashing with prism here prevents accidental capture of values from outer loops. } } @@ -563,13 +562,21 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) } // After the loop body has been evaluated until partitions reach a fixed point - // post.partition either contains contents compatible with the most recent partitions - // or a token indicating invariance. + // Note: inner loops are only re-evaluated when the outer loop passes in new data which - // either causes a re-partitioning or a initial values changes for the first time (fracture). + // either causes a re-partitioning or detects a change in an initial value for the first time (fracture). + // That is if a value invariant with respect to the inner loop changes in the outer loop. /** ----------------------------------- COMPUTE LOOP OUTPUTS -------------------------------------- */ + // ============================================================================================= + //This is only required for nested loops + for (size_t i = 0; i < lv.size(); i++){ + thetas_[node].pre.elements[i].disruptor = GVNOrWarn( lv[i].input->origin(), node );; + } + auto hash_from_inputs = gvn_.Op(LOOP_EXIT).OneDisruptorPerPartition(td.pre).End(); + // ============================================================================================= + for (size_t i = 0; i < lv.size(); i++){ auto inv = thetas_[node].post.elements[i].partition == GVN_INVARIANT; auto input_for_lv = GVNOrWarn(lv[i].input->origin(), node); @@ -578,7 +585,11 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) }else{ RegisterGVN( lv[i].output, - gvn_.Op(LOOP_EXIT).Arg(input_for_lv).Arg(thetas_[node].post.elements[i].partition).End() + gvn_.Op(LOOP_EXIT) + .Arg(thetas_[node].post.elements[i].partition) + .Arg(hash_from_inputs) + .Arg(input_for_lv) + .End() ); } } @@ -603,6 +614,7 @@ void PartialRedundancyElimination::GVN_FinalizeThetaNode(rvsdg::Node * node) } } GVN_VisitAllSubRegions(node); + thetas_[node].stat_iteration_count++; // Do not update output of thetas, just the gvn values in the loop body. }); } diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index ac4c99bed..b488ee689 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -56,9 +56,9 @@ namespace jlm::rvsdg::gvn { inline std::string to_string(GVN_Val v) { auto n = static_cast(v); - std::string s = "" + std::to_string(n); - if (s.length() > 5){ - s = s.substr(0,4) + "..."; + std::string s = std::to_string(n); + if (s.length() > 8){ + s = s.substr(0,3) + "..." + s.substr(s.length() - 3, s.length()); } //if ((v & GVN_HAS_DEPS) == 0) {s += "";} @@ -453,6 +453,21 @@ namespace jlm::rvsdg::gvn { } return *this; } + GVN_Manager& OneDisruptorPerPartition(BrittlePrism& brittle) + { + // Call Arg( ) once for each partition + { + brittle.OrderByPartition(); + size_t i = 0; + while (i < brittle.elements.size()) + { + Arg(brittle.elements[i].disruptor); // !!!! + i += brittle.SpanPartition(i); + } + brittle.OrderByOriginal(); + } + return *this; + } private: void DefineConst(GVN_Val v) From fd5e59b1ddee042c98ab300de37d18754297ff44 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Wed, 5 Nov 2025 14:49:53 +0100 Subject: [PATCH 50/52] Factored out merging of values at loop entry. --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index c3c1c9e3d..01a7dbcc5 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -473,13 +473,18 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) }); } +static rvsdg::gvn::GVN_Val mergeIntoLV(rvsdg::gvn::GVN_Manager& gvn, rvsdg::gvn::GVN_Val olderValue, rvsdg::gvn::GVN_Val newerValue, rvsdg::gvn::GVN_Val prism) +{ + if (olderValue == newerValue){return olderValue;} + auto MERGE_ON_LOOP_ENTRY = gvn.FromStr("MERGE_ON_LOOP_ENTRY"); + return gvn.Op(MERGE_ON_LOOP_ENTRY).Arg(prism).Arg(olderValue).Arg(newerValue).End(); +} + void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) { MatchType(*node, [this,node](rvsdg::ThetaNode& tn){ using namespace jlm::rvsdg::gvn; auto LOOP_EXIT = gvn_.FromStr("LOOP_EXIT"); - auto LOOP_BACK = gvn_.FromStr("LOOP_BACK"); - auto OUTPUT_PARTITION = gvn_.FromStr("OUTPUT_PARTITION"); auto OP_PRISM = gvn_.FromStr("prism"); auto lv = tn.GetLoopVars(); @@ -500,9 +505,12 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) std::cout << TR_RED << "Warning nested loops not tested yet." << TR_RESET << std::endl; // Similar to loop back at the end of simple loops. for (size_t i = 0; i < lv.size(); i++){ - auto from_outer = GVNOrWarn( lv[i].input->origin(), node ); - auto merged = gvn_.Op(GVN_OP_ANY_ORDERED).Arg(from_outer).Arg(thetas_[node].pre.elements[i].partition).End(); - thetas_[node].pre.elements[i].disruptor = merged; + thetas_[node].pre.elements[i].disruptor = mergeIntoLV( + gvn_, + thetas_[node].pre.elements[i].partition, + GVNOrWarn( lv[i].input->origin(), node ), + thetas_[node].prism + ); } } } @@ -536,6 +544,14 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) GVN_Val predicate = GVNOrPanic( tn.predicate()->origin(), node ); td.prism = gvn_.Op(OP_PRISM).Arg(predicate).FromPartitions(td.post).End(); + // Why hashing solely with the predicate might not work: + // Update in parallel + // i -> i + 1 + a , i control the predicate. + // a -> a + 2 + b + // b -> b + 3 + c + // c -> c + 4 + K , K eventually affects i + // however, it reaches a fixed point in 2 iterations + // Note: this doesn't use rvsdg::ThetaLoopVarIsInvariant() // Because we pass in data from outside the theta it might be possible // to detect more invariance such as from @@ -553,9 +569,11 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) }else{ auto lv_old = td.pre.elements[i].disruptor; auto lv_newer = td.post.elements[i].disruptor; - td.pre.elements[i].disruptor = gvn_.Op(LOOP_BACK) .Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); - td.post.elements[i].partition = gvn_.Op(OUTPUT_PARTITION).Arg(td.prism).Arg(lv_old).Arg(lv_newer).End(); + // hashing with prism here prevents accidental capture of values from outer loops. + auto g = mergeIntoLV(gvn_, lv_old, lv_newer, td.prism); + td.pre.elements[i].disruptor = g; + td.post.elements[i].partition = g; } } td.stat_iteration_count++; From 94f96e7d8a932ac6931a0d29197a672f246e2412 Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 11 Nov 2025 19:23:08 +0100 Subject: [PATCH 51/52] Fixed some issues --- jlm/llvm/Makefile.sub | 4 +- jlm/llvm/opt/PartialRedundancyElimination.cpp | 302 ++++-------------- jlm/llvm/opt/PartialRedundancyElimination.hpp | 15 +- jlm/llvm/opt/gvn.cpp | 26 +- jlm/llvm/opt/gvn.hpp | 20 +- jlm/tooling/Command.cpp | 1 - jlm/tooling/CommandLine.cpp | 6 +- 7 files changed, 80 insertions(+), 294 deletions(-) diff --git a/jlm/llvm/Makefile.sub b/jlm/llvm/Makefile.sub index e3a27d772..23e2675bc 100644 --- a/jlm/llvm/Makefile.sub +++ b/jlm/llvm/Makefile.sub @@ -63,13 +63,13 @@ libllvm_SOURCES = \ jlm/llvm/opt/alias-analyses/Steensgaard.cpp \ jlm/llvm/opt/CommonNodeElimination.cpp \ jlm/llvm/opt/DeadNodeElimination.cpp \ - jlm/llvm/opt/PartialRedundancyElimination.cpp \ jlm/llvm/opt/gvn.cpp \ jlm/llvm/opt/IfConversion.cpp \ jlm/llvm/opt/inlining.cpp \ jlm/llvm/opt/InvariantValueRedirection.cpp \ jlm/llvm/opt/LoadChainSeparation.cpp \ jlm/llvm/opt/LoopUnswitching.cpp \ + jlm/llvm/opt/PartialRedundancyElimination.cpp \ jlm/llvm/opt/PredicateCorrelation.cpp \ jlm/llvm/opt/pull.cpp \ jlm/llvm/opt/push.cpp \ @@ -86,7 +86,6 @@ libllvm_HEADERS = \ \ jlm/llvm/opt/unroll.hpp \ jlm/llvm/opt/DeadNodeElimination.hpp \ - jlm/llvm/opt/PartialRedundancyElimination.hpp \ jlm/llvm/opt/gvn.hpp \ jlm/llvm/opt/inlining.hpp \ jlm/llvm/opt/CommonNodeElimination.hpp \ @@ -117,6 +116,7 @@ libllvm_HEADERS = \ jlm/llvm/opt/InvariantValueRedirection.hpp \ jlm/llvm/opt/LoadChainSeparation.hpp \ jlm/llvm/opt/LoopUnswitching.hpp \ + jlm/llvm/opt/PartialRedundancyElimination.hpp \ jlm/llvm/opt/PredicateCorrelation.hpp \ jlm/llvm/opt/RvsdgTreePrinter.hpp \ jlm/llvm/opt/ScalarEvolution.hpp \ diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 01a7dbcc5..42b93cd07 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -18,21 +18,17 @@ #define TR_CYAN TR_FG(64, 255, 255) #define TR_GRAY TR_FG(52,52,52) -#include "../../../tests/test-operation.hpp" -#include "../../rvsdg/gamma.hpp" -#include "../../rvsdg/lambda.hpp" -#include "../../rvsdg/delta.hpp" -#include "../../rvsdg/MatchType.hpp" -#include "../../rvsdg/node.hpp" -#include "../../rvsdg/nullary.hpp" -#include "../../rvsdg/structural-node.hpp" -#include "../../rvsdg/theta.hpp" -#include "../../util/GraphWriter.hpp" -#include "../ir/operators/call.hpp" -#include "../ir/operators/IntegerOperations.hpp" -#include "../ir/operators/operators.hpp" +#include "jlm/rvsdg/gamma.hpp" +#include "jlm/rvsdg/lambda.hpp" +#include "jlm/rvsdg/delta.hpp" +#include "jlm/rvsdg/MatchType.hpp" +#include "jlm/rvsdg/node.hpp" +#include "jlm/rvsdg/structural-node.hpp" +#include "jlm/rvsdg/theta.hpp" +#include "jlm/util/GraphWriter.hpp" +#include "jlm/llvm/ir/operators/IntegerOperations.hpp" #include "PartialRedundancyElimination.hpp" -#include "../../rvsdg/control.hpp" +#include "jlm/rvsdg/control.hpp" #include #include #include @@ -53,46 +49,15 @@ #include #include -#include "../../rvsdg/binary.hpp" -#include "../../util/common.hpp" -#include "../ir/types.hpp" -#include "gvn.hpp" +#include "jlm/rvsdg/binary.hpp" +#include "jlm/util/common.hpp" +#include "jlm/llvm/ir/types.hpp" +#include "jlm/llvm/opt/gvn.hpp" #include #include -/** - * Partial redundancy elimination: - * -invariants: after each insertion of a new GVN all GVN shall have unique values - * -including op clusters - * -GVN source: - * -create a variant type for flows into an operator and gvn from constants - * -GVN map responsible for comparing constants - * -Collisions: - * -a GVN value is always generated when a new tuple of (op, flow0, flow1, ...) is inserted - * -on a collision generate a unique symbol - * -this prevents accidental matches downstream and keep the invariant of one gvn per - * value -it should be possible to recompute tuples from edges -outputs -operators: -keep around - * vectors of leaves with a count after performing the operation -vecs only required for internal - * nodes in op clusters -compare vectors rather than GVN arguments -in effect operator nodes whose - * args have the same op return a vector of leaf inputs to the operator dag -tracebacks from op - * nodes will thus bypasses non-leaf nodes -vectors of leaf counts stored per output edge -possible - * to discard for too large vectors, preventing excess memory usage -Theta nodes: -Complicated: - * -initial values passed into thetas must not match - * -the outputs might depend on other loop variables transitively - * -Solution: - * -dynamically switch between which table to insert values into - * -compute a GVN value by setting each loop input to a simple (OP-LOOP-PARAM, index) - * -these are placed in a separate table - * -this value unique identifies a loop - * -this scales well as each loop body is only scanned once for identifying structure - * and once afterwards to trickle down values from outer contexts - * -for a given set of loop inputs and loop hash, outputs are unique - * -that is treat theta nodes as operators from the outside - * -once such hashes have been calculated for all thetas proceed by passing - * -values into thetas hashed with loop hashes - * -two identical loops called with the same values give the same outputs - */ + namespace jlm::rvsdg { @@ -126,50 +91,31 @@ class IndentMan } }; -/** -------------------------------------------------------------------------------------------- **/ - -namespace jlm::llvm -{ - -/** \brief PRE statistics - * - */ -class PartialRedundancyElimination::Statistics final : public util::Statistics -{ - const char * MarkTimerLabel_ = "MarkTime"; - const char * SweepTimerLabel_ = "SweepTime"; - +class DbgPrint{ public: - ~Statistics() override = default; - - explicit Statistics(const util::FilePath & sourceFile) - : util::Statistics(Statistics::Id::PartialRedundancyElimination, sourceFile) - {} + const bool verbose; - void - StartMarkStatistics(const rvsdg::Graph & graph) noexcept - { - AddMeasurement(Label::NumRvsdgNodesBefore, rvsdg::nnodes(&graph.GetRootRegion())); - AddMeasurement(Label::NumRvsdgInputsBefore, rvsdg::ninputs(&graph.GetRootRegion())); - AddTimer(MarkTimerLabel_).start(); + explicit DbgPrint(const bool verbose):verbose(verbose){} + template + DbgPrint& operator<<(const T& message) { + if (verbose) {std::cout << message;} + return *this; } - void - StopMarkStatistics() noexcept - { - GetTimer(MarkTimerLabel_).stop(); - } - - static std::unique_ptr - Create(const util::FilePath & sourceFile) - { - return std::make_unique(sourceFile); + DbgPrint& operator<<(std::ostream& (*o)(std::ostream&)) { + if (verbose) {std::cout << o;} + return *this; } }; +static DbgPrint dbg_out(false); + /** -------------------------------------------------------------------------------------------- **/ +namespace jlm::llvm +{ +/** -------------------------------------------------------------------------------------------- **/ PartialRedundancyElimination::~PartialRedundancyElimination() noexcept {} PartialRedundancyElimination::PartialRedundancyElimination() : @@ -191,7 +137,7 @@ void PartialRedundancyElimination::TraverseTopDownRecursively(rvsdg::Region& reg for (auto& reg : sn.Subregions()) { this->TraverseTopDownRecursively(reg, cb); - std::cout << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; + dbg_out << ind() << TR_GRAY << "..........................." << TR_RESET << std::endl; } }); } @@ -210,39 +156,20 @@ PartialRedundancyElimination::Run( rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) { - std::cout << TR_BLUE << "Hello JLM its me." << TR_RESET << std::endl; - jlm::rvsdg::gvn::RunAllTests(); auto & rvsdg = module.Rvsdg(); - auto statistics = Statistics::Create(module.SourceFilePath().value()); - auto& root = rvsdg.GetRootRegion(); this->TraverseTopDownRecursively(root, initialize_stats); - std::cout << TR_GRAY << "=================================================" << TR_RESET << std::endl; - std::cout <DebugString() << kv.first->GetNodeId() << " Iteration count: " << ic << TR_RESET << std::endl; - } - if (gvn_.stat_collisions){ throw std::runtime_error("gvn_.stat_collisions"); } - // PassDownRegion(root); } /** -------------------------------------------------------------------------------------------- **/ static size_t reg_counter = 0; -void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, rvsdg::Node* node) +void PartialRedundancyElimination::dump_region(rvsdg::Node* node) { std::string name = node->DebugString() + std::to_string(node->GetNodeId()); @@ -256,22 +183,22 @@ void PartialRedundancyElimination::dump_region(PartialRedundancyElimination* pe, my_dot_writer.WriteGraphs(my_graph_writer , reg, false); std::string full_name = "reg_dump/" + name+"__"+std::to_string(reg_counter)+"__.dot"; reg_counter++; - std::cout<< TR_RED<stat_theta_count++; }); - MatchType(*node, [&pe, &node](rvsdg::GammaNode& tn){ + MatchType(*node, [&pe](rvsdg::GammaNode& _){ pe->stat_gamma_count++; }); } @@ -289,50 +216,9 @@ void PartialRedundancyElimination::GVN_VisitAllSubRegions(rvsdg::Node* node) }); } -struct StateEdgesAt -{ - std::optional< std::pair > io_state; - std::optional< std::pair > mem_state; -}; -/* -static StateEdgesAt findStateIndices(rvsdg::Node* node) -{ - StateEdgesAt edge_indices; - edge_indices.io_state = std::make_pair(0,0); - edge_indices.mem_state = std::make_pair(0,0); - - bool found_io_state = false; - bool found_mem_state = false; - - for (size_t i = 0 ; i < node->ninputs() ; i++){ - if ( rvsdg::is(node->input(i)->Type()) ){ - edge_indices.mem_state->first = i; - found_io_state = true; - } - if ( rvsdg::is(node->input(i)->Type()) ){ - edge_indices.io_state->first = i; - found_mem_state = true; - } - } - - for (size_t o = 0 ; o < node->noutputs() ; o++){ - if ( rvsdg::is(node->output(o)->Type()) ){ - edge_indices.mem_state->second = o; - } - if ( rvsdg::is(node->input(o)->Type()) ){ - edge_indices.io_state->second = o; - } - } - - if (!found_io_state){edge_indices.io_state = std::nullopt;} - if (!found_mem_state){edge_indices.mem_state = std::nullopt;} - - return edge_indices; -} -*/ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) { - std::cout << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; + dbg_out << ind() << "Leaf node: " << node->DebugString() << node->GetNodeId() << std::endl; // Treats state edges just like any other value if (node->ninputs() && node->noutputs()){ auto op_opaque = gvn_.FromStr( node->DebugString() ); @@ -352,7 +238,6 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) MatchType(node->GetOperation(), [this, node](const jlm::llvm::IntegerConstantOperation& iconst){ - //std::cout << TR_RED << "FOUND: " << iconst.Representation().str() << TR_RESET << std::endl; size_t value = 0; auto istr = iconst.Representation().str(); for (size_t i = 0 ; i < istr.length() ; i++){ @@ -372,8 +257,6 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) auto a = GVNOrWarn( node->input(0)->origin(), node); auto b = GVNOrWarn( node->input(1)->origin(), node); - //std::cout << TR_RED << "BINOP" << to_string(op) << "[" << to_string(a) << " , " << to_string(b) << "]" << TR_RESET << std::endl; - RegisterGVN(node->output(0), gvn_.Op(op).Arg(a).Arg(b).End()); } ); @@ -398,8 +281,6 @@ void PartialRedundancyElimination::GVN_VisitLeafNode(rvsdg::Node* node) if (jlm::rvsdg::gvn::GVN_IsSmallValue(pred)){ RegisterGVN( node->output(0), mop.alternative(static_cast(pred)) ); } - //std::cout << "MAPPED MATCH" << std::endl; - //dump_node(this, node); }); } @@ -408,7 +289,7 @@ void PartialRedundancyElimination::GVN_VisitGammaNode(rvsdg::Node * node) using namespace jlm::rvsdg::gvn; MatchType(*node, [this,node](rvsdg::GammaNode& gn){ - std::cout << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + dbg_out << ind() << TR_YELLOW << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; //Route gvn values into alternatives for (auto br : gn.GetEntryVars()){ @@ -502,7 +383,7 @@ void PartialRedundancyElimination::GVN_VisitThetaNode(rvsdg::Node * node) }else{ ///// Only called for nested loops. Not tested yet. { - std::cout << TR_RED << "Warning nested loops not tested yet." << TR_RESET << std::endl; + dbg_out << TR_RED << "Warning nested loops not tested yet." << TR_RESET << std::endl; // Similar to loop back at the end of simple loops. for (size_t i = 0; i < lv.size(); i++){ thetas_[node].pre.elements[i].disruptor = mergeIntoLV( @@ -650,7 +531,7 @@ void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) auto from = arg.input->origin(); RegisterGVN(arg.inner, GVNOrWarn(from, node)); } - std::cout << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + dbg_out << ind() << TR_PURPLE << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(node); }); } @@ -658,20 +539,20 @@ void PartialRedundancyElimination::GVN_VisitLambdaNode(rvsdg::Node * node) void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) { MatchTypeWithDefault(*node, - [this,node](rvsdg::DeltaNode& dn){ - std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; + [this,node](rvsdg::DeltaNode& _){ + dbg_out << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; GVN_VisitAllSubRegions(node); }, - [this,node](rvsdg::ThetaNode& tn){ + [this,node](rvsdg::ThetaNode& _){ switch (gvn_mode_thetas_){ case ThetaMode::GVN_FIND_FIXED_POINT: GVN_VisitThetaNode(node); break; case ThetaMode::GVN_FINALIZE: GVN_FinalizeThetaNode(node); break; }; }, - [this,node](rvsdg::GammaNode& gn){ + [this,node](rvsdg::GammaNode& _){ GVN_VisitGammaNode(node); }, - [this,node](rvsdg::LambdaNode& ln){ + [this,node](rvsdg::LambdaNode& _){ GVN_VisitLambdaNode(node); }, //DEFAULT @@ -684,101 +565,30 @@ void PartialRedundancyElimination::GVN_VisitNode(rvsdg::Node* node) void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, rvsdg::Node* node) { using namespace jlm::rvsdg::gvn; - std::cout << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; + dbg_out << ind() << TR_BLUE << node->DebugString() << "<"<GetNodeId() <<">"<< TR_RESET; MatchType(*node, [&pe, &node](rvsdg::LambdaNode& ln){ - std::cout <GVNOrZero(arg); + dbg_out << " : " << pe->GVNOrZero(arg); } - std::cout << TR_RESET; - }); - - MatchType(node->GetOperation(), [&pe, node](const jlm::llvm::IntegerConstantOperation& iconst) - { - JLM_ASSERT(node->noutputs() == 1); - std::cout << TR_CYAN; - std::cout << to_string(pe->GVNOrZero( node->output(0) )); - std::cout << TR_RESET; + dbg_out << TR_RESET; }); for (size_t i = 0; i < node->ninputs(); i++){ - std::cout << TR_GREEN; - std::cout << " : " << to_string(pe->GVNOrZero( node->input(i)->origin() )); + dbg_out << TR_GREEN; + dbg_out << " : " << to_string(pe->GVNOrZero( node->input(i)->origin() )); } - std::cout << TR_GRAY << " => "; + + dbg_out << TR_GRAY << " => "; for (size_t i = 0; i < node->noutputs(); i++){ - std::cout << TR_RED; - std::cout << " : " << to_string(pe->GVNOrZero( node->output(i) )); + dbg_out << TR_RED; + dbg_out << " : " << to_string(pe->GVNOrZero( node->output(i) )); } - std::cout << std::endl; -} - -/** ********************************************************************************************* */ -/** ********************************************************************************************* */ -/** ********************************************************************************************* */ -void PartialRedundancyElimination::PassDownRegion(rvsdg::Region& reg) -{ - IndentMan indenter = IndentMan(); - - for (rvsdg::Node* node : rvsdg::TopDownTraverser(®)){PassDownNode(node);} + dbg_out << std::endl; } -void PartialRedundancyElimination::PassDownAllSubRegions(rvsdg::Node* node) -{ - MatchType(*node,[this](rvsdg::StructuralNode& sn){ - for (auto& reg : sn.Subregions()){PassDownRegion(reg);} - }); -} - -void PartialRedundancyElimination::PassDownNode(rvsdg::Node* node) -{ - MatchTypeWithDefault(*node, - [this,node](rvsdg::DeltaNode& dn){ - std::cout << ind() << TR_CYAN << node->DebugString() << node->GetNodeId() << TR_RESET << std::endl; - PassDownAllSubRegions(node); - }, - [this,node](rvsdg::ThetaNode& tn){ - PassDownThetaNode(node); - }, - [this, node](rvsdg::GammaNode& gn) - { - PassDownGammaNode(node); - }, - [this,node](rvsdg::LambdaNode& ln){ - PassDownLambdaNode(node); - }, - //DEFAULT - [this, node](){ - PassDownLeafNode(node); - } - ); -} - -void PartialRedundancyElimination::PassDownThetaNode(rvsdg::Node* node) -{ - PassDownAllSubRegions(node); -} - -void PartialRedundancyElimination::PassDownGammaNode(rvsdg::Node* node) -{ - PassDownAllSubRegions(node); -} - -void PartialRedundancyElimination::PassDownLeafNode(rvsdg::Node* node) -{ - std::cout << ind() <DebugString()<GetNodeId() << TR_RESET; -} - -void -PartialRedundancyElimination::PassDownLambdaNode(rvsdg::Node * ln) -{ - std::cout << ind() << TR_GREEN << ln->GetNodeId() << TR_RESET; - PassDownAllSubRegions(ln); -} - - } diff --git a/jlm/llvm/opt/PartialRedundancyElimination.hpp b/jlm/llvm/opt/PartialRedundancyElimination.hpp index 18e7cfcbb..edd1ba3e2 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.hpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.hpp @@ -3,8 +3,8 @@ * See COPYING for terms of redistribution. */ -#ifndef JLM_LLVM_OPT_PartialRedundancyElimination_HPP -#define JLM_LLVM_OPT_PartialRedundancyElimination_HPP +#ifndef JLM_LLVM_OPT_PARTIAL_REDUNDANCY_ELIMINATION_HPP +#define JLM_LLVM_OPT_PARTIAL_REDUNDANCY_ELIMINATION_HPP #include "gvn.hpp" #include "jlm/llvm/ir/operators/IntegerOperations.hpp" @@ -33,6 +33,7 @@ namespace jlm::llvm{ struct ThetaData { + ThetaData():stat_iteration_count(0), prism(0){} size_t stat_iteration_count; jlm::rvsdg::gvn::GVN_Val prism; jlm::rvsdg::gvn::BrittlePrism pre; @@ -74,7 +75,7 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation ThetaMode gvn_mode_thetas_ = ThetaMode::GVN_FIND_FIXED_POINT; void TraverseTopDownRecursively(rvsdg::Region& reg, void(*cb)(PartialRedundancyElimination* pe, rvsdg::Node* node)); - static void dump_region( PartialRedundancyElimination *pe, rvsdg::Node* node); + static void dump_region(rvsdg::Node* node); static void dump_node( PartialRedundancyElimination *pe, rvsdg::Node* node); static void initialize_stats( PartialRedundancyElimination *pe, rvsdg::Node* node); @@ -123,14 +124,6 @@ class PartialRedundancyElimination final : public jlm::rvsdg::Transformation void GVN_FinalizeThetaNode(rvsdg::Node * node); void GVN_VisitLambdaNode(rvsdg::Node* ln); void GVN_VisitLeafNode(rvsdg::Node* node); - - void PassDownRegion(rvsdg::Region& reg); - void PassDownAllSubRegions(rvsdg::Node* node); - void PassDownNode(rvsdg::Node* node); - void PassDownGammaNode(rvsdg::Node* gn); - void PassDownThetaNode(rvsdg::Node* tn); - void PassDownLambdaNode(rvsdg::Node* ln); - void PassDownLeafNode(rvsdg::Node* node); }; } diff --git a/jlm/llvm/opt/gvn.cpp b/jlm/llvm/opt/gvn.cpp index dca965d66..40306d564 100644 --- a/jlm/llvm/opt/gvn.cpp +++ b/jlm/llvm/opt/gvn.cpp @@ -1,12 +1,9 @@ -// -// Created by lars-astrup-sundt on 10/27/25. -// +/* + * Copyright 2025 Lars Astrup Sundt + * See COPYING for terms of redistribution. + */ -#include "gvn.hpp" - -// -// Created by lars-astrup-sundt on 10/26/25. -// +#include "jlm/llvm/opt/gvn.hpp" bool jlm::rvsdg::gvn::gvn_verbose = false; namespace jlm::rvsdg::gvn { @@ -155,19 +152,6 @@ namespace jlm::rvsdg::gvn { if (gvn_verbose){std::cout << "=======================================" << std::endl;} if (gvn_verbose){p2.dump();} if (gvn_verbose){std::cout << "=======================================" << std::endl;} - - auto shatter_good = [](BrittlePrismEle& ele, size_t index) { - ele.partition = index; - }; - auto shatter_bad = [](BrittlePrismEle& ele, size_t index) { - ele.partition = 88; - }; - Shatter(p0, shatter_good); - try { - Shatter(p2, shatter_bad); - }catch (std::runtime_error& e) { - if (gvn_verbose){std::cout << "success : should throw : detected bad shatter : " << e.what() << std::endl;} - } } void BrittlePrism::Test1() diff --git a/jlm/llvm/opt/gvn.hpp b/jlm/llvm/opt/gvn.hpp index b488ee689..e01d63eac 100644 --- a/jlm/llvm/opt/gvn.hpp +++ b/jlm/llvm/opt/gvn.hpp @@ -1,19 +1,18 @@ -// -// Created by lars-astrup-sundt on 10/27/25. -// +/* +* Copyright 2025 Lars Astrup Sundt + * See COPYING for terms of redistribution. + */ -#ifndef JLM_GVN_H -#define JLM_GVN_H + +#ifndef JLM_LLVM_OPT_GVN_H +#define JLM_LLVM_OPT_GVN_H #include #include #include #include #include -#include -#include - -#include +#include namespace jlm::rvsdg::gvn { extern bool gvn_verbose; @@ -314,6 +313,7 @@ namespace jlm::rvsdg::gvn { class GVN_Manager{ struct GVN_Deps { + GVN_Deps():op(0){} GVN_Val op; std::vector > args; void push(GVN_Val v, size_t count){args.emplace_back(v, count);} @@ -524,7 +524,7 @@ namespace jlm::rvsdg::gvn { return SymGen(0); } GVN_Val SymGen(GVN_Val flags) { - GVN_Val g; + GVN_Val g = 0; do{ g = random() & ~GVN_MASK; g |= flags | GVN_IS_SYMBOLIC; diff --git a/jlm/tooling/Command.cpp b/jlm/tooling/Command.cpp index 34a89f950..c9f11ef1e 100644 --- a/jlm/tooling/Command.cpp +++ b/jlm/tooling/Command.cpp @@ -425,7 +425,6 @@ JlmOptCommand::CreateTransformation(JlmOptCommandLineOptions::OptimizationId opt return std::make_unique(); case JlmOptCommandLineOptions::OptimizationId::PartialRedundancyElimination: return std::make_unique(); - return std::make_shared(); case JlmOptCommandLineOptions::OptimizationId::FunctionInlining: return std::make_shared(); case JlmOptCommandLineOptions::OptimizationId::IfConversion: diff --git a/jlm/tooling/CommandLine.cpp b/jlm/tooling/CommandLine.cpp index 9e975dd77..fea9f087b 100644 --- a/jlm/tooling/CommandLine.cpp +++ b/jlm/tooling/CommandLine.cpp @@ -148,8 +148,8 @@ JlmOptCommandLineOptions::ToCommandLineArgument(OptimizationId optimizationId) OptimizationCommandLineArgument::CommonNodeElimination_ }, { OptimizationId::DeadNodeElimination, OptimizationCommandLineArgument::DeadNodeElimination_ }, -{ OptimizationId::PartialRedundancyElimination, -OptimizationCommandLineArgument::PartialRedundancyElimination_ }, + { OptimizationId::PartialRedundancyElimination, + OptimizationCommandLineArgument::PartialRedundancyElimination_ }, { OptimizationId::FunctionInlining, OptimizationCommandLineArgument::FunctionInlining_ }, { OptimizationId::IfConversion, OptimizationCommandLineArgument::IfConversion_ }, { OptimizationId::InvariantValueRedirection, @@ -519,7 +519,7 @@ JlcCommandLineParser::ParseCommandLineArguments(int argc, const char * const * a "Collect dead node elimination pass statistics."), CreateStatisticsOption( util::Statistics::Id::PartialRedundancyElimination, - "Collect dead node elimination2 pass statistics."), + "PRE under construction. Currently performs GVN."), CreateStatisticsOption( util::Statistics::Id::FunctionInlining, "Collect function inlining pass statistics."), From 3068e07f79867fc497a05d4cc042f95f69a74b2f Mon Sep 17 00:00:00 2001 From: Lars Astrup Sundt Date: Tue, 11 Nov 2025 19:30:45 +0100 Subject: [PATCH 52/52] Trivial change --- jlm/llvm/opt/PartialRedundancyElimination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlm/llvm/opt/PartialRedundancyElimination.cpp b/jlm/llvm/opt/PartialRedundancyElimination.cpp index 42b93cd07..5470c39f4 100644 --- a/jlm/llvm/opt/PartialRedundancyElimination.cpp +++ b/jlm/llvm/opt/PartialRedundancyElimination.cpp @@ -576,7 +576,7 @@ void PartialRedundancyElimination::dump_node(PartialRedundancyElimination* pe, r }); for (size_t i = 0; i < node->ninputs(); i++){ - dbg_out << TR_GREEN; + dbg_out << TR_YELLOW; dbg_out << " : " << to_string(pe->GVNOrZero( node->input(i)->origin() )); }