Skip to content

[mlir][spirv] Add SPIR-V NonSemantic.Graph.DebugInfo#199519

Merged
davidegrohmann merged 1 commit into
llvm:mainfrom
davidegrohmann:mlir-spirv-non-semantic-graph-debug-info
May 28, 2026
Merged

[mlir][spirv] Add SPIR-V NonSemantic.Graph.DebugInfo#199519
davidegrohmann merged 1 commit into
llvm:mainfrom
davidegrohmann:mlir-spirv-non-semantic-graph-debug-info

Conversation

@davidegrohmann
Copy link
Copy Markdown
Contributor

@davidegrohmann davidegrohmann commented May 25, 2026

Add serialization and deserialization support for the SPIR-V
NonSemantic.Graph.DebugInfo.1 extended instruction set used by ARM graph
modules.

When debug info emission is enabled, serialize DebugGraph, DebugOperation,
and DebugTensor records for graph objects, TOSA operations, graph tensors,
and tensor constants. Emit the records after the SPIR-V objects they
reference, and make DebugOperation point at the DebugGraph result id.

Deserialize these records back into MLIR locations and diagnose malformed
or undefined debug-info references.

Enable SPV_KHR_non_semantic_info in the default TOSA-to-SPIR-V target
environment so debug info can be emitted by default.

Specification:
https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Graph.DebugInfo.asciidoc

@llvmorg-github-actions
Copy link
Copy Markdown

llvmorg-github-actions Bot commented May 25, 2026

@llvm/pr-subscribers-mlir-spirv

@llvm/pr-subscribers-mlir-tosa

Author: Davide Grohmann (davidegrohmann)

Changes

Add serialization and deserialization support for the NonSemantic.Graph.DebugInfo.1 extended instruction set used with ARM graph modules.

Definition:
https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Graph.DebugInfo.asciidoc

Serialize DebugGraph, DebugOperation, and DebugTensor records when debug info is enabled. DebugOperation now references the DebugGraph result id, and debug graph records are emitted before operations that reference them. DebugTensor records cover graph inputs, graph outputs, and tensor-typed spirv.Constant results.

Deserialize the debug records back into MLIR locations for graphs, TOSA operation results, tensors, and materialized tensor constants. Avoid default-inserting empty Values while applying debug records, and diagnose undefined debug ids instead.

Add round-trip lit coverage for FileLineColLoc, NameLoc, FusedLoc, grouped TOSA ops, and missing extension diagnostics. Add a binary-level serialization unit test for DebugGraph/DebugOperation references, ordering, and DebugTensor records for inputs, outputs, and constants.


Patch is 46.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/199519.diff

10 Files Affected:

  • (added) mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h (+40)
  • (modified) mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp (+1)
  • (modified) mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp (+108-2)
  • (modified) mlir/lib/Target/SPIRV/Deserialization/Deserializer.h (+8)
  • (modified) mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp (+161)
  • (modified) mlir/lib/Target/SPIRV/Serialization/Serializer.cpp (+40-5)
  • (modified) mlir/lib/Target/SPIRV/Serialization/Serializer.h (+28)
  • (added) mlir/test/Target/SPIRV/debug-info.mlir (+131)
  • (modified) mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp (+46)
  • (modified) mlir/unittests/Dialect/SPIRV/SerializationTest.cpp (+206)
diff --git a/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h b/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h
new file mode 100644
index 0000000000000..88ee32c5defb1
--- /dev/null
+++ b/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h
@@ -0,0 +1,40 @@
+//===- SPIRVExtInstSets.h - SPIR-V ext inst sets ----------------*- 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 file declares extended instruction set constants used by SPIR-V
+// (de)serialization.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_SPIRV_SPIRVEXTINSTSETS_H
+#define MLIR_TARGET_SPIRV_SPIRVEXTINSTSETS_H
+
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
+
+namespace mlir {
+namespace spirv {
+
+/// Extension set name for TOSA ops.
+constexpr StringLiteral extTosa("TOSA.001000.1");
+
+/// Extension set name for non-semantic graph debug info.
+constexpr StringLiteral extDebugInfo("NonSemantic.Graph.DebugInfo.1");
+
+/// Instruction opcodes in the NonSemantic.Graph.DebugInfo.1 extended
+/// instruction set.
+enum class GraphDebugInfoExtInst : uint32_t {
+  DebugGraph = 1,
+  DebugOperation = 2,
+  DebugTensor = 3,
+};
+
+} // namespace spirv
+} // namespace mlir
+
+#endif // MLIR_TARGET_SPIRV_SPIRVEXTINSTSETS_H
diff --git a/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp b/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp
index bef30e84b3289..b5ae5c0275b26 100644
--- a/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp
+++ b/mlir/lib/Conversion/TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp
@@ -51,6 +51,7 @@ spirv::VerCapExtAttr getDefaultVerCapExtAttr(MLIRContext *context) {
           spirv::Extension::SPV_EXT_replicated_composites,
           spirv::Extension::SPV_KHR_bfloat16,
           spirv::Extension::SPV_EXT_float8,
+          spirv::Extension::SPV_KHR_non_semantic_info,
       },
       context);
 }
diff --git a/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp b/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
index f65b559ed1369..54cd47716a254 100644
--- a/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
+++ b/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
@@ -17,6 +17,7 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/Location.h"
 #include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
+#include "mlir/Target/SPIRV/SPIRVExtInstSets.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Debug.h"
@@ -35,6 +36,12 @@ static inline spirv::Opcode extractOpcode(uint32_t word) {
   return static_cast<spirv::Opcode>(word & 0xffff);
 }
 
+/// Returns a NameLoc location from the given debug info string.
+static inline NameLoc getLocFromDebugInfoString(OpBuilder &builder,
+                                                StringRef source) {
+  return NameLoc::get(builder.getStringAttr(source));
+}
+
 //===----------------------------------------------------------------------===//
 // Instruction
 //===----------------------------------------------------------------------===//
@@ -42,7 +49,10 @@ static inline spirv::Opcode extractOpcode(uint32_t word) {
 Value spirv::Deserializer::getValue(uint32_t id) {
   if (auto constInfo = getConstant(id)) {
     // Materialize a `spirv.Constant` op at every use site.
-    return spirv::ConstantOp::create(opBuilder, unknownLoc, constInfo->second,
+    Location loc = unknownLoc;
+    if (auto locAttr = constantLocMap.lookup(id))
+      loc = Location(locAttr);
+    return spirv::ConstantOp::create(opBuilder, loc, constInfo->second,
                                      constInfo->first);
   }
   if (std::optional<std::pair<Attribute, Type>> constCompositeReplicateInfo =
@@ -171,8 +181,13 @@ LogicalResult spirv::Deserializer::processInstruction(
     return processCapability(operands);
   case spirv::Opcode::OpExtension:
     return processExtension(operands);
-  case spirv::Opcode::OpExtInst:
+  case spirv::Opcode::OpExtInst: {
+    auto setIt = operands.size() >= 4 ? extendedInstSets.find(operands[2])
+                                      : extendedInstSets.end();
+    if (setIt != extendedInstSets.end() && setIt->second == extDebugInfo)
+      return processDebugInfoExtInst(operands, deferInstructions);
     return processExtInst(operands);
+  }
   case spirv::Opcode::OpExtInstImport:
     return processExtInstImport(operands);
   case spirv::Opcode::OpMemberName:
@@ -388,6 +403,97 @@ LogicalResult spirv::Deserializer::processUndef(ArrayRef<uint32_t> operands) {
   return success();
 }
 
+LogicalResult
+spirv::Deserializer::processDebugInfoExtInst(ArrayRef<uint32_t> operands,
+                                             bool deferInstructions) {
+  if (deferInstructions) {
+    deferredInstructions.emplace_back(spirv::Opcode::OpExtInst, operands);
+    return success();
+  }
+
+  if (operands.size() < 4) {
+    return emitError(unknownLoc,
+                     "OpExtInst must have at least 4 operands, result type "
+                     "<id>, result <id>, set <id> and instruction opcode");
+  }
+
+  auto &extensionSetName = extendedInstSets[operands[2]];
+  assert(extensionSetName == extDebugInfo);
+
+  auto getDebugLoc = [&](uint32_t stringID) -> FailureOr<Location> {
+    auto stringIt = debugInfoMap.find(stringID);
+    if (stringIt == debugInfoMap.end()) {
+      emitError(unknownLoc, "undefined string <id> ")
+          << stringID << " in DebugInfo";
+      return failure();
+    }
+    Location loc = getLocFromDebugInfoString(opBuilder, stringIt->second);
+    return loc;
+  };
+
+  auto instructionID = static_cast<spirv::GraphDebugInfoExtInst>(operands[3]);
+  switch (instructionID) {
+  case spirv::GraphDebugInfoExtInst::DebugGraph: {
+    if (operands.size() < 6)
+      return emitError(unknownLoc, "DebugGraph must have graph and string IDs");
+    auto &graphID = operands[4];
+    auto &stringID = operands[5];
+    auto graphIt = graphMap.find(graphID);
+    if (graphIt == graphMap.end())
+      return emitError(unknownLoc, "undefined graph <id> ")
+             << graphID << " in DebugGraph";
+    FailureOr<Location> loc = getDebugLoc(stringID);
+    if (failed(loc))
+      return failure();
+    graphIt->second->setLoc(*loc);
+    break;
+  }
+  case spirv::GraphDebugInfoExtInst::DebugOperation: {
+    if (operands.size() < 7)
+      return emitError(unknownLoc,
+                       "DebugOperation must have graph, string and "
+                       "instruction IDs");
+    auto &stringID = operands[5];
+    FailureOr<Location> loc = getDebugLoc(stringID);
+    if (failed(loc))
+      return failure();
+    SmallVector<uint32_t> operationIDs;
+    operationIDs.append(std::next(operands.begin(), 6), operands.end());
+    for (auto &operationID : operationIDs) {
+      auto valueIt = valueMap.find(operationID);
+      if (valueIt == valueMap.end())
+        return emitError(unknownLoc, "undefined operation <id> ")
+               << operationID << " in DebugOperation";
+      valueIt->second.setLoc(*loc);
+    }
+    break;
+  }
+  case spirv::GraphDebugInfoExtInst::DebugTensor: {
+    if (operands.size() < 6)
+      return emitError(unknownLoc, "DebugTensor must have tensor and string IDs");
+    auto &stringID = operands[5];
+    auto &tensorID = operands[4];
+    FailureOr<Location> loc = getDebugLoc(stringID);
+    if (failed(loc))
+      return failure();
+    if (constantMap.contains(tensorID)) {
+      constantLocMap[tensorID] = *loc;
+      break;
+    }
+    auto valueIt = valueMap.find(tensorID);
+    if (valueIt == valueMap.end())
+      return emitError(unknownLoc, "undefined tensor <id> ")
+             << tensorID << " in DebugTensor";
+    valueIt->second.setLoc(*loc);
+    break;
+  }
+  default:
+    return failure();
+  }
+
+  return success();
+}
+
 LogicalResult spirv::Deserializer::processExtInst(ArrayRef<uint32_t> operands) {
   if (operands.size() < 4) {
     return emitError(unknownLoc,
diff --git a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
index b2adbb5518789..d2b7dbaad4655 100644
--- a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
+++ b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
@@ -563,6 +563,11 @@ class Deserializer {
   /// other entries.
   LogicalResult processExtInst(ArrayRef<uint32_t> operands);
 
+  /// Processes a SPIR-V OpExtInst with given `operands` for a DebugInfo
+  /// extension instruction.
+  LogicalResult processDebugInfoExtInst(ArrayRef<uint32_t> operands,
+                                        bool deferInstructions);
+
   /// Dispatches the deserialization of extended instruction set operation based
   /// on the extended instruction set name, and instruction opcode. This is
   /// autogenerated from ODS.
@@ -632,6 +637,9 @@ class Deserializer {
   /// (and type) here. Later when it's used, we materialize the constant.
   DenseMap<uint32_t, std::pair<Attribute, Type>> constantMap;
 
+  // Result <id> to debug location for constants materialized from constantMap.
+  DenseMap<uint32_t, LocationAttr> constantLocMap;
+
   // Result <id> to replicated constant attribute and type mapping.
   ///
   /// In the SPIR-V binary format, OpConstantCompositeReplicateEXT is placed in
diff --git a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
index 841fc55a8627a..83341519fbc9e 100644
--- a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
+++ b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
@@ -16,6 +16,7 @@
 #include "mlir/Dialect/SPIRV/IR/SPIRVEnums.h"
 #include "mlir/IR/RegionGraphTraits.h"
 #include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
+#include "mlir/Target/SPIRV/SPIRVExtInstSets.h"
 #include "llvm/ADT/DepthFirstIterator.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Debug.h"
@@ -24,6 +25,33 @@
 
 using namespace mlir;
 
+namespace {
+// Location::print() emits MLIR syntax such as `loc("name")` or
+// `loc(fused["op", "file":1:2])`. NonSemantic.Graph.DebugInfo stores the
+// source/debug name itself in an OpString, so keep this conversion to the
+// payload string explicit.
+std::string getDebugInfoStringFromLoc(Location loc) {
+  if (auto fileLineCol = dyn_cast<FileLineColLoc>(loc)) {
+    return fileLineCol.getFilename().str() + ":" +
+           std::to_string(fileLineCol.getLine()) + ":" +
+           std::to_string(fileLineCol.getColumn());
+  }
+  if (auto nameLoc = dyn_cast<NameLoc>(loc)) {
+    return nameLoc.getName().str();
+  }
+  if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
+    std::ostringstream result;
+    std::transform(
+        fusedLoc.getLocations().begin(), fusedLoc.getLocations().end(),
+        std::ostream_iterator<std::string>(result, ";"),
+        [&](const Location loc) { return getDebugInfoStringFromLoc(loc); });
+
+    return result.str();
+  }
+  return "";
+}
+} // namespace
+
 /// A pre-order depth-first visitor function for processing basic blocks.
 ///
 /// Visits the basic blocks starting from the given `headerBlock` in pre-order
@@ -61,6 +89,9 @@ LogicalResult Serializer::processConstantOp(spirv::ConstantOp op) {
   if (auto resultID =
           prepareConstant(op.getLoc(), op.getType(), op.getValue())) {
     valueIDMap[op.getResult()] = resultID;
+    if (isa<spirv::TensorArmType>(op.getType()) &&
+        failed(encodeDebugInfoTensorInst(op.getResult())))
+      return failure();
     return success();
   }
   return failure();
@@ -386,6 +417,121 @@ LogicalResult Serializer::processFuncOp(spirv::FuncOp op) {
   return success();
 }
 
+LogicalResult Serializer::encodeDebugStringInst(const std::string &str,
+                                                uint32_t &stringID) {
+  if (!options.emitDebugInfo)
+    return success();
+
+  SmallVector<uint32_t, 2> operands;
+  stringID = getNextID();
+  operands.push_back(stringID);
+  spirv::encodeStringLiteralInto(operands, str);
+  encodeInstructionInto(debug, spirv::Opcode::OpString, operands);
+
+  return success();
+}
+
+LogicalResult Serializer::encodeDebugInfoGraphInst(spirv::GraphARMOp op,
+                                                   uint32_t &debugGraphID) {
+  if (!options.emitDebugInfo)
+    return success();
+
+  processVoidType(typesGlobalValues);
+
+  uint32_t stringID = 0;
+  if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(op.getLoc()),
+                                   stringID)))
+    return failure();
+
+  SmallVector<uint32_t, 6> operands;
+  operands.push_back(getTypeID(getVoidType()));
+  debugGraphID = getNextID();
+  operands.push_back(debugGraphID);
+  uint32_t graphID = getOrCreateFunctionID(op.getName());
+  operands.push_back(graphID);
+  operands.push_back(stringID);
+
+  if (failed(encodeExtensionInstruction(
+          nullptr, extDebugInfo,
+          static_cast<uint32_t>(GraphDebugInfoExtInst::DebugGraph), operands,
+          graphsDebugInfo)))
+    return failure();
+
+  return success();
+}
+
+LogicalResult
+Serializer::encodeDebugInfoOperationInst(uint32_t debugGraphID,
+                                         SetVector<Operation *> ops) {
+  if (!options.emitDebugInfo)
+    return success();
+
+  if (ops.empty())
+    return success();
+
+  SmallVector<uint32_t, 4> instructionIDs;
+  for (auto op : ops)
+    for (auto result : op->getOpResults())
+      instructionIDs.push_back(getValueID(result));
+
+  if (instructionIDs.empty())
+    return success();
+
+  processVoidType(typesGlobalValues);
+
+  uint32_t stringID = 0;
+  if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(ops[0]->getLoc()),
+                                   stringID)))
+    return failure();
+
+  SmallVector<uint32_t, 5> operands;
+  operands.push_back(getTypeID(getVoidType()));
+  operands.push_back(getNextID());
+  operands.push_back(debugGraphID);
+  operands.push_back(stringID);
+  operands.append(instructionIDs);
+
+  if (failed(encodeExtensionInstruction(
+          nullptr, extDebugInfo,
+          static_cast<uint32_t>(GraphDebugInfoExtInst::DebugOperation),
+          operands,
+          graphsDebugInfo)))
+    return failure();
+
+  return success();
+}
+
+LogicalResult Serializer::encodeDebugInfoTensorInst(Value tensor) {
+  if (!options.emitDebugInfo)
+    return success();
+
+  processVoidType(typesGlobalValues);
+
+  auto it = valueIDMap.find(tensor);
+  if (it == valueIDMap.end())
+    return success();
+  auto tensorID = it->second;
+
+  uint32_t stringID = 0;
+  if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(tensor.getLoc()),
+                                   stringID)))
+    return failure();
+
+  SmallVector<uint32_t, 4> operands;
+  operands.push_back(getTypeID(getVoidType()));
+  operands.push_back(getNextID());
+  operands.push_back(tensorID);
+  operands.push_back(stringID);
+
+  if (failed(encodeExtensionInstruction(
+          nullptr, extDebugInfo,
+          static_cast<uint32_t>(GraphDebugInfoExtInst::DebugTensor), operands,
+          graphsDebugInfo)))
+    return failure();
+
+  return success();
+}
+
 LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
   if (op.getNumResults() < 1) {
     return op.emitError("cannot serialize graph with no return types");
@@ -423,6 +569,9 @@ LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
 
     encodeInstructionInto(functionHeader, spirv::Opcode::OpGraphInputARM,
                           inputOperands);
+
+    if (failed(encodeDebugInfoTensorInst(arg)))
+      return failure();
   }
 
   if (failed(processBlock(&op.front(), /*omitLabel=*/true)))
@@ -443,6 +592,15 @@ LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
   functionHeader.clear();
   functionBody.clear();
 
+  uint32_t debugGraphID = 0;
+  if (failed(encodeDebugInfoGraphInst(op, debugGraphID)))
+    return failure();
+
+  for (const auto &[loc, ops] : tosaOpsMap[funcID]) {
+    if (failed(encodeDebugInfoOperationInst(debugGraphID, ops)))
+      return failure();
+  }
+
   return success();
 }
 
@@ -492,6 +650,9 @@ Serializer::processGraphOutputsARMOp(spirv::GraphOutputsARMOp op) {
     outputOperands.push_back(outputID);
     outputOperands.push_back(indexID);
 
+    if (failed(encodeDebugInfoTensorInst(value)))
+      return failure();
+
     encodeInstructionInto(functionBody, spirv::Opcode::OpGraphSetOutputARM,
                           outputOperands);
   }
diff --git a/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp b/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
index 11a7bf66d792d..553fcce6c5ea7 100644
--- a/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
+++ b/mlir/lib/Target/SPIRV/Serialization/Serializer.cpp
@@ -173,6 +173,7 @@ void Serializer::collect(SmallVectorImpl<uint32_t> &binary) {
   binary.append(typesGlobalValues.begin(), typesGlobalValues.end());
   binary.append(functions.begin(), functions.end());
   binary.append(graphs.begin(), graphs.end());
+  binary.append(graphsDebugInfo.begin(), graphsDebugInfo.end());
 }
 
 #ifndef NDEBUG
@@ -632,6 +633,16 @@ Serializer::processTypeImpl(Location loc, Type type, uint32_t &typeID,
   return emitError(loc, "failed to process type: ") << type;
 }
 
+void Serializer::processVoidType(SmallVectorImpl<uint32_t> &binary) {
+  auto voidType = getVoidType();
+  uint32_t voidTypeID = getTypeID(voidType);
+  if (!voidTypeID) {
+    voidTypeID = getNextID();
+    encodeInstructionInto(binary, spirv::Opcode::OpTypeVoid, {voidTypeID});
+    typeIDMap[voidType] = voidTypeID;
+  }
+}
+
 LogicalResult Serializer::prepareBasicType(
     Location loc, Type type, uint32_t resultID, spirv::Opcode &typeEnum,
     SmallVectorImpl<uint32_t> &operands, bool &deferSerialization,
@@ -1612,7 +1623,7 @@ LogicalResult Serializer::emitPhiForBlockArguments(Block *block) {
 
 LogicalResult Serializer::encodeExtensionInstruction(
     Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
-    ArrayRef<uint32_t> operands) {
+    ArrayRef<uint32_t> operands, SmallVectorImpl<uint32_t> &binary) {
   // Check if the extension has been imported.
   auto &setID = extendedInstSetIDMap[extensionSetName];
   if (!setID) {
@@ -1635,8 +1646,20 @@ LogicalResult Serializer::encodeExtensionInstruction(
   extInstOperands.push_back(setID);
   extInstOperands.push_back(extensionOpcode);
   extInstOperands.append(std::next(operands.begin(), 2), operands.end());
-  encodeInstructionInto(functionBody, spirv::Opcode::OpExtInst,
-                        extInstOperands);
+  encodeInstructionInto(binary, spirv::Opcode::OpExtInst, extInstOperands);
+  return success();
+}
+
+LogicalResult Serializer::encodeExtensionInstruction(
+    Operation *op, StringRef extensionSetName, uint32_t extensionOpcode,
+    ArrayRef<uint32_t> operands) {
+  if (failed(encodeExtensionInstruction(op, extensionSetName, extensionOpcode,
+                                        operands, functionBody)))
+    return failure();
+
+  if (extensionSetName == extTosa)
+    updateTosaOpsMap(op);
+
   return success();
 }
 
@@ -1751,8 +1774,10 @@ LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
   for (Value operand : op->getOperands())
     operands.push_back(getValueID(operand));
 
-  if (failed(emitDebugLine(functionBody, loc)))
-    return failure();
+  if (extInstSet != extTosa)
+    // OpLine cannot be present in graphs
+    if (failed(emitDebugLine(functionBody, loc)))
+      return failure();
 
   if (extInstSet.empty()) {
     encodeInstructionInto(functionBody, static_cast<spirv::Opcode>(opcode),
@@ -1772,6 +1797,16 @@ LogicalResult Serializer::processOpWithoutGrammarAttr(Operation *op,
   return success();
 }
 
+void Serializer::updateTosaOpsMap(Operation *op) {
+  if (!options.emitDebugInfo)
+    return;
+
+  if (auto graphOp = dyn_cast<spirv::GraphARMOp>(op->getParentOp())) {
+    if (auto graphID = getFunctionID(graphOp.getName()))
+      tosaOpsMap[graphID][op->getLoc()].insert(op);
+  }
+}
+
 LogicalResult Serializer::emitDecoration(uint32_t target,
                                          spirv::Decoration decoration,
                                          ArrayRef<uint32_t> params) {
diff --git a/mlir/lib/Target/SPIRV/Serialization/Serializer.h b/mlir/lib/Target/SPIRV/...
[truncated]

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 25, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.

@davidegrohmann davidegrohmann force-pushed the mlir-spirv-non-semantic-graph-debug-info branch from a84778a to af91b92 Compare May 25, 2026 11:53
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 25, 2026

🐧 Linux x64 Test Results

  • 8094 tests passed
  • 619 tests skipped

✅ The build succeeded and all tests passed.

Comment thread mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/Serializer.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/Serializer.cpp Outdated
Comment thread mlir/unittests/Dialect/SPIRV/SerializationTest.cpp Outdated
@davidegrohmann davidegrohmann force-pushed the mlir-spirv-non-semantic-graph-debug-info branch 2 times, most recently from 41bf39f to f8581a4 Compare May 26, 2026 13:30
Copy link
Copy Markdown
Contributor

@IgWod IgWod left a comment

Choose a reason for hiding this comment

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

Looks good % two small comments.

Comment thread mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h Outdated
Comment thread mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h Outdated
@davidegrohmann davidegrohmann force-pushed the mlir-spirv-non-semantic-graph-debug-info branch from f8581a4 to 4235b59 Compare May 26, 2026 14:52
Comment thread mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h Outdated
Comment thread mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h Outdated
Comment thread mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp Outdated
Comment thread mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp Outdated
Copy link
Copy Markdown
Contributor

@IgWod IgWod left a comment

Choose a reason for hiding this comment

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

No more comments from me, thanks!

@davidegrohmann davidegrohmann force-pushed the mlir-spirv-non-semantic-graph-debug-info branch from 4235b59 to 68eae6e Compare May 27, 2026 08:24
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 27, 2026

🪟 Windows x64 Test Results

  • 3779 tests passed
  • 430 tests skipped

✅ The build succeeded and all tests passed.

Add serialization and deserialization support for the
NonSemantic.Graph.DebugInfo.1 extended instruction set used with ARM
graph modules.

Definition:
https://github.com/KhronosGroup/SPIRV-Registry/blob/main/nonsemantic/NonSemantic.Graph.DebugInfo.asciidoc

Serialize DebugGraph, DebugOperation, and DebugTensor records when
debug info is enabled. DebugOperation now references the DebugGraph
result id, and debug graph records are emitted before operations that
reference them. DebugTensor records cover graph inputs, graph outputs,
and tensor-typed spirv.Constant results.

Deserialize the debug records back into MLIR locations for graphs,
TOSA operation results, tensors, and materialized tensor
constants. Avoid default-inserting empty Values while applying debug
records, and diagnose undefined debug ids instead.

Add round-trip lit coverage for FileLineColLoc, NameLoc, FusedLoc,
grouped TOSA ops, and missing extension diagnostics. Add a
binary-level serialization unit test for DebugGraph/DebugOperation
references, ordering, and DebugTensor records for inputs, outputs, and
constants.

Signed-off-by: Mohammadreza Ameri Mahabadian <mohammadreza.amerimahabadian@arm.com>
Signed-off-by: Davide Grohmann <davide.grohmann@arm.com>
Change-Id: If71c026aa08b2bf9052eba35b173e1ce4498cb9d
@davidegrohmann davidegrohmann force-pushed the mlir-spirv-non-semantic-graph-debug-info branch from cbe96e3 to b9025eb Compare May 27, 2026 15:56
@davidegrohmann davidegrohmann merged commit af77286 into llvm:main May 28, 2026
11 checks passed
@davidegrohmann davidegrohmann deleted the mlir-spirv-non-semantic-graph-debug-info branch May 28, 2026 08:39
@llvm-ci
Copy link
Copy Markdown

llvm-ci commented May 28, 2026

LLVM Buildbot has detected a new failure on builder release-noassertions-warnings running on google-integrate-b2 while building mlir at step 6 "build-default".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/228/builds/260

Here is the relevant piece of the build log for the reference
Step 6 (build-default) failure: cmake (failure)
...
53.127 [649/17/7910] Linking CXX executable bin/clang-import-test
53.138 [649/16/7911] Linking CXX static library lib/libclangInterpreter.a
53.345 [649/15/7912] Building CXX object tools/mlir/lib/Target/SPIRV/Serialization/CMakeFiles/obj.MLIRSPIRVSerialization.dir/Serialization.cpp.o
53.413 [649/14/7913] Linking CXX shared module lib/CheckerDependencyHandlingAnalyzerPlugin.so
53.440 [649/13/7914] Linking CXX shared module lib/SampleAnalyzerPlugin.so
53.467 [649/12/7915] Linking CXX shared module lib/CheckerOptionHandlingAnalyzerPlugin.so
53.974 [649/11/7916] Building CXX object tools/mlir/lib/Target/SPIRV/CMakeFiles/obj.MLIRSPIRVTranslateRegistration.dir/TranslateRegistration.cpp.o
54.360 [649/10/7917] Building CXX object tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/Deserialization.cpp.o
54.563 [649/9/7918] Building AMDGPUGenInstrInfo.inc...
55.286 [649/8/7919] Building CXX object tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/DeserializeOps.cpp.o
FAILED: tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/DeserializeOps.cpp.o 
CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines,time_macros /usr/bin/ccache /usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/release-noassertions-warnings-build/build/tools/mlir/lib/Target/SPIRV/Deserialization -I/b/release-noassertions-warnings-build/llvm-project/mlir/lib/Target/SPIRV/Deserialization -I/b/release-noassertions-warnings-build/build/tools/mlir/include -I/b/release-noassertions-warnings-build/llvm-project/mlir/include -I/b/release-noassertions-warnings-build/build/include -I/b/release-noassertions-warnings-build/llvm-project/llvm/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp -Wundef -Werror=mismatched-tags -Werror=global-constructors -O3 -DNDEBUG -std=c++17 -fno-exceptions -funwind-tables -fno-rtti -Winvalid-pch -Xclang -include-pch -Xclang /b/release-noassertions-warnings-build/build/lib/Support/CMakeFiles/LLVMSupport.dir/cmake_pch.hxx.pch -Xclang -include -Xclang /b/release-noassertions-warnings-build/build/lib/Support/CMakeFiles/LLVMSupport.dir/cmake_pch.hxx -MD -MT tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/DeserializeOps.cpp.o -MF tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/DeserializeOps.cpp.o.d -o tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/DeserializeOps.cpp.o -c /b/release-noassertions-warnings-build/llvm-project/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
/b/release-noassertions-warnings-build/llvm-project/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp:420:14: error: unused variable 'extensionSetName' [-Werror,-Wunused-variable]
  420 |   StringRef &extensionSetName = extendedInstSets[operands[2]];
      |              ^~~~~~~~~~~~~~~~
1 error generated.
56.422 [649/7/7920] Building CXX object tools/mlir/lib/Target/SPIRV/Serialization/CMakeFiles/obj.MLIRSPIRVSerialization.dir/Serializer.cpp.o
57.425 [649/6/7921] Building AMDGPUGenAsmMatcher.inc...
58.944 [649/5/7922] Building CXX object tools/mlir/lib/Target/SPIRV/Deserialization/CMakeFiles/obj.MLIRSPIRVDeserialization.dir/Deserializer.cpp.o
59.399 [649/4/7923] Building CXX object tools/mlir/lib/Conversion/TosaToSPIRVTosa/CMakeFiles/obj.MLIRTosaToSPIRVTosa.dir/TosaToSPIRVTosaPass.cpp.o
60.797 [649/3/7924] Building AMDGPUGenRegisterBank.inc...
62.184 [649/2/7925] Building CXX object tools/mlir/lib/Target/SPIRV/Serialization/CMakeFiles/obj.MLIRSPIRVSerialization.dir/SerializeOps.cpp.o
63.689 [649/1/7926] Building AMDGPUGenRegisterInfo.inc...
ninja: build stopped: subcommand failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants