@@ -99,8 +99,13 @@ static cl::opt<bool> SpecializeOnAddresses(
99
99
" func-specialization-on-address" , cl::init(false ), cl::Hidden,
100
100
cl::desc(" Enable function specialization on the address of global values" ));
101
101
102
- // TODO: This needs checking to see the impact on compile-times, which is why
103
- // this is off by default for now.
102
+ // Disabled by default as it can significantly increase compilation times.
103
+ // Running nikic's compile time tracker on x86 with instruction count as the
104
+ // metric shows 3-4% regression for SPASS while being neutral for all other
105
+ // benchmarks of the llvm test suite.
106
+ //
107
+ // https://llvm-compile-time-tracker.com
108
+ // https://github.com/nikic/llvm-compile-time-tracker
104
109
static cl::opt<bool > EnableSpecializationForLiteralConstant (
105
110
" function-specialization-for-literal-constant" , cl::init(false ), cl::Hidden,
106
111
cl::desc(" Enable specialization of functions that take a literal constant "
@@ -110,17 +115,17 @@ namespace {
110
115
// Bookkeeping struct to pass data from the analysis and profitability phase
111
116
// to the actual transform helper functions.
112
117
struct SpecializationInfo {
113
- ArgInfo Arg; // Stores the {formal,actual} argument pair.
114
- InstructionCost Gain; // Profitability: Gain = Bonus - Cost.
115
-
116
- SpecializationInfo (Argument *A, Constant *C, InstructionCost G)
117
- : Arg(A, C), Gain(G){};
118
+ SmallVector<ArgInfo, 8 > Args; // Stores the {formal,actual} argument pairs.
119
+ InstructionCost Gain; // Profitability: Gain = Bonus - Cost.
118
120
};
119
121
} // Anonymous namespace
120
122
121
123
using FuncList = SmallVectorImpl<Function *>;
122
- using ConstList = SmallVector<Constant *>;
123
- using SpecializationList = SmallVector<SpecializationInfo>;
124
+ using CallArgBinding = std::pair<CallBase *, Constant *>;
125
+ using CallSpecBinding = std::pair<CallBase *, SpecializationInfo>;
126
+ // We are using MapVector because it guarantees deterministic iteration
127
+ // order across executions.
128
+ using SpecializationMap = SmallMapVector<CallBase *, SpecializationInfo, 8 >;
124
129
125
130
// Helper to check if \p LV is either a constant or a constant
126
131
// range with a single element. This should cover exactly the same cases as the
@@ -307,17 +312,15 @@ class FunctionSpecializer {
307
312
LLVM_DEBUG (dbgs () << " FnSpecialization: Specialization cost for "
308
313
<< F->getName () << " is " << Cost << " \n " );
309
314
310
- SpecializationList Specializations;
311
- calculateGains (F, Cost, Specializations);
312
- if (Specializations.empty ()) {
313
- LLVM_DEBUG (dbgs () << " FnSpecialization: no possible constants found\n " );
315
+ SmallVector<CallSpecBinding, 8 > Specializations;
316
+ if (!calculateGains (F, Cost, Specializations)) {
317
+ LLVM_DEBUG (dbgs () << " FnSpecialization: No possible constants found\n " );
314
318
continue ;
315
319
}
316
320
317
- for (SpecializationInfo &S : Specializations) {
318
- specializeFunction (F, S, WorkList);
319
- Changed = true ;
320
- }
321
+ Changed = true ;
322
+ for (auto &Entry : Specializations)
323
+ specializeFunction (F, Entry.second , WorkList);
321
324
}
322
325
323
326
updateSpecializedFuncs (Candidates, WorkList);
@@ -392,72 +395,79 @@ class FunctionSpecializer {
392
395
return Clone;
393
396
}
394
397
395
- // / This function decides whether it's worthwhile to specialize function \p F
396
- // / based on the known constant values its arguments can take on, i.e. it
397
- // / calculates a gain and returns a list of actual arguments that are deemed
398
- // / profitable to specialize. Specialization is performed on the first
399
- // / interesting argument. Specializations based on additional arguments will
400
- // / be evaluated on following iterations of the main IPSCCP solve loop.
401
- void calculateGains (Function *F, InstructionCost Cost,
402
- SpecializationList &WorkList) {
398
+ // / This function decides whether it's worthwhile to specialize function
399
+ // / \p F based on the known constant values its arguments can take on. It
400
+ // / only discovers potential specialization opportunities without actually
401
+ // / applying them.
402
+ // /
403
+ // / \returns true if any specializations have been found.
404
+ bool calculateGains (Function *F, InstructionCost Cost,
405
+ SmallVectorImpl<CallSpecBinding> &WorkList) {
406
+ SpecializationMap Specializations;
403
407
// Determine if we should specialize the function based on the values the
404
408
// argument can take on. If specialization is not profitable, we continue
405
409
// on to the next argument.
406
410
for (Argument &FormalArg : F->args ()) {
407
411
// Determine if this argument is interesting. If we know the argument can
408
412
// take on any constant values, they are collected in Constants.
409
- ConstList ActualArgs;
413
+ SmallVector<CallArgBinding, 8 > ActualArgs;
410
414
if (!isArgumentInteresting (&FormalArg, ActualArgs)) {
411
415
LLVM_DEBUG (dbgs () << " FnSpecialization: Argument "
412
416
<< FormalArg.getNameOrAsOperand ()
413
417
<< " is not interesting\n " );
414
418
continue ;
415
419
}
416
420
417
- for (auto *ActualArg : ActualArgs) {
418
- InstructionCost Gain =
419
- ForceFunctionSpecialization
420
- ? 1
421
- : getSpecializationBonus (&FormalArg, ActualArg) - Cost;
421
+ for (const auto &Entry : ActualArgs) {
422
+ CallBase *Call = Entry.first ;
423
+ Constant *ActualArg = Entry.second ;
422
424
423
- if (Gain <= 0 )
424
- continue ;
425
- WorkList.push_back ({&FormalArg, ActualArg, Gain});
426
- }
425
+ auto I = Specializations.insert ({Call, SpecializationInfo ()});
426
+ SpecializationInfo &S = I.first ->second ;
427
427
428
- if (WorkList.empty ())
429
- continue ;
430
-
431
- // Sort the candidates in descending order.
432
- llvm::stable_sort (WorkList, [](const SpecializationInfo &L,
433
- const SpecializationInfo &R) {
434
- return L.Gain > R.Gain ;
435
- });
436
-
437
- // Truncate the worklist to 'MaxClonesThreshold' candidates if
438
- // necessary.
439
- if (WorkList.size () > MaxClonesThreshold) {
440
- LLVM_DEBUG (dbgs () << " FnSpecialization: Number of candidates exceed "
441
- << " the maximum number of clones threshold.\n "
442
- << " FnSpecialization: Truncating worklist to "
443
- << MaxClonesThreshold << " candidates.\n " );
444
- WorkList.erase (WorkList.begin () + MaxClonesThreshold, WorkList.end ());
428
+ if (I.second )
429
+ S.Gain = ForceFunctionSpecialization ? 1 : 0 - Cost;
430
+ if (!ForceFunctionSpecialization)
431
+ S.Gain += getSpecializationBonus (&FormalArg, ActualArg);
432
+ S.Args .push_back ({&FormalArg, ActualArg});
445
433
}
434
+ }
435
+
436
+ // Remove unprofitable specializations.
437
+ Specializations.remove_if (
438
+ [](const auto &Entry) { return Entry.second .Gain <= 0 ; });
439
+
440
+ // Clear the MapVector and return the underlying vector.
441
+ WorkList = Specializations.takeVector ();
442
+
443
+ // Sort the candidates in descending order.
444
+ llvm::stable_sort (WorkList, [](const auto &L, const auto &R) {
445
+ return L.second .Gain > R.second .Gain ;
446
+ });
447
+
448
+ // Truncate the worklist to 'MaxClonesThreshold' candidates if necessary.
449
+ if (WorkList.size () > MaxClonesThreshold) {
450
+ LLVM_DEBUG (dbgs () << " FnSpecialization: Number of candidates exceed "
451
+ << " the maximum number of clones threshold.\n "
452
+ << " FnSpecialization: Truncating worklist to "
453
+ << MaxClonesThreshold << " candidates.\n " );
454
+ WorkList.erase (WorkList.begin () + MaxClonesThreshold, WorkList.end ());
455
+ }
446
456
447
- LLVM_DEBUG (dbgs () << " FnSpecialization: Specializations for function "
448
- << F->getName () << " \n " ;
449
- for (SpecializationInfo &S
450
- : WorkList) {
457
+ LLVM_DEBUG (dbgs () << " FnSpecialization: Specializations for function "
458
+ << F->getName () << " \n " ;
459
+ for (const auto &Entry
460
+ : WorkList) {
461
+ dbgs () << " FnSpecialization: Gain = " << Entry.second .Gain
462
+ << " \n " ;
463
+ for (const ArgInfo &Arg : Entry.second .Args )
451
464
dbgs () << " FnSpecialization: FormalArg = "
452
- << S. Arg .Formal ->getNameOrAsOperand ()
465
+ << Arg.Formal ->getNameOrAsOperand ()
453
466
<< " , ActualArg = "
454
- << S.Arg .Actual ->getNameOrAsOperand ()
455
- << " , Gain = " << S.Gain << " \n " ;
456
- });
467
+ << Arg.Actual ->getNameOrAsOperand () << " \n " ;
468
+ });
457
469
458
- // FIXME: Only one argument per function.
459
- break ;
460
- }
470
+ return !WorkList.empty ();
461
471
}
462
472
463
473
bool isCandidateFunction (Function *F) {
@@ -490,12 +500,12 @@ class FunctionSpecializer {
490
500
Function *Clone = cloneCandidateFunction (F, Mappings);
491
501
492
502
// Rewrite calls to the function so that they call the clone instead.
493
- rewriteCallSites (Clone, S.Arg , Mappings);
503
+ rewriteCallSites (Clone, S.Args , Mappings);
494
504
495
505
// Initialize the lattice state of the arguments of the function clone,
496
506
// marking the argument on which we specialized the function constant
497
507
// with the given value.
498
- Solver.markArgInFuncSpecialization (Clone, S.Arg );
508
+ Solver.markArgInFuncSpecialization (Clone, S.Args );
499
509
500
510
// Mark all the specialized functions
501
511
WorkList.push_back (Clone);
@@ -641,7 +651,8 @@ class FunctionSpecializer {
641
651
// /
642
652
// / \returns true if the function should be specialized on the given
643
653
// / argument.
644
- bool isArgumentInteresting (Argument *A, ConstList &Constants) {
654
+ bool isArgumentInteresting (Argument *A,
655
+ SmallVectorImpl<CallArgBinding> &Constants) {
645
656
// For now, don't attempt to specialize functions based on the values of
646
657
// composite types.
647
658
if (!A->getType ()->isSingleValueType () || A->user_empty ())
@@ -681,7 +692,8 @@ class FunctionSpecializer {
681
692
682
693
// / Collect in \p Constants all the constant values that argument \p A can
683
694
// / take on.
684
- void getPossibleConstants (Argument *A, ConstList &Constants) {
695
+ void getPossibleConstants (Argument *A,
696
+ SmallVectorImpl<CallArgBinding> &Constants) {
685
697
Function *F = A->getParent ();
686
698
687
699
// Iterate over all the call sites of the argument's parent function.
@@ -723,23 +735,24 @@ class FunctionSpecializer {
723
735
724
736
if (isa<Constant>(V) && (Solver.getLatticeValueFor (V).isConstant () ||
725
737
EnableSpecializationForLiteralConstant))
726
- Constants.push_back (cast<Constant>(V));
738
+ Constants.push_back ({&CS, cast<Constant>(V)} );
727
739
}
728
740
}
729
741
730
742
// / Rewrite calls to function \p F to call function \p Clone instead.
731
743
// /
732
744
// / This function modifies calls to function \p F as long as the actual
733
- // / argument matches the one in \p Arg . Note that for recursive calls we
734
- // / need to compare against the cloned formal argument .
745
+ // / arguments match those in \p Args . Note that for recursive calls we
746
+ // / need to compare against the cloned formal arguments .
735
747
// /
736
748
// / Callsites that have been marked with the MinSize function attribute won't
737
749
// / be specialized and rewritten.
738
- void rewriteCallSites (Function *Clone, const ArgInfo &Arg ,
750
+ void rewriteCallSites (Function *Clone, const SmallVectorImpl< ArgInfo> &Args ,
739
751
ValueToValueMapTy &Mappings) {
740
- Function *F = Arg.Formal ->getParent ();
741
- unsigned ArgNo = Arg.Formal ->getArgNo ();
742
- SmallVector<CallBase *, 4 > CallSitesToRewrite;
752
+ assert (!Args.empty () && " Specialization without arguments" );
753
+ Function *F = Args[0 ].Formal ->getParent ();
754
+
755
+ SmallVector<CallBase *, 8 > CallSitesToRewrite;
743
756
for (auto *U : F->users ()) {
744
757
if (!isa<CallInst>(U) && !isa<InvokeInst>(U))
745
758
continue ;
@@ -758,9 +771,16 @@ class FunctionSpecializer {
758
771
<< " \n " );
759
772
if (/* recursive call */
760
773
(CS->getFunction () == Clone &&
761
- CS->getArgOperand (ArgNo) == Mappings[Arg.Formal ]) ||
774
+ all_of (Args,
775
+ [CS, &Mappings](const ArgInfo &Arg) {
776
+ unsigned ArgNo = Arg.Formal ->getArgNo ();
777
+ return CS->getArgOperand (ArgNo) == Mappings[Arg.Formal ];
778
+ })) ||
762
779
/* normal call */
763
- CS->getArgOperand (ArgNo) == Arg.Actual ) {
780
+ all_of (Args, [CS](const ArgInfo &Arg) {
781
+ unsigned ArgNo = Arg.Formal ->getArgNo ();
782
+ return CS->getArgOperand (ArgNo) == Arg.Actual ;
783
+ })) {
764
784
CS->setCalledFunction (Clone);
765
785
Solver.markOverdefined (CS);
766
786
}
@@ -891,7 +911,7 @@ bool llvm::runFunctionSpecialization(
891
911
// Initially resolve the constants in all the argument tracked functions.
892
912
RunSCCPSolver (FuncDecls);
893
913
894
- SmallVector<Function *, 2 > WorkList;
914
+ SmallVector<Function *, 8 > WorkList;
895
915
unsigned I = 0 ;
896
916
while (FuncSpecializationMaxIters != I++ &&
897
917
FS.specializeFunctions (FuncDecls, WorkList)) {
0 commit comments