@@ -45,9 +45,16 @@ static cl::opt<int>
4545 cl::desc (" Number of runs for heuristics that converge" ),
4646 cl::init(20 ), cl::Hidden);
4747
48- static cl::opt<int > PresetII (" aie-postpipeliner-target-ii" ,
49- cl::desc (" II for which to allow the solver" ),
50- cl::init(0 ), cl::Hidden);
48+ static cl::opt<bool >
49+ UseSolver (" aie-postpipeliner-solver" ,
50+ cl::desc (" Use the solver as fallback after heuristics fail" ),
51+ cl::init(false ), cl::Hidden);
52+
53+ static cl::opt<int >
54+ PresetII (" aie-postpipeliner-target-ii" ,
55+ cl::desc (" Run solver-only at this II; bypasses MaxII and "
56+ " skips heuristics" ),
57+ cl::init(0 ), cl::Hidden);
5158
5259static cl::opt<int >
5360 SolverRetries (" aie-postpipeliner-solver-retries" ,
@@ -166,15 +173,41 @@ bool PostPipeliner::isPostPipelineCandidate(MachineBasicBlock &LoopBlock) {
166173 return false ;
167174 }
168175
169- if (PresetII) {
176+ // Determine TargetII. Precedence (highest first):
177+ // 1. --aie-postpipeliner-target-ii=N (CLI): hard one-shot limit. The
178+ // post-pipeliner attempts exactly N (bypassing
179+ // --aie-postpipeliner-maxii) and runs the solver only, skipping
180+ // heuristics.
181+ // 2. --aie-postpipeliner-solver (CLI): no TargetII. Heuristics still
182+ // run at every II in [ResMII, MaxII]; the solver runs as a fallback
183+ // after heuristics fail at each II. This flag overrides the pragma
184+ // below.
185+ // 3. Loop-pragma initiation interval: soft hint. Heuristics run at
186+ // every II in [ResMII, MaxII] (matches pre-commit behavior); the
187+ // solver additionally runs at II == TargetII as a last resort.
188+ // All three sources require an actual solver backend to do anything
189+ // useful: when the build has no solver compiled in, getSolvers() returns
190+ // an empty vector and every solver call would silently no-op. Treat the
191+ // requests as unsupported in that configuration so we keep the pre-commit
192+ // behavior (heuristics only, full II search up to
193+ // --aie-postpipeliner-maxii).
194+ if (!Solver::hasSolver ()) {
195+ if (PresetII || UseSolver || getInitiationInterval (getLoopID (LoopBlock))) {
196+ DEBUG_SUMMARY (
197+ dbgs () << " PostPipeliner: ignoring TargetII/solver request, "
198+ " no solver compiled in\n " );
199+ }
200+ } else if (PresetII) {
170201 TargetII = PresetII;
171- return true ;
172- }
173- auto ParsedInitiationInterval = getInitiationInterval (getLoopID (LoopBlock));
174- if (ParsedInitiationInterval) {
175- TargetII = *ParsedInitiationInterval;
176- DEBUG_SUMMARY (dbgs () << " PostPipeliner: TargetII=" << TargetII << " \n " );
202+ TargetIIIsHardLimit = true ;
203+ } else if (!UseSolver) {
204+ if (auto Pragma = getInitiationInterval (getLoopID (LoopBlock)))
205+ TargetII = *Pragma;
177206 }
207+ if (TargetII)
208+ DEBUG_SUMMARY (dbgs () << " PostPipeliner: TargetII=" << TargetII
209+ << (TargetIIIsHardLimit ? " (hard)" : " (soft)" )
210+ << " \n " );
178211
179212 return true ;
180213}
@@ -1439,41 +1472,60 @@ static const ConfigStrategy::Configuration Heuristics[] = {
14391472
14401473bool PostPipeliner::tryApproaches () {
14411474 DEBUG_SUMMARY (dbgs () << " -- MinLength=" << MinLength << " \n " );
1442- int HeuristicIndex = 0 ;
1443- for (const auto &Config : Heuristics) {
1444- if (Heuristic >= 0 && Heuristic != HeuristicIndex++) {
1445- continue ;
1446- }
1447- const int StrategyLength = MinLength + Config.ExtraStages * II;
1448- ConfigStrategy S (*DAG, Info, StrategyLength, Config.TopDown ,
1449- Config.Alternate , Config.Components , Config.Modifiers );
1450- resetSchedule (/* FullReset=*/ true );
1451- for (int Run = 0 ; Run < Config.Runs && Run < HeuristicRuns; Run++) {
1452- DEBUG_SUMMARY (dbgs () << " --- Strategy " << S.name () << " run=" << Run
1453- << " trying II=" << II << " \n " );
1454- if (scheduleWithStrategy (S)) {
1455- DEBUG_SUMMARY (dbgs ()
1456- << " Strategy " << S.name () << " run=" << Run
1457- << " found NS=" << NStages << " II=" << II << " \n " );
1458- return true ;
1475+
1476+ // Decide which strategies to run at this II:
1477+ // - CLI --aie-postpipeliner-target-ii (TargetIIIsHardLimit): solver
1478+ // only, skip heuristics. The CLI flag is a hard request to spend
1479+ // solver time and the user has already seen what heuristics produce.
1480+ // - Pragma TargetII: heuristics on every II, plus solver as a last
1481+ // resort at II == TargetII.
1482+ // - --aie-postpipeliner-solver (UseSolver): heuristics on every II,
1483+ // plus solver as a fallback at every II.
1484+ // Belt-and-braces: isPostPipelineCandidate already drops these requests
1485+ // in builds without a solver backend, but re-check Solver::hasSolver()
1486+ // here so we never call solve() with an empty solver list.
1487+ const bool SolverAvailable = Solver::hasSolver ();
1488+ const bool SolverOnly = TargetIIIsHardLimit;
1489+ const bool RunHeuristics = !SolverOnly;
1490+ const bool SolverAtThisII =
1491+ UseSolver || SolverOnly || (TargetII != 0 && II == TargetII);
1492+ const bool RunSolver = SolverAvailable && SolverAtThisII;
1493+
1494+ if (RunHeuristics) {
1495+ int HeuristicIndex = 0 ;
1496+ for (const auto &Config : Heuristics) {
1497+ if (Heuristic >= 0 && Heuristic != HeuristicIndex++) {
1498+ continue ;
14591499 }
1460- if (!S.checkAndResetChanged ()) {
1461- // If nothing changed, there's no use in rerunning.
1462- break ;
1500+ const int StrategyLength = MinLength + Config.ExtraStages * II;
1501+ ConfigStrategy S (*DAG, Info, StrategyLength, Config.TopDown ,
1502+ Config.Alternate , Config.Components , Config.Modifiers );
1503+ resetSchedule (/* FullReset=*/ true );
1504+ for (int Run = 0 ; Run < Config.Runs && Run < HeuristicRuns; Run++) {
1505+ DEBUG_SUMMARY (dbgs () << " --- Strategy " << S.name () << " run=" << Run
1506+ << " trying II=" << II << " \n " );
1507+ if (scheduleWithStrategy (S)) {
1508+ DEBUG_SUMMARY (dbgs ()
1509+ << " Strategy " << S.name () << " run=" << Run
1510+ << " found NS=" << NStages << " II=" << II << " \n " );
1511+ return true ;
1512+ }
1513+ if (!S.checkAndResetChanged ()) {
1514+ // If nothing changed, there's no use in rerunning.
1515+ break ;
1516+ }
1517+ resetSchedule (/* FullReset=*/ false );
14631518 }
1464- resetSchedule (/* FullReset=*/ false );
1519+ DEBUG_SUMMARY (dbgs () << " Strategy " << S.name () << " failed\n " );
1520+ }
1521+ IterCountSlackStrategy Relaxed (*DAG, Info, MinLength + II);
1522+ resetSchedule (/* FullReset=*/ true );
1523+ if (scheduleWithStrategy (Relaxed)) {
1524+ return true ;
14651525 }
1466- DEBUG_SUMMARY (dbgs () << " Strategy " << S.name () << " failed\n " );
1467- }
1468- IterCountSlackStrategy Relaxed (*DAG, Info, MinLength + II);
1469- resetSchedule (/* FullReset=*/ true );
1470- if (scheduleWithStrategy (Relaxed)) {
1471- return true ;
14721526 }
14731527
1474- // TargetII is the OK from the user to spend some time reaching this II.
1475- // Therefore, if we haven't found a solution yet, bring in the big guns.
1476- if (II == TargetII) {
1528+ if (RunSolver) {
14771529 const SolverData Data = createSolverData ();
14781530 const int NS = MinLength / II;
14791531 if (solve (Data, NS, false )) {
0 commit comments