From e67a201c114cccfaac32296a57942c3625d50339 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Mon, 4 May 2026 07:14:34 +0200 Subject: [PATCH 1/7] LLVM: add AggregateAllocaSplittin pass --- jlm/llvm/Makefile.sub | 3 + jlm/llvm/opt/AggregateAllocaSplitting.cpp | 603 ++++++++++++++++++ jlm/llvm/opt/AggregateAllocaSplitting.hpp | 52 ++ .../opt/AggregateAllocaSplittingTests.cpp | 321 ++++++++++ jlm/tooling/Command.cpp | 6 +- jlm/tooling/CommandLine.cpp | 11 + jlm/tooling/CommandLine.hpp | 1 + jlm/util/Statistics.cpp | 1 + jlm/util/Statistics.hpp | 1 + 9 files changed, 997 insertions(+), 2 deletions(-) create mode 100644 jlm/llvm/opt/AggregateAllocaSplitting.cpp create mode 100644 jlm/llvm/opt/AggregateAllocaSplitting.hpp create mode 100644 jlm/llvm/opt/AggregateAllocaSplittingTests.cpp diff --git a/jlm/llvm/Makefile.sub b/jlm/llvm/Makefile.sub index ad430d802..bdeae0ec5 100644 --- a/jlm/llvm/Makefile.sub +++ b/jlm/llvm/Makefile.sub @@ -62,6 +62,7 @@ libllvm_SOURCES = \ jlm/llvm/opt/alias-analyses/PointsToGraph.cpp \ jlm/llvm/opt/alias-analyses/PointsToGraphAliasAnalysis.cpp \ jlm/llvm/opt/alias-analyses/RegionAwareModRefSummarizer.cpp \ + jlm/llvm/opt/AggregateAllocaSplitting.cpp \ jlm/llvm/opt/CommonNodeElimination.cpp \ jlm/llvm/opt/DeadNodeElimination.cpp \ jlm/llvm/opt/IfConversion.cpp \ @@ -110,6 +111,7 @@ libllvm_HEADERS = \ jlm/llvm/opt/alias-analyses/PointsToAnalysis.hpp \ jlm/llvm/opt/alias-analyses/PointsToGraph.hpp \ jlm/llvm/opt/alias-analyses/PointsToGraphAliasAnalysis.hpp \ + jlm/llvm/opt/AggregateAllocaSplitting.hpp \ jlm/llvm/opt/pull.hpp \ jlm/llvm/opt/reduction.hpp \ jlm/llvm/opt/IfConversion.hpp \ @@ -230,6 +232,7 @@ run-libllvm-tests_SOURCES = \ jlm/llvm/opt/alias-analyses/PointsToGraphTests.cpp \ jlm/llvm/opt/alias-analyses/RegionAwareModRefSummarizerTests.cpp \ \ + jlm/llvm/opt/AggregateAllocaSplittingTests.cpp \ jlm/llvm/opt/CommonNodeEliminationTests.cpp \ jlm/llvm/opt/DeadNodeEliminationTests.cpp \ jlm/llvm/opt/IfConversionTests.cpp \ diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp new file mode 100644 index 000000000..85b19f7b0 --- /dev/null +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -0,0 +1,603 @@ +/* + * Copyright 2026 Nico Reißmann + * See COPYING for terms of redistribution. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jlm::llvm +{ + +class AggregateAllocaSplitting::Statistics final : public util::Statistics +{ + const char * numAggregateAllocaNodesLabel_ = "#AggregateAllocaNodes"; + const char * numAggregateStructAllocaNodesLabel_ = "#AggregateStructAllocaNodes"; + const char * numSplitableTypeAggregateAllocaNodesLabel_ = "#SplitableTypeAggregateAllocaNodes"; + const char * numSplitableAggregateAllocaNodesLabel_ = "#SplitableAggregateAllocaNodes"; + const char * aggregateAllocaSplittingTimerLabel_ = "AggregateAllocaSplittingTime"; + +public: + ~Statistics() noexcept override = default; + + explicit Statistics(util::FilePath filePath) + : util::Statistics(Id::AggregateAllocaSplitting, std::move(filePath)) + {} + + void + start() + { + AddTimer(aggregateAllocaSplittingTimerLabel_).start(); + } + + void + stop( + const size_t numAggregateAllocaNodes, + const size_t numAggregateStructAllocaNodes, + const size_t numSplitableTypeAggregateAllocaNodes, + const size_t numSplitableAggregateAllocaNodes) + { + GetTimer(aggregateAllocaSplittingTimerLabel_).stop(); + AddMeasurement(numAggregateAllocaNodesLabel_, numAggregateAllocaNodes); + AddMeasurement(numAggregateStructAllocaNodesLabel_, numAggregateStructAllocaNodes); + AddMeasurement( + numSplitableTypeAggregateAllocaNodesLabel_, + numSplitableTypeAggregateAllocaNodes); + AddMeasurement(numSplitableAggregateAllocaNodesLabel_, numSplitableAggregateAllocaNodes); + } + + static std::unique_ptr + create(util::FilePath filePath) + { + return std::make_unique(std::move(filePath)); + } +}; + +struct AggregateAllocaSplitting::Context +{ + size_t numAggregateAllocaNodes = 0; + size_t numAggregateStructAllocaNodes = 0; + size_t numSplitableTypeAggregateAllocaNodes = 0; + size_t numSplitableAggregateAllocaNodes = 0; +}; + +struct AggregateAllocaSplitting::AllocaTraceInfo +{ + explicit AllocaTraceInfo(rvsdg::SimpleNode & allocaNode) + : allocaNode(&allocaNode) + {} + + rvsdg::SimpleNode * allocaNode = nullptr; + std::vector allocaConsumers{}; +}; + +AggregateAllocaSplitting::~AggregateAllocaSplitting() noexcept = default; + +AggregateAllocaSplitting::AggregateAllocaSplitting() + : Transformation("AggregateAllocaSplitting") +{} + +bool +AggregateAllocaSplitting::isSplitableType(const rvsdg::Type & type) +{ + // FIXME: We currently only look at alloca nodes with a struct type. We might be able + // to do something for alloca nodes with array types as well. + const auto structType = dynamic_cast(&type); + if (!structType) + return false; + + for (const auto & elementType : structType->elementTypes()) + { + if (IsAggregateType(*elementType)) + { + // FIXME: We currently only look at alloca nodes that do not contain nested aggregate types. + return false; + } + } + + return true; +} + +std::optional +AggregateAllocaSplitting::isSplitable(rvsdg::SimpleNode & allocaNode) +{ + [[maybe_unused]] auto allocaOperation = + dynamic_cast(&allocaNode.GetOperation()); + JLM_ASSERT(allocaOperation && isSplitableType(*allocaOperation->allocatedType())); + + auto & address = AllocaOperation::getPointerOutput(allocaNode); + + bool isSplitable = true; + AllocaTraceInfo allocaTraceInfo(allocaNode); + + util::HashSet visited; + util::HashSet toVisit{ &address }; + auto addToVisitSet = [&](rvsdg::Output & output) + { + if (!visited.Contains(&output) && !toVisit.Contains(&output)) + { + toVisit.insert(&output); + } + }; + auto removeFromVisitSet = [&]() + { + const auto output = *toVisit.Items().begin(); + toVisit.Remove(output); + [[maybe_unused]] auto inserted = visited.insert(output); + JLM_ASSERT(inserted); + return output; + }; + + while (!toVisit.IsEmpty() && isSplitable) + { + const auto currentOutput = removeFromVisitSet(); + + for (auto & user : currentOutput->Users()) + { + if (!isSplitable) + { + // Stop handling users if the previous user was already not splitable + break; + } + + if (auto userRegion = rvsdg::TryGetOwnerRegion(user)) + { + // We should never have an alloca connected to a graph export + JLM_ASSERT(userRegion->node()); + + isSplitable = rvsdg::MatchTypeWithDefault( + *userRegion->node(), + [&](const rvsdg::GammaNode & gammaNode) + { + auto & gammaOutput = gammaNode.mapBranchResultToOutput(user); + addToVisitSet(gammaOutput); + return true; + }, + [&](const rvsdg::ThetaNode & thetaNode) + { + const auto loopVar = thetaNode.MapPostLoopVar(user); + addToVisitSet(*loopVar.pre); + addToVisitSet(*loopVar.output); + return true; + }, + [&](const rvsdg::LambdaNode &) + { + return false; + }, + [&]() + { + throw std::logic_error(util::strfmt( + "Unhandled owner region node type: ", + userRegion->node()->DebugString())); + // Silence compiler + return false; + }); + } + else if (auto userNode = rvsdg::TryGetOwnerNode(user)) + { + isSplitable = rvsdg::MatchTypeWithDefault( + *userNode, + [&](rvsdg::GammaNode & gammaNode) + { + auto roleVar = gammaNode.MapInput(user); + if (auto entryVar = std::get_if(&roleVar)) + { + for (auto argument : entryVar->branchArgument) + { + addToVisitSet(*argument); + } + } + else + { + throw std::logic_error(util::strfmt("Unhandled role variable.")); + } + + return true; + }, + [&](rvsdg::ThetaNode & thetaNode) + { + const auto loopVar = thetaNode.MapInputLoopVar(user); + addToVisitSet(*loopVar.pre); + return true; + }, + [&](rvsdg::SimpleNode & simpleNode) + { + auto & operation = simpleNode.GetOperation(); + return rvsdg::MatchTypeWithDefault( + operation, +#if 0 + [&](const LoadOperation &) + { + return true; + }, + [&](const StoreOperation &) + { + if (&user != &StoreOperation::AddressInput(simpleNode)) + return false; + + return true; + }, +#endif + [&](const MemSetOperation &) + { + // FIXME: We currently do not handle memset operations + return false; + + // allocaTraceInfo.allocaConsumers.push_back(&simpleNode); + // return true; + }, + [&](const MemCpyOperation &) + { + // FIXME: We currently do not handle memcpy operations + return false; + + // allocaTraceInfo.allocaConsumers.push_back(&simpleNode); + // return true; + }, + [&](const GetElementPtrOperation &) + { + JLM_ASSERT(userNode->input(0) == &user); + for (size_t n = 1; n < userNode->ninputs(); n++) + { + if (!tryGetConstantSignedInteger(*userNode->input(n)->origin()).has_value()) + { + return false; + } + } + + allocaTraceInfo.allocaConsumers.push_back(&simpleNode); + // addToVisitSet(*simpleNode.output(0)); + return true; + }, + [&]() + { + return false; + }); + }, + [&]() + { + throw std::logic_error( + util::strfmt("Unhandled node type: ", userNode->DebugString())); + // Silence compiler + return false; + }); + } + else + { + throw std::logic_error("Unhandled owner type"); + } + } + } + + if (!isSplitable) + return std::nullopt; + + for (const auto allocaConsumer : allocaTraceInfo.allocaConsumers) + { + if (!checkGetElementPtrUsers(*allocaConsumer)) + return std::nullopt; + } + + return std::make_optional(allocaTraceInfo); +} + +bool +AggregateAllocaSplitting::checkGetElementPtrUsers(const rvsdg::SimpleNode & gepNode) +{ + [[maybe_unused]] auto gepOperation = + dynamic_cast(&gepNode.GetOperation()); + auto & address = *gepNode.output(0); + + bool hasOnlyLoadsAndStores = true; + + util::HashSet visited; + util::HashSet toVisit{ &address }; + auto addToVisitSet = [&](rvsdg::Output & output) + { + if (!visited.Contains(&output) && !toVisit.Contains(&output)) + { + toVisit.insert(&output); + } + }; + auto removeFromVisitSet = [&]() + { + const auto output = *toVisit.Items().begin(); + toVisit.Remove(output); + [[maybe_unused]] auto inserted = visited.insert(output); + JLM_ASSERT(inserted); + return output; + }; + + while (!toVisit.IsEmpty() && hasOnlyLoadsAndStores) + { + const auto currentOutput = removeFromVisitSet(); + for (auto & user : currentOutput->Users()) + { + if (!hasOnlyLoadsAndStores) + { + // Stop handling users if the previous user was already not a load or store + break; + } + + if (auto userRegion = rvsdg::TryGetOwnerRegion(user)) + { + // We should never have a gep node connected to a graph export + JLM_ASSERT(userRegion->node()); + + hasOnlyLoadsAndStores = rvsdg::MatchTypeWithDefault( + *userRegion->node(), + [&](const rvsdg::GammaNode & gammaNode) + { + auto & gammaOutput = gammaNode.mapBranchResultToOutput(user); + addToVisitSet(gammaOutput); + return true; + }, + [&](const rvsdg::ThetaNode & thetaNode) + { + const auto loopVar = thetaNode.MapPostLoopVar(user); + addToVisitSet(*loopVar.pre); + addToVisitSet(*loopVar.output); + return true; + }, + [&](const rvsdg::LambdaNode &) + { + return false; + }, + [&]() + { + throw std::logic_error(util::strfmt( + "Unhandled owner region node type: ", + userRegion->node()->DebugString())); + // Silence compiler + return false; + }); + } + else if (auto userNode = rvsdg::TryGetOwnerNode(user)) + { + hasOnlyLoadsAndStores = rvsdg::MatchTypeWithDefault( + *userNode, + [&](const rvsdg::GammaNode & gammaNode) + { + auto roleVar = gammaNode.MapInput(user); + if (auto entryVar = std::get_if(&roleVar)) + { + for (auto argument : entryVar->branchArgument) + { + addToVisitSet(*argument); + } + } + else + { + throw std::logic_error(util::strfmt("Unhandled role variable.")); + } + + return true; + }, + [&](const rvsdg::ThetaNode & thetaNode) + { + const auto loopVar = thetaNode.MapInputLoopVar(user); + addToVisitSet(*loopVar.pre); + return true; + }, + [&](const rvsdg::SimpleNode & simpleNode) + { + auto & operation = simpleNode.GetOperation(); + return rvsdg::MatchTypeWithDefault( + operation, + [&](const LoadOperation &) + { + return true; + }, + [&](const StoreOperation &) + { + if (&user != &StoreOperation::AddressInput(simpleNode)) + return false; + + return true; + }, + [&](const IOBarrierOperation &) + { + addToVisitSet(*simpleNode.output(0)); + return true; + }, + [&]() + { + return false; + }); + }, + [&]() + { + throw std::logic_error( + util::strfmt("Unhandled node type: ", userNode->DebugString())); + // Silence compiler + return false; + }); + } + else + { + throw std::logic_error("Unhandled owner type"); + } + } + } + + return hasOnlyLoadsAndStores; +} + +std::vector +AggregateAllocaSplitting::findSplitableAllocaNodes(rvsdg::Region & region) const +{ + std::function &)> findAllocaNodes = + [&](rvsdg::Region & region, std::vector & traceInfo) + { + for (auto & node : region.Nodes()) + { + MatchTypeWithDefault( + node, + [&](rvsdg::GammaNode & gammaNode) + { + for (auto & subregion : gammaNode.Subregions()) + findAllocaNodes(subregion, traceInfo); + }, + [&](rvsdg::ThetaNode & thetaNode) + { + findAllocaNodes(*thetaNode.subregion(), traceInfo); + }, + [&](rvsdg::LambdaNode & lambdaNode) + { + findAllocaNodes(*lambdaNode.subregion(), traceInfo); + }, + [&](rvsdg::PhiNode & phiNode) + { + findAllocaNodes(*phiNode.subregion(), traceInfo); + }, + [&](rvsdg::DeltaNode &) + { + // Nothing needs to be done + }, + [&](rvsdg::SimpleNode & simpleNode) + { + const auto allocaOperation = + dynamic_cast(&simpleNode.GetOperation()); + if (!allocaOperation) + return; + + auto & allocaType = *allocaOperation->allocatedType(); + if (is(allocaType)) + { + context_->numAggregateStructAllocaNodes++; + context_->numAggregateAllocaNodes++; + } + else if (IsAggregateType(allocaType)) + { + context_->numAggregateAllocaNodes++; + } + + if (isSplitableType(*allocaOperation->allocatedType())) + { + context_->numSplitableTypeAggregateAllocaNodes++; + if (auto allocaTraceInfo = isSplitable(simpleNode)) + { + context_->numSplitableAggregateAllocaNodes++; + traceInfo.emplace_back(*allocaTraceInfo); + } + } + }, + [&]() + { + throw std::logic_error("Unhandled node type."); + }); + } + }; + + std::vector traceInfo; + findAllocaNodes(region, traceInfo); + return traceInfo; +} + +void +AggregateAllocaSplitting::splitAllocaNode(const AllocaTraceInfo & allocaTraceInfo) +{ + auto & allocaNode = *allocaTraceInfo.allocaNode; + const auto allocaOperation = dynamic_cast(&allocaNode.GetOperation()); + JLM_ASSERT(allocaOperation && isSplitableType(*allocaOperation->allocatedType())); + auto & allocaType = *std::static_pointer_cast(allocaOperation->allocatedType()); + const auto & countInput = AllocaOperation::getCountInput(allocaNode); + const auto alignment = allocaOperation->alignment(); + + // Create alloca nodes for each element in the aggregate type + std::vector elementAllocaNodes; + std::vector allocaMemoryStates; + for (const auto & elementType : allocaType.elementTypes()) + { + auto & elementAlloca = + AllocaOperation::createNode(elementType, *countInput.origin(), alignment); + elementAllocaNodes.push_back(&elementAlloca); + allocaMemoryStates.push_back(&AllocaOperation::getMemoryStateOutput(elementAlloca)); + } + + // Replace alloca node's memory state output + const auto memoryState = MemoryStateMergeOperation::Create(allocaMemoryStates); + AllocaOperation::getMemoryStateOutput(allocaNode).divert_users(memoryState); + + // Replace alloca node consumers + for (auto allocaConsumer : allocaTraceInfo.allocaConsumers) + { + rvsdg::MatchTypeWithDefault( + allocaConsumer->GetOperation(), + [&](const GetElementPtrOperation &) + { + JLM_ASSERT(allocaConsumer->ninputs() == 3); + auto & consumerRegion = *allocaConsumer->region(); + // FIXME: Introduce convenient functions + [[maybe_unused]] auto index0 = + tryGetConstantSignedInteger(*allocaConsumer->input(1)->origin()).value(); + const auto index1 = + tryGetConstantSignedInteger(*allocaConsumer->input(2)->origin()).value(); + JLM_ASSERT(index0 == 0); + + auto elementAlloca = elementAllocaNodes[index1]; + auto & routedAddress = rvsdg::RouteToRegion( + AllocaOperation::getPointerOutput(*elementAlloca), + consumerRegion); + allocaConsumer->output(0)->divert_users(&routedAddress); + }, + [&]() + { + throw std::logic_error( + util::strfmt("Unhandled node type: ", allocaConsumer->DebugString())); + }); + } +} + +void +AggregateAllocaSplitting::splitAllocaNodes(rvsdg::RvsdgModule & rvsdgModule) +{ + const auto traceInfo = findSplitableAllocaNodes(rvsdgModule.Rvsdg().GetRootRegion()); + for (const auto & allocaTraceInfo : traceInfo) + { + splitAllocaNode(allocaTraceInfo); + } + + // Remove all nodes that became dead throughout the transformation + rvsdgModule.Rvsdg().PruneNodes(); +} + +void +AggregateAllocaSplitting::Run( + rvsdg::RvsdgModule & module, + util::StatisticsCollector & statisticsCollector) +{ + context_ = std::make_unique(); + auto statistics = Statistics::create(module.SourceFilePath().value()); + + statistics->start(); + splitAllocaNodes(module); + statistics->stop( + context_->numAggregateAllocaNodes, + context_->numAggregateStructAllocaNodes, + context_->numSplitableTypeAggregateAllocaNodes, + context_->numSplitableAggregateAllocaNodes); + + statisticsCollector.CollectDemandedStatistics(std::move(statistics)); + + // Discard internal state to free up memory after we are done + context_.reset(); +} + +} diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.hpp b/jlm/llvm/opt/AggregateAllocaSplitting.hpp new file mode 100644 index 000000000..4e5b5dd01 --- /dev/null +++ b/jlm/llvm/opt/AggregateAllocaSplitting.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 2026 Nico Reißmann + * See COPYING for terms of redistribution. + */ + +#ifndef JLM_LLVM_OPT_AGGREGATEALLOCASPLITTING_HPP +#define JLM_LLVM_OPT_AGGREGATEALLOCASPLITTING_HPP + +#include + +namespace jlm::llvm +{ + +class AggregateAllocaSplitting final : public rvsdg::Transformation +{ + struct Context; + class Statistics; + struct AllocaTraceInfo; + +public: + ~AggregateAllocaSplitting() noexcept override; + + AggregateAllocaSplitting(); + + void + Run(rvsdg::RvsdgModule & module, util::StatisticsCollector & statisticsCollector) override; + +private: + void + splitAllocaNodes(rvsdg::RvsdgModule & rvsdgModule); + + std::vector + findSplitableAllocaNodes(rvsdg::Region & region) const; + + static void + splitAllocaNode(const AllocaTraceInfo & allocaTraceInfo); + + static bool + checkGetElementPtrUsers(const rvsdg::SimpleNode & gepNode); + + static std::optional + isSplitable(rvsdg::SimpleNode & allocaNode); + + static bool + isSplitableType(const rvsdg::Type & type); + + std::unique_ptr context_{}; +}; + +} + +#endif diff --git a/jlm/llvm/opt/AggregateAllocaSplittingTests.cpp b/jlm/llvm/opt/AggregateAllocaSplittingTests.cpp new file mode 100644 index 000000000..926918e26 --- /dev/null +++ b/jlm/llvm/opt/AggregateAllocaSplittingTests.cpp @@ -0,0 +1,321 @@ +/* + * Copyright 2026 Nico Reißmann + * See COPYING for terms of redistribution. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +assertAllocaWithType(const jlm::rvsdg::Output & output, const jlm::rvsdg::Type & type) +{ + using namespace jlm::llvm; + using namespace jlm::rvsdg; + + auto [allocaNode, allocaOperation] = + jlm::rvsdg::TryGetSimpleNodeAndOptionalOp(output); + EXPECT_TRUE(allocaNode && allocaOperation); + EXPECT_EQ(*allocaOperation->allocatedType(), type); +} + +TEST(AggregateAllocaSplittingTests, getElementPtrTest) +{ + using namespace jlm::llvm; + using namespace jlm::rvsdg; + using namespace jlm::util; + + // Arrange + auto bit32Type = BitType::Create(32); + auto bit64Type = BitType::Create(64); + const auto pointerType = PointerType::Create(); + const auto memoryStateType = MemoryStateType::Create(); + const auto structType = StructType::CreateIdentified({ bit32Type, bit64Type }, false); + const auto functionType = FunctionType::Create({}, { memoryStateType }); + + LlvmRvsdgModule rvsdgModule(FilePath(""), "", ""); + auto & rvsdg = rvsdgModule.Rvsdg(); + + auto lambdaNode = LambdaNode::Create( + rvsdg.GetRootRegion(), + LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage)); + + auto & zero32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0); + auto & zero64Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 64, 0); + auto & one32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1); + auto & allocaNode = AllocaOperation::createNode(structType, *one32Node.output(0), 4); + + auto & gepXNode = GetElementPtrOperation::createNode( + *allocaNode.output(0), + { zero32Node.output(0), zero32Node.output(0) }, + bit32Type); + auto & storeGepXNode = StoreNonVolatileOperation::CreateNode( + *gepXNode.output(0), + *zero32Node.output(0), + { &AllocaOperation::getMemoryStateOutput(allocaNode) }, + 4); + + auto & gepYNode = GetElementPtrOperation::createNode( + *allocaNode.output(0), + { zero32Node.output(0), one32Node.output(0) }, + bit64Type); + auto & storeGepYNode = StoreNonVolatileOperation::CreateNode( + *gepYNode.output(0), + *zero64Node.output(0), + { storeGepXNode.output(0) }, + 4); + + auto lambdaOutput = lambdaNode->finalize({ storeGepYNode.output(0) }); + GraphExport::Create(*lambdaOutput, "f"); + + // Act + StatisticsCollector statisticsCollector; + AggregateAllocaSplitting aggregateAllocaSplitting; + aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector); + + // Assert + // We expect two AllocaOperation, a MemoryStateMergeOperation, and a IntegerConstantOperation + // node + EXPECT_EQ(lambdaNode->subregion()->numNodes(), 8u); + + assertAllocaWithType(*StoreOperation::AddressInput(storeGepXNode).origin(), *bit32Type); + assertAllocaWithType(*StoreOperation::AddressInput(storeGepYNode).origin(), *bit64Type); + + // Check memstate + { + auto [memoryMergeNode, memoryMergeOperation] = + TryGetSimpleNodeAndOptionalOp( + *StoreOperation::getMemoryStateInputs(storeGepXNode).begin()->origin()); + EXPECT_TRUE(memoryMergeNode && memoryMergeOperation); + + EXPECT_EQ(memoryMergeNode->ninputs(), 2u); + assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit32Type); + assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit64Type); + } +} + +TEST(AggregateAllocaSplittingTests, gammaTest) +{ + using namespace jlm::llvm; + using namespace jlm::rvsdg; + using namespace jlm::util; + + // Arrange + auto bit16Type = BitType::Create(16); + auto bit32Type = BitType::Create(32); + auto bit64Type = BitType::Create(64); + const auto controlType = ControlType::Create(2); + const auto pointerType = PointerType::Create(); + const auto memoryStateType = MemoryStateType::Create(); + const auto structType = StructType::CreateIdentified({ bit16Type, bit32Type, bit64Type }, false); + const auto functionType = FunctionType::Create({ controlType }, { memoryStateType }); + + LlvmRvsdgModule rvsdgModule(FilePath(""), "", ""); + auto & rvsdg = rvsdgModule.Rvsdg(); + + auto lambdaNode = LambdaNode::Create( + rvsdg.GetRootRegion(), + LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage)); + auto controlArgument = lambdaNode->GetFunctionArguments()[0]; + + auto & zero = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0); + auto & one = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1); + auto & two = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 2); + auto & allocaNode = AllocaOperation::createNode(structType, *one.output(0), 4); + + auto gammaNode = GammaNode::create(controlArgument, 2); + auto baseAddressEntryVar = gammaNode->AddEntryVar(&AllocaOperation::getPointerOutput(allocaNode)); + auto memoryStateEntryVar = + gammaNode->AddEntryVar(&AllocaOperation::getMemoryStateOutput(allocaNode)); + + Node *storeGep0Node = nullptr, *storeGep1Node = nullptr; + // Subregion 0 + { + auto & zero16Node = IntegerConstantOperation::Create(*gammaNode->subregion(0), 16, 0); + auto & zero32Node = IntegerConstantOperation::Create(*gammaNode->subregion(0), 32, 0); + + auto & gep0Node = GetElementPtrOperation::createNode( + *baseAddressEntryVar.branchArgument[0], + { zero32Node.output(0), zero32Node.output(0) }, + bit16Type); + storeGep0Node = &StoreNonVolatileOperation::CreateNode( + *gep0Node.output(0), + *zero16Node.output(0), + { memoryStateEntryVar.branchArgument[0] }, + 4); + } + + // Subregion 1 + { + auto & zero32Node = IntegerConstantOperation::Create(*gammaNode->subregion(1), 32, 0); + auto & one32Node = IntegerConstantOperation::Create(*gammaNode->subregion(1), 32, 1); + + auto & gep1Node = GetElementPtrOperation::createNode( + *baseAddressEntryVar.branchArgument[1], + { zero32Node.output(0), one32Node.output(0) }, + bit32Type); + storeGep1Node = &StoreNonVolatileOperation::CreateNode( + *gep1Node.output(0), + *zero32Node.output(0), + { memoryStateEntryVar.branchArgument[1] }, + 4); + } + auto baseAddressExitVar = gammaNode->AddExitVar(baseAddressEntryVar.branchArgument); + auto memoryStateExitVar = + gammaNode->AddExitVar({ storeGep0Node->output(0), storeGep1Node->output(0) }); + + auto & gep2Node = GetElementPtrOperation::createNode( + *baseAddressExitVar.output, + { zero.output(0), two.output(0) }, + bit64Type); + + auto & zero64Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 64, 0); + auto & storeGep2Node = StoreNonVolatileOperation::CreateNode( + *gep2Node.output(0), + *zero64Node.output(0), + { memoryStateExitVar.output }, + 4); + + auto lambdaOutput = lambdaNode->finalize({ storeGep2Node.output(0) }); + GraphExport::Create(*lambdaOutput, "f"); + + // Act + StatisticsCollector statisticsCollector; + AggregateAllocaSplitting aggregateAllocaSplitting; + aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector); + + // Assert + // Check gep0 + { + auto & gammaInput = + gammaNode->mapBranchArgumentToInput(*StoreOperation::AddressInput(*storeGep0Node).origin()); + assertAllocaWithType(*gammaInput.origin(), *bit16Type); + } + + // Check gep1 + { + auto & gammaInput = + gammaNode->mapBranchArgumentToInput(*StoreOperation::AddressInput(*storeGep1Node).origin()); + assertAllocaWithType(*gammaInput.origin(), *bit32Type); + } + + // Check gep2 + { + assertAllocaWithType(*StoreOperation::AddressInput(storeGep2Node).origin(), *bit64Type); + } + + // Check memState + { + auto [memoryMergeNode, memoryMergeOperation] = + TryGetSimpleNodeAndOptionalOp( + *memoryStateEntryVar.input->origin()); + EXPECT_TRUE(memoryMergeNode && memoryMergeOperation); + + EXPECT_EQ(memoryMergeNode->ninputs(), 3u); + assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit16Type); + assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit32Type); + assertAllocaWithType(*memoryMergeNode->input(2)->origin(), *bit64Type); + } +} + +TEST(AggregateAllocaSplittingTests, thetaTest) +{ + using namespace jlm::llvm; + using namespace jlm::rvsdg; + using namespace jlm::util; + + // Arrange + auto bit16Type = BitType::Create(16); + auto bit32Type = BitType::Create(32); + const auto pointerType = PointerType::Create(); + const auto memoryStateType = MemoryStateType::Create(); + const auto structType = StructType::CreateIdentified({ bit16Type, bit32Type }, false); + const auto functionType = FunctionType::Create({}, { memoryStateType }); + + LlvmRvsdgModule rvsdgModule(FilePath(""), "", ""); + auto & rvsdg = rvsdgModule.Rvsdg(); + + auto lambdaNode = LambdaNode::Create( + rvsdg.GetRootRegion(), + LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage)); + + auto & one = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1); + auto & allocaNode = AllocaOperation::createNode(structType, *one.output(0), 4); + + Node * storeGep0Node = nullptr; + auto thetaNode = ThetaNode::create(lambdaNode->subregion()); + auto addressLoopVar = thetaNode->AddLoopVar(&AllocaOperation::getPointerOutput(allocaNode)); + auto memoryStateLoopVar = + thetaNode->AddLoopVar(&AllocaOperation::getMemoryStateOutput(allocaNode)); + { + auto & zero16Node = IntegerConstantOperation::Create(*thetaNode->subregion(), 16, 0); + auto & zero32Node = IntegerConstantOperation::Create(*thetaNode->subregion(), 32, 0); + auto & gep0Node = GetElementPtrOperation::createNode( + *addressLoopVar.pre, + { zero32Node.output(0), zero32Node.output(0) }, + bit16Type); + + storeGep0Node = &StoreNonVolatileOperation::CreateNode( + *gep0Node.output(0), + *zero16Node.output(0), + { memoryStateLoopVar.pre }, + 4); + + memoryStateLoopVar.post->divert_to(storeGep0Node->output(0)); + } + + auto & zero32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0); + auto & gep1Node = GetElementPtrOperation::createNode( + *addressLoopVar.output, + { zero32Node.output(0), one.output(0) }, + bit32Type); + auto & storeGep1Node = StoreNonVolatileOperation::CreateNode( + *gep1Node.output(0), + *zero32Node.output(0), + { memoryStateLoopVar.output }, + 4); + + auto lambdaOutput = lambdaNode->finalize({ storeGep1Node.output(0) }); + GraphExport::Create(*lambdaOutput, "f"); + + // Act + StatisticsCollector statisticsCollector; + AggregateAllocaSplitting aggregateAllocaSplitting; + aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector); + + // Assert + // Check gep0 + { + const auto & tracedOutput = traceOutput(*StoreOperation::AddressInput(*storeGep0Node).origin()); + assertAllocaWithType(tracedOutput, *bit16Type); + } + + // Check gep1 + { + const auto & tracedOutput = traceOutput(*StoreOperation::AddressInput(storeGep1Node).origin()); + assertAllocaWithType(tracedOutput, *bit32Type); + } + + // Check memstate + { + auto [memoryMergeNode, memoryMergeOperation] = + TryGetSimpleNodeAndOptionalOp( + *memoryStateLoopVar.input->origin()); + EXPECT_TRUE(memoryMergeNode && memoryMergeOperation); + + EXPECT_EQ(memoryMergeNode->ninputs(), 2u); + assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit16Type); + assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit32Type); + } +} diff --git a/jlm/tooling/Command.cpp b/jlm/tooling/Command.cpp index 6abe6c160..59bbc8bcc 100644 --- a/jlm/tooling/Command.cpp +++ b/jlm/tooling/Command.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -35,14 +36,13 @@ #include #include -#include - #ifdef ENABLE_MLIR #include #include #endif #include +#include #include #include #include @@ -412,6 +412,8 @@ JlmOptCommand::CreateTransformation(JlmOptCommandLineOptions::OptimizationId opt return std::make_shared>(); case JlmOptCommandLineOptions::OptimizationId::AAAndersenRegionAware: return std::make_shared>(); + case JlmOptCommandLineOptions::OptimizationId::AggregateAllocaSplitting: + return std::make_shared(); case JlmOptCommandLineOptions::OptimizationId::CommonNodeElimination: return std::make_shared(); case JlmOptCommandLineOptions::OptimizationId::DeadNodeElimination: diff --git a/jlm/tooling/CommandLine.cpp b/jlm/tooling/CommandLine.cpp index 6040d112d..8468fa4aa 100644 --- a/jlm/tooling/CommandLine.cpp +++ b/jlm/tooling/CommandLine.cpp @@ -94,6 +94,7 @@ JlmOptCommandLineOptions::GetOptimizationIdCommandLineMap() static util::BijectiveMap map = { { OptimizationId::AAAndersenAgnostic, "AAAndersenAgnostic" }, { OptimizationId::AAAndersenRegionAware, "AAAndersenRegionAware" }, + { OptimizationId::AggregateAllocaSplitting, "AggregateAllocaSplitting" }, { OptimizationId::CommonNodeElimination, "CommonNodeElimination" }, { OptimizationId::DeadNodeElimination, "DeadNodeElimination" }, { OptimizationId::FunctionInlining, "FunctionInlining" }, @@ -195,6 +196,7 @@ const util::BijectiveMap & JlmOptCommandLineOptions::GetStatisticsIdCommandLineArguments() { static util::BijectiveMap mapping = { + { util::Statistics::Id::AggregateAllocaSplitting, "print-aggregate-alloca-splitting" }, { util::Statistics::Id::Aggregation, "print-aggregation-time" }, { util::Statistics::Id::AgnosticModRefSummarizer, "print-agnostic-mod-ref-summarization" }, { util::Statistics::Id::AliasAnalysisPrecisionEvaluation, "print-aa-precision-evaluation" }, @@ -697,6 +699,9 @@ JlmOptCommandLineParser::ParseCommandLineArguments(int argc, const char * const cl::list printStatistics( cl::values( + CreateStatisticsOption( + util::Statistics::Id::AggregateAllocaSplitting, + "Write aggregate alloca splitting statistics to file."), CreateStatisticsOption( util::Statistics::Id::Aggregation, "Write aggregation statistics to file."), @@ -826,6 +831,8 @@ JlmOptCommandLineParser::ParseCommandLineArguments(int argc, const char * const auto aAAndersenAgnostic = JlmOptCommandLineOptions::OptimizationId::AAAndersenAgnostic; auto aAAndersenRegionAware = JlmOptCommandLineOptions::OptimizationId::AAAndersenRegionAware; + auto aggregateAllocaSplitting = + JlmOptCommandLineOptions::OptimizationId::AggregateAllocaSplitting; auto commonNodeElimination = JlmOptCommandLineOptions::OptimizationId::CommonNodeElimination; auto deadNodeElimination = JlmOptCommandLineOptions::OptimizationId::DeadNodeElimination; auto functionInlining = JlmOptCommandLineOptions::OptimizationId::FunctionInlining; @@ -854,6 +861,10 @@ JlmOptCommandLineParser::ParseCommandLineArguments(int argc, const char * const aAAndersenRegionAware, JlmOptCommandLineOptions::ToCommandLineArgument(aAAndersenRegionAware), "Andersen alias analysis with region-aware memory state encoding"), + ::clEnumValN( + aggregateAllocaSplitting, + JlmOptCommandLineOptions::ToCommandLineArgument(aggregateAllocaSplitting), + "Split aggregate type alloca nodes"), ::clEnumValN( commonNodeElimination, JlmOptCommandLineOptions::ToCommandLineArgument(commonNodeElimination), diff --git a/jlm/tooling/CommandLine.hpp b/jlm/tooling/CommandLine.hpp index 75cc4d8e5..9c5c07f5b 100644 --- a/jlm/tooling/CommandLine.hpp +++ b/jlm/tooling/CommandLine.hpp @@ -68,6 +68,7 @@ class JlmOptCommandLineOptions final : public CommandLineOptions AAAndersenAgnostic, AAAndersenRegionAware, + AggregateAllocaSplitting, CommonNodeElimination, DeadNodeElimination, FunctionInlining, diff --git a/jlm/util/Statistics.cpp b/jlm/util/Statistics.cpp index a8e8026ff..1ad136581 100644 --- a/jlm/util/Statistics.cpp +++ b/jlm/util/Statistics.cpp @@ -18,6 +18,7 @@ static const util::BijectiveMap & GetStatisticsIdNames() { static util::BijectiveMap mapping = { + { Statistics::Id::AggregateAllocaSplitting, "AggregateAllocaSplitting" }, { Statistics::Id::Aggregation, "Aggregation" }, { Statistics::Id::AgnosticModRefSummarizer, "AgnosticModRefSummarizer" }, { Statistics::Id::AliasAnalysisPrecisionEvaluation, "AliasAnalysisPrecisionEvaluation" }, diff --git a/jlm/util/Statistics.hpp b/jlm/util/Statistics.hpp index e87c55ee4..c52617ff3 100644 --- a/jlm/util/Statistics.hpp +++ b/jlm/util/Statistics.hpp @@ -34,6 +34,7 @@ class Statistics { FirstEnumValue, // must always be the first enum value, used for iteration + AggregateAllocaSplitting, Aggregation, AgnosticModRefSummarizer, AliasAnalysisPrecisionEvaluation, From d01fe69c0e2f56f4523c249fb108cdcb8dd9db64 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sat, 16 May 2026 12:57:50 +0200 Subject: [PATCH 2/7] rename measurement --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index 85b19f7b0..7571d4141 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -30,7 +30,7 @@ class AggregateAllocaSplitting::Statistics final : public util::Statistics const char * numAggregateAllocaNodesLabel_ = "#AggregateAllocaNodes"; const char * numAggregateStructAllocaNodesLabel_ = "#AggregateStructAllocaNodes"; const char * numSplitableTypeAggregateAllocaNodesLabel_ = "#SplitableTypeAggregateAllocaNodes"; - const char * numSplitableAggregateAllocaNodesLabel_ = "#SplitableAggregateAllocaNodes"; + const char * numSplitAggregateAllocaNodesLabel_ = "#SplitAggregateAllocaNodes"; const char * aggregateAllocaSplittingTimerLabel_ = "AggregateAllocaSplittingTime"; public: @@ -51,7 +51,7 @@ class AggregateAllocaSplitting::Statistics final : public util::Statistics const size_t numAggregateAllocaNodes, const size_t numAggregateStructAllocaNodes, const size_t numSplitableTypeAggregateAllocaNodes, - const size_t numSplitableAggregateAllocaNodes) + const size_t numSplitAggregateAllocaNodes) { GetTimer(aggregateAllocaSplittingTimerLabel_).stop(); AddMeasurement(numAggregateAllocaNodesLabel_, numAggregateAllocaNodes); @@ -59,7 +59,7 @@ class AggregateAllocaSplitting::Statistics final : public util::Statistics AddMeasurement( numSplitableTypeAggregateAllocaNodesLabel_, numSplitableTypeAggregateAllocaNodes); - AddMeasurement(numSplitableAggregateAllocaNodesLabel_, numSplitableAggregateAllocaNodes); + AddMeasurement(numSplitAggregateAllocaNodesLabel_, numSplitAggregateAllocaNodes); } static std::unique_ptr @@ -74,7 +74,7 @@ struct AggregateAllocaSplitting::Context size_t numAggregateAllocaNodes = 0; size_t numAggregateStructAllocaNodes = 0; size_t numSplitableTypeAggregateAllocaNodes = 0; - size_t numSplitableAggregateAllocaNodes = 0; + size_t numSplitAggregateAllocaNodes = 0; }; struct AggregateAllocaSplitting::AllocaTraceInfo @@ -493,7 +493,7 @@ AggregateAllocaSplitting::findSplitableAllocaNodes(rvsdg::Region & region) const context_->numSplitableTypeAggregateAllocaNodes++; if (auto allocaTraceInfo = isSplitable(simpleNode)) { - context_->numSplitableAggregateAllocaNodes++; + context_->numSplitAggregateAllocaNodes++; traceInfo.emplace_back(*allocaTraceInfo); } } @@ -592,7 +592,7 @@ AggregateAllocaSplitting::Run( context_->numAggregateAllocaNodes, context_->numAggregateStructAllocaNodes, context_->numSplitableTypeAggregateAllocaNodes, - context_->numSplitableAggregateAllocaNodes); + context_->numSplitAggregateAllocaNodes); statisticsCollector.CollectDemandedStatistics(std::move(statistics)); From e049378a8a0a9e5d7f12108dc8a2eee1359c3c2b Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sat, 16 May 2026 13:01:47 +0200 Subject: [PATCH 3/7] remove dead code --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 30 ----------------------- 1 file changed, 30 deletions(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index 7571d4141..49e75cf70 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -221,35 +221,6 @@ AggregateAllocaSplitting::isSplitable(rvsdg::SimpleNode & allocaNode) auto & operation = simpleNode.GetOperation(); return rvsdg::MatchTypeWithDefault( operation, -#if 0 - [&](const LoadOperation &) - { - return true; - }, - [&](const StoreOperation &) - { - if (&user != &StoreOperation::AddressInput(simpleNode)) - return false; - - return true; - }, -#endif - [&](const MemSetOperation &) - { - // FIXME: We currently do not handle memset operations - return false; - - // allocaTraceInfo.allocaConsumers.push_back(&simpleNode); - // return true; - }, - [&](const MemCpyOperation &) - { - // FIXME: We currently do not handle memcpy operations - return false; - - // allocaTraceInfo.allocaConsumers.push_back(&simpleNode); - // return true; - }, [&](const GetElementPtrOperation &) { JLM_ASSERT(userNode->input(0) == &user); @@ -262,7 +233,6 @@ AggregateAllocaSplitting::isSplitable(rvsdg::SimpleNode & allocaNode) } allocaTraceInfo.allocaConsumers.push_back(&simpleNode); - // addToVisitSet(*simpleNode.output(0)); return true; }, [&]() From 80421d36c978f22c1818f80e3b50a0053c488d2d Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sat, 16 May 2026 19:48:16 +0200 Subject: [PATCH 4/7] allocaNode default init --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index 49e75cf70..c8a9328ee 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -83,7 +83,7 @@ struct AggregateAllocaSplitting::AllocaTraceInfo : allocaNode(&allocaNode) {} - rvsdg::SimpleNode * allocaNode = nullptr; + rvsdg::SimpleNode * allocaNode; std::vector allocaConsumers{}; }; From fe34f65e0eb1f7c71314aba7ac2c22a207c176c4 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sat, 16 May 2026 20:04:50 +0200 Subject: [PATCH 5/7] more --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 2 +- jlm/llvm/opt/AggregateAllocaSplitting.hpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index c8a9328ee..f02f575cd 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -522,6 +521,7 @@ AggregateAllocaSplitting::splitAllocaNode(const AllocaTraceInfo & allocaTraceInf JLM_ASSERT(index0 == 0); auto elementAlloca = elementAllocaNodes[index1]; + // FIXME: Introduce caching of routed values to avoid duplicated routing. auto & routedAddress = rvsdg::RouteToRegion( AllocaOperation::getPointerOutput(*elementAlloca), consumerRegion); diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.hpp b/jlm/llvm/opt/AggregateAllocaSplitting.hpp index 4e5b5dd01..6eb409aec 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.hpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.hpp @@ -11,6 +11,14 @@ namespace jlm::llvm { +/** + * \brief Aggregate Alloca Splitting Transformation + * + * Aggregate Alloca Splitting splits up \ref AllocaOperation nodes with aggregate types, i.e., + * struct and array types, into multiple \ref AllocaOperation nodes with simple types, i.e., integer + * or floating-point types. The intention is to simplify the processing of these \ref + * AllocaOperation nodes by passes such as \ref StoreValueForwarding. + */ class AggregateAllocaSplitting final : public rvsdg::Transformation { struct Context; From 3630c78638de6800fa0a055448fd8edfb1fc4b65 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sun, 17 May 2026 06:49:31 +0200 Subject: [PATCH 6/7] more --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index f02f575cd..cb722fd31 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -3,6 +3,7 @@ * See COPYING for terms of redistribution. */ +#include #include #include #include @@ -125,25 +126,24 @@ AggregateAllocaSplitting::isSplitable(rvsdg::SimpleNode & allocaNode) bool isSplitable = true; AllocaTraceInfo allocaTraceInfo(allocaNode); - util::HashSet visited; - util::HashSet toVisit{ &address }; + util::HashSet seen; + std::deque toVisit{ &address }; auto addToVisitSet = [&](rvsdg::Output & output) { - if (!visited.Contains(&output) && !toVisit.Contains(&output)) + if (!seen.Contains(&output)) { - toVisit.insert(&output); + toVisit.push_back(&output); } + seen.insert(&output); }; auto removeFromVisitSet = [&]() { - const auto output = *toVisit.Items().begin(); - toVisit.Remove(output); - [[maybe_unused]] auto inserted = visited.insert(output); - JLM_ASSERT(inserted); + const auto output = toVisit.front(); + toVisit.pop_front(); return output; }; - while (!toVisit.IsEmpty() && isSplitable) + while (!toVisit.empty() && isSplitable) { const auto currentOutput = removeFromVisitSet(); @@ -275,25 +275,24 @@ AggregateAllocaSplitting::checkGetElementPtrUsers(const rvsdg::SimpleNode & gepN bool hasOnlyLoadsAndStores = true; - util::HashSet visited; - util::HashSet toVisit{ &address }; + util::HashSet seen; + std::deque toVisit{ &address }; auto addToVisitSet = [&](rvsdg::Output & output) { - if (!visited.Contains(&output) && !toVisit.Contains(&output)) + if (!seen.Contains(&output)) { - toVisit.insert(&output); + toVisit.push_back(&output); } + seen.insert(&output); }; auto removeFromVisitSet = [&]() { - const auto output = *toVisit.Items().begin(); - toVisit.Remove(output); - [[maybe_unused]] auto inserted = visited.insert(output); - JLM_ASSERT(inserted); + const auto output = toVisit.front(); + toVisit.pop_front(); return output; }; - while (!toVisit.IsEmpty() && hasOnlyLoadsAndStores) + while (!toVisit.empty() && hasOnlyLoadsAndStores) { const auto currentOutput = removeFromVisitSet(); for (auto & user : currentOutput->Users()) From ac9c6fba309941425ed96f46987097572ff5ea78 Mon Sep 17 00:00:00 2001 From: Nico Reissmann Date: Sun, 17 May 2026 07:06:41 +0200 Subject: [PATCH 7/7] fix header --- jlm/llvm/opt/AggregateAllocaSplitting.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jlm/llvm/opt/AggregateAllocaSplitting.cpp b/jlm/llvm/opt/AggregateAllocaSplitting.cpp index cb722fd31..9948a68d4 100644 --- a/jlm/llvm/opt/AggregateAllocaSplitting.cpp +++ b/jlm/llvm/opt/AggregateAllocaSplitting.cpp @@ -3,7 +3,6 @@ * See COPYING for terms of redistribution. */ -#include #include #include #include @@ -22,6 +21,8 @@ #include #include +#include + namespace jlm::llvm {