This is an archive of the discontinued LLVM Phabricator instance.

[FuncSpec] Promote stack values before specialization
Needs ReviewPublic

Authored by mtsamis on Jun 9 2023, 9:44 AM.

Details

Summary

After each iteration of the function specializer, constant
stack values are promoted to constant globals in order to
enable recursive function specialization.

This should also be done once before running the specializer:

  • Since by default the specializer is ran once, without an initial call to this function worthwhile specialization opportunities may be missed. This is especially true for Fortran programs where function arguments are passed by reference.
  • This also results in more consistent specialization behaviour when the specializer is ran more than once. Currently there are cases where functions will be specialized depending on if an unrelated specializable function is added.

Diff Detail

Event Timeline

mtsamis created this revision.Jun 9 2023, 9:44 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2023, 9:44 AM
mtsamis requested review of this revision.Jun 9 2023, 9:44 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2023, 9:44 AM
chill added a comment.Jun 9 2023, 9:51 AM

In https://reviews.llvm.org/D145819 this is done for each iteration, IIRC so the same transformation can be applied to the new specialised instances.

Some additional context for this change:

This is part of improving function specialization in exchange2, as initiated by https://reviews.llvm.org/D106426.
Without this change getting the critical digits_2 function to specialize requires force-specialization=true which is undesired.

In general, this helps uncover more specialization opportunities in Fortran code and results in more consistent specialization behaviour when funcspec-max-iters > 1.

In https://reviews.llvm.org/D145819 this is done for each iteration, IIRC so the same transformation can be applied to the new specialised instances.

Thanks for the reference.

I see this moves promoteConstantStackValues() to the beggining of run which means that one the last iteration promoteConstantStackValues is not called.
This can result in some extra unpromoted / unwanted instructions after the specializer, which can be seen when funcspec-max-iters is not very large.
That's why I opted for calling it both initially and then after each iteration.

Would it be worthwhile to suggest merging this with the code on https://reviews.llvm.org/D145819?

I am planning changes to D145819. It makes sense to run promoteConstantStackValues per function and not across the entire module, at least until specialization on literal constants gets enabled. The reason is that there can't be new opportunities in consecutive iterations of run() unless we are specializing a call which uses a return value from a specialization created in the previous iteration, or a call which resides in the body of a specialization. It seems to me that promoteConstantStackValues was specifically written for recursive functions, so perhaprs we could limit it there like:

@@ -476,17 +464,37 @@ bool FunctionSpecializer::run() {
     if (!isCandidateFunction(&F))
       continue;

-    Cost SpecCost = getSpecializationCost(&F);
-    if (!SpecCost.isValid()) {
-      LLVM_DEBUG(dbgs() << "FnSpecialization: Invalid specialization cost for "
-                        << F.getName() << "\n");
-      continue;
+    auto [It, Inserted] = FunctionMetrics.try_emplace(&F);
+    CodeMetrics &Metrics = It->second;
+    //Analyze the function.
+    if (Inserted) {
+      SmallPtrSet<const Value *, 32> EphValues;
+      CodeMetrics::collectEphemeralValues(&F, &GetAC(F), EphValues);
+      for (BasicBlock &BB : F)
+        Metrics.analyzeBasicBlock(&BB, GetTTI(F), EphValues);
     }

+    // If the code metrics reveal that we shouldn't duplicate the function,
+    // or if the code size implies that this function is easy to get inlined,
+    // then we shouldn't specialize it.
+    if (Metrics.notDuplicatable || !Metrics.NumInsts.isValid() ||
+        (!ForceSpecialization && !F.hasFnAttribute(Attribute::NoInline) &&
+         Metrics.NumInsts < MinFunctionSize))
+      continue;
+
+    // TODO: For now only consider recursive functions when running multiple
+    // times. This should change if specialization on literal constants gets
+    // enabled.
+    if (!Inserted && !Metrics.isRecursive && !SpecializeLiteralConstant)
+      continue;
+
     LLVM_DEBUG(dbgs() << "FnSpecialization: Specialization cost for "
-                      << F.getName() << " is " << SpecCost << "\n");
+                      << F.getName() << " is " << Metrics.NumInsts << "\n");
+
+    if (Metrics.isRecursive)
+      promoteConstantStackValues(&F);

-    if (!findSpecializations(&F, SpecCost, AllSpecs, SM)) {
+    if (!findSpecializations(&F, Metrics.NumInsts, AllSpecs, SM)) {
       LLVM_DEBUG(
           dbgs() << "FnSpecialization: No possible specializations found for "
                  << F.getName() << "\n");