40 return std::make_unique<Context>();
49 const std::unordered_map<const rvsdg::ThetaNode *, size_t> &
87 context.NumArithmeticCandidates + context.NumGEPCandidates);
99 const std::unordered_map<const rvsdg::ThetaNode *, size_t> & operationsReducedMap)
102 size_t totalCount = 0;
103 for (
auto & [thetaNode, operationsReduced] : operationsReducedMap)
105 totalCount += operationsReduced;
107 s +=
"ID(" + std::to_string(thetaNode->subregion()->getRegionId())
108 +
")=" + std::to_string(operationsReduced);
111 s +=
"Total=" + std::to_string(totalCount);
115 static std::unique_ptr<Statistics>
118 return std::make_unique<Statistics>(sourceFile);
123 : Transformation(
"LoopStrengthReduction")
130 rvsdg::RvsdgModule & rvsdgModule,
133 auto statistics = Statistics::Create(rvsdgModule.SourceFilePath().value());
139 Context_ = Context::Create();
144 ProcessRegion(rvsdgModule.Rvsdg().GetRootRegion());
146 statistics->Stop(*Context_);
153 for (
auto & node : region.
Nodes())
157 for (
auto & subregion : structuralNode->Subregions())
161 if (
const auto thetaNode =
dynamic_cast<rvsdg::ThetaNode *
>(structuralNode))
164 thetaNode->subregion()->prune(
false);
215 ProcessOutput(*loopVar.post->origin(), thetaNode, candidateOperations, visited);
218 for (
auto & output : candidateOperations.
Items())
231 if (!visited.
insert(&output))
234 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
241 if (
const auto & operation = simpleNode->GetOperation();
242 rvsdg::is<IntegerMulOperation>(operation) || rvsdg::is<IntegerAddOperation>(operation)
243 || rvsdg::is<IntegerSubOperation>(operation) || rvsdg::is<IntegerShlOperation>(operation)
244 || rvsdg::is<GetElementPtrOperation>(operation))
251 candidateOperations.
insert(&output);
257 for (
auto & input : simpleNode->Inputs())
258 ProcessOutput(*input.origin(), thetaNode, candidateOperations, visited);
272 if (
const auto & bitType = std::dynamic_pointer_cast<const rvsdg::BitType>(output.
Type()))
274 const auto numBits = bitType->nbits();
276 if (!newIVOpt.has_value())
279 newIV = &newIVOpt.value();
281 Context_->NumArithmeticOperationsReduced++;
283 else if (std::dynamic_pointer_cast<const PointerType>(output.
Type()))
286 if (!newIVOpt.has_value())
289 newIV = &newIVOpt.value();
291 Context_->NumGEPOperationsReduced++;
295 throw std::logic_error(
"Invalid output type in ReplaceCandidateOperation!");
306 Context_->IncrementOperationsReducedCount(thetaNode);
309 std::optional<rvsdg::Output *>
313 const size_t numBits)
318 auto & targetLoop = chrec.
GetLoop();
333 if (!newIV.has_value())
336 chrecOutput = newIV->pre;
339 if (targetLoop.subregion() == thetaNode.
region())
347 if (traced.region() != thetaNode.
region())
360 std::optional<rvsdg::Output *>
364 const size_t numBits)
366 if (
const auto constant =
dynamic_cast<const SCEVConstant *
>(&scev))
368 const auto value = constant->GetValue();
369 const auto & constantNode =
372 return constantNode.output(0);
374 if (
const auto init =
dynamic_cast<const SCEVInit *
>(&scev))
376 const auto & prePointer = init->GetPrePointer();
377 const auto targetLoop = rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(prePointer);
378 if (targetLoop ==
nullptr)
381 const auto initLoopVar = targetLoop->MapPreLoopVar(prePointer);
383 if (targetLoop->subregion() == thetaNode.
subregion())
385 return initLoopVar.input->origin();
392 if (traced.region() != thetaNode.
region())
403 const auto rightMost = addExpr->GetOperand(addExpr->NumOperands() - 1);
405 if (!rightSideOpt.has_value())
408 const auto newAddExpr = SCEV::CloneAs<SCEVNAryAddExpr>(*addExpr);
409 newAddExpr->RemoveOperand(addExpr->NumOperands() - 1);
411 std::optional<rvsdg::Output *> leftSideOpt;
412 if (newAddExpr->NumOperands() == 1)
415 const auto constantElement = newAddExpr->GetOperand(0);
423 if (!leftSideOpt.has_value())
429 if (
const auto rightIsPtr = rvsdg::is<PointerType>(rightSide->
Type()),
430 leftIsPtr = rvsdg::is<PointerType>(leftSide->
Type());
431 leftIsPtr || rightIsPtr)
433 const auto ptrSide = leftIsPtr ? leftSide : rightSide;
434 auto offsetSide = leftIsPtr ? rightSide : leftSide;
436 const auto ptrType = std::dynamic_pointer_cast<const PointerType>(ptrSide->Type());
446 if (
const auto leftBitType = std::dynamic_pointer_cast<const rvsdg::BitType>(leftSide->
Type());
447 leftBitType->nbits() != numBits)
451 if (
const auto rightBitType =
452 std::dynamic_pointer_cast<const rvsdg::BitType>(rightSide->
Type());
453 rightBitType->nbits() != numBits)
459 jlm::rvsdg::CreateOpNode<IntegerAddOperation>({ leftSide, rightSide }, numBits);
461 return newAddNode.output(0);
465 const auto rightMost = mulExpr->GetOperand(mulExpr->NumOperands() - 1);
467 if (!rightSideOpt.has_value())
470 const auto newMulExpr = SCEV::CloneAs<SCEVNAryMulExpr>(*mulExpr);
471 newMulExpr->RemoveOperand(mulExpr->NumOperands() - 1);
473 std::optional<rvsdg::Output *> leftSideOpt;
474 if (newMulExpr->NumOperands() == 1)
476 const auto constantElement = newMulExpr->GetOperand(0);
484 if (!leftSideOpt.has_value())
490 if (
const auto leftBitType = std::dynamic_pointer_cast<const rvsdg::BitType>(leftSide->
Type());
491 leftBitType->nbits() != numBits)
495 if (
const auto rightBitType =
496 std::dynamic_pointer_cast<const rvsdg::BitType>(rightSide->
Type());
497 rightBitType->nbits() != numBits)
503 jlm::rvsdg::CreateOpNode<IntegerMulOperation>({ leftSide, rightSide }, numBits);
505 return newMulNode.output(0);
512 return HoistChrec(*chrec, thetaNode, numBits);
514 throw std::logic_error(
"Unknown SCEV type in HoistSCEVExpression\n");
517 std::optional<rvsdg::ThetaNode::LoopVar>
521 const size_t numBits)
530 if (!hoistedConstant.has_value())
533 return thetaNode.
AddLoopVar(*hoistedConstant);
538 const auto & stepPtr = chrec.
GetStep();
542 const auto & stepSCEV = *stepPtr;
545 if (
const auto stepConstant =
dynamic_cast<const SCEVConstant *
>(stepSCEV.get()))
547 const auto stepValue = stepConstant->GetValue();
548 const auto & stepValueNode =
550 stepOutput = stepValueNode.output(0);
552 else if (
const auto stepInit =
dynamic_cast<const SCEVInit *
>(stepSCEV.get()))
554 stepOutput = &stepInit->GetPrePointer();
556 else if (
const auto stepNAryExpr =
dynamic_cast<const SCEVNAryExpr *
>(stepSCEV.get()))
559 if (!hoistedStep.has_value())
562 const auto newStepIV = thetaNode.
AddLoopVar(*hoistedStep);
563 stepOutput = newStepIV.pre;
571 if (!hoistedStart.has_value())
574 auto newIV = thetaNode.
AddLoopVar(*hoistedStart);
577 jlm::rvsdg::CreateOpNode<IntegerAddOperation>({ newIV.pre, stepOutput }, numBits);
582 throw std::logic_error(
"Invalid chrec size in CreateNewArithmeticInductionVariable!");
585 std::optional<rvsdg::ThetaNode::LoopVar>
595 if (!hoistedStart.has_value())
604 const auto & stepPtr = chrec.
GetStep();
608 const auto & stepSCEV = *stepPtr;
611 if (
const auto stepConstant =
dynamic_cast<const SCEVConstant *
>(stepSCEV.get()))
613 const auto stepValue = stepConstant->GetValue();
614 const auto & stepValueNode =
616 stepOutput = stepValueNode.output(0);
618 else if (
const auto stepInit =
dynamic_cast<const SCEVInit *
>(stepSCEV.get()))
620 stepOutput = &stepInit->GetPrePointer();
622 else if (
const auto stepNAryExpr =
dynamic_cast<const SCEVNAryExpr *
>(stepSCEV.get()))
625 if (!hoistedStep.has_value())
628 const auto newStepIV = thetaNode.
AddLoopVar(*hoistedStep);
629 stepOutput = newStepIV.pre;
638 if (!hoistedStart.has_value())
641 const auto newIV = thetaNode.
AddLoopVar(*hoistedStart);
643 if (
const auto stepBitType =
644 std::dynamic_pointer_cast<const rvsdg::BitType>(stepOutput->
Type());
645 stepBitType->nbits() != 64)
655 newIV.post->divert_to(newGep);
660 throw std::logic_error(
"Invalid chrec size in CreateNewGEPInductionVariable!");
668 if (rvsdg::is<GetElementPtrOperation>(operation))
671 Context_->NumArithmeticCandidates++;
677 if (rvsdg::is<IntegerBinaryOperation>(operation) && !
ContainsMul(output))
681 if (
const auto & chrec =
ChrecMap_.at(&output);
696 if (rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(output))
708 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
713 for (
const auto & input : simpleNode->Inputs())
728 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
733 if (
const auto & simpleOperation = simpleNode->GetOperation();
734 rvsdg::is<IntegerMulOperation>(simpleOperation)
735 || rvsdg::is<IntegerShlOperation>(simpleOperation))
738 for (
const auto & input : simpleNode->Inputs())
static jlm::util::StatisticsCollector statisticsCollector
static rvsdg::Output * create(rvsdg::Output *baseAddress, const std::vector< rvsdg::Output * > &indices, std::shared_ptr< const rvsdg::Type > pointeeType)
static rvsdg::Node & Create(rvsdg::Region ®ion, IntegerValueRepresentation representation)
size_t NumArithmeticOperationsReduced
size_t NumGEPOperationsReduced
static std::unique_ptr< Context > Create()
std::unordered_map< const rvsdg::ThetaNode *, size_t > OperationsReducedMap_
const std::unordered_map< const rvsdg::ThetaNode *, size_t > & GetOperationsReducedMap() const
void IncrementOperationsReducedCount(const rvsdg::ThetaNode &theta)
~Context() noexcept=default
size_t NumArithmeticCandidates
~Statistics() noexcept override=default
static std::string GetStatisticsString(const std::unordered_map< const rvsdg::ThetaNode *, size_t > &operationsReducedMap)
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
void Stop(const Context &context) noexcept
bool IsValidCandidateOperation(const rvsdg::Output &output, const rvsdg::SimpleOperation &operation)
~LoopStrengthReduction() noexcept override
std::optional< rvsdg::ThetaNode::LoopVar > CreateNewArithmeticInductionVariable(const SCEVChainRecurrence &chrec, rvsdg::ThetaNode &thetaNode, size_t numBits)
std::optional< rvsdg::Output * > HoistChrec(const SCEVChainRecurrence &chrec, const rvsdg::ThetaNode &thetaNode, size_t numBits)
void ReplaceCandidateOperation(rvsdg::Output &output, rvsdg::ThetaNode &thetaNode)
std::optional< rvsdg::Output * > HoistSCEVExpresssion(const SCEV &scev, rvsdg::ThetaNode &thetaNode, size_t numBits)
void ProcessOutput(rvsdg::Output &output, rvsdg::ThetaNode &thetaNode, util::HashSet< rvsdg::Output * > &candidateOperations, util::HashSet< rvsdg::Output * > &visited)
bool DependsOnInductionVariable(const rvsdg::Output &output)
std::unordered_map< const rvsdg::Output *, bool > ContainsMulMemo_
std::unordered_map< const rvsdg::Output *, bool > DependsOnIVMemo_
void ReduceStrength(rvsdg::ThetaNode &thetaNode)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEVChainRecurrence > > ChrecMap_
bool ContainsMul(const rvsdg::Output &output)
void ProcessRegion(rvsdg::Region ®ion)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEV > > SCEVMap_
std::unique_ptr< Context > Context_
std::optional< rvsdg::ThetaNode::LoopVar > CreateNewGEPInductionVariable(const SCEVChainRecurrence &chrec, rvsdg::ThetaNode &thetaNode)
rvsdg::ThetaNode & GetLoop() const noexcept
SCEV * GetStartValue() const
static bool IsConstant(const SCEVChainRecurrence &chrec)
rvsdg::Output & GetOutput() const noexcept
std::optional< std::unique_ptr< SCEV > > GetStep() const
static bool IsInvariantInLoop(const SCEVChainRecurrence &chrec, const rvsdg::ThetaNode &theta)
static bool IsAffine(const SCEVChainRecurrence &chrec)
static std::unique_ptr< llvm::ThreeAddressCode > create(const Variable *operand, const std::shared_ptr< const rvsdg::Type > &type)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEVChainRecurrence > > GetChrecMap() const
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static bool StructurallyEqual(const SCEV &a, const SCEV &b)
static bool IsUnknown(const SCEV &scev)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEV > > GetSCEVMap() const
static std::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
rvsdg::Region * region() const noexcept
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
void divert_users(jlm::rvsdg::Output *new_origin)
Represent acyclic RVSDG subgraphs.
static bool isAncestor(const rvsdg::Region ®ion, const rvsdg::Region &ancestor) noexcept
NodeRange Nodes() noexcept
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
rvsdg::Region * subregion() const noexcept
LoopVar AddLoopVar(rvsdg::Output *origin)
Creates a new loop-carried variable.
bool insert(ItemType item)
IteratorRange< ItemConstIterator > Items() const noexcept
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
util::Timer & GetTimer(const std::string &name)
util::Timer & AddTimer(std::string name)
void AddMeasurement(std::string name, T value)
Global memory state passed between functions.
rvsdg::Output & traceOutput(rvsdg::Output &output, const rvsdg::Region *withinRegion)
Output & RouteToRegion(Output &output, Region ®ion)
Description of a loop-carried variable.
rvsdg::Output * pre
Variable before iteration (input argument to subregion).
rvsdg::Input * post
Variable after iteration (output result from subregion).
static const char * NumGepOperationsReduced
static const char * NumGepLSRCandidates
static const char * NumOperationsReduced
static const char * Timer
static const char * NumArithmeticOperationsReduced
static const char * NumLSRCandidates
static const char * NumArithmeticLSRCandidates