This is an archive of the discontinued LLVM Phabricator instance.

ThinLTO: Verify bitcode before lauching the ThinLTOCodeGenerator.
ClosedPublic

Authored by aprantl on May 12 2017, 1:47 PM.

Details

Summary

This patch invokes the Verifier on each module after loading, analogously to what regular LTO is doing. Besides diagnosing malformed bitcode, this allows it to strip out invalid debug info from bitcode produced by older, more buggy versions of LLVM. The new code is pretty much the same as in LTOCodeGenerator.cpp.

rdar://problem/31233625

Diff Detail

Repository
rL LLVM

Event Timeline

aprantl created this revision.May 12 2017, 1:47 PM
mehdi_amini added inline comments.May 12 2017, 2:08 PM
lib/LTO/ThinLTOCodeGenerator.cpp
186 ↗(On Diff #98837)

How does it work with lazy-loaded modules?

223 ↗(On Diff #98837)

We're gonna reverify here, aren't we?

Interesting. The linker invocation I was debugging was using the

if (CodeGenOnly) {
  // Perform only parallel codegen and return.
 ...

codepath, which doesn't initializer the PM the same way.

The codegen-only path also does not exercise the cross-module importing.

This turns out to be quite ugly.
There are two different code paths, by their llvm-lto options:
-thinlto-action=run uses PassManagerBuilder and sets VerifyInput=true, which causes a VerifierLegacyPass to be scheduled
-thinlto-action=codegen (which mimics llc) doesn't.
The obvious correct fix for -thinlto-action=codegen is to also schedule a Verifier pass. That is easy — but wait.

The VerifierLegacyPass pass doesn't work at all like one might expect. The crux is that VerifierLegacyPass is a FunctionPass, which implements runOnFunction for function-level verification (this part is fine) and does all of its module-level verification in doFinalization(), but doFinalization runs when the PassManager is done with running the entire pipeline, so the per-module verifications are running *after* the bugs happen that they are supposed to prevent.

I think that the right solution for this is to turn VerifierLegacyPass into a ModulePass so the per-module verification can be scheduled to run first.

That is not all, however. There is also the problem that the verifier wants to assert on invalid debug info, which makes this behavior untestable in an asserts build. I will also have to thread a flag to disable the assertion into the VerifierLegacyPass so we can write a testcase for it.

Back to the drawing board...

What about disabling the verification as part of the pass pipeline and just do the manual call to the verifier on loading?

The thing that is messy is that we want to avoid verifying all the buffers on every cross-module import, but I'm not sure about the debug-info dropping?

aprantl updated this revision to Diff 99078.May 15 2017, 4:07 PM

Yes I think you are right. There are too many places that assume the Verifier is as function pass.
The updated patch disabled input verification in the pass manager builder and manually verifies the input.

(+@pcc)
Someone should take care of making sure this is also handled by the new LTO API.

lib/LTO/ThinLTOCodeGenerator.cpp
440 ↗(On Diff #99078)

The name seems misleading: the module hasn't been "merged" here (no importing happened at this point).

470 ↗(On Diff #99078)

How costly is it to do it two times?
I guess there is not much choice any way with respect to debug info....

Do we have testing for this?

aprantl updated this revision to Diff 99190.May 16 2017, 1:47 PM

What an entangled mess. This patch adds a boatload of testcases including the case where a function with broken debug info is cross-module-imported into a verified correct module. It also avoids re-verifying the module as much as possible.

aprantl marked 2 inline comments as done.May 16 2017, 1:49 PM
aprantl added inline comments.
lib/LTO/ThinLTOCodeGenerator.cpp
470 ↗(On Diff #99078)

The verifier is not terribly expensive, but it does visit every entity in the module once.

mehdi_amini added inline comments.May 16 2017, 2:02 PM
include/llvm/LTO/legacy/ThinLTOCodeGenerator.h
335 ↗(On Diff #99190)

This assumes that there exists a main module, which is a foreign concept to ThinLTOCodegenerator till now AFAIK. I'm not sure what you're trying to express here?
Especially, this seems used only in the codegen, which is only use in a very specific mode (i.e. split model where you store optimized bitcode and reprocess to perform only the codegen). In such case either you want to verify all the modules that you're about to codegen or none. I have the impression that you'll check only the first one. Not sure if I missed something.

lib/LTO/ThinLTOCodeGenerator.cpp
190 ↗(On Diff #99190)

I'm still not sure how this plays with importing: what happens here with a lazy-loaded module?

I currently think we need to verify here only non-lazy-loaded modules (i.e. main modules). And implement another verification after materialization, but before importing in FunctionImporter.cpp

214 ↗(On Diff #99190)

That shouldn't be necessary if we checked every module at load time.

aprantl updated this revision to Diff 99214.May 16 2017, 3:15 PM

This assumes that there exists a main module, which is a foreign concept to ThinLTOCodegenerator till now AFAIK. I'm not sure what you're trying to express here?
Especially, this seems used only in the codegen, which is only use in a very specific mode (i.e. split model where you store optimized bitcode and reprocess to perform only the codegen). In such case either you want to verify all the modules that you're about to codegen or none. I have the impression that you'll check only the first one. Not sure if I missed something.

In the attached version I removed the flag. This will cause the

void ThinLTOCodeGenerator::run() // CodeGenOnly=true

path to verify the module twice. I'm not particularly concerned by this though and it keeps the code simpler.

I also implemented the suggestion to verify in FunctionImporter.

Thanks!

aprantl updated this revision to Diff 99216.May 16 2017, 3:16 PM

Fix typos.

aprantl added inline comments.May 16 2017, 3:18 PM
lib/LTO/ThinLTOCodeGenerator.cpp
186 ↗(On Diff #98837)

How can I simulate a lazy-loaded module using llvm-lto? I would like to add a test for this.

This assumes that there exists a main module, which is a foreign concept to ThinLTOCodegenerator till now AFAIK. I'm not sure what you're trying to express here?
Especially, this seems used only in the codegen, which is only use in a very specific mode (i.e. split model where you store optimized bitcode and reprocess to perform only the codegen). In such case either you want to verify all the modules that you're about to codegen or none. I have the impression that you'll check only the first one. Not sure if I missed something.

In the attached version I removed the flag. This will cause the

void ThinLTOCodeGenerator::run() // CodeGenOnly=true

path to verify the module twice. I'm not particularly concerned by this though and it keeps the code simpler.

It seems to me that it indicates that the check in the codegen() method is redundant now. Why do we need it? What about just removing it?

It seems to me that it indicates that the check in the codegen() method is redundant now. Why do we need it? What about just removing it?

Because llvm-lto invokes codegen() directly after loading the module itself (wihtout going through ThinLTOCodeGenerator for the loading):

void codegen() {
  if (InputFilenames.size() != 1 && !OutputFilename.empty())
    report_fatal_error("Can't handle a single output filename and multiple "
                       "input files, do not provide an output filename and "
                       "the output files will be suffixed from the input "
                       "ones.");
  if (!ThinLTOIndex.empty())
    errs() << "Warning: -thinlto-index ignored for codegen stage";

  for (auto &Filename : InputFilenames) {
    LLVMContext Ctx;
    auto TheModule = loadModule(Filename, Ctx);

    auto Buffer = ThinGenerator.codegen(*TheModule);

It seems to me that it indicates that the check in the codegen() method is redundant now. Why do we need it? What about just removing it?

Because llvm-lto invokes codegen() directly after loading the module itself (wihtout going through ThinLTOCodeGenerator for the loading):

It is likely before run() was improved to be able to handle CodeGenOnly, we should retarget llvm-lto to use run() instead. I can do this if you want (likely tonight or tomorrow night)

lib/LTO/ThinLTOCodeGenerator.cpp
186 ↗(On Diff #98837)

Anything that do cross-module importing will do lazy-loading. So two modules and one function calling another function in the other module. I think test/ThinLTO/X86/funcimport.ll should have such an example.

It seems to me that it indicates that the check in the codegen() method is redundant now. Why do we need it? What about just removing it?

Because llvm-lto invokes codegen() directly after loading the module itself (wihtout going through ThinLTOCodeGenerator for the loading):

It is likely before run() was improved to be able to handle CodeGenOnly, we should retarget llvm-lto to use run() instead. I can do this if you want (likely tonight or tomorrow night)

lib/LTO/ThinLTOCodeGenerator.cpp
186 ↗(On Diff #98837)

If that is so then my cross-module import tests (the last two llvm-lto invocations) are already testing this. The lazy-loaded modules will use the PostProcess verifier in FunctionImport.

It is likely before run() was improved to be able to handle CodeGenOnly, we should retarget llvm-lto to use run() instead. I can do this if you want (likely tonight or tomorrow night)

Thanks for the offer! Please let me know if you won't get to it. If you can just quickly sketch what needs to be done I can do the grunt work, too.

It is likely before run() was improved to be able to handle CodeGenOnly, we should retarget llvm-lto to use run() instead. I can do this if you want (likely tonight or tomorrow night)

Thanks for the offer! Please let me know if you won't get to it. If you can just quickly sketch what needs to be done I can do the grunt work, too.

It should be fairly straightforward, just mimicking the C API: it should be only replacing the call to codegen() with two calls to setCodeGenOnly(true) and then run() (that's the best case, but I haven't looked yet, the code may not be setup to do it so easily).

aprantl updated this revision to Diff 99583.May 19 2017, 9:46 AM

Depending on https://reviews.llvm.org/D33360, get rid of the verifier invocation in codegen()

mehdi_amini accepted this revision.May 19 2017, 10:36 AM

LGTM.

include/llvm/Transforms/IPO/FunctionImport.h
58 ↗(On Diff #99583)

What about renaming PostProcess to PostBitcodeLoading? It seems more accurate to me.

lib/LTO/ThinLTOCodeGenerator.cpp
205 ↗(On Diff #99583)

Just curious: are the { } pair required?

This revision is now accepted and ready to land.May 19 2017, 10:36 AM
aprantl marked an inline comment as done.May 19 2017, 10:40 AM

Thanks for the review!

include/llvm/Transforms/IPO/FunctionImport.h
58 ↗(On Diff #99583)

Makes sense!

lib/LTO/ThinLTOCodeGenerator.cpp
205 ↗(On Diff #99583)

Yes, otherwise we are getting an error: no viable conversion from 'void (llvm::Module &)' to 'Optional<std::function<void (Module &)> >'

This revision was automatically updated to reflect the committed changes.