diff --git a/mlir/CMakeLists.txt b/mlir/CMakeLists.txt --- a/mlir/CMakeLists.txt +++ b/mlir/CMakeLists.txt @@ -73,6 +73,7 @@ # Tools needs to come late to ensure that MLIR_ALL_LIBS is populated. # Generally things after this point may depend on MLIR_ALL_LIBS or libMLIR.so. add_subdirectory(tools) +add_subdirectory(lib/CAPI) if( LLVM_INCLUDE_EXAMPLES ) add_subdirectory(examples) diff --git a/mlir/include/mlir-c/IR.h b/mlir/include/mlir-c/IR.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir-c/IR.h @@ -0,0 +1,178 @@ +//===- IR.h - C Interface for Core MLIR APIs ----------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_C_IR_H +#define MLIR_C_IR_H + +#ifdef __cplusplus +extern "C" { +#endif + +//============================================================================// +// Opaque type declarations. +// +// Types are exposed to C bindings as structs containing opaque pointers. They +// are not supposed to be inspected from C. This allows the underlying +// representaiton to change without affecting the API users. The use of structs +// instead of typedefs enables some type safety as structs are not implicitly +// convertible to each other. +// +// Most types are non-owning and only point to a fragment of the IR owned by +// somebody else. Owning types are prefixed with `_owning`, except for +// `mlir_context`, which is always owning. The result of any creation function +// is an owning struct, which can be deleted. Owning structs are not accepted +// by any "inspection" function. Instead, the caller is expected to get the +// non-owning couterpart and use it for further inspection. It is impossible +// to create an owning struct from its non-owning counterpart, which provides +// some guarantee against accidental deletion. Owning structs are expected to +// be explicitly deleted when they are no longer necessary. + +#define DEFINE_C_BIND_STRUCT(name, storage) \ + struct name { \ + storage *ptr; \ + }; \ + typedef struct name name; + +DEFINE_C_BIND_STRUCT(mlir_context, void); +DEFINE_C_BIND_STRUCT(mlir_operation, void); +DEFINE_C_BIND_STRUCT(mlir_block, void); +DEFINE_C_BIND_STRUCT(mlir_region, void); + +DEFINE_C_BIND_STRUCT(mlir_value, const void); +DEFINE_C_BIND_STRUCT(mlir_attribute, const void); +DEFINE_C_BIND_STRUCT(mlir_type, const void); +DEFINE_C_BIND_STRUCT(mlir_location, const void); + +DEFINE_C_BIND_STRUCT(mlir_owning_module, void) +DEFINE_C_BIND_STRUCT(mlir_owning_region, void); +DEFINE_C_BIND_STRUCT(mlir_owning_block, void); +DEFINE_C_BIND_STRUCT(mlir_owning_operation, void); + +#undef DEFINE_C_BIND_STRUCT + +struct mlir_named_attribute { + const char *name; + mlir_attribute attribute; +}; +typedef struct mlir_named_attribute mlir_named_attribute; + +//----------------------------------------------------------------------------// +// Context + +/// Creates or destroys the context. Transfers ownership. +mlir_context mlirContextCreate(); +void mlirContextDestroy(mlir_context context); + +//============================================================================// +// Location + +/// Creates a location in the context. +mlir_location mlirLocationCreateFileLineCol(mlir_context context, + const char *filename, unsigned line, + unsigned col); +mlir_location mlirLocationCreateUnknown(mlir_context context); + +//============================================================================// +// Module + +/// Parses a module from string. +mlir_owning_module mlirModuleParse(mlir_context context, const char *module); +/// Deletes a module. +void mlirModuleDestroy(mlir_owning_module module); + +/// Views the module as a generic operation. +mlir_operation mlirModuleGetOperation(mlir_owning_module module); + +//============================================================================// +// Operation + +//----------------------------------------------------------------------------// +// Creation and Deletion. + +/// Creates an operation. +mlir_owning_operation mlirOperationCreate( + const char *name, mlir_location location, unsigned nResults, + mlir_type *results, unsigned nOperands, mlir_value *operands, + unsigned nAttributes, mlir_named_attribute *attributes, unsigned nRegions, + mlir_owning_region *regions, unsigned nSuccessors, mlir_block *successors); + +/// Gets an inspectable operation. +mlir_operation mlirOperationGet(mlir_owning_operation op); + +/// Deletes an operation. +void mlirOperationDestroy(mlir_owning_operation op); + +//----------------------------------------------------------------------------// +// Inspection. + +int mlirOperationIsNull(mlir_operation op); +unsigned mlirOperationGetNumRegions(mlir_operation op); +mlir_region mlirOperationGetRegion(mlir_operation op, unsigned pos); +mlir_operation mlirOperationGetNextInBlock(mlir_operation op); +unsigned mlirOperationGetNumOperands(mlir_operation op); +mlir_value mlirOperationGetOperand(mlir_operation op, unsigned pos); +unsigned mlirOperationGetNumResults(mlir_operation op); +mlir_value mlirOperationGetResult(mlir_operation op, unsigned pos); +unsigned mlirOperationGetNumSuccessors(mlir_operation op); +mlir_block mlirOperationGetSuccessor(mlir_operation op, unsigned pos); +unsigned mlirOperationGetNumAttributes(mlir_operation op); +mlir_named_attribute mlirOperationGetAttribute(mlir_operation op, unsigned pos); +mlir_attribute mlirOperationGetAttributeByName(mlir_operation op, + const char *name); +void mlirOperationDump(mlir_operation op); + +//============================================================================// +// Region + +mlir_owning_region mlirRegionCreate(); +mlir_region mlirRegionGet(mlir_owning_region region); +mlir_block mlirRegionGetFirstBlock(mlir_region region); +void mlirRegionAppendBlock(mlir_region region, mlir_owning_block block); +void mlirRegionInsertBlock(mlir_region region, unsigned pos, + mlir_owning_block block); +void mlirRegionDestroy(mlir_owning_region region); +int mlirRegionIsNull(mlir_region region); + +//============================================================================// +// Block + +mlir_owning_block mlirBlockCreate(); +mlir_block mlirBlockGet(mlir_owning_block block); +mlir_block mlirBlockGetNextInRegion(mlir_block block); +mlir_operation mlirBlockGetFirstOperation(mlir_block block); +void mlirBlockAppendOperation(mlir_block block, + mlir_owning_operation operation); +void mlirBlockInsertOperation(mlir_block block, unsigned pos, + mlir_owning_operation operation); +void mlirBlockDestroy(mlir_owning_block block); +int mlirBlockIsNull(mlir_block block); + +//============================================================================// +// Value + +mlir_type mlirValueGetType(mlir_value value); + +//============================================================================// +// Type + +mlir_type mlirTypeParse(mlir_context context, const char *type); +void mlirTypeDump(mlir_type type); + +//============================================================================// +// Attribute + +mlir_attribute mlirAttributeParse(mlir_context context, const char *attr); +void mlirAttributeDump(mlir_attribute attr); +mlir_named_attribute mlirNamedAttributeCreate(const char *name, + mlir_attribute attr); + +#ifdef __cplusplus +} +#endif + +#endif // MLIR_C_IR_H diff --git a/mlir/lib/CAPI/CMakeLists.txt b/mlir/lib/CAPI/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/CMakeLists.txt @@ -0,0 +1,35 @@ +set(LLVM_OPTIONAL_SOURCES + IR.cpp + test.c +) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +add_mlir_library(MLIRCAPI + IR.cpp + + EXCLUDE_FROM_LIBMLIR + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir-c + + LINK_LIBS PUBLIC + MLIRIR + MLIRParser + MLIRSupport + ${dialect_libs} + ) + +set(LLVM_LINK_COMPONENTS + Core + Support + AsmParser + ) +add_llvm_executable(mlir-capi-test + test.c +) +llvm_update_compile_flags(mlir-capi-test) + +target_link_libraries(mlir-capi-test + PRIVATE + ${dialect_libs} + MLIRCAPI) diff --git a/mlir/lib/CAPI/IR.cpp b/mlir/lib/CAPI/IR.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/IR.cpp @@ -0,0 +1,335 @@ +//===- IR.cpp - C Interface for Core MLIR APIs ----------------------------===// +// +// 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-c/IR.h" + +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/Types.h" +#include "mlir/InitAllDialects.h" +#include "mlir/Parser.h" + +using namespace mlir; + +/* ========================================================================== */ +/* Definitions of methods for non-owning structures used in C API. */ +/* ========================================================================== */ + +#define DEFINE_C_BIND_PTR_METHODS(name, cpptype) \ + static name wrap(cpptype *cpp) { return name{cpp}; } \ + static cpptype *unwrap(name c) { return static_cast(c.ptr); } + +DEFINE_C_BIND_PTR_METHODS(mlir_context, MLIRContext) +DEFINE_C_BIND_PTR_METHODS(mlir_operation, Operation) +DEFINE_C_BIND_PTR_METHODS(mlir_block, Block) +DEFINE_C_BIND_PTR_METHODS(mlir_region, Region) + +#define DEFINE_C_BIND_METHODS(name, cpptype) \ + static name wrap(cpptype cpp) { return name{cpp.getAsOpaquePointer()}; } \ + static cpptype unwrap(name c) { return cpptype::getFromOpaquePointer(c.ptr); } + +DEFINE_C_BIND_METHODS(mlir_attribute, Attribute) +DEFINE_C_BIND_METHODS(mlir_location, Location); +DEFINE_C_BIND_METHODS(mlir_type, Type) +DEFINE_C_BIND_METHODS(mlir_value, Value) + +template +ArrayRef unwrapList(unsigned size, CTy *first, + SmallVectorImpl &storage) { + static_assert( + std::is_same())), CppTy>::value, + "incompatible C and C++ types"); + + if (size == 0) + return ArrayRef(); + + assert(first); + assert(storage.empty()); + storage.reserve(size); + for (unsigned i = 0; i < size; ++i) + storage.push_back(unwrap(*(first + i))); + return storage; +} + +/* ========================================================================== */ +/* Definitions of methods for owning structures used in C API. */ +/* ========================================================================== */ + +#define DEFINE_C_BIND_OWNED_PTR_METHODS(name, cpptype) \ + static std::unique_ptr take(name c) { \ + auto owned = std::unique_ptr(static_cast(c.ptr)); \ + c.ptr = nullptr; \ + return owned; \ + } \ + static name take(std::unique_ptr &&cpp) { \ + return name{cpp.release()}; \ + } \ + static cpptype *get(name c) { return static_cast(c.ptr); } + +DEFINE_C_BIND_OWNED_PTR_METHODS(mlir_owning_block, Block) +DEFINE_C_BIND_OWNED_PTR_METHODS(mlir_owning_region, Region) + +namespace { +struct OperationDeleter { + void operator()(Operation *op) { + if (op) + op->destroy(); + } +}; + +using OwnedOperation = std::unique_ptr; +} // end namespace + +static OwnedOperation take(mlir_owning_operation op) { + auto owned = OwnedOperation(static_cast(op.ptr)); + op.ptr = nullptr; + return owned; +} +static mlir_owning_operation take(OwnedOperation &&op) { + return mlir_owning_operation{op.release()}; +} +static Operation *get(mlir_owning_operation op) { + return static_cast(op.ptr); +} + +/* ========================================================================== */ +/* Methods for working with mlir_context. */ +/* ========================================================================== */ + +mlir_context mlirContextCreate() { + registerAllDialects(); + auto *context = new MLIRContext; + return wrap(context); +} + +void mlirContextDestroy(mlir_context context) { delete unwrap(context); } + +/* ========================================================================== */ +/* Methods for working with mlir_location. */ +/* ========================================================================== */ + +mlir_location mlirLocationCreateFileLineCol(mlir_context context, + const char *filename, unsigned line, + unsigned col) { + return wrap(FileLineColLoc::get(filename, line, col, unwrap(context))); +} + +mlir_location mlirLocationCreateUnknown(mlir_context context) { + return wrap(UnknownLoc::get(unwrap(context))); +} + +/* ========================================================================== */ +/* Methods for working with mlir_module. */ +/* ========================================================================== */ + +mlir_owning_module mlirModuleParse(mlir_context context, const char *module) { + OwningModuleRef owning = parseSourceString(module, unwrap(context)); + return mlir_owning_module{owning.release().getOperation()}; +} + +void mlirModuleDestroy(mlir_owning_module module) { + // Transfer ownership to an OwningModuleRef so that its destructor is called. + OwningModuleRef owning(cast(static_cast(module.ptr))); +} + +mlir_operation mlirModuleGetOperation(mlir_owning_module module) { + return wrap(static_cast(module.ptr)); +} + +/* ========================================================================== */ +/* Methods for working with mlir_operation. */ +/* ========================================================================== */ + +mlir_owning_operation mlirOperationCreate( + const char *name, mlir_location location, unsigned nResults, + mlir_type *results, unsigned nOperands, mlir_value *operands, + unsigned nAttributes, mlir_named_attribute *attributes, unsigned nRegions, + mlir_owning_region *regions, unsigned nSuccessors, mlir_block *successors) { + OperationState state(unwrap(location), name); + SmallVector resultStorage; + SmallVector operandStorage; + SmallVector successorStorage; + state.addTypes(unwrapList(nResults, results, resultStorage)); + state.addOperands(unwrapList(nOperands, operands, operandStorage)); + state.addSuccessors(unwrapList(nSuccessors, successors, successorStorage)); + + for (unsigned i = 0; i < nAttributes; ++i) + state.addAttribute(attributes[i].name, unwrap(attributes[i].attribute)); + + for (unsigned i = 0; i < nRegions; ++i) + state.addRegion(take(regions[i])); + + return mlir_owning_operation{Operation::create(state)}; +} + +mlir_operation mlirOperationGet(mlir_owning_operation op) { + return wrap(get(op)); +} + +void mlirOperationDestroy(mlir_owning_operation op) { + // Transfer ownership to a temporary to call the destructor (custom deleter). + take(op); +} + +int mlirOperationIsNull(mlir_operation op) { return unwrap(op) == nullptr; } + +unsigned mlirOperationGetNumRegions(mlir_operation op) { + return unwrap(op)->getNumRegions(); +} + +mlir_region mlirOperationGetRegion(mlir_operation op, unsigned pos) { + return wrap(&unwrap(op)->getRegion(pos)); +} + +mlir_operation mlirOperationGetNextInBlock(mlir_operation op) { + return wrap(unwrap(op)->getNextNode()); +} + +unsigned mlirOperationGetNumOperands(mlir_operation op) { + return unwrap(op)->getNumOperands(); +} + +mlir_value mlirOperationGetOperand(mlir_operation op, unsigned pos) { + return wrap(unwrap(op)->getOperand(pos)); +} + +unsigned mlirOperationGetNumResults(mlir_operation op) { + return unwrap(op)->getNumResults(); +} + +mlir_value mlirOperationGetResult(mlir_operation op, unsigned pos) { + return wrap(unwrap(op)->getResult(pos)); +} + +unsigned mlirOperationGetNumSuccessors(mlir_operation op) { + return unwrap(op)->getNumSuccessors(); +} + +mlir_block mlirOperationGetSuccessor(mlir_operation op, unsigned pos) { + return wrap(unwrap(op)->getSuccessor(pos)); +} + +unsigned mlirOperationGetNumAttributes(mlir_operation op) { + return unwrap(op)->getAttrs().size(); +} + +mlir_named_attribute mlirOperationGetAttribute(mlir_operation op, + unsigned pos) { + NamedAttribute attr = unwrap(op)->getAttrs()[pos]; + return mlir_named_attribute{attr.first.c_str(), wrap(attr.second)}; +} + +mlir_attribute mlirOperationGetAttributeByName(mlir_operation op, + const char *name) { + return wrap(unwrap(op)->getAttr(name)); +} + +void mlirOperationDump(mlir_operation op) { return unwrap(op)->dump(); } + +/* ========================================================================== */ +/* Methods for working with mlir_region. */ +/* ========================================================================== */ + +mlir_owning_region mlirRegionCreate() { + return take(std::make_unique()); +} + +mlir_region mlirRegionGet(mlir_owning_region region) { + return wrap(get(region)); +} + +mlir_block mlirRegionGetFirstBlock(mlir_region region) { + Region *cppRegion = unwrap(region); + if (cppRegion->empty()) + return wrap(static_cast(nullptr)); + return wrap(&cppRegion->front()); +} + +void mlirRegionAppendBlock(mlir_region region, mlir_owning_block block) { + unwrap(region)->push_back(take(block).release()); +} + +void mlirRegionInsertBlock(mlir_region region, unsigned pos, + mlir_owning_block block) { + auto &blockList = unwrap(region)->getBlocks(); + blockList.insert(std::next(blockList.begin(), pos), take(block).release()); +} + +void mlirRegionDestroy(mlir_owning_region region) { + delete static_cast(region.ptr); +} + +int mlirRegionIsNull(mlir_region region) { return unwrap(region) == nullptr; } + +/* ========================================================================== */ +/* Methods for working with mlir_block. */ +/* ========================================================================== */ + +mlir_owning_block mlirBlockCreate() { return take(std::make_unique()); } + +mlir_block mlirBlockGet(mlir_owning_block block) { return wrap(get(block)); } + +mlir_block mlirBlockGetNextInRegion(mlir_block block) { + return wrap(unwrap(block)->getNextNode()); +} + +mlir_operation mlirBlockGetFirstOperation(mlir_block block) { + Block *cppBlock = unwrap(block); + if (cppBlock->empty()) + return wrap(static_cast(nullptr)); + return wrap(&cppBlock->front()); +} + +void mlirBlockAppendOperation(mlir_block block, + mlir_owning_operation operation) { + unwrap(block)->push_back(take(operation).release()); +} + +void mlirBlockInsertOperation(mlir_block block, unsigned pos, + mlir_owning_operation operation) { + auto &opList = unwrap(block)->getOperations(); + opList.insert(std::next(opList.begin(), pos), take(operation).release()); +} + +void mlirBlockDestroy(mlir_owning_block block) { take(block); } + +int mlirBlockIsNull(mlir_block block) { return unwrap(block) == nullptr; } + +/* ========================================================================== */ +/* Methods for working with mlir_value. */ +/* ========================================================================== */ + +mlir_type mlirValueGetType(mlir_value value) { + return wrap(unwrap(value).getType()); +} + +/* ========================================================================== */ +/* Methods for working with mlir_type. */ +/* ========================================================================== */ + +mlir_type mlirTypeParse(mlir_context context, const char *type) { + return wrap(mlir::parseType(type, unwrap(context))); +} + +void mlirTypeDump(mlir_type type) { unwrap(type).dump(); } + +/* ========================================================================== */ +/* Methods for working with mlir_attribute. */ +/* ========================================================================== */ + +mlir_attribute mlirAttributeParse(mlir_context context, const char *attr) { + return wrap(mlir::parseAttribute(attr, unwrap(context))); +} + +void mlirAttributeDump(mlir_attribute attr) { unwrap(attr).dump(); } + +mlir_named_attribute mlirNamedAttributeCreate(const char *name, + mlir_attribute attr) { + return mlir_named_attribute{name, attr}; +} diff --git a/mlir/lib/CAPI/test.c b/mlir/lib/CAPI/test.c new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/test.c @@ -0,0 +1,64 @@ +//===- test.c - Simple test of C APIs -------------------------------------===// +// +// 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-c/IR.h" + +#include +#include + +const char *textModule = "func @twice(%arg0: f32) -> f32 {" + " %0 = addf %arg0, %arg0 : f32" + " return %0 : f32" + "}"; + +int main() { + mlir_context ctx = mlirContextCreate(); + mlir_owning_module owningModule = mlirModuleParse(ctx, textModule); + mlir_operation module = mlirModuleGetOperation(owningModule); + + assert(mlirOperationGetNumRegions(module) == 1); + mlir_region moduleBody = mlirOperationGetRegion(module, 0); + mlir_block moduleBodyBlock = mlirRegionGetFirstBlock(moduleBody); + mlir_operation func = mlirBlockGetFirstOperation(moduleBodyBlock); + + mlir_attribute name = mlirOperationGetAttributeByName(func, "sym_name"); + fprintf(stderr, "Found function: "); + mlirAttributeDump(name); + mlir_attribute typeAttr = mlirOperationGetAttributeByName(func, "type"); + fprintf(stderr, " with type "); + mlirAttributeDump(typeAttr); + fprintf(stderr, "\n"); + + mlir_region funcBody = mlirOperationGetRegion(func, 0); + mlir_block funcBodyBlock = mlirRegionGetFirstBlock(funcBody); + mlir_operation op = mlirBlockGetFirstOperation(funcBodyBlock); + while (!mlirOperationIsNull(op)) { + fprintf(stderr, "Found operation: "); + mlirOperationDump(op); + fprintf(stderr, "\n"); + op = mlirOperationGetNextInBlock(op); + } + + mlir_owning_block otherBlockOwned = mlirBlockCreate(); + mlir_block otherBlock = mlirBlockGet(otherBlockOwned); + mlirRegionAppendBlock(funcBody, otherBlockOwned); + + mlir_attribute attr = mlirAttributeParse(ctx, "affine_map<(i) -> (i,i)>"); + mlir_named_attribute named = mlirNamedAttributeCreate("some_map", attr); + + mlir_location loc = mlirLocationCreateUnknown(ctx); + mlir_owning_operation newOp = + mlirOperationCreate("std.br", loc, /*nResults=*/0, NULL, /*nOperands=*/0, + NULL, /*nAttributes*/ 1, &named, /*nRegions=*/0, NULL, + /*nSuccessors*/ 1, &otherBlock); + mlirBlockAppendOperation(otherBlock, newOp); + + mlirOperationDump(module); + + return 0; +}