Skip to content

Commit b6a98b9

Browse files
authored
[mlir] Report line number from file rather than split (llvm#150982)
Add convention for lexer if the last file is contained in the first, then the first is used for error reporting. This requires that these two overlap to make it easy to find the corresponding spots. Enables going from ``` within split at mlir/test/IR/invalid.mlir::10 offset :6:9: error: reference to an undefined block ``` to ``` mlir/test/IR/invalid.mlir:15:9: error: reference to an undefined block ``` This does change the split to not produce always null terminated buffers and tools that need it, need to do so themselves (which is mostly by copying - this may have little actual impact as previously this was a copy too).
1 parent b30034d commit b6a98b9

File tree

9 files changed

+127
-37
lines changed

9 files changed

+127
-37
lines changed

mlir/include/mlir/IR/Diagnostics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,10 @@ class SourceMgrDiagnosticVerifierHandler : public SourceMgrDiagnosticHandler {
639639
/// verified correctly, failure otherwise.
640640
LogicalResult verify();
641641

642+
/// Register this handler with the given context. This is intended for use
643+
/// with the splitAndProcessBuffer function.
644+
void registerInContext(MLIRContext *ctx);
645+
642646
private:
643647
/// Process a single diagnostic.
644648
void process(Diagnostic &diag);

mlir/include/mlir/Support/ToolUtilities.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,16 @@
2121

2222
namespace llvm {
2323
class MemoryBuffer;
24+
class MemoryBufferRef;
2425
} // namespace llvm
2526

2627
namespace mlir {
28+
// A function that processes a chunk of a buffer and writes the result to an
29+
// output stream.
2730
using ChunkBufferHandler = function_ref<LogicalResult(
31+
std::unique_ptr<llvm::MemoryBuffer> chunkBuffer,
32+
const llvm::MemoryBufferRef &sourceBuffer, raw_ostream &os)>;
33+
using NoSourceChunkBufferHandler = function_ref<LogicalResult(
2834
std::unique_ptr<llvm::MemoryBuffer> chunkBuffer, raw_ostream &os)>;
2935

3036
extern inline const char *const kDefaultSplitMarker = "// -----";
@@ -45,6 +51,15 @@ splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
4551
ChunkBufferHandler processChunkBuffer, raw_ostream &os,
4652
llvm::StringRef inputSplitMarker = kDefaultSplitMarker,
4753
llvm::StringRef outputSplitMarker = "");
54+
55+
/// Same as above, but for case where the original buffer is not used while
56+
/// processing the chunk.
57+
LogicalResult
58+
splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
59+
NoSourceChunkBufferHandler processChunkBuffer,
60+
raw_ostream &os,
61+
llvm::StringRef inputSplitMarker = kDefaultSplitMarker,
62+
llvm::StringRef outputSplitMarker = "");
4863
} // namespace mlir
4964

5065
#endif // MLIR_SUPPORT_TOOLUTILITIES_H

mlir/lib/IR/Diagnostics.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -821,15 +821,7 @@ SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
821821
for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
822822
(void)impl->computeExpectedDiags(out, mgr, mgr.getMemoryBuffer(i + 1));
823823

824-
// Register a handler to verify the diagnostics.
825-
setHandler([&](Diagnostic &diag) {
826-
// Process the main diagnostics.
827-
process(diag);
828-
829-
// Process each of the notes.
830-
for (auto &note : diag.getNotes())
831-
process(note);
832-
});
824+
registerInContext(ctx);
833825
}
834826

835827
SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
@@ -862,6 +854,17 @@ LogicalResult SourceMgrDiagnosticVerifierHandler::verify() {
862854
return impl->status;
863855
}
864856

857+
void SourceMgrDiagnosticVerifierHandler::registerInContext(MLIRContext *ctx) {
858+
ctx->getDiagEngine().registerHandler([&](Diagnostic &diag) {
859+
// Process the main diagnostics.
860+
process(diag);
861+
862+
// Process each of the notes.
863+
for (auto &note : diag.getNotes())
864+
process(note);
865+
});
866+
}
867+
865868
/// Process a single diagnostic.
866869
void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
867870
return process(diag.getLocation(), diag.str(), diag.getSeverity());

mlir/lib/Support/ToolUtilities.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "mlir/Support/LLVM.h"
1515
#include "llvm/Support/SourceMgr.h"
1616
#include "llvm/Support/raw_ostream.h"
17+
#include <string>
18+
#include <utility>
1719

1820
using namespace mlir;
1921

@@ -22,18 +24,18 @@ mlir::splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
2224
ChunkBufferHandler processChunkBuffer,
2325
raw_ostream &os, llvm::StringRef inputSplitMarker,
2426
llvm::StringRef outputSplitMarker) {
27+
llvm::MemoryBufferRef originalBufferRef = originalBuffer->getMemBufferRef();
2528
// If splitting is disabled, we process the full input buffer.
2629
if (inputSplitMarker.empty())
27-
return processChunkBuffer(std::move(originalBuffer), os);
30+
return processChunkBuffer(std::move(originalBuffer), originalBufferRef, os);
2831

2932
const int inputSplitMarkerLen = inputSplitMarker.size();
3033

31-
auto *origMemBuffer = originalBuffer.get();
3234
SmallVector<StringRef, 8> rawSourceBuffers;
3335
const int checkLen = 2;
3436
// Split dropping the last checkLen chars to enable flagging near misses.
35-
origMemBuffer->getBuffer().split(rawSourceBuffers,
36-
inputSplitMarker.drop_back(checkLen));
37+
originalBufferRef.getBuffer().split(rawSourceBuffers,
38+
inputSplitMarker.drop_back(checkLen));
3739
if (rawSourceBuffers.empty())
3840
return success();
3941

@@ -79,11 +81,17 @@ mlir::splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
7981
auto interleaveFn = [&](StringRef subBuffer) {
8082
auto splitLoc = SMLoc::getFromPointer(subBuffer.data());
8183
unsigned splitLine = fileSourceMgr.getLineAndColumn(splitLoc).first;
82-
auto subMemBuffer = llvm::MemoryBuffer::getMemBufferCopy(
83-
subBuffer, Twine("within split at ") +
84-
origMemBuffer->getBufferIdentifier() + ":" +
85-
Twine(splitLine) + " offset ");
86-
if (failed(processChunkBuffer(std::move(subMemBuffer), os)))
84+
std::string name((Twine("within split at ") +
85+
originalBufferRef.getBufferIdentifier() + ":" +
86+
Twine(splitLine) + " offset ")
87+
.str());
88+
// Use MemoryBufferRef to avoid copying the buffer & keep at same location
89+
// relative to the original buffer.
90+
auto subMemBuffer =
91+
llvm::MemoryBuffer::getMemBuffer(llvm::MemoryBufferRef(subBuffer, name),
92+
/*RequiresNullTerminator=*/false);
93+
if (failed(
94+
processChunkBuffer(std::move(subMemBuffer), originalBufferRef, os)))
8795
hadFailure = true;
8896
};
8997
llvm::interleave(sourceBuffers, os, interleaveFn,
@@ -92,3 +100,16 @@ mlir::splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
92100
// If any fails, then return a failure of the tool.
93101
return failure(hadFailure);
94102
}
103+
104+
LogicalResult
105+
mlir::splitAndProcessBuffer(std::unique_ptr<llvm::MemoryBuffer> originalBuffer,
106+
NoSourceChunkBufferHandler processChunkBuffer,
107+
raw_ostream &os, llvm::StringRef inputSplitMarker,
108+
llvm::StringRef outputSplitMarker) {
109+
auto process = [&](std::unique_ptr<llvm::MemoryBuffer> chunkBuffer,
110+
const llvm::MemoryBufferRef &, raw_ostream &os) {
111+
return processChunkBuffer(std::move(chunkBuffer), os);
112+
};
113+
return splitAndProcessBuffer(std::move(originalBuffer), process, os,
114+
inputSplitMarker, outputSplitMarker);
115+
}

mlir/lib/Tools/mlir-opt/MlirOptMain.cpp

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -508,20 +508,29 @@ performActions(raw_ostream &os,
508508

509509
/// Parses the memory buffer. If successfully, run a series of passes against
510510
/// it and print the result.
511-
static LogicalResult processBuffer(raw_ostream &os,
512-
std::unique_ptr<MemoryBuffer> ownedBuffer,
513-
const MlirOptMainConfig &config,
514-
DialectRegistry &registry,
515-
llvm::ThreadPoolInterface *threadPool) {
511+
static LogicalResult
512+
processBuffer(raw_ostream &os, std::unique_ptr<MemoryBuffer> ownedBuffer,
513+
llvm::MemoryBufferRef sourceBuffer,
514+
const MlirOptMainConfig &config, DialectRegistry &registry,
515+
SourceMgrDiagnosticVerifierHandler *verifyHandler,
516+
llvm::ThreadPoolInterface *threadPool) {
516517
// Tell sourceMgr about this buffer, which is what the parser will pick up.
517518
auto sourceMgr = std::make_shared<SourceMgr>();
519+
// Add the original buffer to the source manager to use for determining
520+
// locations.
521+
sourceMgr->AddNewSourceBuffer(
522+
llvm::MemoryBuffer::getMemBuffer(sourceBuffer,
523+
/*RequiresNullTerminator=*/false),
524+
SMLoc());
518525
sourceMgr->AddNewSourceBuffer(std::move(ownedBuffer), SMLoc());
519526

520527
// Create a context just for the current buffer. Disable threading on creation
521528
// since we'll inject the thread-pool separately.
522529
MLIRContext context(registry, MLIRContext::Threading::DISABLED);
523530
if (threadPool)
524531
context.setThreadPool(*threadPool);
532+
if (verifyHandler)
533+
verifyHandler->registerInContext(&context);
525534

526535
StringRef irdlFile = config.getIrdlFile();
527536
if (!irdlFile.empty() && failed(loadIRDLDialects(irdlFile, context)))
@@ -545,17 +554,12 @@ static LogicalResult processBuffer(raw_ostream &os,
545554
return performActions(os, sourceMgr, &context, config);
546555
}
547556

548-
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
549-
*sourceMgr, &context, config.verifyDiagnosticsLevel());
550-
551557
// Do any processing requested by command line flags. We don't care whether
552558
// these actions succeed or fail, we only care what diagnostics they produce
553559
// and whether they match our expectations.
554560
(void)performActions(os, sourceMgr, &context, config);
555561

556-
// Verify the diagnostic handler to make sure that each of the diagnostics
557-
// matched.
558-
return sourceMgrHandler.verify();
562+
return success();
559563
}
560564

561565
std::pair<std::string, std::string>
@@ -624,14 +628,31 @@ LogicalResult mlir::MlirOptMain(llvm::raw_ostream &outputStream,
624628
if (threadPoolCtx.isMultithreadingEnabled())
625629
threadPool = &threadPoolCtx.getThreadPool();
626630

631+
SourceMgr sourceMgr;
632+
sourceMgr.AddNewSourceBuffer(
633+
llvm::MemoryBuffer::getMemBuffer(buffer->getMemBufferRef(),
634+
/*RequiresNullTerminator=*/false),
635+
SMLoc());
636+
// Note: this creates a verifier handler independent of the the flag set, as
637+
// internally if the flag is not set, a new scoped diagnostic handler is
638+
// created which would intercept the diagnostics and verify them.
639+
SourceMgrDiagnosticVerifierHandler sourceMgrHandler(
640+
sourceMgr, &threadPoolCtx, config.verifyDiagnosticsLevel());
627641
auto chunkFn = [&](std::unique_ptr<MemoryBuffer> chunkBuffer,
628-
raw_ostream &os) {
629-
return processBuffer(os, std::move(chunkBuffer), config, registry,
630-
threadPool);
642+
llvm::MemoryBufferRef sourceBuffer, raw_ostream &os) {
643+
return processBuffer(
644+
os, std::move(chunkBuffer), sourceBuffer, config, registry,
645+
config.shouldVerifyDiagnostics() ? &sourceMgrHandler : nullptr,
646+
threadPool);
631647
};
632-
return splitAndProcessBuffer(std::move(buffer), chunkFn, outputStream,
633-
config.inputSplitMarker(),
634-
config.outputSplitMarker());
648+
LogicalResult status = splitAndProcessBuffer(
649+
llvm::MemoryBuffer::getMemBuffer(buffer->getMemBufferRef(),
650+
/*RequiresNullTerminator=*/false),
651+
chunkFn, outputStream, config.inputSplitMarker(),
652+
config.outputSplitMarker());
653+
if (config.shouldVerifyDiagnostics() && failed(sourceMgrHandler.verify()))
654+
status = failure();
655+
return status;
635656
}
636657

637658
LogicalResult mlir::MlirOptMain(int argc, char **argv,

mlir/lib/Tools/mlir-translate/MlirTranslateMain.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ LogicalResult mlir::mlirTranslateMain(int argc, char **argv,
135135
// Processes the memory buffer with a new MLIRContext.
136136
auto processBuffer = [&](std::unique_ptr<llvm::MemoryBuffer> ownedBuffer,
137137
raw_ostream &os) {
138+
// Many of the translations expect a null-terminated buffer while splitting
139+
// the buffer does not guarantee null-termination. Make a copy of the buffer
140+
// to ensure null-termination.
141+
if (!ownedBuffer->getBuffer().ends_with('\0')) {
142+
ownedBuffer = llvm::MemoryBuffer::getMemBufferCopy(
143+
ownedBuffer->getBuffer(), ownedBuffer->getBufferIdentifier());
144+
}
138145
// Temporary buffers for chained translation processing.
139146
std::string dataIn;
140147
std::string dataOut;

mlir/test/IR/diagnostic-nosplit.mlir

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: not mlir-opt %s -o - --split-input-file 2>&1 | FileCheck %s
2+
// This test verifies that diagnostic handler doesn't emit splits.
3+
4+
5+
// -----
6+
7+
8+
9+
func.func @constant_out_of_range() {
10+
// CHECK: mlir:11:8: error: 'arith.constant'
11+
%x = "arith.constant"() {value = 100} : () -> i1
12+
return
13+
}

mlir/test/IR/top-level.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ func.func private @foo()
66

77
// -----
88

9-
// expected-error@-3 {{source must contain a single top-level operation, found: 2}}
9+
// expected-error@-9 {{source must contain a single top-level operation, found: 2}}
1010
func.func private @bar()
1111
func.func private @baz()
1212

1313
// -----
1414

15-
// expected-error@-3 {{source must contain a single top-level operation, found: 0}}
15+
// expected-error@-15 {{source must contain a single top-level operation, found: 0}}

mlir/tools/mlir-pdll/mlir-pdll.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ int main(int argc, char **argv) {
201201
llvm::raw_string_ostream outputStrOS(outputStr);
202202
auto processFn = [&](std::unique_ptr<llvm::MemoryBuffer> chunkBuffer,
203203
raw_ostream &os) {
204+
// Split does not guarantee null-termination. Make a copy of the buffer to
205+
// ensure null-termination.
206+
if (!chunkBuffer->getBuffer().ends_with('\0')) {
207+
chunkBuffer = llvm::MemoryBuffer::getMemBufferCopy(
208+
chunkBuffer->getBuffer(), chunkBuffer->getBufferIdentifier());
209+
}
204210
return processBuffer(os, std::move(chunkBuffer), outputType, includeDirs,
205211
dumpODS, includedFiles);
206212
};

0 commit comments

Comments
 (0)