diff --git a/mlir/include/mlir-c/Diagnostics.h b/mlir/include/mlir-c/Diagnostics.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir-c/Diagnostics.h @@ -0,0 +1,87 @@ +/*===-- mlir-c/Diagnostics.h - MLIR Diagnostic subsystem C API ----*- 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 APIs accessing MLIR Diagnostics subsystem. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef MLIR_C_DIAGNOSTICS_H +#define MLIR_C_DIAGNOSTICS_H + +#include "mlir-c/IR.h" +#include "mlir-c/Support.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** An opaque reference to a dignostic, always owned by the diagnostics engine + * (context). Must not be stored outside of the diagnostic handler. */ +struct MlirDiagnostic { + void *ptr; +}; +typedef struct MlirDiagnostic MlirDiagnostic; + +/** Severity of a diagnostic. */ +enum MlirDiagnosticSeverity { + MlirDiagnosticError, + MlirDiagnosticWarning, + MlirDiagnosticNote, + MlirDiagnosticRemark +}; +typedef enum MlirDiagnosticSeverity MlirDiagnosticSeverity; + +/** Opaque identifier of a diagnostic handler, useful to detach a handler. */ +typedef uint64_t MlirDiagnosticHandlerID; + +/** Diagnostic handler type. Acceps a reference to a diagnostic, which is only + * guaranteed to be live during the call. If the handler processed the + * diagnostic completely, it is expected to return success. Otherwise, it is + * expected to return failure to indicate that other handlers should attempt to + * process the diagnostic. */ +typedef MlirLogicalResult (*MlirDiagnosticHandler)(MlirDiagnostic); + +/** Prints a diagnostic using the provided callback. */ +void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MlirStringCallback callback, + void *userData); + +/** Returns the location at which the diagnostic is reported. */ +MlirLocation mlirDiagnosticGetLocation(MlirDiagnostic diagnostic); + +/** Returns the severity of the diagnostic. */ +MlirDiagnosticSeverity mlirDiagnosticGetSeverity(MlirDiagnostic diagnostic); + +/** Returns the number of notes attached to the diagnostic. */ +intptr_t mlirDiagnosticGetNumNotes(MlirDiagnostic diagnostic); + +/** Returns `pos`-th note attached to the diagnostic. Expects `pos` to be a + * valid zero-based index into the list of notes. */ +MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos); + +/** Attaches the diagnostic handler to the context. Handlers are invoked in the + * reverse order of attachment until one of them processes the diagnostic + * completely. Returns an identifier that can be used to detach the handler. */ +MlirDiagnosticHandlerID +mlirContextAttachDiagnosticHandler(MlirContext context, + MlirDiagnosticHandler handler); + +/** Detaches an attached diagnostic handler from the context given its + * identifier. */ +void mlirContextDetachDiagnosticHandler(MlirContext context, + MlirDiagnosticHandlerID id); + +/** Emits an error at the given location through the diagnostics engine. Used + * for testing purposes. */ +void mlirEmitError(MlirLocation location, const char *message); + +#ifdef __cplusplus +} +#endif + +#endif // MLIR_C_DIAGNOSTICS_H diff --git a/mlir/include/mlir-c/Support.h b/mlir/include/mlir-c/Support.h --- a/mlir/include/mlir-c/Support.h +++ b/mlir/include/mlir-c/Support.h @@ -50,6 +50,42 @@ */ MlirStringRef mlirStringRefCreateFromCString(const char *str); +/*============================================================================*/ +/* MlirLogicalResult. */ +/*============================================================================*/ + +/** A logical result value, essentially a boolean with named states. LLVM + * convention for using boolean values to designate success or failure of an + * operation is a moving target, so MLIR opted for an explicit class. + * Instances of MlirLogicalResult must only be inspected using the associated + * functions. */ +struct MlirLogicalResult { + int8_t value; +}; +typedef struct MlirLogicalResult MlirLogicalResult; + +/** Checks if the given logical result represents a success. */ +inline int mlirLogicalResultIsSuccess(MlirLogicalResult res) { + return res.value != 0; +} + +/** Checks if the given logical result represents a failure. */ +inline int mlirLogicalResultIsFailure(MlirLogicalResult res) { + return res.value == 0; +} + +/** Creates a logical result representing a success. */ +inline static MlirLogicalResult mlirLogicalResultSuccess() { + MlirLogicalResult res = {1}; + return res; +} + +/** Creates a logical result representing a failure. */ +inline static MlirLogicalResult mlirLogicalResultFailure() { + MlirLogicalResult res = {0}; + return res; +} + #ifdef __cplusplus } #endif diff --git a/mlir/include/mlir/CAPI/Diagnostics.h b/mlir/include/mlir/CAPI/Diagnostics.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/CAPI/Diagnostics.h @@ -0,0 +1,28 @@ +//===- IR.h - C API Utils for MLIR Diagnostics ------------------*- 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_CAPI_DIAGNOSTICS_H +#define MLIR_CAPI_DIAGNOSTICS_H + +#include "mlir-c/Diagnostics.h" +#include + +namespace mlir { +class Diagnostic; +} // namespace mlir + +inline mlir::Diagnostic &unwrap(MlirDiagnostic diagnostic) { + assert(diagnostic.ptr && "unexpected null diagnostic"); + return *(static_cast(diagnostic.ptr)); +} + +inline MlirDiagnostic wrap(mlir::Diagnostic &diagnostic) { + return {&diagnostic}; +} + +#endif // MLIR_CAPI_DIAGNOSTICS_H diff --git a/mlir/include/mlir/CAPI/Support.h b/mlir/include/mlir/CAPI/Support.h --- a/mlir/include/mlir/CAPI/Support.h +++ b/mlir/include/mlir/CAPI/Support.h @@ -16,6 +16,7 @@ #define MLIR_CAPI_SUPPORT_H #include "mlir-c/Support.h" +#include "mlir/Support/LogicalResult.h" #include "llvm/ADT/StringRef.h" /// Converts a StringRef into its MLIR C API equivalent. @@ -28,4 +29,14 @@ return llvm::StringRef(ref.data, ref.length); } +inline MlirLogicalResult wrap(mlir::LogicalResult res) { + if (mlir::succeeded(res)) + return mlirLogicalResultSuccess(); + return mlirLogicalResultFailure(); +} + +inline mlir::LogicalResult unwrap(MlirLogicalResult res) { + return mlir::success(mlirLogicalResultIsSuccess(res)); +} + #endif // MLIR_CAPI_SUPPORT_H diff --git a/mlir/lib/CAPI/IR/CMakeLists.txt b/mlir/lib/CAPI/IR/CMakeLists.txt --- a/mlir/lib/CAPI/IR/CMakeLists.txt +++ b/mlir/lib/CAPI/IR/CMakeLists.txt @@ -1,6 +1,7 @@ # Main API. add_mlir_library(MLIRCAPIIR AffineMap.cpp + Diagnostics.cpp IR.cpp StandardAttributes.cpp StandardTypes.cpp diff --git a/mlir/lib/CAPI/IR/Diagnostics.cpp b/mlir/lib/CAPI/IR/Diagnostics.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/CAPI/IR/Diagnostics.cpp @@ -0,0 +1,75 @@ +//===- Diagnostics.cpp - C Interface for MLIR Diagnostics -----------------===// +// +// 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/Diagnostics.h" +#include "mlir/CAPI/Diagnostics.h" +#include "mlir/CAPI/IR.h" +#include "mlir/CAPI/Support.h" +#include "mlir/CAPI/Utils.h" +#include "mlir/IR/Diagnostics.h" + +using namespace mlir; + +void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MlirStringCallback callback, + void *userData) { + detail::CallbackOstream stream(callback, userData); + unwrap(diagnostic).print(stream); + stream.flush(); +} + +MlirLocation mlirDiagnosticGetLocation(MlirDiagnostic diagnostic) { + return wrap(unwrap(diagnostic).getLocation()); +} + +MlirDiagnosticSeverity mlirDiagnosticGetSeverity(MlirDiagnostic diagnostic) { + switch (unwrap(diagnostic).getSeverity()) { + case mlir::DiagnosticSeverity::Error: + return MlirDiagnosticError; + case mlir::DiagnosticSeverity::Warning: + return MlirDiagnosticWarning; + case mlir::DiagnosticSeverity::Note: + return MlirDiagnosticNote; + case mlir::DiagnosticSeverity::Remark: + return MlirDiagnosticRemark; + } + llvm_unreachable("unhandled diagnostic severity"); +} + +// Notes are stored in a vector, so note iterator range is a pair of +// random access iterators, for which it is cheap to compute the size. +intptr_t mlirDiagnosticGetNumNotes(MlirDiagnostic diagnostic) { + return static_cast(llvm::size(unwrap(diagnostic).getNotes())); +} + +// Notes are stored in a vector, so the iterator is a random access iterator, +// cheap to advance multiple steps at a time. +MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos) { + return wrap(*std::next(unwrap(diagnostic).getNotes().begin(), pos)); +} + +MlirDiagnosticHandlerID +mlirContextAttachDiagnosticHandler(MlirContext context, + MlirDiagnosticHandler handler) { + assert(handler && "unexpected null diagnostic handler"); + DiagnosticEngine::HandlerID id = + unwrap(context)->getDiagEngine().registerHandler( + [handler](Diagnostic &diagnostic) { + return unwrap(handler(wrap(diagnostic))); + }); + return static_cast(id); +} + +void mlirContextDetachDiagnosticHandler(MlirContext context, + MlirDiagnosticHandlerID id) { + unwrap(context)->getDiagEngine().eraseHandler( + static_cast(id)); +} + +void mlirEmitError(MlirLocation location, const char *message) { + emitError(unwrap(location)) << message; +} diff --git a/mlir/test/CAPI/ir.c b/mlir/test/CAPI/ir.c --- a/mlir/test/CAPI/ir.c +++ b/mlir/test/CAPI/ir.c @@ -10,8 +10,9 @@ /* RUN: mlir-capi-ir-test 2>&1 | FileCheck %s */ -#include "mlir-c/IR.h" #include "mlir-c/AffineMap.h" +#include "mlir-c/Diagnostics.h" +#include "mlir-c/IR.h" #include "mlir-c/Registration.h" #include "mlir-c/StandardAttributes.h" #include "mlir-c/StandardDialect.h" @@ -827,6 +828,28 @@ return 0; } +// Wraps a diagnostic into additional text we can match against. +MlirLogicalResult errorHandler(MlirDiagnostic diagnostic) { + fprintf(stderr, "processing diagnostic <<\n"); + mlirDiagnosticPrint(diagnostic, printToStderr, NULL); + fprintf(stderr, "\n"); + MlirLocation loc = mlirDiagnosticGetLocation(diagnostic); + mlirLocationPrint(loc, printToStderr, NULL); + assert(mlirDiagnosticGetNumNotes(diagnostic) == 0); + fprintf(stderr, ">> end of diagnostic\n"); + return mlirLogicalResultSuccess(); +} + +void testDiagnostics() { + MlirContext ctx = mlirContextCreate(); + MlirDiagnosticHandlerID id = + mlirContextAttachDiagnosticHandler(ctx, errorHandler); + MlirLocation loc = mlirLocationUnknownGet(ctx); + mlirEmitError(loc, "test diagnostics"); + mlirContextDetachDiagnosticHandler(ctx, id); + mlirEmitError(loc, "more test diagnostics"); +} + int main() { MlirContext ctx = mlirContextCreate(); mlirRegisterAllDialects(ctx); @@ -982,5 +1005,16 @@ mlirContextDestroy(ctx); + fprintf(stderr, "@test_diagnostics\n"); + testDiagnostics(); + // clang-format off + // CHECK-LABEL: @test_diagnostics + // CHECK: processing diagnostic << + // CHECK: test diagnostics + // CHECK: loc(unknown) + // CHECK: >> end of diagnostic + // CHECK-NOT: processing diagnostic + // CHECK: more test diagnostics + return 0; }