[mlir][LLVM] Finer-grained control for C interface emission

C interface emission is controlled by a flag and has coarse granularity.
With this coarse control, interfaces are emitted for all external functions.
This makes is easy to get undefined symbols.

This revision adds support for controlling per-function emission with an "emit_c_interface" attribute.
This commit is contained in:
Nicolas Vasilache 2020-04-02 13:02:40 -04:00
parent 12fcbcecff
commit add9f1a5dc
3 changed files with 26 additions and 8 deletions

View File

@ -374,12 +374,14 @@ overridden by the user.*
### C-compatible wrapper emission
In practical cases, it may be desirable to have externally-facing functions
with a single attribute corresponding to a MemRef argument. When interfacing
with LLVM IR produced from C, the code needs to respect the corresponding
calling convention. The conversion to the LLVM dialect provides an option to
generate wrapper functions that take memref descriptors as pointers-to-struct
compatible with data types produced by Clang when compiling C sources.
In practical cases, it may be desirable to have externally-facing functions with
a single attribute corresponding to a MemRef argument. When interfacing with
LLVM IR produced from C, the code needs to respect the corresponding calling
convention. The conversion to the LLVM dialect provides an option to generate
wrapper functions that take memref descriptors as pointers-to-struct compatible
with data types produced by Clang when compiling C sources. The generation of
such wrapper functions can additionally be controlled at a function granularity
by setting the `llvm.emit_c_interface` unit attribute.
More specifically, a memref argument is converted into a pointer-to-struct
argument of type `{T*, T*, i64, i64[N], i64[N]}*` in the wrapper function, where

View File

@ -932,6 +932,7 @@ protected:
/// FuncOp legalization pattern that converts MemRef arguments to pointers to
/// MemRef descriptors (LLVM struct data types) containing all the MemRef type
/// information.
static constexpr StringRef kEmitIfaceAttrName = "llvm.emit_c_interface";
struct FuncOpConversion : public FuncOpConversionBase {
FuncOpConversion(LLVMTypeConverter &converter, bool emitCWrappers)
: FuncOpConversionBase(converter), emitWrappers(emitCWrappers) {}
@ -942,7 +943,7 @@ struct FuncOpConversion : public FuncOpConversionBase {
auto funcOp = cast<FuncOp>(op);
auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
if (emitWrappers) {
if (emitWrappers || funcOp.getAttrOfType<UnitAttr>(kEmitIfaceAttrName)) {
if (newFuncOp.isExternal())
wrapExternalFunction(rewriter, op->getLoc(), typeConverter, funcOp,
newFuncOp);
@ -2821,7 +2822,6 @@ struct LLVMLoweringPass : public ModulePass<LLVMLoweringPass> {
if (failed(applyPartialConversion(m, target, patterns, &typeConverter)))
signalPassFailure();
}
};
} // end namespace

View File

@ -1,4 +1,5 @@
// RUN: mlir-opt -convert-std-to-llvm='emit-c-wrappers=1' %s | FileCheck %s
// RUN: mlir-opt -convert-std-to-llvm %s | FileCheck %s --check-prefix=EMIT_C_ATTRIBUTE
// This tests the default memref calling convention and the emission of C
// wrappers. We don't need to separate runs because the wrapper-emission
@ -72,6 +73,7 @@ func @caller() {
}
// CHECK-LABEL: @callee
// EMIT_C_ATTRIBUTE-LABEL: @callee
func @callee(%arg0: memref<?xf32>, %arg1: index) {
%0 = load %arg0[%arg1] : memref<?xf32>
return
@ -93,3 +95,17 @@ func @callee(%arg0: memref<?xf32>, %arg1: index) {
// Forward the descriptor components to the call.
// CHECK: llvm.call @callee(%[[ALLOC]], %[[ALIGN]], %[[OFFSET]], %[[SIZE]], %[[STRIDE]], %{{.*}}) : (!llvm<"float*">, !llvm<"float*">, !llvm.i64, !llvm.i64, !llvm.i64, !llvm.i64) -> ()
// EMIT_C_ATTRIBUTE-NOT: @mlir_ciface_callee
// CHECK-LABEL: @other_callee
// EMIT_C_ATTRIBUTE-LABEL: @other_callee
func @other_callee(%arg0: memref<?xf32>, %arg1: index) attributes { llvm.emit_c_interface } {
%0 = load %arg0[%arg1] : memref<?xf32>
return
}
// CHECK: @_mlir_ciface_other_callee
// CHECK: llvm.call @other_callee
// EMIT_C_ATTRIBUTE: @_mlir_ciface_other_callee
// EMIT_C_ATTRIBUTE: llvm.call @other_callee