BuildModule: optionally avoid adding new OpLine instructions (#4033)
* BuildModule: optionally avoid adding new OpLine instructions
Fixes #4029 for my use case
* Fix formatting
* Create last_line_inst_ only if doing extra line tracking
diff --git a/source/opt/build_module.cpp b/source/opt/build_module.cpp
index fc76a3c..3b606dc 100644
--- a/source/opt/build_module.cpp
+++ b/source/opt/build_module.cpp
@@ -50,11 +50,20 @@
MessageConsumer consumer,
const uint32_t* binary,
const size_t size) {
+ return BuildModule(env, consumer, binary, size, true);
+}
+
+std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
+ MessageConsumer consumer,
+ const uint32_t* binary,
+ const size_t size,
+ bool extra_line_tracking) {
auto context = spvContextCreate(env);
SetContextMessageConsumer(context, consumer);
auto irContext = MakeUnique<opt::IRContext>(env, consumer);
opt::IrLoader loader(consumer, irContext->module());
+ loader.SetExtraLineTracking(extra_line_tracking);
spv_result_t status = spvBinaryParse(context, &loader, binary, size,
SetSpvHeader, SetSpvInst, nullptr);
diff --git a/source/opt/build_module.h b/source/opt/build_module.h
index c9d1cf2..29eaf66 100644
--- a/source/opt/build_module.h
+++ b/source/opt/build_module.h
@@ -27,7 +27,15 @@
// Builds an Module returns the owning IRContext from the given SPIR-V
// |binary|. |size| specifies number of words in |binary|. The |binary| will be
// decoded according to the given target |env|. Returns nullptr if errors occur
-// and sends the errors to |consumer|.
+// and sends the errors to |consumer|. When |extra_line_tracking| is true,
+// extra OpLine instructions are injected to better presere line numbers while
+// later transforms mutate the module.
+std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
+ MessageConsumer consumer,
+ const uint32_t* binary, size_t size,
+ bool extra_line_tracking);
+
+// Like above, with extra line tracking turned on.
std::unique_ptr<opt::IRContext> BuildModule(spv_target_env env,
MessageConsumer consumer,
const uint32_t* binary,
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index 4a44309..06099ce 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -92,7 +92,8 @@
std::unique_ptr<Instruction> spv_inst(
new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
if (!spv_inst->dbg_line_insts().empty()) {
- if (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine) {
+ if (extra_line_tracking_ &&
+ (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) {
last_line_inst_ = std::unique_ptr<Instruction>(
spv_inst->dbg_line_insts().back().Clone(module()->context()));
}
diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h
index d0610f1..16bc2c7 100644
--- a/source/opt/ir_loader.h
+++ b/source/opt/ir_loader.h
@@ -63,6 +63,10 @@
// or a missing OpFunctionEnd. Resolves internal bookkeeping.
void EndModule();
+ // Sets whether extra OpLine instructions should be injected to better
+ // track line information.
+ void SetExtraLineTracking(bool flag) { extra_line_tracking_ = flag; }
+
private:
// Consumer for communicating messages to outside.
const MessageConsumer& consumer_;
@@ -78,11 +82,17 @@
std::unique_ptr<BasicBlock> block_;
// Line related debug instructions accumulated thus far.
std::vector<Instruction> dbg_line_info_;
- // Line instruction that should be applied to the next instruction.
+ // If doing extra line tracking, this is the line instruction that should be
+ // applied to the next instruction. Otherwise it always contains null.
std::unique_ptr<Instruction> last_line_inst_;
// The last DebugScope information that IrLoader::AddInstruction() handled.
DebugScope last_dbg_scope_;
+
+ // When true, do extra line information tracking: Additional OpLine
+ // instructions will be injected to help track line info more robustly during
+ // transformations.
+ bool extra_line_tracking_ = true;
};
} // namespace opt
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index 8b77aa3..475dd23 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -19,7 +19,7 @@
#include <utility>
#include <vector>
-#include "gtest/gtest.h"
+#include "gmock/gmock.h"
#include "source/opt/build_module.h"
#include "source/opt/def_use_manager.h"
#include "source/opt/ir_context.h"
@@ -29,6 +29,8 @@
namespace opt {
namespace {
+using ::testing::ContainerEq;
+
constexpr uint32_t kOpLineOperandLineIndex = 1;
void DoRoundTripCheck(const std::string& text) {
@@ -236,6 +238,100 @@
}
}
+TEST(IrBuilder, BuildModule_WithoutExtraLines) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Vertex %main "main"
+%file = OpString "my file"
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%main = OpFunction %void None %voidfn
+%100 = OpLabel
+%1 = OpFAdd %float %float_1 %float_1
+OpLine %file 1 0
+%2 = OpFMul %float %1 %1
+%3 = OpFSub %float %2 %2
+OpReturn
+OpFunctionEnd
+)";
+
+ std::vector<uint32_t> binary;
+ SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+ ASSERT_TRUE(t.Assemble(text, &binary,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS));
+
+ // This is the function we're testing.
+ std::unique_ptr<IRContext> context = BuildModule(
+ SPV_ENV_UNIVERSAL_1_5, nullptr, binary.data(), binary.size(), false);
+ ASSERT_NE(nullptr, context);
+
+ spvtools::opt::analysis::DefUseManager* def_use_mgr =
+ context->get_def_use_mgr();
+
+ std::vector<SpvOp> opcodes;
+ for (auto* inst = def_use_mgr->GetDef(1);
+ inst && (inst->opcode() != SpvOpFunctionEnd); inst = inst->NextNode()) {
+ inst->ForEachInst(
+ [&opcodes](spvtools::opt::Instruction* sub_inst) {
+ opcodes.push_back(sub_inst->opcode());
+ },
+ true);
+ }
+
+ EXPECT_THAT(opcodes,
+ ContainerEq(std::vector<SpvOp>{SpvOpFAdd, SpvOpLine, SpvOpFMul,
+ SpvOpFSub, SpvOpReturn}));
+}
+
+TEST(IrBuilder, BuildModule_WithExtraLines_IsDefault) {
+ const std::string text = R"(OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint Vertex %main "main"
+%file = OpString "my file"
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float_1 = OpConstant %float 1
+%main = OpFunction %void None %voidfn
+%100 = OpLabel
+%1 = OpFAdd %float %float_1 %float_1
+OpLine %file 1 0
+%2 = OpFMul %float %1 %1
+%3 = OpFSub %float %2 %2
+OpReturn
+OpFunctionEnd
+)";
+
+ std::vector<uint32_t> binary;
+
+ SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+ ASSERT_TRUE(t.Assemble(text, &binary,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS));
+
+ // This is the function we're testing.
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_5, nullptr, binary.data(), binary.size());
+
+ spvtools::opt::analysis::DefUseManager* def_use_mgr =
+ context->get_def_use_mgr();
+
+ std::vector<SpvOp> opcodes;
+ for (auto* inst = def_use_mgr->GetDef(1);
+ inst && (inst->opcode() != SpvOpFunctionEnd); inst = inst->NextNode()) {
+ inst->ForEachInst(
+ [&opcodes](spvtools::opt::Instruction* sub_inst) {
+ opcodes.push_back(sub_inst->opcode());
+ },
+ true);
+ }
+
+ EXPECT_THAT(opcodes, ContainerEq(std::vector<SpvOp>{
+ SpvOpFAdd, SpvOpLine, SpvOpFMul, SpvOpLine,
+ SpvOpFSub, SpvOpLine, SpvOpReturn}));
+}
+
TEST(IrBuilder, ConsumeDebugInfoInst) {
// /* HLSL */
//