I noticed that the FuncInlinerInterface specifies that all call operations can be inlined. How do I override this behavior? Ideally, I would like some functions with a “noinline” attribute to be not inlined even after the inlining pass.
We are generally missing a proper cost model for the inliner. Designing and implementing that would be nice. The first step towards that could be proposing an attribute that can be used for any op that implements the inliner interfaces.
We have been working on the inliner, specifically in the context of inlining LLVM. We currently handle the noinline attribute (among others).
A more general solution would be nice if you are interested in working on this
For example, a solution could be adding a bool hasNoInline() method to CallableOpInterface, defaulting to returning false, which func.func and llvm.func can override. There are already attributes for this on llvm.func, but func.func would need an additional (unary?) attribute that can be set. This could then be queried from isLegalToInline in the generic InlinerInterface.
If the goal is to develop a cost model for the inliner, it may also be worth considering to put the noinline attribute on the call rather than on the function. Annotating the calls allows a possible cost model to control the inlining based on properties of call site and callable.
A cost model could then be implemented as separate passes that runs before the inlining and annotates all the call sites that should not be inlined.
Add a hasNoInline method in CallableOpInterface that returns false by default.
Modify DialectInlinerInterface to let it check ops that inherit CallableOpInterface and call hasNoInline()
Remove existing code that checks the noinline attributes to determine if inlining should happen.
The other solution is to modify the func dialect just the same way as you did for the llvm dialect, just checking the noinline attribute in isLegalToInline functions.
Does your dialect implement its own CallOpInterface? In that case, you can just implement the DialectInlinerInterface for your dialect, and implement isLegalInline in whatever way is convenient for you, for example by adding an attribute to the call and/or the callable.
If you are relying on func.call and func.func, you would need to extend the callable (and/or perhaps on the call, as @gysit suggested) with a way to mark this, such as a new attribute, and check it in its DialectInlinerInterfaceimplementation.
Yeah, I defined our own func and call ops yesterday…
If you are relying on func.call and func.func, you would need to extend the callable (and/or perhaps on the call, as @gysit suggested) with a way to mark this, such as a new attribute, and check it in its DialectInlinerInterfaceimplementation.
I see, that seems neat. But I think we may not want to use an “attribute” to check if inlining is necessary or not, how about use a pass to analyze all call ops and then set each call op’s noInline property which could be queried using hasNoInline?
I see, that seems neat. But I think we may not want to use an “attribute” to check if inlining is necessary or not, how about use a pass to analyze all call ops and then set each call op’s noInline property which could be queried using hasNoInline ?
How would you store the noInline information? I may be wrong but I believe the isLegalInline function has access to the IR only (but not to an analysis or any other state). An attribute thus seems like a good solution for storing the information in IR?
I see. Yeah, I was thinking about adding an interface function to CallOpInterface, but the interface function still has to access the attribute of the IR.
Right. I think adding a new interface method mainly makes sense if we want to establish a noinline mechanism that works for all dialects. If you add the functionality only to your own dialect, you may be better of by accessing the attribute directly in isLegalToInline as @mehdi_amini pointed out.