Jlm
Loading...
Searching...
No Matches
LoopStrengthReduction.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 Andreas Lilleby Hjulstad <andreas.lilleby.hjulstad@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
12#include <jlm/rvsdg/gamma.hpp>
15
16#include <algorithm>
17
18namespace jlm::llvm
19{
21{
22public:
23 ~Context() noexcept = default;
24
25 Context() = default;
26
27 Context(const Context &) = delete;
28
29 Context(Context &&) = delete;
30
31 Context &
32 operator=(const Context &) = delete;
33
34 Context &
35 operator=(Context &&) = delete;
36
37 static std::unique_ptr<Context>
39 {
40 return std::make_unique<Context>();
41 }
42
43 void
48
49 const std::unordered_map<const rvsdg::ThetaNode *, size_t> &
51 {
53 }
54
56 size_t NumGEPCandidates = 0;
59
60private:
61 std::unordered_map<const rvsdg::ThetaNode *, size_t> OperationsReducedMap_;
62};
63
65{
66
67public:
68 ~Statistics() noexcept override = default;
69
70 explicit Statistics(const util::FilePath & sourceFile)
71 : util::Statistics(Id::LoopStrengthReduction, sourceFile)
72 {}
73
74 void
75 Start() noexcept
76 {
77 AddTimer(Label::Timer).start();
78 }
79
80 void
81 Stop(const Context & context) noexcept
82 {
83 GetTimer(Label::Timer).stop();
84
86 Label::NumLSRCandidates,
87 context.NumArithmeticCandidates + context.NumGEPCandidates);
88 AddMeasurement(Label::NumArithmeticLSRCandidates, context.NumArithmeticCandidates);
89 AddMeasurement(Label::NumGepLSRCandidates, context.NumGEPCandidates);
90 AddMeasurement(Label::NumArithmeticOperationsReduced, context.NumArithmeticOperationsReduced);
91 AddMeasurement(Label::NumGepOperationsReduced, context.NumGEPOperationsReduced);
93 Label::NumOperationsReduced,
94 GetStatisticsString(context.GetOperationsReducedMap()));
95 }
96
97 static std::string
99 const std::unordered_map<const rvsdg::ThetaNode *, size_t> & operationsReducedMap)
100 {
101 std::string s = "";
102 size_t totalCount = 0;
103 for (auto & [thetaNode, operationsReduced] : operationsReducedMap)
104 {
105 totalCount += operationsReduced;
106
107 s += "ID(" + std::to_string(thetaNode->subregion()->getRegionId())
108 + ")=" + std::to_string(operationsReduced);
109 s += ",";
110 }
111 s += "Total=" + std::to_string(totalCount);
112 return s;
113 }
114
115 static std::unique_ptr<Statistics>
116 Create(const util::FilePath & sourceFile)
117 {
118 return std::make_unique<Statistics>(sourceFile);
119 }
120};
121
123 : Transformation("LoopStrengthReduction")
124{}
125
127
128void
130 rvsdg::RvsdgModule & rvsdgModule,
131 util::StatisticsCollector & statisticsCollector)
132{
133 auto statistics = Statistics::Create(rvsdgModule.SourceFilePath().value());
134 statistics->Start();
135
136 ScalarEvolution scalarEvolution;
137 scalarEvolution.Run(rvsdgModule, statisticsCollector);
138
139 Context_ = Context::Create();
140
141 ChrecMap_ = scalarEvolution.GetChrecMap();
142 SCEVMap_ = scalarEvolution.GetSCEVMap();
143
144 ProcessRegion(rvsdgModule.Rvsdg().GetRootRegion());
145
146 statistics->Stop(*Context_);
147 statisticsCollector.CollectDemandedStatistics(std::move(statistics));
148}
149
150void
152{
153 for (auto & node : region.Nodes())
154 {
155 if (const auto structuralNode = dynamic_cast<rvsdg::StructuralNode *>(&node))
156 {
157 for (auto & subregion : structuralNode->Subregions())
158 {
159 ProcessRegion(subregion);
160 }
161 if (const auto thetaNode = dynamic_cast<rvsdg::ThetaNode *>(structuralNode))
162 {
163 ReduceStrength(*thetaNode);
164 thetaNode->subregion()->prune(false);
165 }
166 }
167 }
168}
169
170void
172{
173 // General algorithm from prof. Pingali's notes:
174 // https://www.cs.utexas.edu/~pingali/CS380C/2019/lectures/strengthReduction.pdf
175 // Adapted to work with RVSDG and to make use of the computed recurrences from the scalar
176 // evolution analysis.
177 //
178 // In short, we look for candidate operations and replace them with a new induction variable.
179 //
180 // A candidate operation must satisfy four conditions:
181 // 1. The operation is either arithmetic (+, * or -) or a GEP operation
182 // 2. The SCEV tree of the operation is a linear combination of loop variables and constants
183 // 3. For arithmetic operations, the subtree must contain at least one multiplication node
184 // (IntegerMulOperation), ensuring we only reduce operations that actually benefit from
185 // strength reduction.
186 // 4. Its SCEV evaluates to a chain recurrence of the form {a,+,b}, meaning the value
187 // can be expressed as a start value plus a constant step per iteration. This is what makes
188 // it a valid induction variable candidate. For GEP operations, a, is the base
189 // address of the array we are indexing into.
190 //
191 // Modifying the RVSDG is done as follows:
192 //
193 // For an arithmetic candidate operation j with recurrence {a,+,b}:
194 // 1. Introduce a new loop variable initialized to the base value a
195 // 2. Replace all uses of j with the new loop variable
196 // If b is not 0:
197 // 3. Update the new loop variable each iteration by incrementing it by b
198 //
199 // For a GEP which has the recurrence {Init(a),+,b}, where Init(a) is the SCEV for the base
200 // address loop variable:
201 // 1. Introduce a new loop variable initialized to the base address
202 // 2. Replace all uses of the original GEP with the new loop variable
203 // 3. Update the new loop variable each iteration by advancing it by offset b using a new GEP
204 // In the case where b is invariant, the GEP can be eliminated entirely.
205
206 // We traverse the nodes in the theta node in a bottom-up manner starting at the origin output of
207 // the post values for the loop variables. We look for candidate operations and add them to the
208 // stack of operations to be reduced.
209 util::HashSet<rvsdg::Output *> candidateOperations;
211 DependsOnIVMemo_.clear();
212 ContainsMulMemo_.clear();
213 for (const auto loopVar : thetaNode.GetLoopVars())
214 {
215 ProcessOutput(*loopVar.post->origin(), thetaNode, candidateOperations, visited);
216 }
217
218 for (auto & output : candidateOperations.Items())
219 {
220 ReplaceCandidateOperation(*output, thetaNode);
221 }
222}
223
224void
226 rvsdg::Output & output,
227 rvsdg::ThetaNode & thetaNode,
228 util::HashSet<rvsdg::Output *> & candidateOperations,
230{
231 if (!visited.insert(&output))
232 return;
233
234 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
235
236 if (!simpleNode)
237 return;
238
239 // Multiplication, addition, subtraction, shl and GEP operations are candidates for strength
240 // reduction
241 if (const auto & operation = simpleNode->GetOperation();
245 {
246 if (SCEVMap_.find(&output) == SCEVMap_.end())
247 return;
248
249 if (IsValidCandidateOperation(output, operation))
250 {
251 candidateOperations.insert(&output);
252 return; // Return early to not create unnecessary induction variables for nested operations
253 }
254 }
255
256 // For non-candidate operations, we traverse through to the node's inputs
257 for (auto & input : simpleNode->Inputs())
258 ProcessOutput(*input.origin(), thetaNode, candidateOperations, visited);
259}
260
261void
263 rvsdg::Output & output,
264 rvsdg::ThetaNode & thetaNode)
265{
266 JLM_ASSERT(ChrecMap_.find(&output) != ChrecMap_.end());
267 auto & chrec = ChrecMap_.at(&output);
268
269 JLM_ASSERT(chrec->NumOperands() <= 2);
270
271 const rvsdg::ThetaNode::LoopVar * newIV = nullptr;
272 if (const auto & bitType = std::dynamic_pointer_cast<const rvsdg::BitType>(output.Type()))
273 {
274 const auto numBits = bitType->nbits();
275 const auto newIVOpt = CreateNewArithmeticInductionVariable(*chrec, thetaNode, numBits);
276 if (!newIVOpt.has_value())
277 return;
278
279 newIV = &newIVOpt.value();
280
281 Context_->NumArithmeticOperationsReduced++;
282 }
283 else if (std::dynamic_pointer_cast<const PointerType>(output.Type()))
284 {
285 const auto newIVOpt = CreateNewGEPInductionVariable(*chrec, thetaNode);
286 if (!newIVOpt.has_value())
287 return;
288
289 newIV = &newIVOpt.value();
290
291 Context_->NumGEPOperationsReduced++;
292 }
293 else
294 {
295 throw std::logic_error("Invalid output type in ReplaceCandidateOperation!");
296 }
297
298 JLM_ASSERT(newIV != nullptr);
299
300 output.divert_users(newIV->pre);
301 // Insert the chrec for the new induction variable
302 // NOTE: This only updates the *copy* of the original chrec map from the scalar evolution
303 // analysis. In the future, we would want to insert this into the "global" chrec map so other
304 // analyses and transformations can use it as well.
305 ChrecMap_[newIV->pre] = std::move(chrec);
306 Context_->IncrementOperationsReducedCount(thetaNode);
307}
308
309std::optional<rvsdg::Output *>
311 const SCEVChainRecurrence & chrec,
312 const rvsdg::ThetaNode & thetaNode,
313 const size_t numBits)
314{
315 if (!SCEVChainRecurrence::IsInvariantInLoop(chrec, thetaNode))
316 return std::nullopt;
317
318 auto & targetLoop = chrec.GetLoop();
319
320 rvsdg::Output * chrecOutput = nullptr;
322 {
323 // The chrec stored in the chrec map corresponds with the chrec we are processing (it is an
324 // induction variable). This means that the chrec has a corresponding output which we can use.
325 chrecOutput = &chrec.GetOutput();
326 }
327 else
328 {
329 // The chrec doesn't have a corresponding output. This can happen in cases where we have folded
330 // some constant into the chrec of an induction variable, either with addition or
331 // multiplication. In this case, we need to create a new induction variable for the chrec.
332 const auto newIV = CreateNewArithmeticInductionVariable(chrec, targetLoop, numBits);
333 if (!newIV.has_value())
334 return std::nullopt;
335
336 chrecOutput = newIV->pre;
337 }
338
339 if (targetLoop.subregion() == thetaNode.region())
340 {
341 return chrecOutput;
342 }
343
344 if (rvsdg::Region::isAncestor(*targetLoop.subregion(), *thetaNode.subregion()))
345 {
346 auto & traced = llvm::traceOutput(*chrecOutput, thetaNode.subregion());
347 if (traced.region() != thetaNode.region())
348 return std::nullopt;
349
350 return &traced;
351 }
352
353 // This is fine to do and wont throw since we know, due to the cases above and the invariance
354 // check at the start, that the region of the chrec output is guaranteed to be an ancestor of the
355 // theta region
356 auto & routed = rvsdg::RouteToRegion(*chrecOutput, *thetaNode.region());
357 return &routed;
358}
359
360std::optional<rvsdg::Output *>
362 const SCEV & scev,
363 rvsdg::ThetaNode & thetaNode,
364 const size_t numBits)
365{
366 if (const auto constant = dynamic_cast<const SCEVConstant *>(&scev))
367 {
368 const auto value = constant->GetValue();
369 const auto & constantNode =
370 IntegerConstantOperation::Create(*thetaNode.region(), numBits, value);
371
372 return constantNode.output(0);
373 }
374 if (const auto init = dynamic_cast<const SCEVInit *>(&scev))
375 {
376 const auto & prePointer = init->GetPrePointer();
377 const auto targetLoop = rvsdg::TryGetRegionParentNode<rvsdg::ThetaNode>(prePointer);
378 if (targetLoop == nullptr)
379 return std::nullopt;
380
381 const auto initLoopVar = targetLoop->MapPreLoopVar(prePointer);
382
383 if (targetLoop->subregion() == thetaNode.subregion())
384 {
385 return initLoopVar.input->origin();
386 }
387
388 if (rvsdg::Region::isAncestor(*targetLoop->subregion(), *thetaNode.subregion()))
389 {
390 auto & traced = llvm::traceOutput(*initLoopVar.pre, thetaNode.region());
391
392 if (traced.region() != thetaNode.region())
393 return std::nullopt;
394
395 return &traced;
396 }
397
398 auto & routed = rvsdg::RouteToRegion(*initLoopVar.pre, *thetaNode.region());
399 return &routed;
400 }
401 if (const auto addExpr = dynamic_cast<const SCEVNAryAddExpr *>(&scev))
402 {
403 const auto rightMost = addExpr->GetOperand(addExpr->NumOperands() - 1);
404 const auto rightSideOpt = HoistSCEVExpresssion(*rightMost, thetaNode, numBits);
405 if (!rightSideOpt.has_value())
406 return std::nullopt;
407
408 const auto newAddExpr = SCEV::CloneAs<SCEVNAryAddExpr>(*addExpr);
409 newAddExpr->RemoveOperand(addExpr->NumOperands() - 1);
410
411 std::optional<rvsdg::Output *> leftSideOpt;
412 if (newAddExpr->NumOperands() == 1)
413 {
414 // Only a constant
415 const auto constantElement = newAddExpr->GetOperand(0);
416 leftSideOpt = HoistSCEVExpresssion(*constantElement, thetaNode, numBits);
417 }
418 else
419 {
420 leftSideOpt = HoistSCEVExpresssion(*newAddExpr, thetaNode, numBits);
421 }
422
423 if (!leftSideOpt.has_value())
424 return std::nullopt;
425
426 rvsdg::Output * leftSide = *leftSideOpt;
427 rvsdg::Output * rightSide = *rightSideOpt;
428 // If either side is a pointer, we use a GEP instead of IntegerAddOperation
429 if (const auto rightIsPtr = rvsdg::is<PointerType>(rightSide->Type()),
430 leftIsPtr = rvsdg::is<PointerType>(leftSide->Type());
431 leftIsPtr || rightIsPtr)
432 {
433 const auto ptrSide = leftIsPtr ? leftSide : rightSide;
434 auto offsetSide = leftIsPtr ? rightSide : leftSide;
435
436 const auto ptrType = std::dynamic_pointer_cast<const PointerType>(ptrSide->Type());
437 JLM_ASSERT(ptrType);
438 auto newGep = GetElementPtrOperation::create(
439 ptrSide,
440 { offsetSide },
441 rvsdg::BitType::Create(8)); // Byte
442
443 return newGep;
444 }
445
446 if (const auto leftBitType = std::dynamic_pointer_cast<const rvsdg::BitType>(leftSide->Type());
447 leftBitType->nbits() != numBits)
448 {
449 leftSide = &SExtOperation::create(numBits, *leftSide);
450 }
451 if (const auto rightBitType =
452 std::dynamic_pointer_cast<const rvsdg::BitType>(rightSide->Type());
453 rightBitType->nbits() != numBits)
454 {
455 rightSide = &SExtOperation::create(numBits, *rightSide);
456 }
457
458 auto & newAddNode =
459 jlm::rvsdg::CreateOpNode<IntegerAddOperation>({ leftSide, rightSide }, numBits);
460
461 return newAddNode.output(0);
462 }
463 if (const auto mulExpr = dynamic_cast<const SCEVNAryMulExpr *>(&scev))
464 {
465 const auto rightMost = mulExpr->GetOperand(mulExpr->NumOperands() - 1);
466 const auto rightSideOpt = HoistSCEVExpresssion(*rightMost, thetaNode, numBits);
467 if (!rightSideOpt.has_value())
468 return std::nullopt;
469
470 const auto newMulExpr = SCEV::CloneAs<SCEVNAryMulExpr>(*mulExpr);
471 newMulExpr->RemoveOperand(mulExpr->NumOperands() - 1);
472
473 std::optional<rvsdg::Output *> leftSideOpt;
474 if (newMulExpr->NumOperands() == 1)
475 {
476 const auto constantElement = newMulExpr->GetOperand(0);
477 leftSideOpt = HoistSCEVExpresssion(*constantElement, thetaNode, numBits);
478 }
479 else
480 {
481 leftSideOpt = HoistSCEVExpresssion(*newMulExpr, thetaNode, numBits);
482 }
483
484 if (!leftSideOpt.has_value())
485 return std::nullopt;
486
487 rvsdg::Output * leftSide = *leftSideOpt;
488 rvsdg::Output * rightSide = *rightSideOpt;
489 // Sign extend in cases where the input does not match the expected number of bits
490 if (const auto leftBitType = std::dynamic_pointer_cast<const rvsdg::BitType>(leftSide->Type());
491 leftBitType->nbits() != numBits)
492 {
493 leftSide = &SExtOperation::create(numBits, *leftSide);
494 }
495 if (const auto rightBitType =
496 std::dynamic_pointer_cast<const rvsdg::BitType>(rightSide->Type());
497 rightBitType->nbits() != numBits)
498 {
499 rightSide = &SExtOperation::create(numBits, *rightSide);
500 }
501
502 auto & newMulNode =
503 jlm::rvsdg::CreateOpNode<IntegerMulOperation>({ leftSide, rightSide }, numBits);
504
505 return newMulNode.output(0);
506 }
507 if (const auto chrec = dynamic_cast<const SCEVChainRecurrence *>(&scev))
508 {
510 return std::nullopt;
511
512 return HoistChrec(*chrec, thetaNode, numBits);
513 }
514 throw std::logic_error("Unknown SCEV type in HoistSCEVExpression\n");
515}
516
517std::optional<rvsdg::ThetaNode::LoopVar>
519 const SCEVChainRecurrence & chrec,
520 rvsdg::ThetaNode & thetaNode,
521 const size_t numBits)
522{
523 const auto & startSCEV = chrec.GetStartValue();
524
526 {
527 // Chrec has the form {a}, which indicates a loop-invariant (trivial induction variable).
528 // We can hoist this out of the loop by creating a new loop variable.
529 const auto hoistedConstant = HoistSCEVExpresssion(*startSCEV, thetaNode, numBits);
530 if (!hoistedConstant.has_value())
531 return std::nullopt;
532
533 return thetaNode.AddLoopVar(*hoistedConstant);
534 }
536 {
537 // Chrec has the form {a,+,b} which is a basic induction variable
538 const auto & stepPtr = chrec.GetStep();
539
540 JLM_ASSERT(stepPtr.has_value());
541
542 const auto & stepSCEV = *stepPtr;
543
544 rvsdg::Output * stepOutput = nullptr;
545 if (const auto stepConstant = dynamic_cast<const SCEVConstant *>(stepSCEV.get()))
546 {
547 const auto stepValue = stepConstant->GetValue();
548 const auto & stepValueNode =
549 IntegerConstantOperation::Create(*thetaNode.subregion(), numBits, stepValue);
550 stepOutput = stepValueNode.output(0);
551 }
552 else if (const auto stepInit = dynamic_cast<const SCEVInit *>(stepSCEV.get()))
553 {
554 stepOutput = &stepInit->GetPrePointer();
555 }
556 else if (const auto stepNAryExpr = dynamic_cast<const SCEVNAryExpr *>(stepSCEV.get()))
557 {
558 const auto hoistedStep = HoistSCEVExpresssion(*stepNAryExpr, thetaNode, numBits);
559 if (!hoistedStep.has_value())
560 return std::nullopt;
561
562 const auto newStepIV = thetaNode.AddLoopVar(*hoistedStep);
563 stepOutput = newStepIV.pre;
564 }
565 else
566 {
567 return std::nullopt;
568 }
569
570 const auto hoistedStart = HoistSCEVExpresssion(*startSCEV, thetaNode, numBits);
571 if (!hoistedStart.has_value())
572 return std::nullopt;
573
574 auto newIV = thetaNode.AddLoopVar(*hoistedStart);
575
576 auto & newAddNode =
577 jlm::rvsdg::CreateOpNode<IntegerAddOperation>({ newIV.pre, stepOutput }, numBits);
578 newIV.post->divert_to(newAddNode.output(0));
579
580 return newIV;
581 }
582 throw std::logic_error("Invalid chrec size in CreateNewArithmeticInductionVariable!");
583}
584
585std::optional<rvsdg::ThetaNode::LoopVar>
587 const SCEVChainRecurrence & chrec,
588 rvsdg::ThetaNode & thetaNode)
589{
590 const auto & baseAddressSCEV = chrec.GetStartValue();
592 {
593 const auto hoistedStart = HoistSCEVExpresssion(*baseAddressSCEV, thetaNode, 64);
594
595 if (!hoistedStart.has_value())
596 return std::nullopt;
597
598 return thetaNode.AddLoopVar(*hoistedStart);
599 }
601 {
602 // Chrec has the form {Init(a),+,b} where Init(a) is the (initial) value of the base address
603 // and b is the offset (in bytes).
604 const auto & stepPtr = chrec.GetStep();
605
606 JLM_ASSERT(stepPtr.has_value());
607
608 const auto & stepSCEV = *stepPtr;
609
610 rvsdg::Output * stepOutput = nullptr;
611 if (const auto stepConstant = dynamic_cast<const SCEVConstant *>(stepSCEV.get()))
612 {
613 const auto stepValue = stepConstant->GetValue();
614 const auto & stepValueNode =
615 IntegerConstantOperation::Create(*thetaNode.subregion(), 64, stepValue);
616 stepOutput = stepValueNode.output(0);
617 }
618 else if (const auto stepInit = dynamic_cast<const SCEVInit *>(stepSCEV.get()))
619 {
620 stepOutput = &stepInit->GetPrePointer();
621 }
622 else if (const auto stepNAryExpr = dynamic_cast<const SCEVNAryExpr *>(stepSCEV.get()))
623 {
624 const auto hoistedStep = HoistSCEVExpresssion(*stepNAryExpr, thetaNode, 64);
625 if (!hoistedStep.has_value())
626 return std::nullopt;
627
628 const auto newStepIV = thetaNode.AddLoopVar(*hoistedStep);
629 stepOutput = newStepIV.pre;
630 }
631 else
632 {
633 return std::nullopt;
634 }
635
636 const auto hoistedStart = HoistSCEVExpresssion(*baseAddressSCEV, thetaNode, 64);
637
638 if (!hoistedStart.has_value())
639 return std::nullopt;
640
641 const auto newIV = thetaNode.AddLoopVar(*hoistedStart);
642
643 if (const auto stepBitType =
644 std::dynamic_pointer_cast<const rvsdg::BitType>(stepOutput->Type());
645 stepBitType->nbits() != 64)
646 {
647 stepOutput = &SExtOperation::create(64, *stepOutput);
648 }
649
650 auto newGep = GetElementPtrOperation::create(
651 newIV.pre,
652 { stepOutput },
653 rvsdg::BitType::Create(8)); // Byte
654
655 newIV.post->divert_to(newGep);
656
657 return newIV;
658 }
659
660 throw std::logic_error("Invalid chrec size in CreateNewGEPInductionVariable!");
661}
662
663bool
665 const rvsdg::Output & output,
666 const rvsdg::SimpleOperation & operation)
667{
669 Context_->NumGEPCandidates++;
670 else
671 Context_->NumArithmeticCandidates++;
672
673 if (!DependsOnInductionVariable(output))
674 return false;
675
676 // We only reduce arithmetic operations if they contain a multiplication somewhere
677 if (rvsdg::is<IntegerBinaryOperation>(operation) && !ContainsMul(output))
678 return false;
679
680 // We only support invariant and affine recurrences (1-2 operands) that are not unknown
681 if (const auto & chrec = ChrecMap_.at(&output);
682 chrec->NumOperands() > 2 || ScalarEvolution::IsUnknown(*chrec))
683 return false;
684
685 return true;
686}
687
688bool
690{
691 if (const auto it = DependsOnIVMemo_.find(&output); it != DependsOnIVMemo_.end())
692 return it->second;
693
694 // Check if the current output is an induction variable (loop variable with predictable
695 // evolution)
697 {
698 const auto it = ChrecMap_.find(&output);
699 if (it == ChrecMap_.end())
700 return DependsOnIVMemo_[&output] = false;
701
702 if (const auto & chrec = it->second; ScalarEvolution::IsUnknown(*chrec))
703 return DependsOnIVMemo_[&output] = false;
704
705 return DependsOnIVMemo_[&output] = true;
706 }
707
708 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
709
710 if (!simpleNode)
711 return DependsOnIVMemo_[&output] = false;
712
713 for (const auto & input : simpleNode->Inputs())
714 {
715 if (DependsOnInductionVariable(*input.origin()))
716 return DependsOnIVMemo_[&output] = true;
717 }
718
719 return DependsOnIVMemo_[&output] = false;
720}
721
722bool
724{
725 if (const auto it = ContainsMulMemo_.find(&output); it != ContainsMulMemo_.end())
726 return it->second;
727
728 const auto & simpleNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(output);
729
730 if (!simpleNode)
731 return ContainsMulMemo_[&output] = false;
732
733 if (const auto & simpleOperation = simpleNode->GetOperation();
734 rvsdg::is<IntegerMulOperation>(simpleOperation)
735 || rvsdg::is<IntegerShlOperation>(simpleOperation))
736 return ContainsMulMemo_[&output] = true;
737
738 for (const auto & input : simpleNode->Inputs())
739 {
740 if (ContainsMul(*input.origin()))
741 return ContainsMulMemo_[&output] = true;
742 }
743
744 return ContainsMulMemo_[&output] = false;
745}
746}
static jlm::util::StatisticsCollector statisticsCollector
static rvsdg::Output * create(rvsdg::Output *baseAddress, const std::vector< rvsdg::Output * > &indices, std::shared_ptr< const rvsdg::Type > gepType)
static rvsdg::Node & Create(rvsdg::Region &region, IntegerValueRepresentation representation)
static std::unique_ptr< Context > Create()
std::unordered_map< const rvsdg::ThetaNode *, size_t > OperationsReducedMap_
void IncrementOperationsReducedCount(const rvsdg::ThetaNode &theta)
const std::unordered_map< const rvsdg::ThetaNode *, size_t > & GetOperationsReducedMap() const
static std::unique_ptr< Statistics > Create(const util::FilePath &sourceFile)
~Statistics() noexcept override=default
static std::string GetStatisticsString(const std::unordered_map< const rvsdg::ThetaNode *, size_t > &operationsReducedMap)
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 &region)
std::unordered_map< const rvsdg::Output *, std::unique_ptr< SCEV > > SCEVMap_
std::optional< rvsdg::ThetaNode::LoopVar > CreateNewGEPInductionVariable(const SCEVChainRecurrence &chrec, rvsdg::ThetaNode &thetaNode)
rvsdg::ThetaNode & GetLoop() const noexcept
rvsdg::Output & GetOutput() const noexcept
static bool IsConstant(const SCEVChainRecurrence &chrec)
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 rvsdg::Output & create(size_t ndstbits, rvsdg::Output &operand)
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.
Definition type.cpp:45
void divert_to(Output *new_origin)
Definition node.cpp:64
rvsdg::Region * region() const noexcept
Definition node.hpp:761
void divert_users(jlm::rvsdg::Output *new_origin)
Definition node.hpp:301
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
static bool isAncestor(const rvsdg::Region &region, const rvsdg::Region &ancestor) noexcept
Definition region.cpp:474
NodeRange Nodes() noexcept
Definition region.hpp:375
std::vector< LoopVar > GetLoopVars() const
Returns all loop variables.
Definition theta.cpp:176
rvsdg::Region * subregion() const noexcept
Definition theta.hpp:79
LoopVar AddLoopVar(rvsdg::Output *origin)
Creates a new loop-carried variable.
Definition theta.cpp:49
bool insert(ItemType item)
Definition HashSet.hpp:210
IteratorRange< ItemConstIterator > Items() const noexcept
Definition HashSet.hpp:223
void CollectDemandedStatistics(std::unique_ptr< Statistics > statistics)
Statistics Interface.
util::Timer & GetTimer(const std::string &name)
util::Timer & AddTimer(std::string name)
void AddMeasurement(std::string name, T value)
void start() noexcept
Definition time.hpp:54
void stop() noexcept
Definition time.hpp:67
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
rvsdg::Output & traceOutput(rvsdg::Output &output, const rvsdg::Region *withinRegion)
Definition Trace.cpp:62
Output & RouteToRegion(Output &output, Region &region)
Definition node.cpp:381
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
Description of a loop-carried variable.
Definition theta.hpp:50
rvsdg::Output * pre
Variable before iteration (input argument to subregion).
Definition theta.hpp:58
rvsdg::Input * post
Variable after iteration (output result from subregion).
Definition theta.hpp:62