Skip to content

[mlir][BytecodeReader] Fix FusedLoc bytecode builder #99632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

uenoku
Copy link
Member

@uenoku uenoku commented Jul 19, 2024

FusedLoc::get(MLIRContext*, ArrayRef<Location>) is not guaranteed to return FusedLoc but it is used when reading bytecode. This PR changes to use a different getter FusedLoc::get(MLIRContext*, ArrayRef<Location>, Attribute attribute) which always returns FusedLoc.

Fix #99626.

@llvmbot llvmbot added mlir:core MLIR Core Infrastructure mlir mlir:ods labels Jul 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 19, 2024

@llvm/pr-subscribers-mlir-ods
@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-core

Author: Hideto Ueno (uenoku)

Changes

FusedLoc::get(MLIRContext*, ArrayRef&lt;Location&gt;) is not guaranteed to return FusedLoc but it is used when reading bytecode. This PR changes to use a different getter FusedLoc::get(MLIRContext*, ArrayRef&lt;Location&gt;, Attribute attribute) which always returns FusedLoc.

Fix #99626.


Full diff: https://github.com/llvm/llvm-project/pull/99632.diff

2 Files Affected:

  • (modified) mlir/include/mlir/IR/BuiltinDialectBytecode.td (+3-2)
  • (modified) mlir/unittests/Bytecode/BytecodeTest.cpp (+22)
diff --git a/mlir/include/mlir/IR/BuiltinDialectBytecode.td b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
index f50b5dd7ad822..0062bcbf6ea11 100644
--- a/mlir/include/mlir/IR/BuiltinDialectBytecode.td
+++ b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
@@ -101,12 +101,12 @@ def FileLineColLoc : DialectAttribute<(attr
   VarInt:$column
 )>;
 
-let cType = "FusedLoc",
-    cBuilder = "cast<FusedLoc>(get<FusedLoc>(context, $_args))" in {
+let cType = "FusedLoc" in {
 def FusedLoc : DialectAttribute<(attr
   Array<Location>:$locations
 )> {
   let printerPredicate = "!$_val.getMetadata()";
+  let cBuilder = "cast<FusedLoc>(get<FusedLoc>(context, $_args, Attribute()))";
 }
 
 def FusedLocWithMetadata : DialectAttribute<(attr
@@ -114,6 +114,7 @@ def FusedLocWithMetadata : DialectAttribute<(attr
   Attribute:$metadata
 )> {
   let printerPredicate = "$_val.getMetadata()";
+  let cBuilder = "cast<FusedLoc>(get<FusedLoc>(context, $_args))";
 }
 }
 
diff --git a/mlir/unittests/Bytecode/BytecodeTest.cpp b/mlir/unittests/Bytecode/BytecodeTest.cpp
index a37a2afc22645..43649819057d0 100644
--- a/mlir/unittests/Bytecode/BytecodeTest.cpp
+++ b/mlir/unittests/Bytecode/BytecodeTest.cpp
@@ -10,6 +10,7 @@
 #include "mlir/Bytecode/BytecodeWriter.h"
 #include "mlir/IR/AsmState.h"
 #include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/OpImplementation.h"
 #include "mlir/IR/OwningOpRef.h"
 #include "mlir/Parser/Parser.h"
@@ -148,3 +149,24 @@ TEST(Bytecode, OpWithoutProperties) {
   EXPECT_TRUE(OperationEquivalence::computeHash(op.get()) ==
               OperationEquivalence::computeHash(roundtripped));
 }
+
+TEST(Bytecode, FusedLocCrash) {
+  MLIRContext context;
+  OpBuilder builder(&context);
+  SmallVector<Location> locs;
+  FusedLoc fusedLoc = FusedLoc::get(&context, locs, {});
+
+  auto module = builder.create<mlir::ModuleOp>(fusedLoc, "test");
+
+  // Write the module to bytecode
+  std::string buffer;
+  llvm::raw_string_ostream ostream(buffer);
+  ASSERT_TRUE(succeeded(writeBytecodeToFile(module, ostream)));
+  ostream.flush();
+
+  // Parse it back
+  ParserConfig parseConfig(&context);
+  OwningOpRef<Operation *> roundTripModule =
+      parseSourceString<Operation *>(buffer, parseConfig);
+  ASSERT_TRUE(roundTripModule);
+}

OwningOpRef<Operation *> roundTripModule =
parseSourceString<Operation *>(buffer, parseConfig);
ASSERT_TRUE(roundTripModule);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write a lit test running with mlir-opt for this instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this issue is not reproducible from mlir textual format since fused[] is converted into unknown in the asm parser. What do you think?

$ cat bar.mlir
module @TestLocFused {
} loc(#loc)
#loc = loc(fused[])

$ mlir-opt bar.mlir -mlir-print-debuginfo
module @TestLocFused {
} loc(#loc)
#loc = loc(unknown)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The in-memory IR should round-trip to both bytecode and text: if it isn't the case right now we need to fix both the bytecode and textual printer/parser.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Textual printer/parser are working fine since FusedLoc is converted into Unknown in the parser. I think the bug is only in BytecodeReader.

Copy link
Contributor

@mikeurbach mikeurbach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not super familiar with the FusedLoc builders, but I came to say thank you. This fixes a confusing bug that's bitten me on and off for months.

@uenoku uenoku requested a review from joker-eph July 19, 2024 21:22
MLIRContext context;
OpBuilder builder(&context);
SmallVector<Location> locs;
FusedLoc fusedLoc = FusedLoc::get(&context, locs, {});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we disallow this at the constructor? It feels weird to have a fused location with no locations. I don't know when one would actually want this behavior to be valid.

Copy link
Member Author

@uenoku uenoku Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disallowing empty location looks reasonable but the issue itself is not specific to empty location. We should be able to get the same failure when fused loc has a single location (this is the code) e.g:

FileLineColLoc loc = ...;
FusedLoc fusedLoc = FusedLoc::get(&context, {loc}, {}) 

@jpienaar
Copy link
Member

I'm not super familiar with the FusedLoc builders, but I came to say thank you. This fixes a confusing bug that's bitten me on and off for months.

How do you even get in this situation? And is intentional? (UnknownLoc wouldn't support FusedLoc metadata, so this isn't a lossless result of the builder).

@uenoku
Copy link
Member Author

uenoku commented Jul 21, 2024

How do you even get in this situation? And is intentional? (UnknownLoc wouldn't support FusedLoc metadata, so this isn't a lossless result of the builder).

CIRCT has a pass that drops specific locations (basically same as mlir::StripDebugInfoPass but CIRCT one takes a predicate). In that pass we use FusedLoc::get(MLIRContext*, ArrayRef<Location>, Attribute) which could create fused location with empty or single location

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mlir:core MLIR Core Infrastructure mlir:ods mlir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[mlir][BytecodeReader] Crash when reading FusedLoc with empty elements
5 participants