diff --git a/mlir/include/mlir-c/Conversion.h b/mlir/include/mlir-c/Conversion.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir-c/Conversion.h @@ -0,0 +1,22 @@ +//===-- mlir-c/Conversion.h - Conversion passes initialization ----*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header declares the registration and creation method for conversion +// passes. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_C_CONVERSIONS_H +#define MLIR_C_CONVERSIONS_H + +#include "mlir-c/Support.h" + +#include "mlir/Conversion/Passes.capi.h.inc" + +#endif // MLIR_C_CONVERSIONS_H diff --git a/mlir/include/mlir-c/ExecutionEngine.h b/mlir/include/mlir-c/ExecutionEngine.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir-c/ExecutionEngine.h @@ -0,0 +1,63 @@ +//===-- mlir-c/ExecutionEngine.h - Execution engine management ---*- C -*-====// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header provides basic access to the MLIR JIT. This is minimalist and +// experimental at the moment. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_C_EXECUTIONENGINE_H +#define MLIR_C_EXECUTIONENGINE_H + +#include "mlir-c/IR.h" +#include "mlir-c/Support.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFINE_C_API_STRUCT(name, storage) \ + struct name { \ + storage *ptr; \ + }; \ + typedef struct name name + +DEFINE_C_API_STRUCT(MlirExecutionEngine, void); + +#undef DEFINE_C_API_STRUCT + +/// Creates an ExecutionEngine for the provided ModuleOp. The ModuleOp is +/// expected to be "translatable" to LLVM IR (only contains operations in +/// dialects that implement the `LLVMTranslationDialectInterface`). The module +/// ownership stays with the client and can be destroyed as soon as the call +/// returns. +/// TODO: figure out options (optimization level, etc.). +MLIR_CAPI_EXPORTED MlirExecutionEngine mlirExecutionEngineCreate(MlirModule op); + +/// Destroy an ExecutionEngine instance. +MLIR_CAPI_EXPORTED void mlirExecutionEngineDestroy(MlirExecutionEngine jit); + +/// Checks whether an execution engine is null. +static inline bool mlirExecutionEngineIsNull(MlirExecutionEngine jit) { + return !jit.ptr; +} + +/// Invoke a native function in the execution engine by name with the arguments +/// and result of the invoked function passed as an array of pointers. The +/// function must have been tagged with the `llvm.emit_c_interface` attribute. +/// Returns a failure if the execution fails for any reason (the function name +/// can't be resolved for instance). +MLIR_CAPI_EXPORTED MlirLogicalResult mlirExecutionEngineInvokePacked( + MlirExecutionEngine jit, MlirStringRef name, void **arguments); + +#ifdef __cplusplus +} +#endif + +#endif // EXECUTIONENGINE_H diff --git a/mlir/include/mlir-c/Registration.h b/mlir/include/mlir-c/Registration.h --- a/mlir/include/mlir-c/Registration.h +++ b/mlir/include/mlir-c/Registration.h @@ -57,6 +57,9 @@ /// TODO: Remove this function once the real registration API is finished. MLIR_CAPI_EXPORTED void mlirRegisterAllDialects(MlirContext context); +/// Register all translations to LLVM IR for dialects that can support it. +MLIR_CAPI_EXPORTED void mlirRegisterAllLLVMTranslations(MlirContext context); + #ifdef __cplusplus } #endif diff --git a/mlir/include/mlir/CAPI/ExecutionEngine.h b/mlir/include/mlir/CAPI/ExecutionEngine.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/CAPI/ExecutionEngine.h @@ -0,0 +1,24 @@ +//===- IR.h - C API Utils for Core MLIR classes -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains declarations of implementation details of the C API for +// core MLIR classes. This file should not be included from C++ code other than +// C API implementation nor from C code. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_CAPI_EXECUTIONENGINE_H +#define MLIR_CAPI_EXECUTIONENGINE_H + +#include "mlir-c/ExecutionEngine.h" +#include "mlir/CAPI/Wrap.h" +#include "mlir/ExecutionEngine/ExecutionEngine.h" + +DEFINE_C_API_PTR_METHODS(MlirExecutionEngine, mlir::ExecutionEngine) + +#endif // MLIR_CAPI_EXECUTIONENGINE_H diff --git a/mlir/include/mlir/Conversion/CMakeLists.txt b/mlir/include/mlir/Conversion/CMakeLists.txt --- a/mlir/include/mlir/Conversion/CMakeLists.txt +++ b/mlir/include/mlir/Conversion/CMakeLists.txt @@ -1,6 +1,8 @@ set(LLVM_TARGET_DEFINITIONS Passes.td) mlir_tablegen(Passes.h.inc -gen-pass-decls -name Conversion) +mlir_tablegen(Passes.capi.h.inc -gen-pass-capi-header --prefix Conversion) +mlir_tablegen(Passes.capi.cpp.inc -gen-pass-capi-impl --prefix Conversion) add_public_tablegen_target(MLIRConversionPassIncGen) add_mlir_doc(Passes -gen-pass-doc ConversionPasses ./) diff --git a/mlir/lib/CAPI/CMakeLists.txt b/mlir/lib/CAPI/CMakeLists.txt --- a/mlir/lib/CAPI/CMakeLists.txt +++ b/mlir/lib/CAPI/CMakeLists.txt @@ -1,6 +1,8 @@ +add_subdirectory(Dialect) +add_subdirectory(Conversion) +add_subdirectory(ExecutionEngine) add_subdirectory(IR) add_subdirectory(Registration) -add_subdirectory(Dialect) add_subdirectory(Transforms) diff --git a/mlir/lib/CAPI/Conversion/CMakeLists.txt b/mlir/lib/CAPI/Conversion/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/Conversion/CMakeLists.txt @@ -0,0 +1,7 @@ +get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +add_mlir_public_c_api_library(MLIRCAPIConversion + Passes.cpp + + LINK_LIBS PUBLIC + ${conversion_libs} +) diff --git a/mlir/lib/CAPI/Conversion/Passes.cpp b/mlir/lib/CAPI/Conversion/Passes.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/Conversion/Passes.cpp @@ -0,0 +1,26 @@ +//===- Conversion.cpp - C API for Conversion Passes -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Conversion/Passes.h" +#include "mlir/CAPI/Pass.h" +#include "mlir/Pass/Pass.h" + +// Must include the declarations as they carry important visibility attributes. +#include "mlir/Conversion/Passes.capi.h.inc" + +using namespace mlir; + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mlir/Conversion/Passes.capi.cpp.inc" + +#ifdef __cplusplus +} +#endif diff --git a/mlir/lib/CAPI/ExecutionEngine/CMakeLists.txt b/mlir/lib/CAPI/ExecutionEngine/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/ExecutionEngine/CMakeLists.txt @@ -0,0 +1,7 @@ +# Main API shared library. +add_mlir_public_c_api_library(MLIRCEXECUTIONENGINE + ExecutionEngine.cpp + + LINK_LIBS PUBLIC + MLIRExecutionEngine +) diff --git a/mlir/lib/CAPI/ExecutionEngine/ExecutionEngine.cpp b/mlir/lib/CAPI/ExecutionEngine/ExecutionEngine.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/ExecutionEngine/ExecutionEngine.cpp @@ -0,0 +1,49 @@ +//===- ExecutionEngine.cpp - C API for MLIR JIT ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/CAPI/ExecutionEngine.h" +#include "mlir/CAPI/IR.h" +#include "mlir/CAPI/Support.h" +#include "llvm/Support/TargetSelect.h" + +using namespace mlir; + +#ifdef __cplusplus +extern "C" { +#endif + +MlirExecutionEngine mlirExecutionEngineCreate(MlirModule op) { + static bool init_once = [] { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + return true; + }(); + (void)init_once; + + auto jitOrError = ExecutionEngine::create(unwrap(op)); + if (!jitOrError) { + consumeError(jitOrError.takeError()); + return MlirExecutionEngine{nullptr}; + } + return wrap(jitOrError->release()); +} + +MlirLogicalResult mlirExecutionEngineInvokePacked(MlirExecutionEngine jit, + MlirStringRef name, + void **arguments) { + const std::string ifaceName = ("_mlir_ciface_" + unwrap(name)).str(); + llvm::Error error = unwrap(jit)->invokePacked( + ifaceName, MutableArrayRef{arguments, (size_t)0}); + if (error) + return wrap(failure()); + return wrap(success()); +} + +#ifdef __cplusplus +} +#endif diff --git a/mlir/lib/CAPI/Registration/Registration.cpp b/mlir/lib/CAPI/Registration/Registration.cpp --- a/mlir/lib/CAPI/Registration/Registration.cpp +++ b/mlir/lib/CAPI/Registration/Registration.cpp @@ -10,9 +10,14 @@ #include "mlir/CAPI/IR.h" #include "mlir/InitAllDialects.h" +#include "mlir/Target/LLVMIR.h" void mlirRegisterAllDialects(MlirContext context) { mlir::registerAllDialects(*unwrap(context)); // TODO: we may not want to eagerly load here. unwrap(context)->loadAllAvailableDialects(); } + +void mlirRegisterAllLLVMTranslations(MlirContext context) { + mlir::registerLLVMDialectTranslation(*unwrap(context)); +} diff --git a/mlir/test/CAPI/CMakeLists.txt b/mlir/test/CAPI/CMakeLists.txt --- a/mlir/test/CAPI/CMakeLists.txt +++ b/mlir/test/CAPI/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_OPTIONAL_SOURCES + execution_engine.c ir.c pass.c ) @@ -28,3 +29,16 @@ PRIVATE MLIRPublicAPI ) + +add_llvm_executable(mlir-capi-execution-engine-test + execution_engine.c +DEPENDS + MLIRConversionPassIncGen + ) +llvm_update_compile_flags(mlir-capi-execution-engine-test) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +target_link_libraries(mlir-capi-execution-engine-test + PRIVATE + MLIRPublicAPI + ) diff --git a/mlir/test/CAPI/execution_engine.c b/mlir/test/CAPI/execution_engine.c new file mode 100644 --- /dev/null +++ b/mlir/test/CAPI/execution_engine.c @@ -0,0 +1,81 @@ +//===- execution_engine.c - Test for the C bindings for the MLIR JIT-------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +/* RUN: mlir-capi-execution-engine-test 2>&1 | FileCheck %s + */ + +#include "mlir-c/Conversion.h" +#include "mlir-c/ExecutionEngine.h" +#include "mlir-c/IR.h" +#include "mlir-c/Registration.h" + +#include +#include +#include +#include +#include + +void lowerModuleToLLVM(MlirContext ctx, MlirModule module) { + MlirPassManager pm = mlirPassManagerCreate(ctx); + mlirPassManagerAddOwnedPass(pm, mlirCreateConversionConvertStandardToLLVM()); + MlirLogicalResult status = mlirPassManagerRun(pm, module); + if (mlirLogicalResultIsFailure(status)) { + fprintf(stderr, "Unexpected failure running pass pipeline\n"); + exit(2); + } + mlirPassManagerDestroy(pm); +} + +// CHECK-LABEL: Running test 'testSimpleExecution' +void testSimpleExecution() { + MlirContext ctx = mlirContextCreate(); + mlirRegisterAllDialects(ctx); + MlirModule module = mlirModuleCreateParse( + ctx, mlirStringRefCreateFromCString( + // clang-format off +"module { \n" +" func @add(%arg0 : i32) -> i32 attributes { llvm.emit_c_interface } { \n" +" %res = std.addi %arg0, %arg0 : i32 \n" +" return %res : i32 \n" +" } \n" +"}")); + // clang-format on + lowerModuleToLLVM(ctx, module); + mlirOperationDump(mlirModuleGetOperation(module)); + mlirRegisterAllLLVMTranslations(ctx); + MlirExecutionEngine jit = mlirExecutionEngineCreate(module); + if (mlirExecutionEngineIsNull(jit)) { + fprintf(stderr, "Execution engine creation failed"); + exit(2); + } + int input = 42; + int result = -1; + void *args[2] = {&input, &result}; + if (mlirLogicalResultIsFailure(mlirExecutionEngineInvokePacked( + jit, mlirStringRefCreateFromCString("add"), args))) { + fprintf(stderr, "Execution engine creation failed"); + abort(); + } + // CHECK: Input: 42 Result: 84 + printf("Input: %d Result: %d\n", input, result); + mlirModuleDestroy(module); + mlirContextDestroy(ctx); +} + +int main() { + +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) +#define TEST(test) \ + printf("Running test '" STRINGIFY(test) "'\n"); \ + test(); + + TEST(testSimpleExecution); + return 0; +} diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt --- a/mlir/test/CMakeLists.txt +++ b/mlir/test/CMakeLists.txt @@ -42,6 +42,7 @@ set(MLIR_TEST_DEPENDS FileCheck count not + mlir-capi-execution-engine-test mlir-capi-ir-test mlir-capi-pass-test mlir-cpu-runner