please advise on PassManager

Hello,

I’ve recently changed AddressSanitizer (asan) compiler pass from ModulePass to FunctionPass and it could a bit of mayhem.

The problem is that asan FunctionPass instruments a function foo, then foo gets inlined into bar, then bar gets instrumented
and thus the code of foo gets instrumented twice (which causes run-time crash).
This happens only at -O0; at -O1 we get the correct order of events for some reason (foo gets inlined, then foo and bar are instrumented).

The code looks like this:

tools/clang/lib/CodeGen/BackendUtil.cpp

if (LangOpts.AddressSanitizer) {
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
addAddressSanitizerPass);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
addAddressSanitizerPass);
}

lib/Transforms/IPO/PassManagerBuilder.cpp
void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) {

// If all optimizations are disabled, just run the always-inline pass.
if (OptLevel == 0) {
if (Inliner) {
MPM.add(Inliner);
Inliner = 0;
}
addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);
return;
}

if (Inliner) {
MPM.add(Inliner);
Inliner = 0;
}


addExtensionsToPM(EP_OptimizerLast, MPM);

At O0 we use PassManagerBuilder::EP_EnabledOnOptLevel0 insertion point, at -O1+ we use PassManagerBuilder::EP_OptimizerLast,
but the logic looks the same.
What do I miss?

Thanks!

–kcc

I’m not sure what the difference here is between -O0 and -O1, but this is intended behavior. This “interlacing” of passes is really important for C++ optimization (among other things) where optimizing a function can dramatically shrink it (due to abstraction penalties being removed)… which then makes it a much strongly inline candidate.

-Chris

Yes, this is the intended behavior of the CGSCC pass, and in an optimized
build there is a pass manager extension hook for putting a function pass
into this process.

However, the -O1 (and higher) pass managers expose the OptimizeLast
extension point after several module passes, and thus they run in their own
function pass manager. This is desirable for at least some of the users of
that extension point. But in -O0, the *only other pass* is the inliner, and
it's not really the normal inliner, it is just the always-inliner. There is
no extension point offered which would not fall into this inliner's CGSCC
pass manager, because we cannot even express such an extension point today.

I've almost finished an interim[1] fix to this which will take the
following form:

1) Introduce a new 'BarrierNoop' module pass which has no effect other than
to go into the optimization pipeline, and terminate any of the implicitly
nested pass managers in place. This will even be useful on the 'opt'
commandline, for example to build two independent CGSCC pass managers.

2) Teach the -O0 extension point that ASan and other "late instrumentation"
passes use to have a barrier in the pipeline before it.

potential 3) I could also add more extension points that provide finer
grained control over this and/or clarify the naming of the extension points
to document the guarantees they provide in terms of composing with other
pass managers.

Sound reasonable?

[1]: Note, I say interim because if I can ever get time to do the pass
manager restructuring, that will have a side-effect of making these nested
managers explicit instead of implicit, and thus removing the need for a
dummy pass (which is quite a hack) to control them. Instead, they would be
more directly controlled.

Yes. This sounds like a quite reasonable hack to me, please document it as such very clearly though :slight_smile:

-Chris