[Mlir-commits] [mlir] [mlir][spirv] Add SPIR-V NonSemantic.Graph.DebugInfo (PR #199519)
Davide Grohmann
llvmlistbot at llvm.org
Tue May 26 06:30:47 PDT 2026
https://github.com/davidegrohmann updated https://github.com/llvm/llvm-project/pull/199519
>From f8581a47c2f1ef0ad9dde74ac4d19057c63b76d8 Mon Sep 17 00:00:00 2001
From: Mohammadreza Ameri Mahabadian <mohammadreza.amerimahabadian at arm.com>
Date: Tue, 10 Mar 2026 11:00:32 +0000
Subject: [PATCH] [mlir][spirv] Add SPIR-V NonSemantic.Graph.DebugInfo
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 at arm.com>
Signed-off-by: Davide Grohmann <davide.grohmann at arm.com>
Change-Id: If71c026aa08b2bf9052eba35b173e1ce4498cb9d
---
.../mlir/Target/SPIRV/SPIRVExtInstSets.h | 51 +++++
.../TosaToSPIRVTosa/TosaToSPIRVTosaPass.cpp | 1 +
.../SPIRV/Deserialization/DeserializeOps.cpp | 118 +++++++++++-
.../SPIRV/Deserialization/Deserializer.h | 8 +
.../SPIRV/Serialization/SerializeOps.cpp | 174 ++++++++++++++++++
.../Target/SPIRV/Serialization/Serializer.cpp | 35 +++-
.../Target/SPIRV/Serialization/Serializer.h | 29 +++
.../Target/SPIRV/TranslateRegistration.cpp | 8 +-
mlir/test/Target/SPIRV/debug-negative.mlir | 5 -
.../SPIRV/graph-debug-info-negative.mlir | 14 ++
mlir/test/Target/SPIRV/graph-debug-info.mlir | 143 ++++++++++++++
.../Dialect/SPIRV/DeserializationTest.cpp | 81 ++++++++
12 files changed, 654 insertions(+), 13 deletions(-)
create mode 100644 mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h
delete mode 100644 mlir/test/Target/SPIRV/debug-negative.mlir
create mode 100644 mlir/test/Target/SPIRV/graph-debug-info-negative.mlir
create mode 100644 mlir/test/Target/SPIRV/graph-debug-info.mlir
diff --git a/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h b/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h
new file mode 100644
index 0000000000000..524bda4f275d3
--- /dev/null
+++ b/mlir/include/mlir/Target/SPIRV/SPIRVExtInstSets.h
@@ -0,0 +1,51 @@
+//===- 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,
+};
+
+constexpr bool isValidGraphDebugInfoExtInst(uint32_t opcode) {
+ switch (opcode) {
+ case static_cast<uint32_t>(GraphDebugInfoExtInst::DebugGraph):
+ case static_cast<uint32_t>(GraphDebugInfoExtInst::DebugOperation):
+ case static_cast<uint32_t>(GraphDebugInfoExtInst::DebugTensor):
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // 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..dba72a17ac44b 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 (LocationAttr 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,14 @@ LogicalResult spirv::Deserializer::processInstruction(
return processCapability(operands);
case spirv::Opcode::OpExtension:
return processExtension(operands);
- case spirv::Opcode::OpExtInst:
+ case spirv::Opcode::OpExtInst: {
+ DenseMap<uint32_t, StringRef>::iterator 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 +404,104 @@ 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");
+ }
+
+ StringRef &extensionSetName = extendedInstSets[operands[2]];
+ assert(extensionSetName == extDebugInfo);
+
+ Type resultType = getType(operands[0]);
+ if (!resultType || !isVoidType(resultType))
+ return emitError(unknownLoc,
+ "DebugInfo instructions must have OpTypeVoid result type");
+
+ auto getDebugLoc = [&](uint32_t stringID) -> FailureOr<Location> {
+ DenseMap<uint32_t, StringRef>::iterator stringIt =
+ debugInfoMap.find(stringID);
+ if (stringIt == debugInfoMap.end()) {
+ return emitError(unknownLoc, "undefined string <id> ")
+ << stringID << " in DebugInfo";
+ }
+ return Location(getLocFromDebugInfoString(opBuilder, stringIt->second));
+ };
+
+ if (!spirv::isValidGraphDebugInfoExtInst(operands[3]))
+ return emitError(unknownLoc, "unknown DebugInfo instruction opcode: ")
+ << operands[3];
+
+ 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");
+ uint32_t graphID = operands[4];
+ uint32_t stringID = operands[5];
+ DenseMap<uint32_t, spirv::GraphARMOp>::iterator 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");
+ uint32_t 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 (uint32_t operationID : operationIDs) {
+ DenseMap<uint32_t, Value>::iterator 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");
+ uint32_t stringID = operands[5];
+ uint32_t tensorID = operands[4];
+ FailureOr<Location> loc = getDebugLoc(stringID);
+ if (failed(loc))
+ return failure();
+ if (constantMap.contains(tensorID)) {
+ constantLocMap[tensorID] = *loc;
+ break;
+ }
+ DenseMap<uint32_t, Value>::iterator valueIt = valueMap.find(tensorID);
+ if (valueIt == valueMap.end())
+ return emitError(unknownLoc, "undefined tensor <id> ")
+ << tensorID << " in DebugTensor";
+ valueIt->second.setLoc(*loc);
+ break;
+ }
+ }
+
+ 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..83fe3d063a2f4 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,34 @@
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;
+ llvm::interleave(
+ fusedLoc.getLocations(),
+ [&](Location fusedChildLoc) {
+ result << getDebugInfoStringFromLoc(fusedChildLoc);
+ },
+ [&] { result << ";"; });
+ 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 +90,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 +418,132 @@ LogicalResult Serializer::processFuncOp(spirv::FuncOp op) {
return success();
}
+LogicalResult Serializer::encodeDebugStringInst(const std::string &str,
+ uint32_t &stringID) {
+ if (!options.emitDebugInfo)
+ return success();
+
+ if (uint32_t existingID = debugStringIDMap.lookup(str)) {
+ stringID = existingID;
+ return success();
+ }
+
+ SmallVector<uint32_t, 2> operands;
+ stringID = getNextID();
+ debugStringIDMap[str] = stringID;
+ 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();
+
+ uint32_t voidTypeID = 0;
+ if (failed(processType(op.getLoc(), getVoidType(), voidTypeID)))
+ return failure();
+
+ uint32_t stringID = 0;
+ if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(op.getLoc()),
+ stringID)))
+ return failure();
+
+ SmallVector<uint32_t, 6> operands;
+ operands.push_back(voidTypeID);
+ 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,
+ const SetVector<Operation *> &ops) {
+ if (!options.emitDebugInfo)
+ return success();
+
+ if (ops.empty())
+ return success();
+
+ SmallVector<uint32_t, 4> instructionIDs;
+ for (Operation *op : ops)
+ for (OpResult result : op->getOpResults())
+ instructionIDs.push_back(getValueID(result));
+
+ if (instructionIDs.empty())
+ return success();
+
+ uint32_t voidTypeID = 0;
+ if (failed(processType(ops[0]->getLoc(), getVoidType(), voidTypeID)))
+ return failure();
+
+ uint32_t stringID = 0;
+ if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(ops[0]->getLoc()),
+ stringID)))
+ return failure();
+
+ SmallVector<uint32_t, 5> operands;
+ operands.push_back(voidTypeID);
+ 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();
+
+ uint32_t voidTypeID = 0;
+ if (failed(processType(tensor.getLoc(), getVoidType(), voidTypeID)))
+ return failure();
+
+ DenseMap<Value, uint32_t>::iterator it = valueIDMap.find(tensor);
+ if (it == valueIDMap.end())
+ return success();
+ uint32_t tensorID = it->second;
+
+ uint32_t stringID = 0;
+ if (failed(encodeDebugStringInst(getDebugInfoStringFromLoc(tensor.getLoc()),
+ stringID)))
+ return failure();
+
+ SmallVector<uint32_t, 4> operands;
+ operands.push_back(voidTypeID);
+ 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 +581,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 +604,16 @@ LogicalResult Serializer::processGraphARMOp(spirv::GraphARMOp op) {
functionHeader.clear();
functionBody.clear();
+ uint32_t debugGraphID = 0;
+ if (failed(encodeDebugInfoGraphInst(op, debugGraphID)))
+ return failure();
+
+ for (const DenseMap<Location, SetVector<Operation *>>::value_type
+ &debugEntry : tosaOpsMap[funcID]) {
+ if (failed(encodeDebugInfoOperationInst(debugGraphID, debugEntry.second)))
+ return failure();
+ }
+
return success();
}
@@ -492,6 +663,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..136a7f27a6e1d 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
@@ -1612,7 +1613,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 +1636,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 +1764,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 +1787,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 (uint32_t 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/Serialization/Serializer.h
index e43556fab9acf..dcb22fdaeb0fe 100644
--- a/mlir/lib/Target/SPIRV/Serialization/Serializer.h
+++ b/mlir/lib/Target/SPIRV/Serialization/Serializer.h
@@ -15,6 +15,7 @@
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
#include "mlir/IR/Builders.h"
+#include "mlir/Target/SPIRV/SPIRVExtInstSets.h"
#include "mlir/Target/SPIRV/Serialization.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
@@ -328,6 +329,23 @@ class Serializer {
uint32_t opcode,
ArrayRef<uint32_t> operands);
+ LogicalResult encodeExtensionInstruction(Operation *op,
+ StringRef extensionSetName,
+ uint32_t opcode,
+ ArrayRef<uint32_t> operands,
+ SmallVectorImpl<uint32_t> &binary);
+
+ LogicalResult encodeDebugStringInst(const std::string &str,
+ uint32_t &stringID);
+
+ LogicalResult encodeDebugInfoGraphInst(spirv::GraphARMOp op,
+ uint32_t &debugGraphID);
+
+ LogicalResult encodeDebugInfoOperationInst(uint32_t debugGraphID,
+ const SetVector<Operation *> &ops);
+
+ LogicalResult encodeDebugInfoTensorInst(Value tensor);
+
uint32_t getValueID(Value val) const { return valueIDMap.lookup(val); }
LogicalResult processAddressOfOp(spirv::AddressOfOp addressOfOp);
@@ -363,6 +381,9 @@ class Serializer {
// Utilities
//===--------------------------------------------------------------------===//
+ /// Updates tosaOpsMap after ensuring that the op is inside a graph.
+ void updateTosaOpsMap(Operation *op);
+
/// Emits an OpDecorate instruction to decorate the given `target` with the
/// given `decoration`.
LogicalResult emitDecoration(uint32_t target, spirv::Decoration decoration,
@@ -397,6 +418,9 @@ class Serializer {
/// use by other debug instructions.
uint32_t fileID = 0;
+ /// Map from graph debug info string payloads to their OpString <id>s.
+ llvm::StringMap<uint32_t> debugStringIDMap;
+
/// The next available result <id>.
uint32_t nextID = 1;
@@ -417,6 +441,7 @@ class Serializer {
SmallVector<uint32_t, 0> typesGlobalValues;
SmallVector<uint32_t, 0> functions;
SmallVector<uint32_t, 0> graphs;
+ SmallVector<uint32_t, 0> graphsDebugInfo;
/// Recursive struct references are serialized as OpTypePointer instructions
/// to the recursive struct type. However, the OpTypePointer instruction
@@ -485,6 +510,10 @@ class Serializer {
/// Map from extended instruction set name to <id>s.
llvm::StringMap<uint32_t> extendedInstSetIDMap;
+ /// Map of graph <id> to map of locations in that graph to set of tosa ops in
+ /// that location
+ DenseMap<uint32_t, DenseMap<Location, SetVector<Operation *>>> tosaOpsMap;
+
/// Map from values used in OpPhi instructions to their offset in the
/// `functions` section.
///
diff --git a/mlir/lib/Target/SPIRV/TranslateRegistration.cpp b/mlir/lib/Target/SPIRV/TranslateRegistration.cpp
index 796354e154c02..be2586f1c69ab 100644
--- a/mlir/lib/Target/SPIRV/TranslateRegistration.cpp
+++ b/mlir/lib/Target/SPIRV/TranslateRegistration.cpp
@@ -122,6 +122,11 @@ serializeModule(spirv::ModuleOp moduleOp, raw_ostream &output,
namespace mlir {
void registerToSPIRVTranslation() {
+ static llvm::cl::opt<bool> emitDebugInfo(
+ "spirv-emit-debug-info",
+ llvm::cl::desc("Emit SPIR-V debug information during serialization"),
+ llvm::cl::init(false));
+
static llvm::cl::opt<std::string> validationFilesPrefix(
"spirv-save-validation-files-with-prefix",
llvm::cl::desc(
@@ -137,7 +142,8 @@ void registerToSPIRVTranslation() {
"serialize-spirv", "serialize SPIR-V dialect",
[](spirv::ModuleOp moduleOp, raw_ostream &output) {
return serializeModule(moduleOp, output,
- {true, false, !validationFilesPrefix.empty(),
+ {true, emitDebugInfo,
+ !validationFilesPrefix.empty(),
validationFilesPrefix});
},
[](DialectRegistry ®istry) {
diff --git a/mlir/test/Target/SPIRV/debug-negative.mlir b/mlir/test/Target/SPIRV/debug-negative.mlir
deleted file mode 100644
index 2c826870b5f02..0000000000000
--- a/mlir/test/Target/SPIRV/debug-negative.mlir
+++ /dev/null
@@ -1,5 +0,0 @@
-// RUN: mlir-translate %s --test-spirv-roundtrip-debug --no-implicit-module --verify-diagnostics
-
-// expected-error at below {{SPV_KHR_non_semantic_info extension not available}}
-spirv.module Logical GLSL450 requires #spirv.vce<v1.3, [Shader], []> attributes {spirv.target_env = #spirv.target_env<#spirv.vce<v1.3, [Shader], []>, #spirv.resource_limits<>>} {
-}
diff --git a/mlir/test/Target/SPIRV/graph-debug-info-negative.mlir b/mlir/test/Target/SPIRV/graph-debug-info-negative.mlir
new file mode 100644
index 0000000000000..b6696edf81f1b
--- /dev/null
+++ b/mlir/test/Target/SPIRV/graph-debug-info-negative.mlir
@@ -0,0 +1,14 @@
+// RUN: mlir-translate %s --test-spirv-roundtrip-debug --no-implicit-module --verify-diagnostics --split-input-file
+
+// expected-error at below {{SPV_KHR_non_semantic_info extension not available}}
+spirv.module Logical GLSL450 requires #spirv.vce<v1.3, [Shader], []> attributes {spirv.target_env = #spirv.target_env<#spirv.vce<v1.3, [Shader], []>, #spirv.resource_limits<>>} {
+}
+
+// -----
+
+// expected-error @+1 {{SPV_KHR_non_semantic_info extension not available}}
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph]> {
+ spirv.ARM.Graph @g(%arg0: !spirv.arm.tensor<1xi8>) -> (!spirv.arm.tensor<1xi8>) {
+ spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1xi8>
+ }
+}
diff --git a/mlir/test/Target/SPIRV/graph-debug-info.mlir b/mlir/test/Target/SPIRV/graph-debug-info.mlir
new file mode 100644
index 0000000000000..42edf4b3b7b0f
--- /dev/null
+++ b/mlir/test/Target/SPIRV/graph-debug-info.mlir
@@ -0,0 +1,143 @@
+// RUN: mlir-translate -no-implicit-module -split-input-file --verify-diagnostics -mlir-print-debuginfo -test-spirv-roundtrip-debug %s | FileCheck %s
+// RUN: %if spirv-tools %{ rm -rf %t %}
+// RUN: %if spirv-tools %{ mkdir %t %}
+// RUN: %if spirv-tools %{ mlir-translate --no-implicit-module --serialize-spirv --spirv-emit-debug-info --split-input-file --spirv-save-validation-files-with-prefix=%t/module %s %}
+// RUN: %if spirv-tools %{ spirv-val %t %}
+
+//===----------------------------------------------------------------------===//
+// spirv DebugInfo: FileLineCol Locations
+//===----------------------------------------------------------------------===//
+
+// CHECK: #loc[[LOC_TENSOR0:.*]] = loc("{{.*}}debug-info.mlir{{.*}}:21:27")
+// CHECK: #loc[[LOC_TENSOR1:.*]] = loc("{{.*}}debug-info.mlir{{.*}}:21:67")
+// CHECK: #loc[[LOC_TENSOR2:.*]] = loc("{{.*}}debug-info.mlir{{.*}}:21:105")
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, Int16, Int64, Float16, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph, SPV_KHR_non_semantic_info]> {
+ spirv.GlobalVariable @conv2d_arg_0 bind(0, 0) : !spirv.ptr<!spirv.arm.tensor<1x16x16x1xi8>, UniformConstant>
+ spirv.GlobalVariable @conv2d_arg_1 bind(0, 1) : !spirv.ptr<!spirv.arm.tensor<8x3x3x1xi8>, UniformConstant>
+ spirv.GlobalVariable @conv2d_arg_2 bind(0, 2) : !spirv.ptr<!spirv.arm.tensor<8xi32>, UniformConstant>
+ spirv.GlobalVariable @conv2d_res_0 bind(0, 3) : !spirv.ptr<!spirv.arm.tensor<1x14x14x8xi32>, UniformConstant>
+ spirv.ARM.GraphEntryPoint @conv2d, @conv2d_arg_0, @conv2d_arg_1, @conv2d_arg_2, @conv2d_res_0
+ // CHECK: spirv.ARM.Graph @{{.*}}(%arg0: !spirv.arm.tensor<1x16x16x1xi8> loc("{{.*}}debug-info.mlir{{.*}}:21:27"), %arg1: !spirv.arm.tensor<8x3x3x1xi8> loc("{{.*}}debug-info.mlir{{.*}}:21:67"), %arg2: !spirv.arm.tensor<8xi32> loc("{{.*}}debug-info.mlir{{.*}}:21:105")) -> !spirv.arm.tensor<1x14x14x8xi32> attributes {entry_point = true} {
+ spirv.ARM.Graph @conv2d(%arg0: !spirv.arm.tensor<1x16x16x1xi8>, %arg1: !spirv.arm.tensor<8x3x3x1xi8>, %arg2: !spirv.arm.tensor<8xi32>) -> (!spirv.arm.tensor<1x14x14x8xi32>) {
+ %0 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ %1 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ // CHECK: {{%.*}} = spirv.Tosa.Conv2D{{.*}}loc(#loc[[LOC_OP:.*]])
+ %2 = spirv.Tosa.Conv2D pad = [0, 0, 0, 0], stride = [1, 1], dilation = [1, 1], acc_type = <INT32>, local_bound = false, %arg0, %arg1, %arg2, %0, %1 : !spirv.arm.tensor<1x16x16x1xi8>, !spirv.arm.tensor<8x3x3x1xi8>, !spirv.arm.tensor<8xi32>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<1x14x14x8xi32>
+ spirv.ARM.GraphOutputs %2 : !spirv.arm.tensor<1x14x14x8xi32>
+ // CHECK: } loc(#loc[[LOC_GRAPH:.*]])
+ }
+}
+// CHECK-DAG: #loc[[LOC_GRAPH]] = loc("{{.*}}debug-info.mlir{{.*}}:21:3")
+// CHECK-DAG: #loc[[LOC_OP]] = loc("{{.*}}debug-info.mlir{{.*}}:25:12")
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv DebugInfo: Name Locations
+//===----------------------------------------------------------------------===//
+
+// CHECK: #loc[[NAME_TENSOR0:.*]] = loc("tensor_0")
+// CHECK: #loc[[NAME_TENSOR1:.*]] = loc("tensor_1")
+// CHECK: #loc[[NAME_TENSOR2:.*]] = loc("tensor_2")
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, Int16, Int64, Float16, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph, SPV_KHR_non_semantic_info]> {
+ spirv.GlobalVariable @conv2d_arg_0 bind(0, 0) : !spirv.ptr<!spirv.arm.tensor<1x16x16x1xi8>, UniformConstant>
+ spirv.GlobalVariable @conv2d_arg_1 bind(0, 1) : !spirv.ptr<!spirv.arm.tensor<8x3x3x1xi8>, UniformConstant>
+ spirv.GlobalVariable @conv2d_arg_2 bind(0, 2) : !spirv.ptr<!spirv.arm.tensor<8xi32>, UniformConstant>
+ spirv.GlobalVariable @conv2d_res_0 bind(0, 3) : !spirv.ptr<!spirv.arm.tensor<1x14x14x8xi32>, UniformConstant>
+ spirv.ARM.GraphEntryPoint @conv2d, @conv2d_arg_0, @conv2d_arg_1, @conv2d_arg_2, @conv2d_res_0
+ // CHECK: spirv.ARM.Graph @{{.*}}(%arg0: !spirv.arm.tensor<1x16x16x1xi8> loc("tensor_0"), %arg1: !spirv.arm.tensor<8x3x3x1xi8> loc("tensor_1"), %arg2: !spirv.arm.tensor<8xi32> loc("tensor_2")) -> !spirv.arm.tensor<1x14x14x8xi32> attributes {entry_point = true} {
+ spirv.ARM.Graph @conv2d(%arg0: !spirv.arm.tensor<1x16x16x1xi8> loc("tensor_0") , %arg1: !spirv.arm.tensor<8x3x3x1xi8> loc("tensor_1"), %arg2: !spirv.arm.tensor<8xi32> loc("tensor_2")) -> (!spirv.arm.tensor<1x14x14x8xi32>) {
+ %0 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ %1 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ // CHECK: {{%.*}} = spirv.Tosa.Conv2D{{.*}}loc(#loc[[NAME_OP:.*]])
+ %2 = spirv.Tosa.Conv2D pad = [0, 0, 0, 0], stride = [1, 1], dilation = [1, 1], acc_type = <INT32>, local_bound = false, %arg0, %arg1, %arg2, %0, %1 : !spirv.arm.tensor<1x16x16x1xi8>, !spirv.arm.tensor<8x3x3x1xi8>, !spirv.arm.tensor<8xi32>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<1x14x14x8xi32> loc("op_0")
+ spirv.ARM.GraphOutputs %2 : !spirv.arm.tensor<1x14x14x8xi32>
+ // CHECK: } loc(#loc[[NAME_GRAPH:.*]])
+ } loc("graph_0")
+}
+// CHECK-DAG: #loc[[NAME_GRAPH]] = loc("graph_0")
+// CHECK-DAG: #loc[[NAME_OP]] = loc("op_0")
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv DebugInfo: Multiple tosa ops with same locations
+//===----------------------------------------------------------------------===//
+
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, Int16, Int64, Float16, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph, SPV_KHR_non_semantic_info]> {
+ spirv.GlobalVariable @test_arg_0 bind(0, 0) : !spirv.ptr<!spirv.arm.tensor<2x9x3x32xi16>, UniformConstant>
+ spirv.GlobalVariable @test_res_0 bind(0, 1) : !spirv.ptr<!spirv.arm.tensor<2x9x3x32xi8>, UniformConstant>
+ spirv.ARM.GraphEntryPoint @test, @test_arg_0, @test_res_0
+ spirv.ARM.Graph @test(%arg0: !spirv.arm.tensor<2x9x3x32xi16> loc("tensor_0")) -> (!spirv.arm.tensor<2x9x3x32xi8>) attributes {entry_point = true} {
+ %weight = spirv.ARM.GraphConstant {graph_constant_id = 0 : i32} : !spirv.arm.tensor<32x1x1x32xi8>
+ %bias = spirv.ARM.GraphConstant {graph_constant_id = 1 : i32} : !spirv.arm.tensor<32xi64>
+ %0 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi16>
+ %1 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ // CHECK: %[[CONV2D:.*]] = spirv.Tosa.Conv2D{{.*}}loc(#loc[[SAME_OP:.*]])
+ %conv2d = spirv.Tosa.Conv2D pad = [0, 0, 0, 0], stride = [1, 1], dilation = [1, 1], acc_type = <INT48>, local_bound = false, %arg0, %weight, %bias, %0, %1 : !spirv.arm.tensor<2x9x3x32xi16>, !spirv.arm.tensor<32x1x1x32xi8>, !spirv.arm.tensor<32xi64>, !spirv.arm.tensor<1xi16>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<2x9x3x32xi64> loc("op_0")
+ %multiplier = spirv.ARM.GraphConstant {graph_constant_id = 2 : i32} : !spirv.arm.tensor<1xi16>
+ %shift = spirv.ARM.GraphConstant {graph_constant_id = 3 : i32} : !spirv.arm.tensor<1xi8>
+ %5 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi64>
+ %6 = spirv.Constant dense<-4> : !spirv.arm.tensor<1xi8>
+ // CHECK: {{%.*}} = spirv.Tosa.Rescale{{.*}}loc(#loc[[SAME_OP]])
+ %rescale = spirv.Tosa.Rescale scale32 = false, rounding_mode = <SingleRound>, per_channel = false, input_unsigned = false, output_unsigned = false, %conv2d, %multiplier, %shift, %5, %6 : !spirv.arm.tensor<2x9x3x32xi64>, !spirv.arm.tensor<1xi16>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi64>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<2x9x3x32xi8> loc("op_0")
+ spirv.ARM.GraphOutputs %rescale : !spirv.arm.tensor<2x9x3x32xi8>
+ // CHECK: } loc(#loc[[SAME_GRAPH:.*]])
+ } loc("graph_0")
+}
+// CHECK-DAG: #loc[[SAME_GRAPH]] = loc("graph_0")
+// CHECK-DAG: #loc[[SAME_OP]] = loc("op_0")
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv DebugInfo: Multiple tosa ops with differing locations
+//===----------------------------------------------------------------------===//
+
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, Int16, Int64, Float16, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph, SPV_KHR_non_semantic_info]> {
+ spirv.GlobalVariable @test_arg_0 bind(0, 0) : !spirv.ptr<!spirv.arm.tensor<2x9x3x32xi16>, UniformConstant>
+ spirv.GlobalVariable @test_res_0 bind(0, 1) : !spirv.ptr<!spirv.arm.tensor<2x9x3x32xi8>, UniformConstant>
+ spirv.ARM.GraphEntryPoint @test, @test_arg_0, @test_res_0
+ spirv.ARM.Graph @test(%arg0: !spirv.arm.tensor<2x9x3x32xi16> loc("tensor_0")) -> (!spirv.arm.tensor<2x9x3x32xi8>) attributes {entry_point = true} {
+ %weight = spirv.ARM.GraphConstant {graph_constant_id = 0 : i32} : !spirv.arm.tensor<32x1x1x32xi8>
+ %bias = spirv.ARM.GraphConstant {graph_constant_id = 1 : i32} : !spirv.arm.tensor<32xi64>
+ %0 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi16>
+ %1 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ // CHECK: %[[CONV2D:.*]] = spirv.Tosa.Conv2D{{.*}}loc(#loc[[MULTI_OP0:.*]])
+ %conv2d = spirv.Tosa.Conv2D pad = [0, 0, 0, 0], stride = [1, 1], dilation = [1, 1], acc_type = <INT48>, local_bound = false, %arg0, %weight, %bias, %0, %1 : !spirv.arm.tensor<2x9x3x32xi16>, !spirv.arm.tensor<32x1x1x32xi8>, !spirv.arm.tensor<32xi64>, !spirv.arm.tensor<1xi16>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<2x9x3x32xi64> loc("op_0")
+ %multiplier = spirv.ARM.GraphConstant {graph_constant_id = 2 : i32} : !spirv.arm.tensor<1xi16>
+ %shift = spirv.ARM.GraphConstant {graph_constant_id = 3 : i32} : !spirv.arm.tensor<1xi8>
+ %5 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi64>
+ %6 = spirv.Constant dense<-4> : !spirv.arm.tensor<1xi8>
+ // CHECK: {{%.*}} = spirv.Tosa.Rescale{{.*}}loc(#loc[[MULTI_OP1:.*]])
+ %rescale = spirv.Tosa.Rescale scale32 = false, rounding_mode = <SingleRound>, per_channel = false, input_unsigned = false, output_unsigned = false, %conv2d, %multiplier, %shift, %5, %6 : !spirv.arm.tensor<2x9x3x32xi64>, !spirv.arm.tensor<1xi16>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi64>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<2x9x3x32xi8> loc("op_1")
+ spirv.ARM.GraphOutputs %rescale : !spirv.arm.tensor<2x9x3x32xi8>
+ // CHECK: } loc(#loc[[MULTI_GRAPH:.*]])
+ } loc("graph_0")
+}
+// CHECK-DAG: #loc[[MULTI_GRAPH]] = loc("graph_0")
+// CHECK-DAG: #loc[[MULTI_OP0]] = loc("op_0")
+// CHECK-DAG: #loc[[MULTI_OP1]] = loc("op_1")
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv DebugInfo: Fused Locations
+//===----------------------------------------------------------------------===//
+
+spirv.module Logical Vulkan requires #spirv.vce<v1.6, [VulkanMemoryModel, Shader, Int8, Int16, Int64, Float16, TensorsARM, GraphARM], [SPV_ARM_tensors, SPV_ARM_graph, SPV_KHR_non_semantic_info]> {
+ spirv.GlobalVariable @fused_arg_0 bind(0, 0) : !spirv.ptr<!spirv.arm.tensor<1x16x16x1xi8>, UniformConstant>
+ spirv.GlobalVariable @fused_arg_1 bind(0, 1) : !spirv.ptr<!spirv.arm.tensor<8x3x3x1xi8>, UniformConstant>
+ spirv.GlobalVariable @fused_arg_2 bind(0, 2) : !spirv.ptr<!spirv.arm.tensor<8xi32>, UniformConstant>
+ spirv.GlobalVariable @fused_res_0 bind(0, 3) : !spirv.ptr<!spirv.arm.tensor<1x14x14x8xi32>, UniformConstant>
+ spirv.ARM.GraphEntryPoint @fused, @fused_arg_0, @fused_arg_1, @fused_arg_2, @fused_res_0
+ spirv.ARM.Graph @fused(%arg0: !spirv.arm.tensor<1x16x16x1xi8> loc("tensor_0"), %arg1: !spirv.arm.tensor<8x3x3x1xi8>, %arg2: !spirv.arm.tensor<8xi32>) -> (!spirv.arm.tensor<1x14x14x8xi32>) {
+ %0 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ %1 = spirv.Constant dense<0> : !spirv.arm.tensor<1xi8>
+ // CHECK: {{%.*}} = spirv.Tosa.Conv2D{{.*}}loc(#loc[[FUSED:.*]])
+ %2 = spirv.Tosa.Conv2D pad = [0, 0, 0, 0], stride = [1, 1], dilation = [1, 1], acc_type = <INT32>, local_bound = false, %arg0, %arg1, %arg2, %0, %1 : !spirv.arm.tensor<1x16x16x1xi8>, !spirv.arm.tensor<8x3x3x1xi8>, !spirv.arm.tensor<8xi32>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<1x14x14x8xi32> loc(fused["op_0", "source.cc":12:34])
+ spirv.ARM.GraphOutputs %2 : !spirv.arm.tensor<1x14x14x8xi32>
+ // CHECK: } loc(#loc[[FUSED_GRAPH:.*]])
+ } loc("graph_0")
+}
+// CHECK-DAG: #loc[[FUSED_GRAPH]] = loc("graph_0")
+// CHECK-DAG: #loc[[FUSED]] = loc("op_0;source.cc:12:34")
diff --git a/mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp b/mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp
index ffe815b9be5e2..dd9fddc527fc6 100644
--- a/mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp
+++ b/mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp
@@ -18,6 +18,7 @@
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Target/SPIRV/SPIRVBinaryUtils.h"
+#include "mlir/Target/SPIRV/SPIRVExtInstSets.h"
#include "gmock/gmock.h"
#include <memory>
@@ -72,12 +73,26 @@ class DeserializationTest : public ::testing::Test {
binary.append(operands.begin(), operands.end());
}
+ uint32_t addDebugInfoExtInstImport() {
+ SmallVector<uint32_t, 4> importOperands = {nextID++};
+ uint32_t extInstSetID = importOperands[0];
+ spirv::encodeStringLiteralInto(importOperands, spirv::extDebugInfo);
+ addInstruction(spirv::Opcode::OpExtInstImport, importOperands);
+ return extInstSetID;
+ }
+
uint32_t addVoidType() {
auto id = nextID++;
addInstruction(spirv::Opcode::OpTypeVoid, {id});
return id;
}
+ uint32_t addBoolType() {
+ auto id = nextID++;
+ addInstruction(spirv::Opcode::OpTypeBool, {id});
+ return id;
+ }
+
uint32_t addIntType(uint32_t bitwidth) {
auto id = nextID++;
addInstruction(spirv::Opcode::OpTypeInt, {id, bitwidth, /*signedness=*/1});
@@ -162,6 +177,72 @@ TEST_F(DeserializationTest, InsufficientWordFailure) {
expectDiagnostic("insufficient words for the last instruction");
}
+TEST_F(DeserializationTest, OpExtInstMissingOperands) {
+ addHeader();
+ addInstruction(spirv::Opcode::OpExtInst, {1, 2});
+
+ ASSERT_FALSE(deserialize());
+ expectDiagnostic("OpExtInst must have at least 4 operands, result type <id>, "
+ "result <id>, set <id> and instruction opcode");
+}
+
+TEST_F(DeserializationTest, DebugInfoExtInstMissingOperands) {
+ addHeader();
+ uint32_t extInstSetID = addDebugInfoExtInstImport();
+
+ uint32_t voidType = addVoidType();
+ addInstruction(
+ spirv::Opcode::OpExtInst,
+ {voidType, nextID++, extInstSetID,
+ static_cast<uint32_t>(spirv::GraphDebugInfoExtInst::DebugTensor)});
+
+ ASSERT_FALSE(deserialize());
+ expectDiagnostic("DebugTensor must have tensor and string IDs");
+}
+
+TEST_F(DeserializationTest, DebugOperationMissingInstructionIDs) {
+ addHeader();
+ uint32_t extInstSetID = addDebugInfoExtInstImport();
+
+ uint32_t voidType = addVoidType();
+ addInstruction(
+ spirv::Opcode::OpExtInst,
+ {voidType, nextID++, extInstSetID,
+ static_cast<uint32_t>(spirv::GraphDebugInfoExtInst::DebugOperation),
+ /*debugGraphID=*/42, /*stringID=*/43});
+
+ ASSERT_FALSE(deserialize());
+ expectDiagnostic(
+ "DebugOperation must have graph, string and instruction IDs");
+}
+
+TEST_F(DeserializationTest, DebugInfoExtInstNonVoidResultType) {
+ addHeader();
+ uint32_t extInstSetID = addDebugInfoExtInstImport();
+
+ uint32_t boolType = addBoolType();
+ addInstruction(
+ spirv::Opcode::OpExtInst,
+ {boolType, nextID++, extInstSetID,
+ static_cast<uint32_t>(spirv::GraphDebugInfoExtInst::DebugTensor), 42,
+ 43});
+
+ ASSERT_FALSE(deserialize());
+ expectDiagnostic("DebugInfo instructions must have OpTypeVoid result type");
+}
+
+TEST_F(DeserializationTest, DebugInfoExtInstUnknownOpcode) {
+ addHeader();
+ uint32_t extInstSetID = addDebugInfoExtInstImport();
+
+ uint32_t voidType = addVoidType();
+ addInstruction(spirv::Opcode::OpExtInst,
+ {voidType, nextID++, extInstSetID, 99});
+
+ ASSERT_FALSE(deserialize());
+ expectDiagnostic("unknown DebugInfo instruction opcode: 99");
+}
+
//===----------------------------------------------------------------------===//
// Types
//===----------------------------------------------------------------------===//
More information about the Mlir-commits
mailing list