diff --git a/mlir/docs/ConversionToLLVMDialect.md b/mlir/docs/ConversionToLLVMDialect.md --- a/mlir/docs/ConversionToLLVMDialect.md +++ b/mlir/docs/ConversionToLLVMDialect.md @@ -374,12 +374,14 @@ ### 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 `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 diff --git a/mlir/include/mlir/IR/Function.h b/mlir/include/mlir/IR/Function.h --- a/mlir/include/mlir/IR/Function.h +++ b/mlir/include/mlir/IR/Function.h @@ -90,6 +90,12 @@ /// Returns the results types that the callable region produces when executed. ArrayRef getCallableResults() { return getType().getResults(); } + //===--------------------------------------------------------------------===// + // Behavior controlled by attributes. + //===--------------------------------------------------------------------===// + bool emitCInterface(); + void setEmitCInterface(); + private: // This trait needs access to the hooks defined below. friend class OpTrait::FunctionLike; diff --git a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp --- a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp +++ b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp @@ -942,7 +942,7 @@ auto funcOp = cast(op); auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter); - if (emitWrappers) { + if (emitWrappers || funcOp.emitCInterface()) { if (newFuncOp.isExternal()) wrapExternalFunction(rewriter, op->getLoc(), typeConverter, funcOp, newFuncOp); diff --git a/mlir/lib/IR/Function.cpp b/mlir/lib/IR/Function.cpp --- a/mlir/lib/IR/Function.cpp +++ b/mlir/lib/IR/Function.cpp @@ -188,3 +188,12 @@ BlockAndValueMapping mapper; return clone(mapper); } + +//===--------------------------------------------------------------------===// +// Attribute-supported behavior +//===--------------------------------------------------------------------===// +static constexpr StringLiteral kEmitInterfaceAttrName = "emit_c_interface"; +bool FuncOp::emitCInterface() { return !!getAttr(kEmitInterfaceAttrName); } +void FuncOp::setEmitCInterface() { + setAttr(kEmitInterfaceAttrName, UnitAttr::get(getContext())); +} diff --git a/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir b/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir --- a/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir +++ b/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir @@ -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 @@ } // CHECK-LABEL: @callee +// EMIT_C_ATTRIBUTE-LABEL: @callee func @callee(%arg0: memref, %arg1: index) { %0 = load %arg0[%arg1] : memref return @@ -93,3 +95,17 @@ // 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, %arg1: index) attributes { emit_c_interface } { + %0 = load %arg0[%arg1] : memref + 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