Skip to content

Commit 613656d

Browse files
spirv-val: Print source line in Validation message
1 parent 4c2ec2a commit 613656d

8 files changed

Lines changed: 396 additions & 5 deletions

source/diagnostic.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,11 @@ DiagnosticStream::~DiagnosticStream() {
106106
default:
107107
break;
108108
}
109-
if (disassembled_instruction_.size() > 0)
109+
if (!disassembled_instruction_.empty())
110110
stream_ << std::endl << " " << disassembled_instruction_ << std::endl;
111111

112+
if (!shader_debug_info_.empty()) stream_ << shader_debug_info_;
113+
112114
consumer_(level, "input", position_, stream_.str().c_str());
113115
}
114116
}

source/diagnostic.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ class DiagnosticStream {
3030
public:
3131
DiagnosticStream(spv_position_t position, const MessageConsumer& consumer,
3232
const std::string& disassembled_instruction,
33-
spv_result_t error)
33+
spv_result_t error, std::string shader_debug_info = "")
3434
: position_(position),
3535
consumer_(consumer),
3636
disassembled_instruction_(disassembled_instruction),
37-
error_(error) {}
37+
error_(error),
38+
shader_debug_info_(shader_debug_info) {}
3839

3940
// Creates a DiagnosticStream from an expiring DiagnosticStream.
4041
// The new object takes the contents of the other, and prevents the
@@ -62,6 +63,8 @@ class DiagnosticStream {
6263
MessageConsumer consumer_; // Message consumer callback.
6364
std::string disassembled_instruction_;
6465
spv_result_t error_;
66+
// Provide way to pass optional information from ShaderDebugInfo
67+
std::string shader_debug_info_;
6568
};
6669

6770
// Changes the MessageConsumer in |context| to one that updates |diagnostic|

source/val/validate_extensions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,8 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _,
13501350
<< "NonSemantic.Shader.DebugInfo import version " << ver
13511351
<< " is below the minimum supported version " << kNSDIMinVersion;
13521352
}
1353+
1354+
_.RegisterShaderDebugInfo(inst->id());
13531355
}
13541356

13551357
return SPV_SUCCESS;

source/val/validation_state.cpp

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
#include <cassert>
2020
#include <cstdint>
21+
#include <sstream>
2122
#include <stack>
23+
#include <string>
2224
#include <utility>
2325

2426
#include "source/opcode.h"
@@ -29,7 +31,10 @@
2931
#include "source/val/basic_block.h"
3032
#include "source/val/construct.h"
3133
#include "source/val/function.h"
34+
#include "source/val/instruction.h"
3235
#include "spirv-tools/libspirv.h"
36+
#include "spirv/unified1/NonSemanticShaderDebugInfo.h"
37+
#include "spirv/unified1/spirv.hpp11"
3338

3439
namespace spvtools {
3540
namespace val {
@@ -348,10 +353,15 @@ DiagnosticStream ValidationState_t::diag(spv_result_t error_code,
348353
}
349354

350355
std::string disassembly;
351-
if (inst) disassembly = Disassemble(*inst);
356+
std::string shader_debug_info;
357+
if (inst) {
358+
disassembly = Disassemble(*inst);
359+
shader_debug_info = InspectShaderDebugInfo(*inst);
360+
}
352361

353362
return DiagnosticStream({0, 0, inst ? inst->LineNum() : 0},
354-
context_->consumer, disassembly, error_code);
363+
context_->consumer, disassembly, error_code,
364+
shader_debug_info);
355365
}
356366

357367
std::vector<Function>& ValidationState_t::functions() {
@@ -2170,6 +2180,133 @@ std::vector<uint32_t>& ValidationState_t::GetDebugSourceLineLength(
21702180
return it->second;
21712181
}
21722182

2183+
std::string ValidationState_t::InspectShaderDebugInfo(const Instruction& inst) {
2184+
const uint32_t set_id = ShaderDebugInfoSet();
2185+
if (set_id == 0) {
2186+
return ""; // no ShaderDebugInfo found
2187+
} else if (inst.function() == nullptr) {
2188+
// currently only checking for code in function blocks
2189+
return "";
2190+
}
2191+
2192+
// Find the DebugLine that is preceding
2193+
const Instruction* debug_line_inst = nullptr;
2194+
size_t idx = &inst - &ordered_instructions()[0];
2195+
while (idx > 0) {
2196+
const Instruction* prev = &ordered_instructions()[--idx];
2197+
if (prev->opcode() == spv::Op::OpFunction) {
2198+
break;
2199+
}
2200+
2201+
if (prev->opcode() == spv::Op::OpExtInst &&
2202+
prev->GetOperandAs<uint32_t>(2) == set_id &&
2203+
prev->GetOperandAs<uint32_t>(3) ==
2204+
NonSemanticShaderDebugInfoDebugLine) {
2205+
debug_line_inst = prev;
2206+
break;
2207+
}
2208+
}
2209+
2210+
if (!debug_line_inst) return "";
2211+
2212+
const Instruction* debug_source =
2213+
FindDef(debug_line_inst->GetOperandAs<uint32_t>(4));
2214+
if (!debug_source || debug_source->GetOperandAs<uint32_t>(3) !=
2215+
NonSemanticShaderDebugInfoDebugSource) {
2216+
return "";
2217+
}
2218+
2219+
bool is_int32 = false, is_const_int32 = false;
2220+
uint32_t line_start = 0;
2221+
uint32_t line_end = 0;
2222+
uint32_t column_start = 0;
2223+
uint32_t column_end = 0;
2224+
std::tie(is_int32, is_const_int32, line_start) =
2225+
EvalInt32IfConst(debug_line_inst->word(6));
2226+
std::tie(is_int32, is_const_int32, line_end) =
2227+
EvalInt32IfConst(debug_line_inst->word(7));
2228+
std::tie(is_int32, is_const_int32, column_start) =
2229+
EvalInt32IfConst(debug_line_inst->word(8));
2230+
std::tie(is_int32, is_const_int32, column_end) =
2231+
EvalInt32IfConst(debug_line_inst->word(9));
2232+
2233+
std::ostringstream ss;
2234+
2235+
// The left hand side line number, need to make sure if going from line number
2236+
// 99 to 100 that all lines have the same padding
2237+
const size_t vertical_line_padding = std::to_string(line_end).length();
2238+
auto add_vertical_line = [&](uint32_t line_number) {
2239+
size_t padding = 1;
2240+
if (line_number != 0) {
2241+
ss << line_number;
2242+
padding += (vertical_line_padding - std::to_string(line_number).length());
2243+
} else {
2244+
padding += vertical_line_padding;
2245+
}
2246+
for (size_t p = 0; p < padding; p++) {
2247+
ss << " ";
2248+
}
2249+
ss << "|";
2250+
if (line_number != 0) {
2251+
ss << " "; // otherwise test will fail from trailing whitespace being
2252+
// trimmed
2253+
}
2254+
};
2255+
2256+
uint32_t current_line_num = 1;
2257+
auto stream_text = [&](const Instruction* op_string) {
2258+
const std::string text = op_string->GetOperandAs<std::string>(1);
2259+
for (const char c : text) {
2260+
if (current_line_num >= line_start && current_line_num <= line_end) {
2261+
ss << c;
2262+
if (c == '\n' && current_line_num < line_end) {
2263+
add_vertical_line(current_line_num + 1);
2264+
}
2265+
}
2266+
2267+
if (c == '\n') {
2268+
current_line_num++;
2269+
}
2270+
}
2271+
};
2272+
2273+
const Instruction* file_string =
2274+
FindDef(debug_source->GetOperandAs<uint32_t>(4));
2275+
ss << "\n --> " << file_string->GetOperandAs<std::string>(1) << ":"
2276+
<< line_start << ":" << column_start << '\n';
2277+
2278+
add_vertical_line(0);
2279+
ss << '\n';
2280+
2281+
const Instruction* source_string =
2282+
FindDef(debug_source->GetOperandAs<uint32_t>(5));
2283+
add_vertical_line(line_start);
2284+
stream_text(source_string);
2285+
2286+
// Look for any DebugSourceContinued
2287+
size_t src_idx = debug_source - &ordered_instructions()[0] + 1;
2288+
for (; src_idx < ordered_instructions().size(); ++src_idx) {
2289+
const Instruction& continued_insn = ordered_instructions()[src_idx];
2290+
if (continued_insn.opcode() != spv::Op::OpExtInst ||
2291+
continued_insn.GetOperandAs<uint32_t>(2) != set_id ||
2292+
continued_insn.GetOperandAs<uint32_t>(3) !=
2293+
NonSemanticShaderDebugInfoDebugSourceContinued) {
2294+
break;
2295+
}
2296+
2297+
const Instruction* continued_string =
2298+
FindDef(continued_insn.GetOperandAs<uint32_t>(4));
2299+
stream_text(continued_string);
2300+
}
2301+
2302+
// This happens if the error is the last line of source
2303+
if (current_line_num == line_end) ss << "\n";
2304+
2305+
add_vertical_line(0);
2306+
ss << "\n";
2307+
return ss.str();
2308+
}
2309+
21732310
bool ValidationState_t::IsValidStorageClass(
21742311
spv::StorageClass storage_class) const {
21752312
if (spvIsVulkanEnv(context()->target_env)) {

source/val/validation_state.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,10 @@ class ValidationState_t {
10041004
// instruction Will create a new vector if DebugSource is not found
10051005
std::vector<uint32_t>& GetDebugSourceLineLength(uint32_t id);
10061006

1007+
void RegisterShaderDebugInfo(uint32_t id) { shader_debug_info_set_id = id; }
1008+
uint32_t ShaderDebugInfoSet() const { return shader_debug_info_set_id; }
1009+
std::string InspectShaderDebugInfo(const Instruction& inst);
1010+
10071011
private:
10081012
ValidationState_t(const ValidationState_t&);
10091013

@@ -1186,6 +1190,10 @@ class ValidationState_t {
11861190
/// line side of it. (Also will have the DebugSourceContinued source included)
11871191
std::unordered_map<uint32_t, std::vector<uint32_t>> debug_source_line_length_;
11881192

1193+
// Quick check if we have seen NonSemantic.Shader.DebugInfo.*
1194+
// to know to try and print out a source line on an error message
1195+
uint32_t shader_debug_info_set_id = 0;
1196+
11891197
/// Maps ids to friendly names.
11901198
std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper_;
11911199
spvtools::NameMapper name_mapper_;

test/val/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ add_spvtools_unittest(TARGET val_rstuvw
108108
val_ray_query_test.cpp
109109
val_ray_tracing_test.cpp
110110
val_ray_tracing_reorder_test.cpp
111+
val_shader_debug_info_test.cpp
111112
val_small_type_uses_test.cpp
112113
val_ssa_test.cpp
113114
val_state_test.cpp

test/val/val_id_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4562,6 +4562,7 @@ TEST_P(ValidateIdWithMessage, OpFunctionFunctionTypeBad) {
45624562
%5 = OpLabel
45634563
OpReturn
45644564
OpFunctionEnd)";
4565+
printf("%s\n", spirv.c_str());
45654566
CompileSuccessfully(spirv.c_str());
45664567
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
45674568
EXPECT_THAT(getDiagnosticString(),

0 commit comments

Comments
 (0)