Inline constant std::function parameter

Hey LLVM-dev,

I'm trying to inline the following C++ code:

    __attribute__((always_inline)) static void inline compose(const char* s, std::function<void(const char *)> f) {
        std::cout << s << std::endl;
        f(s);
    }
    
    // --------------- Main ---------------
    int main() {
        // Nest three things
        compose("hello world", (const char *s) {
            compose("hello again", (const char *s) {
                compose("hello third time", (const char *s) {
                    return;
                });
            });
        });
    
        return 0;
    }

Here my continuations are of type `std::function<void(const char*)>` and what I wanted from LLVM with the `always_inline` option was to transform it to a single call-site that looks like this:

    // --------------- Main ---------------
    int main() {
        // Nest three things
        std::cout << "hello world" << std::endl;
        std::cout << "hello again" << std::endl;
        std::cout << "hello third time" << std::endl;
        return 0;
    }

However that doesn't seem to be the case, does the inliner not get triggered if the functions are passed as objects (std::function) or am I using the wrong `opt` invokation?

    OPT=opt-6.0
    OPTFLAGS?=-O3 -always-inline -dot-callgraph

Thanks!

In general, the inliner can only inline direct function calls (not virtual calls, or calls through a function pointer or std::function); otherwise, it doesn't know what to inline.

In some cases, optimizations can prove that an indirect call actually calls some specific function, but your testcase isn't one of those cases. In particular, the implementation of std::function is kind of hard for the optimizer to deal with. This could probably be improved.

-Eli

I was afraid that was the case, since std::function is not a first-class object of LLVM.
Every implementation that I can think of that inlines std::function intrinsically seems a bit awkward, in the way that it will be along the lines:

“Treat this class (std::function) in a special way”

A better way would be to generalize for all callables, and say something along the lines of

“If a class has the () operator, see if you can inline that method”

But that too is too specific to C++ and I’m afraid high-level operator semantics will be lost by the time it turns to byte code. Seems to me, the sane solution would be for clang++ to treat std::function as an LLVM function type (https://llvm.org/docs/LangRef.html#function-type) instead of a class object and then the road to these optimizations will open. But I have not looked at the gritty details of the clang implementation, so I could be way off. Anybody willing to step in and fill the blanks would be appreciated.

Regards,
Lef

Yeah, it’s unfortunately a fair bit more complicated than that.

It’s unlikely that LLVM or Clang would ever special-case the IR for std::function specifically. Generally the goal would be to find what parts of LLVM aren’t able to see through the abstractions in std::function to optimize it generally.