[mlir] Initial version of C APIs

Introduce an initial version of C API for MLIR core IR components: Value, Type,
    Attribute, Operation, Region, Block, Location. These APIs allow for both
    inspection and creation of the IR in the generic form and intended for wrapping
    in high-level library- and language-specific constructs. At this point, there
    is no stability guarantee provided for the API.

Reviewed By: stellaraccident, lattner

Differential Revision: https://reviews.llvm.org/D83310
This commit is contained in:
Alex Zinenko 2020-08-05 14:36:16 +02:00
parent f5df5cd558
commit 75f239e975
14 changed files with 1099 additions and 0 deletions

View File

@ -93,6 +93,8 @@ add_subdirectory(tools/mlir-tblgen)
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)

124
mlir/docs/CAPI.md Normal file
View File

@ -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 mlirXGetNum<Y>s(MlirX)` returns the upper bound on the index.
- `MlirY mlirXGet<Y>(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<Y>(MlirX)` returns the first subobject in the list.
- `MlirY mlirYGetNextIn<X>(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<Y>(x); !mlirYIsNull(iter);
iter = mlirYGetNextIn<X>(iter)) {
/* User 'iter'. */
}
```

298
mlir/include/mlir-c/IR.h Normal file
View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
add_subdirectory(IR)
add_subdirectory(Registration)

View File

@ -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
)

341
mlir/lib/CAPI/IR/IR.cpp Normal file
View File

@ -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<cpptype *>(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 <typename CppTy, typename CTy>
static ArrayRef<CppTy> unwrapList(unsigned size, CTy *first,
SmallVectorImpl<CppTy> &storage) {
static_assert(
std::is_same<decltype(unwrap(std::declval<CTy>())), 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<Type, 4> resultStorage;
SmallVector<Value, 8> operandStorage;
SmallVector<Block *, 2> 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<Region>(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<Block *>(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 *>(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<Operation *>(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};
}

View File

@ -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}
)

View File

@ -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(); }

View File

@ -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})

245
mlir/test/CAPI/ir.c Normal file
View File

@ -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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
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<?xf32>");
MlirType funcBodyArgTypes[] = {memrefType, memrefType};
MlirRegion funcBodyRegion = mlirRegionCreate();
MlirBlock funcBody = mlirBlockCreate(
sizeof(funcBodyArgTypes) / sizeof(MlirType), funcBodyArgTypes);
mlirRegionAppendOwnedBlock(funcBodyRegion, funcBody);
MlirAttribute funcTypeAttr =
mlirAttributeParseGet(ctx, "(memref<?xf32>, memref<?xf32>) -> ()");
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<?xf32>, %[[ARG1:.*]]: memref<?xf32>) {
// CHECK: %[[C0:.*]] = constant 0 : index
// CHECK: %[[DIM:.*]] = dim %[[ARG0]], %[[C0]] : memref<?xf32>
// CHECK: %[[C1:.*]] = constant 1 : index
// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[DIM]] step %[[C1]] {
// CHECK: %[[LHS:.*]] = load %[[ARG0]][%[[I]]] : memref<?xf32>
// CHECK: %[[RHS:.*]] = load %[[ARG1]][%[[I]]] : memref<?xf32>
// CHECK: %[[SUM:.*]] = addf %[[LHS]], %[[RHS]] : f32
// CHECK: store %[[SUM]], %[[ARG0]][%[[I]]] : memref<?xf32>
// 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;
}

View File

@ -0,0 +1 @@
config.suffixes.add('.c')

View File

@ -1,3 +1,4 @@
add_subdirectory(CAPI)
add_subdirectory(EDSC)
add_subdirectory(mlir-cpu-runner)
add_subdirectory(SDBM)
@ -40,6 +41,7 @@ configure_lit_site_cfg(
set(MLIR_TEST_DEPENDS
FileCheck count not
MLIRUnitTests
mlir-capi-ir-test
mlir-cpu-runner
mlir-edsc-builder-api-test
mlir-linalg-ods-gen

View File

@ -58,6 +58,7 @@ tools = [
'mlir-opt',
'mlir-tblgen',
'mlir-translate',
'mlir-capi-ir-test',
'mlir-edsc-builder-api-test',
]