Skip to content

Commit 7b5dfb4

Browse files
committed
[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
1 parent 6e6a5ac commit 7b5dfb4

File tree

7 files changed

+273
-1
lines changed

7 files changed

+273
-1
lines changed

mlir/include/mlir-c/Diagnostics.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*===-- mlir-c/Diagnostics.h - MLIR Diagnostic subsystem C API ----*- C -*-===*\
2+
|* *|
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
4+
|* Exceptions. *|
5+
|* See https://llvm.org/LICENSE.txt for license information. *|
6+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
7+
|* *|
8+
|*===----------------------------------------------------------------------===*|
9+
|* *|
10+
|* This header declares the C APIs accessing MLIR Diagnostics subsystem. *|
11+
|* *|
12+
\*===----------------------------------------------------------------------===*/
13+
14+
#ifndef MLIR_C_DIAGNOSTICS_H
15+
#define MLIR_C_DIAGNOSTICS_H
16+
17+
#include "mlir-c/IR.h"
18+
#include "mlir-c/Support.h"
19+
20+
#ifdef __cplusplus
21+
extern "C" {
22+
#endif
23+
24+
/** An opaque reference to a dignostic, always owned by the diagnostics engine
25+
* (context). Must not be stored outside of the diagnostic handler. */
26+
struct MlirDiagnostic {
27+
void *ptr;
28+
};
29+
typedef struct MlirDiagnostic MlirDiagnostic;
30+
31+
/** Severity of a diagnostic. */
32+
enum MlirDiagnosticSeverity {
33+
MlirDiagnosticError,
34+
MlirDiagnosticWarning,
35+
MlirDiagnosticNote,
36+
MlirDiagnosticRemark
37+
};
38+
typedef enum MlirDiagnosticSeverity MlirDiagnosticSeverity;
39+
40+
/** Opaque identifier of a diagnostic handler, useful to detach a handler. */
41+
typedef uint64_t MlirDiagnosticHandlerID;
42+
43+
/** Diagnostic handler type. Acceps a reference to a diagnostic, which is only
44+
* guaranteed to be live during the call. If the handler processed the
45+
* diagnostic completely, it is expected to return success. Otherwise, it is
46+
* expected to return failure to indicate that other handlers should attempt to
47+
* process the diagnostic. */
48+
typedef MlirLogicalResult (*MlirDiagnosticHandler)(MlirDiagnostic);
49+
50+
/** Prints a diagnostic using the provided callback. */
51+
void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MlirStringCallback callback,
52+
void *userData);
53+
54+
/** Returns the location at which the diagnostic is reported. */
55+
MlirLocation mlirDiagnosticGetLocation(MlirDiagnostic diagnostic);
56+
57+
/** Returns the severity of the diagnostic. */
58+
MlirDiagnosticSeverity mlirDiagnosticGetSeverity(MlirDiagnostic diagnostic);
59+
60+
/** Returns the number of notes attached to the diagnostic. */
61+
intptr_t mlirDiagnosticGetNumNotes(MlirDiagnostic diagnostic);
62+
63+
/** Returns `pos`-th note attached to the diagnostic. Expects `pos` to be a
64+
* valid zero-based index into the list of notes. */
65+
MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos);
66+
67+
/** Attaches the diagnostic handler to the context. Handlers are invoked in the
68+
* reverse order of attachment until one of them processes the diagnostic
69+
* completely. Returns an identifier that can be used to detach the handler. */
70+
MlirDiagnosticHandlerID
71+
mlirContextAttachDiagnosticHandler(MlirContext context,
72+
MlirDiagnosticHandler handler);
73+
74+
/** Detaches an attached diagnostic handler from the context given its
75+
* identifier. */
76+
void mlirContextDetachDiagnosticHandler(MlirContext context,
77+
MlirDiagnosticHandlerID id);
78+
79+
/** Emits an error at the given location through the diagnostics engine. Used
80+
* for testing purposes. */
81+
void mlirEmitError(MlirLocation location, const char *message);
82+
83+
#ifdef __cplusplus
84+
}
85+
#endif
86+
87+
#endif // MLIR_C_DIAGNOSTICS_H

mlir/include/mlir-c/Support.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,42 @@ inline MlirStringRef mlirStringRefCreate(const char *str, size_t length) {
5050
*/
5151
MlirStringRef mlirStringRefCreateFromCString(const char *str);
5252

53+
/*============================================================================*/
54+
/* MlirLogicalResult. */
55+
/*============================================================================*/
56+
57+
/** A logical result value, essentially a boolean with named states. LLVM
58+
* convention for using boolean values to designate success or failure of an
59+
* operation is a moving target, so MLIR opted for an explicit class.
60+
* Instances of MlirLogicalResult must only be inspected using the associated
61+
* functions. */
62+
struct MlirLogicalResult {
63+
int8_t value;
64+
};
65+
typedef struct MlirLogicalResult MlirLogicalResult;
66+
67+
/** Checks if the given logical result represents a success. */
68+
inline int mlirLogicalResultIsSuccess(MlirLogicalResult res) {
69+
return res.value != 0;
70+
}
71+
72+
/** Checks if the given logical result represents a failure. */
73+
inline int mlirLogicalResultIsFailure(MlirLogicalResult res) {
74+
return res.value == 0;
75+
}
76+
77+
/** Creates a logical result representing a success. */
78+
inline static MlirLogicalResult mlirLogicalResultSuccess() {
79+
MlirLogicalResult res = {1};
80+
return res;
81+
}
82+
83+
/** Creates a logical result representing a failure. */
84+
inline static MlirLogicalResult mlirLogicalResultFailure() {
85+
MlirLogicalResult res = {0};
86+
return res;
87+
}
88+
5389
#ifdef __cplusplus
5490
}
5591
#endif

mlir/include/mlir/CAPI/Diagnostics.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===- IR.h - C API Utils for MLIR Diagnostics ------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef MLIR_CAPI_DIAGNOSTICS_H
10+
#define MLIR_CAPI_DIAGNOSTICS_H
11+
12+
#include "mlir-c/Diagnostics.h"
13+
#include <cassert>
14+
15+
namespace mlir {
16+
class Diagnostic;
17+
} // namespace mlir
18+
19+
inline mlir::Diagnostic &unwrap(MlirDiagnostic diagnostic) {
20+
assert(diagnostic.ptr && "unexpected null diagnostic");
21+
return *(static_cast<mlir::Diagnostic *>(diagnostic.ptr));
22+
}
23+
24+
inline MlirDiagnostic wrap(mlir::Diagnostic &diagnostic) {
25+
return {&diagnostic};
26+
}
27+
28+
#endif // MLIR_CAPI_DIAGNOSTICS_H

mlir/include/mlir/CAPI/Support.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define MLIR_CAPI_SUPPORT_H
1717

1818
#include "mlir-c/Support.h"
19+
#include "mlir/Support/LogicalResult.h"
1920
#include "llvm/ADT/StringRef.h"
2021

2122
/// Converts a StringRef into its MLIR C API equivalent.
@@ -28,4 +29,14 @@ inline llvm::StringRef unwrap(MlirStringRef ref) {
2829
return llvm::StringRef(ref.data, ref.length);
2930
}
3031

32+
inline MlirLogicalResult wrap(mlir::LogicalResult res) {
33+
if (mlir::succeeded(res))
34+
return mlirLogicalResultSuccess();
35+
return mlirLogicalResultFailure();
36+
}
37+
38+
inline mlir::LogicalResult unwrap(MlirLogicalResult res) {
39+
return mlir::success(mlirLogicalResultIsSuccess(res));
40+
}
41+
3142
#endif // MLIR_CAPI_SUPPORT_H

mlir/lib/CAPI/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Main API.
22
add_mlir_library(MLIRCAPIIR
33
AffineMap.cpp
4+
Diagnostics.cpp
45
IR.cpp
56
StandardAttributes.cpp
67
StandardTypes.cpp

mlir/lib/CAPI/IR/Diagnostics.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===- Diagnostics.cpp - C Interface for MLIR Diagnostics -----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "mlir-c/Diagnostics.h"
10+
#include "mlir/CAPI/Diagnostics.h"
11+
#include "mlir/CAPI/IR.h"
12+
#include "mlir/CAPI/Support.h"
13+
#include "mlir/CAPI/Utils.h"
14+
#include "mlir/IR/Diagnostics.h"
15+
16+
using namespace mlir;
17+
18+
void mlirDiagnosticPrint(MlirDiagnostic diagnostic, MlirStringCallback callback,
19+
void *userData) {
20+
detail::CallbackOstream stream(callback, userData);
21+
unwrap(diagnostic).print(stream);
22+
stream.flush();
23+
}
24+
25+
MlirLocation mlirDiagnosticGetLocation(MlirDiagnostic diagnostic) {
26+
return wrap(unwrap(diagnostic).getLocation());
27+
}
28+
29+
MlirDiagnosticSeverity mlirDiagnosticGetSeverity(MlirDiagnostic diagnostic) {
30+
switch (unwrap(diagnostic).getSeverity()) {
31+
case mlir::DiagnosticSeverity::Error:
32+
return MlirDiagnosticError;
33+
case mlir::DiagnosticSeverity::Warning:
34+
return MlirDiagnosticWarning;
35+
case mlir::DiagnosticSeverity::Note:
36+
return MlirDiagnosticNote;
37+
case mlir::DiagnosticSeverity::Remark:
38+
return MlirDiagnosticRemark;
39+
}
40+
llvm_unreachable("unhandled diagnostic severity");
41+
}
42+
43+
// Notes are stored in a vector, so note iterator range is a pair of
44+
// random access iterators, for which it is cheap to compute the size.
45+
intptr_t mlirDiagnosticGetNumNotes(MlirDiagnostic diagnostic) {
46+
return static_cast<intptr_t>(llvm::size(unwrap(diagnostic).getNotes()));
47+
}
48+
49+
// Notes are stored in a vector, so the iterator is a random access iterator,
50+
// cheap to advance multiple steps at a time.
51+
MlirDiagnostic mlirDiagnosticGetNote(MlirDiagnostic diagnostic, intptr_t pos) {
52+
return wrap(*std::next(unwrap(diagnostic).getNotes().begin(), pos));
53+
}
54+
55+
MlirDiagnosticHandlerID
56+
mlirContextAttachDiagnosticHandler(MlirContext context,
57+
MlirDiagnosticHandler handler) {
58+
assert(handler && "unexpected null diagnostic handler");
59+
DiagnosticEngine::HandlerID id =
60+
unwrap(context)->getDiagEngine().registerHandler(
61+
[handler](Diagnostic &diagnostic) {
62+
return unwrap(handler(wrap(diagnostic)));
63+
});
64+
return static_cast<MlirDiagnosticHandlerID>(id);
65+
}
66+
67+
void mlirContextDetachDiagnosticHandler(MlirContext context,
68+
MlirDiagnosticHandlerID id) {
69+
unwrap(context)->getDiagEngine().eraseHandler(
70+
static_cast<DiagnosticEngine::HandlerID>(id));
71+
}
72+
73+
void mlirEmitError(MlirLocation location, const char *message) {
74+
emitError(unwrap(location)) << message;
75+
}

mlir/test/CAPI/ir.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
/* RUN: mlir-capi-ir-test 2>&1 | FileCheck %s
1111
*/
1212

13-
#include "mlir-c/IR.h"
1413
#include "mlir-c/AffineMap.h"
14+
#include "mlir-c/Diagnostics.h"
15+
#include "mlir-c/IR.h"
1516
#include "mlir-c/Registration.h"
1617
#include "mlir-c/StandardAttributes.h"
1718
#include "mlir-c/StandardDialect.h"
@@ -827,6 +828,28 @@ int registerOnlyStd() {
827828
return 0;
828829
}
829830

831+
// Wraps a diagnostic into additional text we can match against.
832+
MlirLogicalResult errorHandler(MlirDiagnostic diagnostic) {
833+
fprintf(stderr, "processing diagnostic <<\n");
834+
mlirDiagnosticPrint(diagnostic, printToStderr, NULL);
835+
fprintf(stderr, "\n");
836+
MlirLocation loc = mlirDiagnosticGetLocation(diagnostic);
837+
mlirLocationPrint(loc, printToStderr, NULL);
838+
assert(mlirDiagnosticGetNumNotes(diagnostic) == 0);
839+
fprintf(stderr, ">> end of diagnostic\n");
840+
return mlirLogicalResultSuccess();
841+
}
842+
843+
void testDiagnostics() {
844+
MlirContext ctx = mlirContextCreate();
845+
MlirDiagnosticHandlerID id =
846+
mlirContextAttachDiagnosticHandler(ctx, errorHandler);
847+
MlirLocation loc = mlirLocationUnknownGet(ctx);
848+
mlirEmitError(loc, "test diagnostics");
849+
mlirContextDetachDiagnosticHandler(ctx, id);
850+
mlirEmitError(loc, "more test diagnostics");
851+
}
852+
830853
int main() {
831854
MlirContext ctx = mlirContextCreate();
832855
mlirRegisterAllDialects(ctx);
@@ -982,5 +1005,16 @@ int main() {
9821005

9831006
mlirContextDestroy(ctx);
9841007

1008+
fprintf(stderr, "@test_diagnostics\n");
1009+
testDiagnostics();
1010+
// clang-format off
1011+
// CHECK-LABEL: @test_diagnostics
1012+
// CHECK: processing diagnostic <<
1013+
// CHECK: test diagnostics
1014+
// CHECK: loc(unknown)
1015+
// CHECK: >> end of diagnostic
1016+
// CHECK-NOT: processing diagnostic
1017+
// CHECK: more test diagnostics
1018+
9851019
return 0;
9861020
}

0 commit comments

Comments
 (0)