By that I mean an optimization pass (or a combination of them) that turns: void useCallback(void (*callbackfn)()) { // Do something callbackfn(); // Do something else } void myCallback() { // Respond one way } void myOtherCallback() { // Respond another way } void foo() { useCallback(myCallback); useCallback(myOtherCallback); } into: // Keep the original; it'll get removed // by other passes if it's nonpublic and dead. void useCallback(void (*callbackfn)()) { // Do something callbackfn(); // Do something else } void myCallback() { // Respond one way } void myOtherCallback() { // Respond another way } // equivalent of useCallback(myCallback), but doesn't call through a function pointer. void useMyCallback() { // Do something. myCallback(); // Do something else } // equivalent of useCallback(myOtherCallback), but doesn't call through a function pointer. void useMyOtherCallback() { // Do something myOtherCallback(); // Do something else } // Changed to use non-virtual versions created above. void foo() { useMyCallback(); useMyOtherCallback(); } With that transform in place, lots of inlining becomes possible, and direct function calls replace indirect function calls if inlining isn't appropriate. If this transform is combined with argpromotion and scalarrepl, it can be used for devirtualization of C++ virtual function calls. There seems to be an awful lot of C++ code out there that uses templates to perform this same optimization in source code.
Hi Kenneth,> By that I mean an optimization pass (or a combination of them) that turns:...> With that transform in place, lots of inlining becomes possible, and > direct function calls replace indirect function calls if inlining > isn't appropriate. If this transform is combined with argpromotion > and scalarrepl, it can be used for devirtualization of C++ virtual > function calls. > > There seems to be an awful lot of C++ code out there that uses > templates to perform this same optimization in source code.yes, LLVM does this. For example, running your example through the LLVM optimizers gives: define void @foo() nounwind readnone { entry: ret void } As you can see, the indirect function calls were resolved into direct function calls and inlined. I don't know which passes take care of this however. Ciao, Duncan.
When I used -std-compile-opts -disable-inlining, my transform didn't happen. I think in your test, the inline of UseCallback into foo automatically made the function pointer into a constant, which turned it into a direct call that was then inlined. If UseCallback is too big to inline and uses the callback parameter inside a loop, this transform is potentially valuable, particularly if UseCallback is called multiple times with the same callback parameter. Interestingly, when I had foo call UseCallback multiple times with *only* callback1, it yanked the function pointer parameter out of UseCallback and turned the thing into a direct call. (I'm guessing dead argument elimination came into play here) But as soon as I added a call to UseCallback with callback2 to the mix, it went back to not making any indirect call elimination. On Fri, Jun 4, 2010 at 11:11 AM, Duncan Sands <baldrick at free.fr> wrote:> Hi Kenneth, > >> By that I mean an optimization pass (or a combination of them) that turns: > ... >> With that transform in place, lots of inlining becomes possible, and >> direct function calls replace indirect function calls if inlining >> isn't appropriate. If this transform is combined with argpromotion >> and scalarrepl, it can be used for devirtualization of C++ virtual >> function calls. >> >> There seems to be an awful lot of C++ code out there that uses >> templates to perform this same optimization in source code. > > yes, LLVM does this. For example, running your example through the LLVM > optimizers gives: > > define void @foo() nounwind readnone { > entry: > ret void > } > > As you can see, the indirect function calls were resolved into direct > function calls and inlined. > > I don't know which passes take care of this however. > > Ciao, > > Duncan. > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev >
On Fri, Jun 4, 2010 at 7:53 AM, Kenneth Uildriks <kennethuil at gmail.com> wrote:> By that I mean an optimization pass (or a combination of them) that turns: > > void useCallback(void (*callbackfn)()) > { > // Do something > callbackfn(); > // Do something else > } > > void myCallback() > { > // Respond one way > } > > void myOtherCallback() > { > // Respond another way > } > > void foo() > { > useCallback(myCallback); > useCallback(myOtherCallback); > } > > > into: > > // Keep the original; it'll get removed > // by other passes if it's nonpublic and dead. > void useCallback(void (*callbackfn)()) > { > // Do something > callbackfn(); > // Do something else > } > > void myCallback() > { > // Respond one way > } > > void myOtherCallback() > { > // Respond another way > } > > // equivalent of useCallback(myCallback), but doesn't call through a > function pointer. > void useMyCallback() > { > // Do something. > myCallback(); > // Do something else > } > > // equivalent of useCallback(myOtherCallback), but doesn't call > through a function pointer. > void useMyOtherCallback() > { > // Do something > myOtherCallback(); > // Do something else > } > > // Changed to use non-virtual versions created above. > void foo() > { > useMyCallback(); > useMyOtherCallback(); > } > > With that transform in place, lots of inlining becomes possible, and > direct function calls replace indirect function calls if inlining > isn't appropriate. If this transform is combined with argpromotion > and scalarrepl, it can be used for devirtualization of C++ virtual > function calls. > > There seems to be an awful lot of C++ code out there that uses > templates to perform this same optimization in source code.There's a partial specialization pass that can do this sort of thing (IIRC not enabled by default); if you really need the transformation you're describing, you can adapt it appropriately. That said, if the inliner doesn't decide to inline useCallback, duplicating the function without inlining it is probably a bad idea in terms of codesize. IIRC the inliner heuristics already strongly favor inlining in cases like the given example, but it's possible that the inliner heuristics could use some additional tweaking. -Eli