This RFC addresses the lack of a mechanism to handle attributes on input arguments and results while inlining using the DialectInlinerInterface
. This is relevant, for example, for correctness when inlining arguments with the byval
attribute in LLVM::LLVMFunctionOp
in the LLVM dialect, which requires copying the data on the input pointer before passing the value to the inlined operations.
Current state
The DialectInlinerInterface
(defined in InliningUtils.cpp) currently exposes the following transformation hooks:
virtual void handleTerminator(Operation *op, Block *newDest) const;
virtual void handleTerminator(Operation *op,
ArrayRef<Value> valuesToReplace) const;
virtual Operation *materializeCallConversion(OpBuilder &builder, Value input,
Type resultType,
Location conversionLoc) const;
virtual void processInlinedCallBlocks(
Operation *call, iterator_range<Region::iterator> inlinedBlocks) const;
The gap to handling argument/result attributes while inlining is twofold:
- None of the transformation hooks above expose the
CallableOpInterface
operation, which is also the operation that implementsFunctionOpInterface
(and thereby exposes argument/result attributes) in practice. - The
DialectInterfaceInliner
does not currently know about argument/result attributes, as they are implemented onFunctionOpInterface
.
Proposed change
A possible solution is to add hooks that handle each argument and result attributes pair:
/// For an argument with input value `input` and argument `attributes`, allow adding
/// zero or more operations returned in `newOperations` that handle these attributes,
/// then return the new or existing value that should be used by inlined operations.
virtual Value handleArgumentAttributes(OpBuilder &builder,
DictionaryAttr attributes,
Value input,
SmallVectorImpl<Operation *> &newOperations) {
return input;
}
virtual Value handleResultAttributes(OpBuilder &builder,
DictionaryAttr attributes,
Value output,
SmallVectorImpl<Operation *> &newOperations) {
return input;
}
Returning the newOperations
is necessary because of the cleanupState
mechanism in inlineCall
that requires us to know about newly added operations in case of failure.
Additionally, CallableOpInterface
would need to be extended to know about argument/result attributes by adding (for example) the following APIs:
virtual ArrayAttr getCallableArgAttrs() { return {}; }
virtual ArrayAttr getCallableResAttrs() { return {}; }
These two methods would need to be implemented by Func::FuncOp
and LLVM::LLVMFuncOp
and redirected to FunctionOpInterface::getArgAttrsAttr
and FunctionOpInterface::getResAttrsAttr
, respectively.
Alternatives
We thought of two alternative directions that could achieve handling of argument/result attributes, which both seem somewhat inelegant to us:
- Generalize
materializeCallConversion
into some form of monolithic functions that prepare/handle all inputs and outputs in whatever way necessary, passing both theCallOpInterface
and theCallableOpInterface
. - Extend
processInlinedBlocks
to a more general function that can do any additional processing necessary, by passingCallableOpInterface
, as well as the region being inlined into.
Any suggestions for alternative solutions or modifications to the proposed solution are very welcome!
(Tagging @River707 as the author of the DialectInlinerInterface
)