mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-23 12:10:06 +00:00
Avoid integrity check failures caused by propagating line instructions (#4096)
Propagating the OpLine/OpNoLine to preserve the debug information through transformations results in integrity check failures because of the extra line instructions. This commit lets spirv-opt skip the integrity check when the code contains OpLine or OpNoLine.
This commit is contained in:
parent
b1507d0d2b
commit
cec658c116
@ -41,6 +41,7 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
|||||||
++inst_index_;
|
++inst_index_;
|
||||||
const auto opcode = static_cast<SpvOp>(inst->opcode);
|
const auto opcode = static_cast<SpvOp>(inst->opcode);
|
||||||
if (IsDebugLineInst(opcode)) {
|
if (IsDebugLineInst(opcode)) {
|
||||||
|
module()->SetContainsDebugInfo();
|
||||||
last_line_inst_.reset();
|
last_line_inst_.reset();
|
||||||
dbg_line_info_.push_back(
|
dbg_line_info_.push_back(
|
||||||
Instruction(module()->context(), *inst, last_dbg_scope_));
|
Instruction(module()->context(), *inst, last_dbg_scope_));
|
||||||
@ -61,12 +62,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
|||||||
inlined_at = inst->words[kInlinedAtIndex];
|
inlined_at = inst->words[kInlinedAtIndex];
|
||||||
last_dbg_scope_ =
|
last_dbg_scope_ =
|
||||||
DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
|
DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
|
||||||
module()->SetContainsDebugScope();
|
module()->SetContainsDebugInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
|
if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
|
||||||
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
|
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
|
||||||
module()->SetContainsDebugScope();
|
module()->SetContainsDebugInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -78,12 +79,12 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
|||||||
inlined_at = inst->words[kInlinedAtIndex];
|
inlined_at = inst->words[kInlinedAtIndex];
|
||||||
last_dbg_scope_ =
|
last_dbg_scope_ =
|
||||||
DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
|
DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
|
||||||
module()->SetContainsDebugScope();
|
module()->SetContainsDebugInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ext_inst_key == DebugInfoDebugNoScope) {
|
if (ext_inst_key == DebugInfoDebugNoScope) {
|
||||||
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
|
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
|
||||||
module()->SetContainsDebugScope();
|
module()->SetContainsDebugInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class Module {
|
|||||||
using const_inst_iterator = InstructionList::const_iterator;
|
using const_inst_iterator = InstructionList::const_iterator;
|
||||||
|
|
||||||
// Creates an empty module with zero'd header.
|
// Creates an empty module with zero'd header.
|
||||||
Module() : header_({}), contains_debug_scope_(false) {}
|
Module() : header_({}), contains_debug_info_(false) {}
|
||||||
|
|
||||||
// Sets the header to the given |header|.
|
// Sets the header to the given |header|.
|
||||||
void SetHeader(const ModuleHeader& header) { header_ = header; }
|
void SetHeader(const ModuleHeader& header) { header_ = header; }
|
||||||
@ -119,9 +119,9 @@ class Module {
|
|||||||
// Appends a function to this module.
|
// Appends a function to this module.
|
||||||
inline void AddFunction(std::unique_ptr<Function> f);
|
inline void AddFunction(std::unique_ptr<Function> f);
|
||||||
|
|
||||||
// Sets |contains_debug_scope_| as true.
|
// Sets |contains_debug_info_| as true.
|
||||||
inline void SetContainsDebugScope();
|
inline void SetContainsDebugInfo();
|
||||||
inline bool ContainsDebugScope() { return contains_debug_scope_; }
|
inline bool ContainsDebugInfo() { return contains_debug_info_; }
|
||||||
|
|
||||||
// Returns a vector of pointers to type-declaration instructions in this
|
// Returns a vector of pointers to type-declaration instructions in this
|
||||||
// module.
|
// module.
|
||||||
@ -301,8 +301,8 @@ class Module {
|
|||||||
// any instruction. We record them here, so they will not be lost.
|
// any instruction. We record them here, so they will not be lost.
|
||||||
std::vector<Instruction> trailing_dbg_line_info_;
|
std::vector<Instruction> trailing_dbg_line_info_;
|
||||||
|
|
||||||
// This module contains DebugScope or DebugNoScope.
|
// This module contains DebugScope/DebugNoScope or OpLine/OpNoLine.
|
||||||
bool contains_debug_scope_;
|
bool contains_debug_info_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pretty-prints |module| to |str|. Returns |str|.
|
// Pretty-prints |module| to |str|. Returns |str|.
|
||||||
@ -364,7 +364,7 @@ inline void Module::AddFunction(std::unique_ptr<Function> f) {
|
|||||||
functions_.emplace_back(std::move(f));
|
functions_.emplace_back(std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; }
|
inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; }
|
||||||
|
|
||||||
inline Module::inst_iterator Module::capability_begin() {
|
inline Module::inst_iterator Module::capability_begin() {
|
||||||
return capabilities_.begin();
|
return capabilities_.begin();
|
||||||
|
@ -583,10 +583,12 @@ bool Optimizer::Run(const uint32_t* original_binary,
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// We do not keep the result id of DebugScope in struct DebugScope.
|
// We do not keep the result id of DebugScope in struct DebugScope.
|
||||||
// Instead, we assign random ids for them, which results in integrity
|
// Instead, we assign random ids for them, which results in integrity
|
||||||
|
// check failures. In addition, propagating the OpLine/OpNoLine to preserve
|
||||||
|
// the debug information through transformations results in integrity
|
||||||
// check failures. We want to skip the integrity check when the module
|
// check failures. We want to skip the integrity check when the module
|
||||||
// contains DebugScope instructions.
|
// contains DebugScope or OpLine/OpNoLine instructions.
|
||||||
if (status == opt::Pass::Status::SuccessWithoutChange &&
|
if (status == opt::Pass::Status::SuccessWithoutChange &&
|
||||||
!context->module()->ContainsDebugScope()) {
|
!context->module()->ContainsDebugInfo()) {
|
||||||
std::vector<uint32_t> optimized_binary_with_nop;
|
std::vector<uint32_t> optimized_binary_with_nop;
|
||||||
context->module()->ToBinary(&optimized_binary_with_nop,
|
context->module()->ToBinary(&optimized_binary_with_nop,
|
||||||
/* skip_nop = */ false);
|
/* skip_nop = */ false);
|
||||||
|
@ -762,6 +762,124 @@ OpFunctionEnd
|
|||||||
<< "Was expecting the OpNop to have been removed.";
|
<< "Was expecting the OpNop to have been removed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Optimizer, AvoidIntegrityCheckForExtraLineInfo) {
|
||||||
|
// Test that it avoids the integrity check when no optimizations are run and
|
||||||
|
// OpLines are propagated.
|
||||||
|
const std::string before = R"(OpCapability Shader
|
||||||
|
OpCapability Linkage
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
%1 = OpString "Test"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%_ptr_Function_uint = OpTypePointer Function %uint
|
||||||
|
%6 = OpFunction %void None %3
|
||||||
|
%7 = OpLabel
|
||||||
|
OpLine %1 10 0
|
||||||
|
%8 = OpVariable %_ptr_Function_uint Function
|
||||||
|
OpLine %1 10 0
|
||||||
|
%9 = OpVariable %_ptr_Function_uint Function
|
||||||
|
OpLine %1 20 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const std::string after = R"(OpCapability Shader
|
||||||
|
OpCapability Linkage
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
%1 = OpString "Test"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%_ptr_Function_uint = OpTypePointer Function %uint
|
||||||
|
%6 = OpFunction %void None %3
|
||||||
|
%7 = OpLabel
|
||||||
|
OpLine %1 10 0
|
||||||
|
%8 = OpVariable %_ptr_Function_uint Function
|
||||||
|
%9 = OpVariable %_ptr_Function_uint Function
|
||||||
|
OpLine %1 20 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::vector<uint32_t> binary;
|
||||||
|
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||||
|
tools.Assemble(before, &binary);
|
||||||
|
|
||||||
|
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||||
|
|
||||||
|
std::vector<uint32_t> optimized;
|
||||||
|
class ValidatorOptions validator_options;
|
||||||
|
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
|
||||||
|
validator_options, true))
|
||||||
|
<< before << "\n";
|
||||||
|
|
||||||
|
std::string disassembly;
|
||||||
|
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||||
|
|
||||||
|
EXPECT_EQ(after, disassembly)
|
||||||
|
<< "Was expecting the OpLine to have been propagated.";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Optimizer, AvoidIntegrityCheckForDebugScope) {
|
||||||
|
// Test that it avoids the integrity check when the code contains DebugScope.
|
||||||
|
const std::string before = R"(OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "OpenCL.DebugInfo.100"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %main "main"
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
%3 = OpString "simple_vs.hlsl"
|
||||||
|
OpSource HLSL 600 %3
|
||||||
|
OpName %main "main"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%5 = OpTypeFunction %void
|
||||||
|
%6 = OpExtInst %void %1 DebugSource %3
|
||||||
|
%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
|
||||||
|
%main = OpFunction %void None %5
|
||||||
|
%14 = OpLabel
|
||||||
|
%26 = OpExtInst %void %1 DebugScope %7
|
||||||
|
OpReturn
|
||||||
|
%27 = OpExtInst %void %1 DebugNoScope
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const std::string after = R"(OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "OpenCL.DebugInfo.100"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %main "main"
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
%3 = OpString "simple_vs.hlsl"
|
||||||
|
OpSource HLSL 600 %3
|
||||||
|
OpName %main "main"
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%5 = OpTypeFunction %void
|
||||||
|
%6 = OpExtInst %void %1 DebugSource %3
|
||||||
|
%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
|
||||||
|
%main = OpFunction %void None %5
|
||||||
|
%8 = OpLabel
|
||||||
|
%11 = OpExtInst %void %1 DebugScope %7
|
||||||
|
OpReturn
|
||||||
|
%12 = OpExtInst %void %1 DebugNoScope
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::vector<uint32_t> binary;
|
||||||
|
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||||
|
tools.Assemble(before, &binary);
|
||||||
|
|
||||||
|
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||||
|
|
||||||
|
std::vector<uint32_t> optimized;
|
||||||
|
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
|
||||||
|
<< before << "\n";
|
||||||
|
|
||||||
|
std::string disassembly;
|
||||||
|
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||||
|
|
||||||
|
EXPECT_EQ(after, disassembly)
|
||||||
|
<< "Was expecting the result id of DebugScope to have been changed.";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
Loading…
Reference in New Issue
Block a user