[mlir] Add support for diagnostics in C API.

Add basic support for registering diagnostic handlers with the context
(actually, the diagnostic engine contained in the context) and processing
diagnostic messages from the C API.

Reviewed By: stellaraccident

Differential Revision: https://reviews.llvm.org/D88736
This commit is contained in:
Alex Zinenko 2020-10-07 14:38:10 +02:00
parent 6e6a5acf00
commit 7b5dfb400a
7 changed files with 273 additions and 1 deletions

View File

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

View File

@ -50,6 +50,42 @@ inline MlirStringRef mlirStringRefCreate(const char *str, size_t length) {
*/
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

View File

@ -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 <cassert>
namespace mlir {
class Diagnostic;
} // namespace mlir
inline mlir::Diagnostic &unwrap(MlirDiagnostic diagnostic) {
assert(diagnostic.ptr && "unexpected null diagnostic");
return *(static_cast<mlir::Diagnostic *>(diagnostic.ptr));
}
inline MlirDiagnostic wrap(mlir::Diagnostic &diagnostic) {
return {&diagnostic};
}
#endif // MLIR_CAPI_DIAGNOSTICS_H

View File

@ -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 @@ inline llvm::StringRef unwrap(MlirStringRef ref) {
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

View File

@ -1,6 +1,7 @@
# Main API.
add_mlir_library(MLIRCAPIIR
AffineMap.cpp
Diagnostics.cpp
IR.cpp
StandardAttributes.cpp
StandardTypes.cpp

View File

@ -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<intptr_t>(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<MlirDiagnosticHandlerID>(id);
}
void mlirContextDetachDiagnosticHandler(MlirContext context,
MlirDiagnosticHandlerID id) {
unwrap(context)->getDiagEngine().eraseHandler(
static_cast<DiagnosticEngine::HandlerID>(id));
}
void mlirEmitError(MlirLocation location, const char *message) {
emitError(unwrap(location)) << message;
}

View File

@ -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 @@ int registerOnlyStd() {
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 @@ int main() {
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;
}