[spirv-reader] Track line numbers
If OpLine debug instructions are present, use their line and column
numbers. Otherwise, use the instruction number as the line number, starting
counting from 1.
Bug: tint:3
Change-Id: Ia46c10732922b80b5737b13af1ef71f4259a3555
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/27680
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 4f6df45..ee755f2 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -460,6 +460,7 @@
if (!success_) {
return false;
}
+ RegisterLineNumbers();
if (!ParseInternalModuleExceptFunctions()) {
return false;
}
@@ -469,6 +470,47 @@
return success_;
}
+void ParserImpl::RegisterLineNumbers() {
+ Source instruction_number{0, 0};
+
+ // Has there been an OpLine since the last OpNoLine or start of the module?
+ bool in_op_line_scope = false;
+ // The source location provided by the most recent OpLine instruction.
+ Source op_line_source{0, 0};
+ const bool run_on_debug_insts = true;
+ module_->ForEachInst(
+ [this, &in_op_line_scope, &op_line_source,
+ &instruction_number](const spvtools::opt::Instruction* inst) {
+ ++instruction_number.line;
+ switch (inst->opcode()) {
+ case SpvOpLine:
+ in_op_line_scope = true;
+ // TODO(dneto): This ignores the File ID (operand 0), since the Tint
+ // Source concept doesn't represent that.
+ op_line_source.line = inst->GetSingleWordInOperand(1);
+ op_line_source.column = inst->GetSingleWordInOperand(2);
+ break;
+ case SpvOpNoLine:
+ in_op_line_scope = false;
+ break;
+ default:
+ break;
+ }
+ this->inst_source_[inst] =
+ in_op_line_scope ? op_line_source : instruction_number;
+ },
+ run_on_debug_insts);
+}
+
+Source ParserImpl::GetSourceForResultIdForTest(uint32_t id) {
+ const auto* inst = def_use_mgr_->GetDef(id);
+ auto where = inst_source_.find(inst);
+ if (where == inst_source_.end()) {
+ return {};
+ }
+ return where->second;
+}
+
bool ParserImpl::ParseInternalModuleExceptFunctions() {
if (!success_) {
return false;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index f1bcd64..28c3523 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -41,6 +41,7 @@
#include "src/reader/spirv/enum_converter.h"
#include "src/reader/spirv/fail_stream.h"
#include "src/reader/spirv/namer.h"
+#include "src/source.h"
namespace tint {
namespace reader {
@@ -214,6 +215,9 @@
/// @returns true if the parser is still successful.
bool ParseInternalModule();
+ /// Records line numbers for each instruction.
+ void RegisterLineNumbers();
+
/// Walks the internal representation of the module, except for function
/// definitions, to populate the AST form of the module.
/// This is a no-op if the parser has already failed.
@@ -358,6 +362,12 @@
return builtin_position_;
}
+ /// Look up the source record for the SPIR-V instruction with the given
+ /// result ID.
+ /// @param id the SPIR-V result id.
+ /// @return the Source record, or a default one
+ Source GetSourceForResultIdForTest(uint32_t id);
+
private:
/// Converts a specific SPIR-V type to a Tint type. Integer case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
@@ -435,6 +445,12 @@
spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
+ // Maps an instruction to its source location. If no OpLine information
+ // is in effect for the instruction, map the instruction to its position
+ // in the SPIR-V module, counting by instructions, where the first
+ // instruction is line 1.
+ std::unordered_map<const spvtools::opt::Instruction*, Source> inst_source_;
+
/// Maps a SPIR-V ID for an external instruction import to an AST import
std::unordered_map<uint32_t, ast::Import*> import_map_;
// The set of IDs that are imports of the GLSL.std.450 extended instruction
diff --git a/src/reader/spirv/parser_impl_test.cc b/src/reader/spirv/parser_impl_test.cc
index bc80675..d1facdc 100644
--- a/src/reader/spirv/parser_impl_test.cc
+++ b/src/reader/spirv/parser_impl_test.cc
@@ -119,6 +119,92 @@
EXPECT_THAT(p->error(), HasSubstr("Capability Kernel is not allowed"));
}
+TEST_F(SpvParserTest, Impl_Source_NoOpLine) {
+ auto spv = test::Assemble(R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %5 = OpTypeInt 32 0
+ %60 = OpConstantNull %5
+ %main = OpFunction %void None %voidfn
+ %1 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)");
+ auto* p = parser(spv);
+ EXPECT_TRUE(p->Parse());
+ EXPECT_TRUE(p->error().empty());
+ // Use instruction counting.
+ auto s5 = p->GetSourceForResultIdForTest(5);
+ EXPECT_EQ(7u, s5.line);
+ EXPECT_EQ(0u, s5.column);
+ auto s60 = p->GetSourceForResultIdForTest(60);
+ EXPECT_EQ(8u, s60.line);
+ EXPECT_EQ(0u, s60.column);
+ auto s1 = p->GetSourceForResultIdForTest(1);
+ EXPECT_EQ(10u, s1.line);
+ EXPECT_EQ(0u, s1.column);
+}
+
+TEST_F(SpvParserTest, Impl_Source_WithOpLine_WithOpNoLine) {
+ auto spv = test::Assemble(R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %15 = OpString "myfile"
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ OpLine %15 42 53
+ %5 = OpTypeInt 32 0
+ %60 = OpConstantNull %5
+ OpNoLine
+ %main = OpFunction %void None %voidfn
+ %1 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)");
+ auto* p = parser(spv);
+ EXPECT_TRUE(p->Parse());
+ EXPECT_TRUE(p->error().empty());
+ // Use the information from the OpLine that is still in scope.
+ auto s5 = p->GetSourceForResultIdForTest(5);
+ EXPECT_EQ(42u, s5.line);
+ EXPECT_EQ(53u, s5.column);
+ auto s60 = p->GetSourceForResultIdForTest(60);
+ EXPECT_EQ(42u, s60.line);
+ EXPECT_EQ(53u, s60.column);
+ // After OpNoLine, revert back to instruction counting.
+ auto s1 = p->GetSourceForResultIdForTest(1);
+ EXPECT_EQ(13u, s1.line);
+ EXPECT_EQ(0u, s1.column);
+}
+
+TEST_F(SpvParserTest, Impl_Source_InvalidId) {
+ auto spv = test::Assemble(R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %15 = OpString "myfile"
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %main = OpFunction %void None %voidfn
+ %1 = OpLabel
+ OpReturn
+ OpFunctionEnd
+)");
+ auto* p = parser(spv);
+ EXPECT_TRUE(p->Parse());
+ EXPECT_TRUE(p->error().empty());
+ auto s99 = p->GetSourceForResultIdForTest(99);
+ EXPECT_EQ(0u, s99.line);
+ EXPECT_EQ(0u, s99.column);
+}
+
} // namespace
} // namespace spirv
} // namespace reader