Verifier & abort()

Hi,

Through some fat fingered typing of my own, I’ve run into some LLVM Verifier failures recently. I’ve noticed that the basic action the Verifier takes is to print some messages and then call abort(). The XPL Compiler is written as a compile server. It can take requests to compile multiple files from different sources. Therefore, abort() isn’t particularly friendly in this situation. While I should write a compiler that doesn’t fail verificiation, I’m also not perfect. So, I’d prefer it if there was a way to override this behavior.

Would anyone mind if I changed the Verifier to allow installation of a failure handler function that defaults to abort()? I would also like to change the verifier to print to an arbitrary iostream instead of always to std::cerr. This will help me capture the verifier’s output and produce it to the user within an “ICE” message. Again, the default would be std::cerr so if you don’t use the new api, the current behavior is retained.

Reid.

Perhaps another solution would be to throw an exception with the error
message?

Perhaps, but that would violate the programming contract already in
place. Right now LLVM and its users expect failed verifications to cause
an abort(). I wouldn't want to unleash on LLVM the new semantics of now
handling exceptions in every place the verifier is called especially
since the exception is basically saying "corrupt Module, use at your own
risk". Additionally, exceptions can only throw single strings from
single places but the verifier can actually generate multiple lines of
output (from multiple checks) before deciding to abort.

Reid

Well, I just discovered that there is logic in the Verifier with the
AbortBroken flag that would prevent the abort. Unfortunately, there's no
way to set it (anonymous namespace, hidden object instance) if you're
calling verifyModule.

Let me look at the code some more and think on this a bit. There has to
be a solution that will make everyone happy :slight_smile:

Reid.

compiler that doesn't fail verificiation, I'm also not perfect. So, I'd
prefer it if there was a way to override this behavior.

Make sense.

Would anyone mind if I changed the Verifier to allow installation of a
failure handler function that defaults to abort()? I would also like to
change the verifier to print to an arbitrary iostream instead of always
to std::cerr. This will help me capture the verifier's output and
produce it to the user within an "ICE" message. Again, the default would
be std::cerr so if you don't use the new api, the current behavior is
retained.

I think that the best solution would be to print to a stringstream and
then throw the completed string as an exception. The default verifier
pass would catch the exception, print it out, then call abort. Your hooks
could do other things with the exception as you please. Does this make
sense?

-Chris

The verifier should provide two APIs:

1. The Verifier pass should do what it currently does: check the program
   and abort on failure.
2. There should be a VerifyModule function which checks the program and
   throws an exception on error with an error message. I would assume
   that this is the one you would want to use.

I don't think that the verifier can actually emit multiple error messages.
After it finds one problem, it can't really depend on the state of the
module, so it bails out immediately. Throwing the exception would give
the same semantics and allow the caller code to decide how to handle it...

-Chris

The verifier can emit multiple messages of the same kind. For example,
when it processes the list of functions, it processes them all and then
aborts if it finds errors in that list. I've seen the output so this
isn't creative thinking.

So, I'm going to make verifyModule do as you mentioned. The messages
will be gathered in a stringstream and thrown.

Reid.

P.S. Here's an example of verifier code that can emit multiple messages:

bool doFinalization(Module &M) {
      // Scan through, checking all of the external function's linkage now...
      for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
        visitGlobalValue(*I);
                                                                                                                               
      for (Module::giterator I = M.gbegin(), E = M.gend(); I != E; ++I)
        visitGlobalValue(*I);
                                                                                                                               
      // If the module is broken, abort at this time.
      abortIfBroken();
      return false;
    }

The function visitGlobalValue will check one value, print out a message
and set Broken=true. The abort doesn't happen until abortIfBroken() is
called.

Reid.

Ah, ok I see. Still I don't know if that's really behavior that you need
to go out of your way to preserve. Usually if the module is broken, it's
best to just find that out and escape quickly! :slight_smile:

-Chris