diff --git a/mlir/CMakeLists.txt b/mlir/CMakeLists.txt --- a/mlir/CMakeLists.txt +++ b/mlir/CMakeLists.txt @@ -93,6 +93,8 @@ add_subdirectory(include/mlir) add_subdirectory(lib) +# C API needs all dialects for registration, but should be built before tests. +add_subdirectory(lib/CAPI) if (MLIR_INCLUDE_TESTS) add_definitions(-DMLIR_INCLUDE_TESTS) add_subdirectory(unittests) diff --git a/mlir/docs/CAPI.md b/mlir/docs/CAPI.md new file mode 100644 --- /dev/null +++ b/mlir/docs/CAPI.md @@ -0,0 +1,124 @@ +# MLIR C API + +**Current status: Under development, API unstable, built by default.** + +## Design + +Many languages can interoperate with C but have a harder time with C++ due to +name mangling and memory model differences. Although the C API for MLIR can be +used directly from C, it is primarily intended to be wrapped in higher-level +language- or library-specific constructs. Therefore the API tends towards +simplicity and feature minimalism. + +**Note:** while the C API is expected to be more stable than C++ API, it +currently offers no stability guarantees. + +### Scope + +The API is provided for core IR components (attributes, blocks, operations, +regions, types, values), Passes and some fundamental type and attribute kinds. +The core IR API is intentionally low-level, e.g. exposes a plain list of +operation's operands and attributes without attempting to assign "semantic" +names to them. Users of specific dialects are expected to wrap the core API in a +dialect-specific way, for example, by implementing an ODS backend. + +### Object Model + +Core IR components are exposed as opaque _handles_ to an IR object existing in +C++. They are not intended to be inspected by the API users (and, in many cases, +cannot be meaningfully inspected). Instead the users are expected to pass +handles to the appropriate manipulation functions. + +The handle _may or may not_ own the underlying object. + +### Naming Convention and Ownership Model + +All objects are prefixed with `Mlir`. They are typedefs and should be used +without `struct`. + +All functions are prefixed with `mlir`. + +Functions primarily operating on an instance of `MlirX` are prefixed with +`mlirX`. They take the instance being acted upon as their first argument (except +for creation functions). For example, `mlirOperationGetNumOperands` inspects an +`MlirOperation`, which it takes as its first operand. + +The *ownership* model is encoded in the naming convention as follows. + +- By default, the ownership is not transerred. +- Functions that tranfer the ownership of the result to the caller can be in + one of two forms: + * functions that create a new object have the name `mlirXCreate<...>`, for + example, `mlirOperationCreate`; + * functions that detach an object from a parent object have the name + `mlirYTake<...>`, for example `mlirOperationStateTakeRegion`. +- Functions that take ownership of some of their arguments have the form + `mlirY<...>OwnedX<...>` where `X` can refer to the type or any other + sufficiently unique description of the argument, the ownership of which will + be taken by the callee, for example `mlirRegionAppendOwnedBlock`. +- Functions that create an object by default do not transfer its ownership to + the caller, i.e. one of other objects passed in as an argument retains the + ownership, they have the form `mlirX<...>Get`. For example, + `mlirTypeParseGet`. +- Functions that destroy an object owned by the caller are of the form + `mlirXDestroy`. + +If the code owns an object, it is responsible for destroying the object when it +is no longer necessary. If an object that owns other objects is destroyed, any +handles to those objects become invalid. Note that types and attributes are +owned by the `MlirContext` in which they were created. + +### Nullity + +A handle may refer to a _null_ object. It is the responsibility of the caller to +check if an object is null by using `MlirXIsNull(MlirX)`. API functions do _not_ +expect null objects as arguments unless explicitly stated otherwise. API +functions _may_ return null objects. + +### Common Patterns + +The API adopts the following patterns for recurrent functionality in MLIR. + +#### Indexed Components + +An object has an _indexed component_ if it has fields accessible using a +zero-based contiguous integer index, typically arrays. For example, an +`MlirBlock` has its arguments as a indexed component. An object may have several +such components. For example, an `MlirOperation` has attributes, operands, +regions, results and successors. + +For indexed components, the following pair of functions is provided. + +- `unsigned mlirXGetNums(MlirX)` returns the upper bound on the index. +- `MlirY mlirXGet(MlirX, unsigned pos)` returns 'pos'-th subobject. + +Note that the name of subobject in the function does not necessarily match the +type of the subobject. For example, `mlirOperationGetOperand` returns a +`MlirValue`. + +#### Iterable Components + +An object has an _iterable component_ if it has iterators accessing its fields +in some order other than integer indexing, typically linked lists. For example, +an `MlirBlock` has an iterable list of operations it contains. An object may +have several iterable components. + +For iterable components, the following triple of functions is provided. + +- `MlirY mlirXGetFirst(MlirX)` returns the first subobject in the list. +- `MlirY mlirYGetNextIn(MlirY)` returns the next subobject in the list that + contains the given object, or a null object if the given object is the last + in this list. +- `int mlirYIsNull(MlirY)` returns 1 if the given object is null. + +Note that the name of subobject in the function may or may not match its type. + +This approach enables one to iterate as follows. + +```c++ +MlirY iter; +for (iter = mlirXGetFirst(x); !mlirYIsNull(iter); + iter = mlirYGetNextIn(iter)) { + /* User 'iter'. */ +} +``` 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,298 @@ +/*===-- mlir-c/IR.h - C API to Core MLIR IR 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 header declares the C interface to MLIR core IR classes. *| +|* *| +|* Many exotic languages can interoperate with C code but have a harder time *| +|* with C++ due to name mangling. So in addition to C, this interface enables *| +|* tools written in such languages. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#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 + * representation 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. + * + * Instaces of these types may or may not own the underlying object (most often + * only point to an IR fragment without owning it). The ownership semantics is + * defined by how an instance of the type was obtained. + */ +/*============================================================================*/ + +#define DEFINE_C_API_STRUCT(name, storage) \ + struct name { \ + storage *ptr; \ + }; \ + typedef struct name name + +DEFINE_C_API_STRUCT(MlirContext, void); +DEFINE_C_API_STRUCT(MlirOperation, void); +DEFINE_C_API_STRUCT(MlirBlock, void); +DEFINE_C_API_STRUCT(MlirRegion, void); + +DEFINE_C_API_STRUCT(MlirValue, const void); +DEFINE_C_API_STRUCT(MlirAttribute, const void); +DEFINE_C_API_STRUCT(MlirType, const void); +DEFINE_C_API_STRUCT(MlirLocation, const void); +DEFINE_C_API_STRUCT(MlirModule, const void); + +#undef DEFINE_C_API_STRUCT + +/** Named MLIR attribute. + * + * A named attribute is essentially a (name, attrbute) pair where the name is + * a string. + */ +struct MlirNamedAttribute { + const char *name; + MlirAttribute attribute; +}; +typedef struct MlirNamedAttribute MlirNamedAttribute; + +/*============================================================================*/ +/* Context API. */ +/*============================================================================*/ + +/** Creates an MLIR context and transfers its ownership to the caller. */ +MlirContext mlirContextCreate(); + +/** Takes an MLIR context owned by the caller and destroys it. */ +void mlirContextDestroy(MlirContext context); + +/*============================================================================*/ +/* Location API. */ +/*============================================================================*/ + +/** Creates an File/Line/Column location owned by the given context. */ +MlirLocation mlirLocationFileLineColGet(MlirContext context, + const char *filename, unsigned line, + unsigned col); + +/** Creates a location with unknown position owned by the given context. */ +MlirLocation mlirLocationUnknownGet(MlirContext context); + +/*============================================================================*/ +/* Module API. */ +/*============================================================================*/ + +/** Creates a new, empty module and transfers ownership to the caller. */ +MlirModule mlirModuleCreateEmpty(MlirLocation location); + +/** Parses a module from the string and transfers ownership to the caller. */ +MlirModule mlirModuleCreateParse(MlirContext context, const char *module); + +/** Takes a module owned by the caller and deletes it. */ +void mlirModuleDestroy(MlirModule module); + +/** Views the module as a generic operation. */ +MlirOperation mlirModuleGetOperation(MlirModule module); + +/*============================================================================*/ +/* Operation state. */ +/*============================================================================*/ + +/** An auxiliary class for constructing operations. + * + * This class contains all the information necessary to construct the operation. + * It owns the MlirRegions it has pointers to and does not own anything else. + * By default, the state can be constructed from a name and location, the latter + * being also used to access the context, and has no other components. These + * components can be added progressively until the operation is constructed. + * Users are not expected to rely on the internals of this class and should use + * mlirOperationState* functions instead. + */ +struct MlirOperationState { + const char *name; + MlirLocation location; + unsigned nResults; + MlirType *results; + unsigned nOperands; + MlirValue *operands; + unsigned nRegions; + MlirRegion *regions; + unsigned nSuccessors; + MlirBlock *successors; + unsigned nAttributes; + MlirNamedAttribute *attributes; +}; +typedef struct MlirOperationState MlirOperationState; + +/** Constructs an operation state from a name and a location. */ +MlirOperationState mlirOperationStateGet(const char *name, MlirLocation loc); + +/** Adds a list of components to the operation state. */ +void mlirOperationStateAddResults(MlirOperationState *state, unsigned n, + MlirType *results); +void mlirOperationStateAddOperands(MlirOperationState *state, unsigned n, + MlirValue *operands); +void mlirOperationStateAddOwnedRegions(MlirOperationState *state, unsigned n, + MlirRegion *regions); +void mlirOperationStateAddSuccessors(MlirOperationState *state, unsigned n, + MlirBlock *successors); +void mlirOperationStateAddAttributes(MlirOperationState *state, unsigned n, + MlirNamedAttribute *attributes); + +/*============================================================================*/ +/* Operation API. */ +/*============================================================================*/ + +/** Creates an operation and transfers ownership to the caller. */ +MlirOperation mlirOperationCreate(const MlirOperationState *state); + +/** Takes an operation owned by the caller and destroys it. */ +void mlirOperationDestroy(MlirOperation op); + +/** Checks whether the underlying operation is null. */ +int mlirOperationIsNull(MlirOperation op); + +/** Returns the number of regions attached to the given operation. */ +unsigned mlirOperationGetNumRegions(MlirOperation op); + +/** Returns `pos`-th region attached to the operation. */ +MlirRegion mlirOperationGetRegion(MlirOperation op, unsigned pos); + +/** Returns an operation immediately following the given operation it its + * enclosing block. */ +MlirOperation mlirOperationGetNextInBlock(MlirOperation op); + +/** Returns the number of operands of the operation. */ +unsigned mlirOperationGetNumOperands(MlirOperation op); + +/** Returns `pos`-th operand of the operation. */ +MlirValue mlirOperationGetOperand(MlirOperation op, unsigned pos); + +/** Returns the number of results of the operation. */ +unsigned mlirOperationGetNumResults(MlirOperation op); + +/** Returns `pos`-th result of the operation. */ +MlirValue mlirOperationGetResult(MlirOperation op, unsigned pos); + +/** Returns the number of successor blocks of the operation. */ +unsigned mlirOperationGetNumSuccessors(MlirOperation op); + +/** Returns `pos`-th successor of the operation. */ +MlirBlock mlirOperationGetSuccessor(MlirOperation op, unsigned pos); + +/** Returns the number of attributes attached to the operation. */ +unsigned mlirOperationGetNumAttributes(MlirOperation op); + +/** Return `pos`-th attribute of the operation. */ +MlirNamedAttribute mlirOperationGetAttribute(MlirOperation op, unsigned pos); + +/** Returns an attrbute attached to the operation given its name. */ +MlirAttribute mlirOperationGetAttributeByName(MlirOperation op, + const char *name); +void mlirOperationDump(MlirOperation op); + +/*============================================================================*/ +/* Region API. */ +/*============================================================================*/ + +/** Creates a new empty region and transfers ownership to the caller. */ +MlirRegion mlirRegionCreate(); + +/** Takes a region owned by the caller and destroys it. */ +void mlirRegionDestroy(MlirRegion region); + +/** Checks whether a region is null. */ +int mlirRegionIsNull(MlirRegion region); + +/** Gets the first block in the region. */ +MlirBlock mlirRegionGetFirstBlock(MlirRegion region); + +/** Takes a block owned by the caller and appends it to the given region. */ +void mlirRegionAppendOwnedBlock(MlirRegion region, MlirBlock block); + +/** Takes a block owned by the caller and inserts it at `pos` to the given + * region. */ +void mlirRegionInsertOwnedBlock(MlirRegion region, unsigned pos, + MlirBlock block); + +/*============================================================================*/ +/* Block API. */ +/*============================================================================*/ + +/** Creates a new empty block with the given argument types and transfers + * ownership to the caller. */ +MlirBlock mlirBlockCreate(unsigned nArgs, MlirType *args); + +/** Takes a block owned by the caller and destroys it. */ +void mlirBlockDestroy(MlirBlock block); + +/** Checks whether a block is null. */ +int mlirBlockIsNull(MlirBlock block); + +/** Returns the block immediately following the given block in its parent + * region. */ +MlirBlock mlirBlockGetNextInRegion(MlirBlock block); + +/** Returns the first operation in the block. */ +MlirOperation mlirBlockGetFirstOperation(MlirBlock block); + +/** Takes an operation owned by the caller and appends it to the block. */ +void mlirBlockAppendOwnedOperation(MlirBlock block, MlirOperation operation); + +/** Takes an operation owned by the caller and inserts it as `pos` to the block. + */ +void mlirBlockInsertOwnedOperation(MlirBlock block, unsigned pos, + MlirOperation operation); + +/** Returns the number of arguments of the block. */ +unsigned mlirBlockGetNumArguments(MlirBlock block); + +/** Returns `pos`-th argument of the block. */ +MlirValue mlirBlockGetArgument(MlirBlock block, unsigned pos); + +/*============================================================================*/ +/* Value API. */ +/*============================================================================*/ + +/** Returns the type of the value. */ +MlirType mlirValueGetType(MlirValue value); + +/*============================================================================*/ +/* Type API. */ +/*============================================================================*/ + +/** Parses a type. The type is owned by the context. */ +MlirType mlirTypeParseGet(MlirContext context, const char *type); + +/** Prints the type to the standard error stream. */ +void mlirTypeDump(MlirType type); + +/*============================================================================*/ +/* Attribute API. */ +/*============================================================================*/ + +/** Parses an attribute. The attribute is owned by the context. */ +MlirAttribute mlirAttributeParseGet(MlirContext context, const char *attr); + +/** Prints the attrbute to the standard error stream. */ +void mlirAttributeDump(MlirAttribute attr); + +/** Associates an attribute with the name. Takes ownership of neither. */ +MlirNamedAttribute mlirNamedAttributeGet(const char *name, MlirAttribute attr); + +#ifdef __cplusplus +} +#endif + +#endif // MLIR_C_IR_H diff --git a/mlir/include/mlir-c/Registration.h b/mlir/include/mlir-c/Registration.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir-c/Registration.h @@ -0,0 +1,26 @@ +/*===-- mlir-c/Registration.h - Registration functions for MLIR ---*- 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_REGISTRATION_H +#define MLIR_C_REGISTRATION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Registers all dialects known to core MLIR with the system. This must be + * called before creating an MlirContext if it needs access to the registered + * dialects. */ +void mlirRegisterAllDialects(); + +#ifdef __cplusplus +} +#endif + +#endif // MLIR_C_REGISTRATION_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,2 @@ +add_subdirectory(IR) +add_subdirectory(Registration) diff --git a/mlir/lib/CAPI/IR/CMakeLists.txt b/mlir/lib/CAPI/IR/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/IR/CMakeLists.txt @@ -0,0 +1,14 @@ +# Main API. +add_mlir_library(MLIRCAPIIR + IR.cpp + + EXCLUDE_FROM_LIBMLIR + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir-c + + LINK_LIBS PUBLIC + MLIRIR + MLIRParser + MLIRSupport + ) diff --git a/mlir/lib/CAPI/IR/IR.cpp b/mlir/lib/CAPI/IR/IR.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/IR/IR.cpp @@ -0,0 +1,341 @@ +//===- 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/Parser.h" + +using namespace mlir; + +/* ========================================================================== */ +/* Definitions of methods for non-owning structures used in C API. */ +/* ========================================================================== */ + +#define DEFINE_C_API_PTR_METHODS(name, cpptype) \ + static name wrap(cpptype *cpp) { return name{cpp}; } \ + static cpptype *unwrap(name c) { return static_cast(c.ptr); } + +DEFINE_C_API_PTR_METHODS(MlirContext, MLIRContext) +DEFINE_C_API_PTR_METHODS(MlirOperation, Operation) +DEFINE_C_API_PTR_METHODS(MlirBlock, Block) +DEFINE_C_API_PTR_METHODS(MlirRegion, Region) + +#define DEFINE_C_API_METHODS(name, cpptype) \ + static name wrap(cpptype cpp) { return name{cpp.getAsOpaquePointer()}; } \ + static cpptype unwrap(name c) { return cpptype::getFromOpaquePointer(c.ptr); } + +DEFINE_C_API_METHODS(MlirAttribute, Attribute) +DEFINE_C_API_METHODS(MlirLocation, Location); +DEFINE_C_API_METHODS(MlirType, Type) +DEFINE_C_API_METHODS(MlirValue, Value) +DEFINE_C_API_METHODS(MlirModule, ModuleOp) + +template +static ArrayRef unwrapList(unsigned size, CTy *first, + SmallVectorImpl &storage) { + static_assert( + std::is_same())), CppTy>::value, + "incompatible C and C++ types"); + + if (size == 0) + return llvm::None; + + assert(storage.empty() && "expected to populate storage"); + storage.reserve(size); + for (unsigned i = 0; i < size; ++i) + storage.push_back(unwrap(*(first + i))); + return storage; +} + +/* ========================================================================== */ +/* Context API. */ +/* ========================================================================== */ + +MlirContext mlirContextCreate() { + auto *context = new MLIRContext; + return wrap(context); +} + +void mlirContextDestroy(MlirContext context) { delete unwrap(context); } + +/* ========================================================================== */ +/* Location API. */ +/* ========================================================================== */ + +MlirLocation mlirLocationFileLineColGet(MlirContext context, + const char *filename, unsigned line, + unsigned col) { + return wrap(FileLineColLoc::get(filename, line, col, unwrap(context))); +} + +MlirLocation mlirLocationUnknownGet(MlirContext context) { + return wrap(UnknownLoc::get(unwrap(context))); +} + +/* ========================================================================== */ +/* Module API. */ +/* ========================================================================== */ + +MlirModule mlirModuleCreateEmpty(MlirLocation location) { + return wrap(ModuleOp::create(unwrap(location))); +} + +MlirModule mlirModuleCreateParse(MlirContext context, const char *module) { + OwningModuleRef owning = parseSourceString(module, unwrap(context)); + return MlirModule{owning.release().getOperation()}; +} + +void mlirModuleDestroy(MlirModule module) { + // Transfer ownership to an OwningModuleRef so that its destructor is called. + OwningModuleRef(unwrap(module)); +} + +MlirOperation mlirModuleGetOperation(MlirModule module) { + return wrap(unwrap(module).getOperation()); +} + +/* ========================================================================== */ +/* Operation state API. */ +/* ========================================================================== */ + +MlirOperationState mlirOperationStateGet(const char *name, MlirLocation loc) { + MlirOperationState state; + state.name = name; + state.location = loc; + state.nResults = 0; + state.results = nullptr; + state.nOperands = 0; + state.operands = nullptr; + state.nRegions = 0; + state.regions = nullptr; + state.nSuccessors = 0; + state.successors = nullptr; + state.nAttributes = 0; + state.attributes = nullptr; + return state; +} + +#define APPEND_ELEMS(type, sizeName, elemName) \ + state->elemName = \ + (type *)realloc(state->elemName, (state->sizeName + n) * sizeof(type)); \ + memcpy(state->elemName + state->sizeName, elemName, n * sizeof(type)); \ + state->sizeName += n; + +void mlirOperationStateAddResults(MlirOperationState *state, unsigned n, + MlirType *results) { + APPEND_ELEMS(MlirType, nResults, results); +} + +void mlirOperationStateAddOperands(MlirOperationState *state, unsigned n, + MlirValue *operands) { + APPEND_ELEMS(MlirValue, nOperands, operands); +} +void mlirOperationStateAddOwnedRegions(MlirOperationState *state, unsigned n, + MlirRegion *regions) { + APPEND_ELEMS(MlirRegion, nRegions, regions); +} +void mlirOperationStateAddSuccessors(MlirOperationState *state, unsigned n, + MlirBlock *successors) { + APPEND_ELEMS(MlirBlock, nSuccessors, successors); +} +void mlirOperationStateAddAttributes(MlirOperationState *state, unsigned n, + MlirNamedAttribute *attributes) { + APPEND_ELEMS(MlirNamedAttribute, nAttributes, attributes); +} + +/* ========================================================================== */ +/* Operation API. */ +/* ========================================================================== */ + +MlirOperation mlirOperationCreate(const MlirOperationState *state) { + assert(state); + OperationState cppState(unwrap(state->location), state->name); + SmallVector resultStorage; + SmallVector operandStorage; + SmallVector successorStorage; + cppState.addTypes(unwrapList(state->nResults, state->results, resultStorage)); + cppState.addOperands( + unwrapList(state->nOperands, state->operands, operandStorage)); + cppState.addSuccessors( + unwrapList(state->nSuccessors, state->successors, successorStorage)); + + cppState.attributes.reserve(state->nAttributes); + for (unsigned i = 0; i < state->nAttributes; ++i) + cppState.addAttribute(state->attributes[i].name, + unwrap(state->attributes[i].attribute)); + + for (unsigned i = 0; i < state->nRegions; ++i) + cppState.addRegion(std::unique_ptr(unwrap(state->regions[i]))); + + return wrap(Operation::create(cppState)); +} + +void mlirOperationDestroy(MlirOperation op) { unwrap(op)->erase(); } + +int mlirOperationIsNull(MlirOperation op) { return unwrap(op) == nullptr; } + +unsigned mlirOperationGetNumRegions(MlirOperation op) { + return unwrap(op)->getNumRegions(); +} + +MlirRegion mlirOperationGetRegion(MlirOperation op, unsigned pos) { + return wrap(&unwrap(op)->getRegion(pos)); +} + +MlirOperation mlirOperationGetNextInBlock(MlirOperation op) { + return wrap(unwrap(op)->getNextNode()); +} + +unsigned mlirOperationGetNumOperands(MlirOperation op) { + return unwrap(op)->getNumOperands(); +} + +MlirValue mlirOperationGetOperand(MlirOperation op, unsigned pos) { + return wrap(unwrap(op)->getOperand(pos)); +} + +unsigned mlirOperationGetNumResults(MlirOperation op) { + return unwrap(op)->getNumResults(); +} + +MlirValue mlirOperationGetResult(MlirOperation op, unsigned pos) { + return wrap(unwrap(op)->getResult(pos)); +} + +unsigned mlirOperationGetNumSuccessors(MlirOperation op) { + return unwrap(op)->getNumSuccessors(); +} + +MlirBlock mlirOperationGetSuccessor(MlirOperation op, unsigned pos) { + return wrap(unwrap(op)->getSuccessor(pos)); +} + +unsigned mlirOperationGetNumAttributes(MlirOperation op) { + return unwrap(op)->getAttrs().size(); +} + +MlirNamedAttribute mlirOperationGetAttribute(MlirOperation op, unsigned pos) { + NamedAttribute attr = unwrap(op)->getAttrs()[pos]; + return MlirNamedAttribute{attr.first.c_str(), wrap(attr.second)}; +} + +MlirAttribute mlirOperationGetAttributeByName(MlirOperation op, + const char *name) { + return wrap(unwrap(op)->getAttr(name)); +} + +void mlirOperationDump(MlirOperation op) { return unwrap(op)->dump(); } + +/* ========================================================================== */ +/* Region API. */ +/* ========================================================================== */ + +MlirRegion mlirRegionCreate() { return wrap(new Region); } + +MlirBlock mlirRegionGetFirstBlock(MlirRegion region) { + Region *cppRegion = unwrap(region); + if (cppRegion->empty()) + return wrap(static_cast(nullptr)); + return wrap(&cppRegion->front()); +} + +void mlirRegionAppendOwnedBlock(MlirRegion region, MlirBlock block) { + unwrap(region)->push_back(unwrap(block)); +} + +void mlirRegionInsertOwnedBlock(MlirRegion region, unsigned pos, + MlirBlock block) { + auto &blockList = unwrap(region)->getBlocks(); + blockList.insert(std::next(blockList.begin(), pos), unwrap(block)); +} + +void mlirRegionDestroy(MlirRegion region) { + delete static_cast(region.ptr); +} + +int mlirRegionIsNull(MlirRegion region) { return unwrap(region) == nullptr; } + +/* ========================================================================== */ +/* Block API. */ +/* ========================================================================== */ + +MlirBlock mlirBlockCreate(unsigned nArgs, MlirType *args) { + Block *b = new Block; + for (unsigned i = 0; i < nArgs; ++i) + b->addArgument(unwrap(args[i])); + return wrap(b); +} + +MlirBlock mlirBlockGetNextInRegion(MlirBlock block) { + return wrap(unwrap(block)->getNextNode()); +} + +MlirOperation mlirBlockGetFirstOperation(MlirBlock block) { + Block *cppBlock = unwrap(block); + if (cppBlock->empty()) + return wrap(static_cast(nullptr)); + return wrap(&cppBlock->front()); +} + +void mlirBlockAppendOwnedOperation(MlirBlock block, MlirOperation operation) { + unwrap(block)->push_back(unwrap(operation)); +} + +void mlirBlockInsertOwnedOperation(MlirBlock block, unsigned pos, + MlirOperation operation) { + auto &opList = unwrap(block)->getOperations(); + opList.insert(std::next(opList.begin(), pos), unwrap(operation)); +} + +void mlirBlockDestroy(MlirBlock block) { delete unwrap(block); } + +int mlirBlockIsNull(MlirBlock block) { return unwrap(block) == nullptr; } + +unsigned mlirBlockGetNumArguments(MlirBlock block) { + return unwrap(block)->getNumArguments(); +} + +MlirValue mlirBlockGetArgument(MlirBlock block, unsigned pos) { + return wrap(unwrap(block)->getArgument(pos)); +} + +/* ========================================================================== */ +/* Value API. */ +/* ========================================================================== */ + +MlirType mlirValueGetType(MlirValue value) { + return wrap(unwrap(value).getType()); +} + +/* ========================================================================== */ +/* Type API. */ +/* ========================================================================== */ + +MlirType mlirTypeParseGet(MlirContext context, const char *type) { + return wrap(mlir::parseType(type, unwrap(context))); +} + +void mlirTypeDump(MlirType type) { unwrap(type).dump(); } + +/* ========================================================================== */ +/* Attribute API. */ +/* ========================================================================== */ + +MlirAttribute mlirAttributeParseGet(MlirContext context, const char *attr) { + return wrap(mlir::parseAttribute(attr, unwrap(context))); +} + +void mlirAttributeDump(MlirAttribute attr) { unwrap(attr).dump(); } + +MlirNamedAttribute mlirNamedAttributeGet(const char *name, MlirAttribute attr) { + return MlirNamedAttribute{name, attr}; +} diff --git a/mlir/lib/CAPI/Registration/CMakeLists.txt b/mlir/lib/CAPI/Registration/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/Registration/CMakeLists.txt @@ -0,0 +1,14 @@ +# Dialect registration. +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +add_mlir_library(MLIRCAPIRegistration + Registration.cpp + + EXCLUDE_FROM_LIBMLIR + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir-c + + LINK_LIBS PUBLIC + MLIRCAPIIR + ${dialect_libs} + ) diff --git a/mlir/lib/CAPI/Registration/Registration.cpp b/mlir/lib/CAPI/Registration/Registration.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/Registration/Registration.cpp @@ -0,0 +1,13 @@ +//===- Registration.cpp - C Interface for MLIR Registration ---------------===// +// +// 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/Registration.h" + +#include "mlir/InitAllDialects.h" + +void mlirRegisterAllDialects() { mlir::registerAllDialects(); } diff --git a/mlir/test/CAPI/CMakeLists.txt b/mlir/test/CAPI/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/test/CAPI/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_llvm_executable(mlir-capi-ir-test + ir.c + ) +llvm_update_compile_flags(mlir-capi-ir-test) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +target_link_libraries(mlir-capi-ir-test + PRIVATE + MLIRCAPIIR + MLIRCAPIRegistration + ${dialect_libs}) diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c new file mode 100644 --- /dev/null +++ b/mlir/test/CAPI/ir.c @@ -0,0 +1,245 @@ +/*===- ir.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 *| +|* *| +\*===----------------------------------------------------------------------===*/ + +/* RUN: mlir-capi-ir-test 2>&1 | FileCheck %s + */ + +#include "mlir-c/IR.h" +#include "mlir-c/Registration.h" + +#include +#include +#include + +void populateLoopBody(MlirContext ctx, MlirBlock loopBody, + MlirLocation location, MlirBlock funcBody) { + MlirValue iv = mlirBlockGetArgument(loopBody, 0); + MlirValue funcArg0 = mlirBlockGetArgument(funcBody, 0); + MlirValue funcArg1 = mlirBlockGetArgument(funcBody, 1); + MlirType f32Type = mlirTypeParseGet(ctx, "f32"); + + MlirOperationState loadLHSState = mlirOperationStateGet("std.load", location); + MlirValue loadLHSOperands[] = {funcArg0, iv}; + mlirOperationStateAddOperands(&loadLHSState, 2, loadLHSOperands); + mlirOperationStateAddResults(&loadLHSState, 1, &f32Type); + MlirOperation loadLHS = mlirOperationCreate(&loadLHSState); + mlirBlockAppendOwnedOperation(loopBody, loadLHS); + + MlirOperationState loadRHSState = mlirOperationStateGet("std.load", location); + MlirValue loadRHSOperands[] = {funcArg1, iv}; + mlirOperationStateAddOperands(&loadRHSState, 2, loadRHSOperands); + mlirOperationStateAddResults(&loadRHSState, 1, &f32Type); + MlirOperation loadRHS = mlirOperationCreate(&loadRHSState); + mlirBlockAppendOwnedOperation(loopBody, loadRHS); + + MlirOperationState addState = mlirOperationStateGet("std.addf", location); + MlirValue addOperands[] = {mlirOperationGetResult(loadLHS, 0), + mlirOperationGetResult(loadRHS, 0)}; + mlirOperationStateAddOperands(&addState, 2, addOperands); + mlirOperationStateAddResults(&addState, 1, &f32Type); + MlirOperation add = mlirOperationCreate(&addState); + mlirBlockAppendOwnedOperation(loopBody, add); + + MlirOperationState storeState = mlirOperationStateGet("std.store", location); + MlirValue storeOperands[] = {mlirOperationGetResult(add, 0), funcArg0, iv}; + mlirOperationStateAddOperands(&storeState, 3, storeOperands); + MlirOperation store = mlirOperationCreate(&storeState); + mlirBlockAppendOwnedOperation(loopBody, store); + + MlirOperationState yieldState = mlirOperationStateGet("scf.yield", location); + MlirOperation yield = mlirOperationCreate(&yieldState); + mlirBlockAppendOwnedOperation(loopBody, yield); +} + +MlirModule makeAdd(MlirContext ctx, MlirLocation location) { + MlirModule moduleOp = mlirModuleCreateEmpty(location); + MlirOperation module = mlirModuleGetOperation(moduleOp); + MlirRegion moduleBodyRegion = mlirOperationGetRegion(module, 0); + MlirBlock moduleBody = mlirRegionGetFirstBlock(moduleBodyRegion); + + MlirType memrefType = mlirTypeParseGet(ctx, "memref"); + MlirType funcBodyArgTypes[] = {memrefType, memrefType}; + MlirRegion funcBodyRegion = mlirRegionCreate(); + MlirBlock funcBody = mlirBlockCreate( + sizeof(funcBodyArgTypes) / sizeof(MlirType), funcBodyArgTypes); + mlirRegionAppendOwnedBlock(funcBodyRegion, funcBody); + + MlirAttribute funcTypeAttr = + mlirAttributeParseGet(ctx, "(memref, memref) -> ()"); + MlirAttribute funcNameAttr = mlirAttributeParseGet(ctx, "\"add\""); + MlirNamedAttribute funcAttrs[] = { + mlirNamedAttributeGet("type", funcTypeAttr), + mlirNamedAttributeGet("sym_name", funcNameAttr)}; + MlirOperationState funcState = mlirOperationStateGet("func", location); + mlirOperationStateAddAttributes(&funcState, 2, funcAttrs); + mlirOperationStateAddOwnedRegions(&funcState, 1, &funcBodyRegion); + MlirOperation func = mlirOperationCreate(&funcState); + mlirBlockInsertOwnedOperation(moduleBody, 0, func); + + MlirType indexType = mlirTypeParseGet(ctx, "index"); + MlirAttribute indexZeroLiteral = mlirAttributeParseGet(ctx, "0 : index"); + MlirNamedAttribute indexZeroValueAttr = + mlirNamedAttributeGet("value", indexZeroLiteral); + MlirOperationState constZeroState = + mlirOperationStateGet("std.constant", location); + mlirOperationStateAddResults(&constZeroState, 1, &indexType); + mlirOperationStateAddAttributes(&constZeroState, 1, &indexZeroValueAttr); + MlirOperation constZero = mlirOperationCreate(&constZeroState); + mlirBlockAppendOwnedOperation(funcBody, constZero); + + MlirValue funcArg0 = mlirBlockGetArgument(funcBody, 0); + MlirValue constZeroValue = mlirOperationGetResult(constZero, 0); + MlirValue dimOperands[] = {funcArg0, constZeroValue}; + MlirOperationState dimState = mlirOperationStateGet("std.dim", location); + mlirOperationStateAddOperands(&dimState, 2, dimOperands); + mlirOperationStateAddResults(&dimState, 1, &indexType); + MlirOperation dim = mlirOperationCreate(&dimState); + mlirBlockAppendOwnedOperation(funcBody, dim); + + MlirRegion loopBodyRegion = mlirRegionCreate(); + MlirBlock loopBody = mlirBlockCreate(/*nArgs=*/1, &indexType); + mlirRegionAppendOwnedBlock(loopBodyRegion, loopBody); + + MlirAttribute indexOneLiteral = mlirAttributeParseGet(ctx, "1 : index"); + MlirNamedAttribute indexOneValueAttr = + mlirNamedAttributeGet("value", indexOneLiteral); + MlirOperationState constOneState = + mlirOperationStateGet("std.constant", location); + mlirOperationStateAddResults(&constOneState, 1, &indexType); + mlirOperationStateAddAttributes(&constOneState, 1, &indexOneValueAttr); + MlirOperation constOne = mlirOperationCreate(&constOneState); + mlirBlockAppendOwnedOperation(funcBody, constOne); + + MlirValue dimValue = mlirOperationGetResult(dim, 0); + MlirValue constOneValue = mlirOperationGetResult(constOne, 0); + MlirValue loopOperands[] = {constZeroValue, dimValue, constOneValue}; + MlirOperationState loopState = mlirOperationStateGet("scf.for", location); + mlirOperationStateAddOperands(&loopState, 3, loopOperands); + mlirOperationStateAddOwnedRegions(&loopState, 1, &loopBodyRegion); + MlirOperation loop = mlirOperationCreate(&loopState); + mlirBlockAppendOwnedOperation(funcBody, loop); + + populateLoopBody(ctx, loopBody, location, funcBody); + + MlirOperationState retState = mlirOperationStateGet("std.return", location); + MlirOperation ret = mlirOperationCreate(&retState); + mlirBlockAppendOwnedOperation(funcBody, ret); + + return moduleOp; +} + +struct OpListNode { + MlirOperation op; + struct OpListNode *next; +}; +typedef struct OpListNode OpListNode; + +struct ModuleStats { + unsigned numOperations; + unsigned numAttributes; + unsigned numBlocks; + unsigned numRegions; + unsigned numValues; +}; +typedef struct ModuleStats ModuleStats; + +void collectStatsSingle(OpListNode *head, ModuleStats *stats) { + MlirOperation operation = head->op; + stats->numOperations += 1; + stats->numValues += mlirOperationGetNumResults(operation); + stats->numAttributes += mlirOperationGetNumAttributes(operation); + + unsigned numRegions = mlirOperationGetNumRegions(operation); + + stats->numRegions += numRegions; + + for (unsigned i = 0; i < numRegions; ++i) { + MlirRegion region = mlirOperationGetRegion(operation, i); + for (MlirBlock block = mlirRegionGetFirstBlock(region); + !mlirBlockIsNull(block); block = mlirBlockGetNextInRegion(block)) { + ++stats->numBlocks; + stats->numValues += mlirBlockGetNumArguments(block); + + for (MlirOperation child = mlirBlockGetFirstOperation(block); + !mlirOperationIsNull(child); + child = mlirOperationGetNextInBlock(child)) { + OpListNode *node = malloc(sizeof(OpListNode)); + node->op = child; + node->next = head->next; + head->next = node; + } + } + } +} + +void collectStats(MlirOperation operation) { + OpListNode *head = malloc(sizeof(OpListNode)); + head->op = operation; + head->next = NULL; + + ModuleStats stats; + stats.numOperations = 0; + stats.numAttributes = 0; + stats.numBlocks = 0; + stats.numRegions = 0; + stats.numValues = 0; + + do { + collectStatsSingle(head, &stats); + OpListNode *next = head->next; + free(head); + head = next; + } while (head); + + printf("Number of operations: %u\n", stats.numOperations); + printf("Number of attributes: %u\n", stats.numAttributes); + printf("Number of blocks: %u\n", stats.numBlocks); + printf("Number of regions: %u\n", stats.numRegions); + printf("Number of values: %u\n", stats.numValues); +} + +int main() { + mlirRegisterAllDialects(); + MlirContext ctx = mlirContextCreate(); + MlirLocation location = mlirLocationUnknownGet(ctx); + + MlirModule moduleOp = makeAdd(ctx, location); + MlirOperation module = mlirModuleGetOperation(moduleOp); + mlirOperationDump(module); + // clang-format off + // CHECK: module { + // CHECK: func @add(%[[ARG0:.*]]: memref, %[[ARG1:.*]]: memref) { + // CHECK: %[[C0:.*]] = constant 0 : index + // CHECK: %[[DIM:.*]] = dim %[[ARG0]], %[[C0]] : memref + // CHECK: %[[C1:.*]] = constant 1 : index + // CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[DIM]] step %[[C1]] { + // CHECK: %[[LHS:.*]] = load %[[ARG0]][%[[I]]] : memref + // CHECK: %[[RHS:.*]] = load %[[ARG1]][%[[I]]] : memref + // CHECK: %[[SUM:.*]] = addf %[[LHS]], %[[RHS]] : f32 + // CHECK: store %[[SUM]], %[[ARG0]][%[[I]]] : memref + // CHECK: } + // CHECK: return + // CHECK: } + // CHECK: } + // clang-format on + + collectStats(module); + // clang-format off + // CHECK: Number of operations: 13 + // CHECK: Number of attributes: 4 + // CHECK: Number of blocks: 3 + // CHECK: Number of regions: 3 + // CHECK: Number of values: 9 + // clang-format on + + mlirModuleDestroy(moduleOp); + mlirContextDestroy(ctx); + + return 0; +} diff --git a/mlir/test/CAPI/lit.local.cfg b/mlir/test/CAPI/lit.local.cfg new file mode 100644 --- /dev/null +++ b/mlir/test/CAPI/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes.add('.c') diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt --- a/mlir/test/CMakeLists.txt +++ b/mlir/test/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(CAPI) add_subdirectory(EDSC) add_subdirectory(mlir-cpu-runner) add_subdirectory(SDBM) @@ -40,6 +41,7 @@ set(MLIR_TEST_DEPENDS FileCheck count not MLIRUnitTests + mlir-capi-ir-test mlir-cpu-runner mlir-edsc-builder-api-test mlir-linalg-ods-gen diff --git a/mlir/test/lit.cfg.py b/mlir/test/lit.cfg.py --- a/mlir/test/lit.cfg.py +++ b/mlir/test/lit.cfg.py @@ -58,6 +58,7 @@ 'mlir-opt', 'mlir-tblgen', 'mlir-translate', + 'mlir-capi-ir-test', 'mlir-edsc-builder-api-test', ]