blob: 0e8d753c1d3b4334d01cfafc94865d1c6cce6a2f [file] [edit]
//===- remarks.c - Test of the remark engine C API ------------------------===//
//
// 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-remarks-test 2>&1 | FileCheck %s
*/
#include "mlir-c/Remarks.h"
#include "mlir-c/Diagnostics.h"
#include "mlir-c/IR.h"
#include "mlir-c/Support.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void printToStderr(MlirStringRef str, void *userData) {
(void)userData;
fwrite(str.data, 1, str.length, stderr);
}
static MlirStringRef str(const char *s) {
return mlirStringRefCreateFromCString(s);
}
static MlirRemarkCategories allCategories(const char *filter) {
MlirRemarkCategories cats;
memset(&cats, 0, sizeof(cats));
cats.all = str(filter);
return cats;
}
/// Prints every field of the remark and counts the deliveries in userData.
static void remarkCallback(MlirRemark remark, void *userData) {
int *count = (int *)userData;
++*count;
const char *kind = "unknown";
switch (mlirRemarkGetKind(remark)) {
case MlirRemarkKindPassed:
kind = "passed";
break;
case MlirRemarkKindMissed:
kind = "missed";
break;
case MlirRemarkKindFailure:
kind = "failure";
break;
case MlirRemarkKindAnalysis:
kind = "analysis";
break;
case MlirRemarkKindUnknown:
break;
}
MlirStringRef name = mlirRemarkGetRemarkName(remark);
MlirStringRef category = mlirRemarkGetCategoryName(remark);
MlirStringRef fullCategory = mlirRemarkGetFullCategoryName(remark);
MlirStringRef function = mlirRemarkGetFunctionName(remark);
fprintf(stderr,
"remark #%d kind=%s name=%.*s category=%.*s full=%.*s "
"function=%.*s id=%" PRIu64 "\n",
*count, kind, (int)name.length, name.data, (int)category.length,
category.data, (int)fullCategory.length, fullCategory.data,
(int)function.length, function.data, mlirRemarkGetId(remark));
intptr_t numArgs = mlirRemarkGetNumArgs(remark);
for (intptr_t i = 0; i < numArgs; ++i) {
MlirStringRef key = mlirRemarkGetArgKey(remark, i);
MlirStringRef value = mlirRemarkGetArgValue(remark, i);
fprintf(stderr, " arg %.*s=%.*s\n", (int)key.length, key.data,
(int)value.length, value.data);
}
fprintf(stderr, " print: ");
mlirRemarkPrint(remark, printToStderr, NULL);
fprintf(stderr, "\n location: ");
mlirLocationPrint(mlirRemarkGetLocation(remark), printToStderr, NULL);
fprintf(stderr, "\n");
}
static void deleteUserData(void *userData) {
fprintf(stderr, "deleteUserData called (count=%d)\n", *(int *)userData);
}
static void countRemark(MlirRemark remark, void *userData) {
(void)remark;
++*(int *)userData;
}
static bool emit(MlirLocation loc, MlirRemarkKind kind, const char *name,
const char *category, const char *message) {
MlirStringRef keys[1] = {str("factor")};
MlirStringRef values[1] = {str("4")};
return mlirEmitOptimizationRemark(loc, kind, str(name), str(category),
str("inner"), str("main"), str(message), 1,
keys, values);
}
// CHECK-LABEL: @testCallbackStreamer
static void testCallbackStreamer(void) {
fprintf(stderr, "@testCallbackStreamer\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationFileLineColGet(ctx, str("remarks.c"), 7, 3);
// CHECK: no engine: enabled=0 emitted=0
bool emitted = emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "dropped");
fprintf(stderr, "no engine: enabled=%d emitted=%d\n",
mlirContextHasRemarkEngine(ctx), emitted);
int count = 0;
MlirLogicalResult res = mlirContextEnableOptimizationRemarksWithCallback(
ctx, allCategories("Loop"), MlirRemarkPolicyAll, remarkCallback, &count,
deleteUserData, /*printAsEmitRemarks=*/false);
// CHECK: enabled=1 ok=1
fprintf(stderr, "enabled=%d ok=%d\n", mlirContextHasRemarkEngine(ctx),
mlirLogicalResultIsSuccess(res));
// clang-format off
// CHECK: remark #1 kind=passed name=Unroll category=Loop full=Loop:inner function=main id=1
// CHECK: arg RemarkId=1
// CHECK: arg Remark=unrolled
// CHECK: arg factor=4
// CHECK: print: [Passed] Unroll | Category:Loop:inner | Function=main | Remark=unrolled, RemarkId=1, factor=4
// CHECK: location: loc("remarks.c":7:3)
// CHECK: emitted=1
// clang-format on
emitted = emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "unrolled");
fprintf(stderr, "emitted=%d\n", emitted);
// CHECK-NOT: name=Vectorize
// CHECK: count after filtered emit=1
emit(loc, MlirRemarkKindMissed, "Vectorize", "Vector", "not matched");
fprintf(stderr, "count after filtered emit=%d\n", count);
// CHECK: remark #2 kind=missed name=Interchange
// CHECK: remark #3 kind=failure name=Fuse
// CHECK: remark #4 kind=analysis name=TripCount
emit(loc, MlirRemarkKindMissed, "Interchange", "Loop", "no");
emit(loc, MlirRemarkKindFailure, "Fuse", "Loop", "no");
emit(loc, MlirRemarkKindAnalysis, "TripCount", "Loop", "4");
// CHECK: deleteUserData called (count=4)
// CHECK: after finalize: enabled=0 count=4
mlirContextFinalizeOptimizationRemarks(ctx);
fprintf(stderr, "after finalize: enabled=%d count=%d\n",
mlirContextHasRemarkEngine(ctx), count);
// Finalizing without an engine is a no-op.
mlirContextFinalizeOptimizationRemarks(ctx);
mlirContextDestroy(ctx);
}
// CHECK-LABEL: @testFinalPolicy
static void testFinalPolicy(void) {
fprintf(stderr, "@testFinalPolicy\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationUnknownGet(ctx);
int count = 0;
MlirRemarkCategories cats;
memset(&cats, 0, sizeof(cats));
cats.passed = str("Loop");
MlirLogicalResult res = mlirContextEnableOptimizationRemarksWithCallback(
ctx, cats, MlirRemarkPolicyFinal, remarkCallback, &count, NULL, false);
assert(mlirLogicalResultIsSuccess(res));
emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "postponed");
// Only the passed filter is set: a missed remark is not reported.
emit(loc, MlirRemarkKindMissed, "Interchange", "Loop", "dropped");
// CHECK: before finalize count=0
fprintf(stderr, "before finalize count=%d\n", count);
// CHECK: remark #1 kind=passed name=Unroll
// CHECK: after finalize count=1
mlirContextFinalizeOptimizationRemarks(ctx);
fprintf(stderr, "after finalize count=%d\n", count);
mlirContextDestroy(ctx);
}
static MlirLogicalResult diagnosticHandler(MlirDiagnostic diagnostic,
void *userData) {
(void)userData;
const char *severity = "other";
if (mlirDiagnosticGetSeverity(diagnostic) == MlirDiagnosticRemark)
severity = "remark";
fprintf(stderr, "diagnostic %s: ", severity);
mlirDiagnosticPrint(diagnostic, printToStderr, NULL);
fprintf(stderr, "\n");
return mlirLogicalResultSuccess();
}
// CHECK-LABEL: @testEmitAsDiagnostics
static void testEmitAsDiagnostics(void) {
fprintf(stderr, "@testEmitAsDiagnostics\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationUnknownGet(ctx);
MlirDiagnosticHandlerID id =
mlirContextAttachDiagnosticHandler(ctx, diagnosticHandler, NULL, NULL);
MlirLogicalResult res = mlirContextEnableOptimizationRemarks(
ctx, allCategories(".*"), MlirRemarkPolicyAll,
/*printAsEmitRemarks=*/true);
assert(mlirLogicalResultIsSuccess(res));
// clang-format off
// CHECK: diagnostic remark: [Analysis] TripCount | Category:Loop:inner | Function=main | Remark=4, RemarkId=1, factor=4
// clang-format on
emit(loc, MlirRemarkKindAnalysis, "TripCount", "Loop", "4");
mlirContextFinalizeOptimizationRemarks(ctx);
mlirContextDetachDiagnosticHandler(ctx, id);
mlirContextDestroy(ctx);
}
// CHECK-LABEL: @testFileStreamer
static void testFileStreamer(void) {
fprintf(stderr, "@testFileStreamer\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationUnknownGet(ctx);
char yamlPath[] = "mlir-capi-remarks-test.yaml";
MlirLogicalResult res = mlirContextEnableOptimizationRemarksToFile(
ctx, allCategories(".*"), MlirRemarkPolicyAll, MlirRemarkFileFormatYAML,
str(yamlPath), /*printAsEmitRemarks=*/false);
// CHECK: yaml enable ok=1
fprintf(stderr, "yaml enable ok=%d\n", mlirLogicalResultIsSuccess(res));
emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "to file");
mlirContextFinalizeOptimizationRemarks(ctx);
FILE *f = fopen(yamlPath, "r");
bool found = false;
if (f) {
char line[256];
while (fgets(line, sizeof(line), f))
if (strstr(line, "Unroll"))
found = true;
fclose(f);
remove(yamlPath);
}
// CHECK: yaml has remark=1
fprintf(stderr, "yaml has remark=%d\n", found);
// The bitstream file starts with the LLVM remark bitstream magic.
char bitstreamPath[] = "mlir-capi-remarks-test.bitstream";
res = mlirContextEnableOptimizationRemarksToFile(
ctx, allCategories(".*"), MlirRemarkPolicyFinal,
MlirRemarkFileFormatBitstream, str(bitstreamPath),
/*printAsEmitRemarks=*/false);
// CHECK: bitstream enable ok=1
fprintf(stderr, "bitstream enable ok=%d\n", mlirLogicalResultIsSuccess(res));
emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "to file");
mlirContextFinalizeOptimizationRemarks(ctx);
char magic[5] = {0};
f = fopen(bitstreamPath, "rb");
if (f) {
if (fread(magic, 1, 4, f) != 4)
magic[0] = '\0';
fclose(f);
remove(bitstreamPath);
}
// CHECK: bitstream magic=RMRK
fprintf(stderr, "bitstream magic=%s\n", magic);
mlirContextDestroy(ctx);
}
// CHECK-LABEL: @testAccessorDefaults
static void testAccessorDefaults(void) {
fprintf(stderr, "@testAccessorDefaults\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationUnknownGet(ctx);
int count = 0;
MlirLogicalResult res = mlirContextEnableOptimizationRemarksWithCallback(
ctx, allCategories(".*"), MlirRemarkPolicyAll, remarkCallback, &count,
NULL, false);
assert(mlirLogicalResultIsSuccess(res));
// Unset names fall back to placeholders, the only argument is the id, and
// null argument arrays are accepted when numArgs is 0.
// clang-format off
// CHECK: remark #1 kind=passed name=<unknown remark name> category= full= function=<unknown function> id=1
// CHECK: arg RemarkId=1
// CHECK: print: [Passed] | RemarkId=1
// CHECK: location: loc(unknown)
// CHECK: emitted=1
// clang-format on
bool emitted =
mlirEmitOptimizationRemark(loc, MlirRemarkKindPassed, str(""), str(""),
str(""), str(""), str(""), 0, NULL, NULL);
fprintf(stderr, "emitted=%d\n", emitted);
// A sub-category alone is the full category name.
// clang-format off
// CHECK: remark #2 kind=missed name=Name category= full=Sub function=<unknown function> id=2
// CHECK: print: [Missed] Name | Category:Sub | RemarkId=2
// clang-format on
mlirEmitOptimizationRemark(loc, MlirRemarkKindMissed, str("Name"), str(""),
str("Sub"), str(""), str(""), 0, NULL, NULL);
// CHECK: unknown emitted=0
emitted = emit(loc, MlirRemarkKindUnknown, "Name", "Loop", "");
fprintf(stderr, "unknown emitted=%d\n", emitted);
mlirContextFinalizeOptimizationRemarks(ctx);
mlirContextDestroy(ctx);
}
// CHECK-LABEL: @testEnableWhileEnabled
static void testEnableWhileEnabled(void) {
fprintf(stderr, "@testEnableWhileEnabled\n");
MlirContext ctx = mlirContextCreate();
MlirLocation loc = mlirLocationUnknownGet(ctx);
int first = 0, second = 0;
MlirLogicalResult res = mlirContextEnableOptimizationRemarksWithCallback(
ctx, allCategories(".*"), MlirRemarkPolicyAll, countRemark, &first,
deleteUserData, false);
// CHECK: first ok=1
fprintf(stderr, "first ok=%d\n", mlirLogicalResultIsSuccess(res));
// Every enable variant fails while an engine is active; the callback
// variant releases the user data it was handed right away.
// CHECK: deleteUserData called (count=0)
// CHECK: callback ok=0 file ok=0 plain ok=0
res = mlirContextEnableOptimizationRemarksWithCallback(
ctx, allCategories(".*"), MlirRemarkPolicyAll, countRemark, &second,
deleteUserData, false);
MlirLogicalResult fileRes = mlirContextEnableOptimizationRemarksToFile(
ctx, allCategories(".*"), MlirRemarkPolicyAll, MlirRemarkFileFormatYAML,
str("mlir-capi-remarks-test-unused.yaml"), false);
MlirLogicalResult plainRes = mlirContextEnableOptimizationRemarks(
ctx, allCategories(".*"), MlirRemarkPolicyAll, false);
fprintf(stderr, "callback ok=%d file ok=%d plain ok=%d\n",
mlirLogicalResultIsSuccess(res), mlirLogicalResultIsSuccess(fileRes),
mlirLogicalResultIsSuccess(plainRes));
emit(loc, MlirRemarkKindPassed, "Unroll", "Loop", "");
// CHECK: first=1 second=0
fprintf(stderr, "first=%d second=%d\n", first, second);
// Destroying the context with a live engine releases its user data.
// CHECK: deleteUserData called (count=1)
// CHECK: destroyed
mlirContextDestroy(ctx);
fprintf(stderr, "destroyed\n");
}
int main(void) {
testCallbackStreamer();
testFinalPolicy();
testEmitAsDiagnostics();
testFileStreamer();
testAccessorDefaults();
testEnableWhileEnabled();
return 0;
}