Writing LLVM Pass in 2018 — Part II

Analysis —Thing that deserves its own article

Min-Yih Hsu

--

Tasks of collecting program analyses are modeled as Passes in LLVM PassManager too, except that they never(and shouldn’t) modify the IR content. Also, compared to the legacy PassManager, analysis data is managed and developed in such a different way in the new PassManager, that I decided to use an individual article to cover it. This article would talk about how to retrieve analysis data with the new AnalysisManager . So grab your favorite editor, and let’s get started!

One of the significant features you might find in a legacy pass is that analysis manager is deeply integrated with the PassManager. You will fetch certain analysis data via the getAnalysis<…> method, which is a member of the Pass class. In the new PassManager system however, analysis manager is an separated instance that can be used individually in any places. To impress you by this characteristic, let’s use the new AnalysisManager in a legacy Pass! Here is the bootstrapping (skeleton) code:

Looks like there is someone we’re familiar with: PassBuilder. You’re right, we still need its help to register all available analysis Passes into the manager, but after finish, the analysis manager, FunctionAnalysisManager here, can be used individually. An AnalysisManager is responsible to arrange the registered analysis Passes as well as managing their analysis result. For example, caching a result until it becomes invalid due to some modifications on its corresponding IR unit.

So how do we retrieve analysis result from the manager? The answer is similar to the getAnalysis<…> interface in legacy Passes, but with slightly different names and different ways:

The above code use AliasAnalysis as the analysis data we want to get. As you can see, with the old syntax, you need to get a reference to the Pass instance first, then use one of its member functions to get the analysis result. In the contrary, to get the analysis result you only need to call getResult<…> with the desired analysis Pass type(AAManagerin this case), alone with your target IR unit(Function in this case) instance.

Here I want to point out the different return types between getAnalysis and getResult. On one hand, return type of getAnalysis<T> would be T , its template type parameter, which is the type of analysis Pass you desire, not the analysis result. On the other hand, template type parameter T for getResult<T> would still represent type of your desired analysis pass, but return type of getResult would be T::Result , the Result type nested in class T. This difference reveals an important breaking changes on the design of analyses management: Detaching analysis result from analysis Pass. This would make some management, data invalidation, more easier and efficient.

Now let’s go back to the new PassManager. You might already notice that the second argument of the run method in a new PassManager Pass is an AnalysisManager instance for the corresponding IR unit:

So you could just use it without populating by PassBuilder.

If you’re not familiar with syntax of the new PassManager Pass, you could go to my previous article talking about writing a simple “Hello World” Pass for the new PassManager.

Finally, we’re going to talk about analysis data invalidation. We’re only going to cover the part that are used most in a normal Pass, as there are more invalidation techniques involved when you’re developing an analysis Pass.

PreservedAanlyses, the required type that need to be returned from the run method, records a set of analysis data that are still valid after this Pass. If you just looking through the IR without touching anything, then of course all the analyses are still valid after that, which is PreservedAnalyses::all(), the universal preserved set we used in previous examples.

But if you, for example, use some profile data to change the branch probabilities and thus change the block frequency information, you need to remove it from the universal preserved set:

Instead of removing analyses from PreservedSet, it’s actually more common to declare some analyses being preserved. For example, you only know that your Pass would not touch the control flow graph and the loop information within the function:

The preserve<…> method declare a preservation of single analysis, where you pass the desired analysis type as the template parameter. preserveSet<…> on the other hand, is a little special. It would preserve a set of analyses, and you need to pass a analysis set type, which is a concept other than analysis Pass, as the template parameter. There are several available analysis set, for example, the CFGAnalyses here, representing all control-flow related analyses.

There are still some important topics that are not covered in this article. For example:

  • How to write an analysis Pass?
  • How to query whether an analysis is preserved?

Hope you find answers from the source tree and enjoy the process as well :-)

--

--