Implementing platform specific library call simplification

The newlib C library provides iprintf(), a restricted version of printf without support for floating-point formatting. I'd like to add an optimization which turns calls to printf() into calls to iprintf() if the format string has no floating point specifiers.

At the moment I've got this working locally by adding code to the simplify-libcalls pass. However this will break on targets where iprintf() isn't available. Is there a sensible place to add information about which library functions are available for a particular target?

Thanks,

Richard

The newlib C library provides iprintf(), a restricted version of printf
without support for floating-point formatting. I'd like to add an
optimization which turns calls to printf() into calls to iprintf() if
the format string has no floating point specifiers.

Cool, ok. I can see how this would be very useful for a variety of embedded systems.

At the moment I've got this working locally by adding code to the
simplify-libcalls pass. However this will break on targets where
iprintf() isn't available. Is there a sensible place to add information
about which library functions are available for a particular target?

I'd suggest adding a bool argument (HasIPrintf) to the createSimplifyLibCallsPass function and the pass constructor. Then clang (or whatever is setting up the pass manager) can query the appropriate target info to pass down this flag.

Longer term, I'd like to introduce a simple interface (via TargetRegistry) for exposing target libcall info. This would be useful to eliminate the HAVE_FLOORF etc #defines in simplifylibcalls, which break cross compilation.

-Chris

Chris and all,

IMHO I think SimplifyLibcall and similar might be provided by rather
clang (and frontends), than LLVM itself.
Frontends might know better than LLVM (as backend infrastructure).

...Takumi

SimplifyLibcall is applicable to any language that runs with libc (almost all of them), and at LTO time you can have mixed languages. I think that LLVM providing this functionality is goodness, and more generally I'd like to move it into instcombine at some point (when the appropriate "no-builtins" issues and target hooks are dealt with).

-Chris

Hi Richard,

Just to close the loop on this, I went ahead and added a new llvm/Target/TargetLibraryInfo.h and added it to simplifylibcalls. TargetLibraryInfo doesn't handle all the libcalls that SimplifyLibCalls handles yet, but it should provide a starting point for iprintf: just add an iprintf enum to TargetLibraryInfo and make your transformation predicated on TLI->has(LibFunc::iprintf).

It would be great to see this go into mainline,

-Chris

The newlib C library provides iprintf(), a restricted version of printf
without support for floating-point formatting. I'd like to add an
optimization which turns calls to printf() into calls to iprintf() if
the format string has no floating point specifiers.

Cool, ok. I can see how this would be very useful for a variety of embedded systems.

At the moment I've got this working locally by adding code to the
simplify-libcalls pass. However this will break on targets where
iprintf() isn't available. Is there a sensible place to add information
about which library functions are available for a particular target?

I'd suggest adding a bool argument (HasIPrintf) to the createSimplifyLibCallsPass function and the pass constructor. Then clang (or whatever is setting up the pass manager) can query the appropriate target info to pass down this flag.

Sorry to jump into something midstream, but I just happened to read the above paragraph and thought I should comment. Adding a boolean argument to a pass's constructor method is one of the things I have learned not to do. If I ever give a "How Not to Write an LLVM Pass" talk, this will be one of the things in the list.

The problem with boolean default arguments in pass constructors is that they make a pass more difficult to use within the opt and bugpoint tools. Inevitably you'll find some bug that only occurs when the flag is set to a non-default value, you'll fire up bugpoint, pass in the arguments printed by your tool's -debug-pass=Arguments option, and then wonder why the bug doesn't trigger. Eventually you find out that it's due to the boolean flag you put into the constructor not being set the way you want. You then have to manually change the constructor in the source code, recompile, run bugpoint, and then remember to change it back before committing your fix.

Perhaps you can escape this problem because you're working on a pass that is hard-coded into opt and bugpoint, but it'll only work if opt and bugpoint and whatever special tool that this pass is incorporated into all set that boolean in the same way under the same conditions. If someone uses it in a tool that sets it using some non-standard criteria, then you run into this problem.

We went through this with Automatic Pool Allocation and SAFECode: it was one of several things that made bugpoint inconvenient or impossible to use.

I have not actually fixed this problem in Poolalloc/SAFECode yet, but I've got the following ideas to try:

1) Create one version of your pass that sets the boolean flag to true and another version of your pass that sets the boolean flag to false. You can probably use C++ templates or inheritance to do this. Register each pass as a separate pass and have your tool pick which pass to run based upon whatever conditions you want.

For example, the Poolalloc pass has to use either EQTDDataStructures or EQBUDataStructures pass depending on whether it is used for SAFECode. Right now it picks one based on a boolean constructor. Instead, I would have the Poolalloc pass be a template that takes the name of the pass to use as input and create two passes:Poolalloc<EQTDDataStructures> and Poolalloc<EQBUDataStructures>. SAFECode would use Poolalloc<EQTDDataStructures>.

2) Create an analysis group that tells your pass what to do (i.e., your pass calls a method on the analysis group to get the boolean flag). Create two immutable passes belonging to the group and return different values for the boolean flag.

Either of these techniques will ensure that any PassManager based tool can print out a string with -debug-pass=Arguments that can be fed into opt or bugpoint to reproduce the same behavior.

My 4 cents.

-- John T.

FWIW, the patch didn't do that. SimplifyLibcalls depends on TargetLibraryInfo, which is an analysis pass.

-Chris

However, TargetLibraryInfo does take a target triple (not a bool
though) as constructor argument, defaulting to all-unknown.
Maybe instead of being an ImmutablePass it should be a ModulePass so
it can use Module::getTargetTriple() in runOnModule()?
That should make it more compatible with opt & bugpoint.

opt already passes the triple into the pass. bugpoint could do the same if it is interesting. We already have similar issues with TargetData.

-Chris

Hi Chris,

Thanks for this - it looks to be exactly what I need. I'll should have some time next week to put together a patch with the iprintf optimizations.