Includes conversion to corresponding LLVM attributes
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
@ftynse these attributes are specific to func parameters, and not necessarily to LLVM backends (e.g. SPIRV). They are useful for dataflow analysis and would prefer not to have to include LLVMIR everywhere.
[MLIR][Func] Add readonly,writeonly,noalias to func arg attributes
Includes conversion to corresponding LLVM attributes
Differential Revision: https://reviews.llvm.org/D140357
There isn't sufficient context to justify the change in the commit description, and the change itself doesn't seem to do anything else than lifting function argument attributes from LLVM IR to the Func dialect level. Yes, there is SPIR-V, but the change does not include the lowering to SPIR-V or even a description of how it can be done. Blindly copying LLVM IR is acceptable in the LLVM dialect, but not at the higher-level abstractions such as Func. It is unclear to me what these attributes would even mean in the full generality of what MLIR type system allows. For example, what is a writeonly tensor? How do two SPIR-V types noalias? How do even two memrefs noalias when the default conversion to LLVM produces two pointers that precisely do alias each other?
These are useful concepts to have at this level, but they should be modeled with the sufficient generality. We quite likely want to leverage, e.g., memref's multidimensional and structured nature to capture more fine-grained information than we can about two opaque pointers with noalias. Consider posting on Discourse to discuss how to model these properly.
In the meantime, I checked the implementation, and I don't see anything that would prevent one from using llvm.noalias and the likes as an argument attribute in a func.func and regardless of the type.
Understood. This is as you said just lifting these attributes, a future change would use them and I will post it as an RFC to discuss. This change can wait for that, just trying to flush some trivial parts from my backlog.
As an example to answer some of your questions, take:
func.func @conv_relu(%arg0 : tensor<*xf32>, %arg1 : tensor<*xf32>) -> tensor<*xf32> { ... return %result : tensor<*xf32> }
Since tensors are PBV, we can tag the inputs as 'readonly' and the results as 'writeonly' as we know the function cannot read and will fully write the result.
func.func @conv_relu(%arg0 : tensor<*xf32> {func.readonly, func.noalias}, %arg1 : tensor<*xf32> {func.readonly, func.noalias}) -> tensor<*xf32> {func.writeonly, func.noalias} { ... return %result : tensor<*xf32> }
Then bufferization will take care to keep the specified attributes when moving results to out params:
func.func @conv_relu(%arg0 : memref<*xf32> {func.readonly, func.noalias}, %arg1 : memref<*xf32> {func.readonly, func.noalias}, %arg2 : memref<*xf32> {func.writeonly, func.noalias}) { ... return }
Then one can reason about how a function will interact with the memory references that are passed to it. Further, the func can be optimized more aggressively knowing that loads/stores are independent.
clang-format not found in user’s local PATH; not linting file.