|
18 | 18 |
|
19 | 19 | #include <cassert> |
20 | 20 | #include <cstdint> |
| 21 | +#include <sstream> |
21 | 22 | #include <stack> |
| 23 | +#include <string> |
22 | 24 | #include <utility> |
23 | 25 |
|
24 | 26 | #include "source/opcode.h" |
|
29 | 31 | #include "source/val/basic_block.h" |
30 | 32 | #include "source/val/construct.h" |
31 | 33 | #include "source/val/function.h" |
| 34 | +#include "source/val/instruction.h" |
32 | 35 | #include "spirv-tools/libspirv.h" |
| 36 | +#include "spirv/unified1/NonSemanticShaderDebugInfo.h" |
| 37 | +#include "spirv/unified1/spirv.hpp11" |
33 | 38 |
|
34 | 39 | namespace spvtools { |
35 | 40 | namespace val { |
@@ -348,10 +353,15 @@ DiagnosticStream ValidationState_t::diag(spv_result_t error_code, |
348 | 353 | } |
349 | 354 |
|
350 | 355 | 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 | + } |
352 | 361 |
|
353 | 362 | return DiagnosticStream({0, 0, inst ? inst->LineNum() : 0}, |
354 | | - context_->consumer, disassembly, error_code); |
| 363 | + context_->consumer, disassembly, error_code, |
| 364 | + shader_debug_info); |
355 | 365 | } |
356 | 366 |
|
357 | 367 | std::vector<Function>& ValidationState_t::functions() { |
@@ -2170,6 +2180,133 @@ std::vector<uint32_t>& ValidationState_t::GetDebugSourceLineLength( |
2170 | 2180 | return it->second; |
2171 | 2181 | } |
2172 | 2182 |
|
| 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 | + |
2173 | 2310 | bool ValidationState_t::IsValidStorageClass( |
2174 | 2311 | spv::StorageClass storage_class) const { |
2175 | 2312 | if (spvIsVulkanEnv(context()->target_env)) { |
|
0 commit comments