summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Baker <alanbaker@google.com>2018-01-10 14:23:47 -0500
committerAlan Baker <alanbaker@google.com>2018-01-12 11:19:58 -0500
commit672494da13d54cf72d41edc67ecc6b53c561afc6 (patch)
treea9cf3a8aa39bd7d5c7de180c55d20fa45a17f29b
parenteb0c73dad6102fc0d4f03c62fe910348bae43a11 (diff)
downloadSPIRV-Tools-672494da13d54cf72d41edc67ecc6b53c561afc6.tar.gz
SPIRV-Tools-672494da13d54cf72d41edc67ecc6b53c561afc6.tar.bz2
SPIRV-Tools-672494da13d54cf72d41edc67ecc6b53c561afc6.zip
Adding ostream operators for IR structures
* Added for Instruction, BasicBlock, Function and Module * Uses new disassembly functionality that can disassemble individual instructions * For debug use only (no caching is done) * Each output converts module to binary, parses and outputs an individual instruction * Added a test for whole module output * Disabling Microsoft checked iterator warnings * Updated check_copyright.py to accept 2018
-rw-r--r--CMakeLists.txt2
-rw-r--r--source/CMakeLists.txt1
-rw-r--r--source/disassemble.cpp88
-rw-r--r--source/disassemble.h38
-rw-r--r--source/opt/basic_block.cpp13
-rw-r--r--source/opt/basic_block.h4
-rw-r--r--source/opt/function.cpp12
-rw-r--r--source/opt/function.h3
-rw-r--r--source/opt/instruction.cpp23
-rw-r--r--source/opt/instruction.h17
-rw-r--r--source/opt/ir_context.h3
-rw-r--r--source/opt/module.cpp11
-rw-r--r--source/opt/module.h3
-rw-r--r--test/opt/module_test.cpp70
-rwxr-xr-xutils/check_copyright.py4
15 files changed, 287 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6feb6f1..e76a6636 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,7 +85,7 @@ if(${COMPILER_IS_LIKE_GNU})
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
endif()
elseif(MSVC)
- set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS /wd4800)
+ set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800)
if(${SPIRV_WERROR})
set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index a306730a..61a41122 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -228,6 +228,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/binary.h
${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h
${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h
${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index 5230a502..909886c0 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -25,6 +25,7 @@
#include "assembly_grammar.h"
#include "binary.h"
#include "diagnostic.h"
+#include "disassemble.h"
#include "ext_inst.h"
#include "name_mapper.h"
#include "opcode.h"
@@ -352,6 +353,52 @@ spv_result_t DisassembleInstruction(
return disassembler->HandleInstruction(*parsed_instruction);
}
+// Simple wrapper class to provide extra data necessary for targeted
+// instruction disassembly.
+class WrappedDisassembler {
+ public:
+ WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
+ : disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
+
+ Disassembler* disassembler() { return disassembler_; }
+ const uint32_t* inst_binary() const { return inst_binary_; }
+ size_t word_count() const { return word_count_; }
+
+ private:
+ Disassembler* disassembler_;
+ const uint32_t* inst_binary_;
+ const size_t word_count_;
+};
+
+spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
+ uint32_t /* magic */, uint32_t version,
+ uint32_t generator, uint32_t id_bound,
+ uint32_t schema) {
+ assert(user_data);
+ auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+ return wrapped->disassembler()->HandleHeader(endian, version, generator,
+ id_bound, schema);
+}
+
+spv_result_t DisassembleTargetInstruction(
+ void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
+ assert(user_data);
+ auto wrapped = static_cast<WrappedDisassembler*>(user_data);
+ // Check if this is the instruction we want to disassemble.
+ if (wrapped->word_count() == parsed_instruction->num_words &&
+ std::equal(wrapped->inst_binary(),
+ wrapped->inst_binary() + wrapped->word_count(),
+ parsed_instruction->words)) {
+ // Found the target instruction. Disassemble it and signal that we should
+ // stop searching so we don't output the same instruction again.
+ if (auto error =
+ wrapped->disassembler()->HandleInstruction(*parsed_instruction))
+ return error;
+ return SPV_REQUESTED_TERMINATION;
+ }
+ return SPV_SUCCESS;
+}
+
} // anonymous namespace
spv_result_t spvBinaryToText(const spv_const_context context,
@@ -386,3 +433,44 @@ spv_result_t spvBinaryToText(const spv_const_context context,
return disassembler.SaveTextResult(pText);
}
+
+std::string spvtools::spvInstructionBinaryToText(const spv_target_env env,
+ const uint32_t* instCode,
+ const size_t instWordCount,
+ const uint32_t* code,
+ const size_t wordCount,
+ const uint32_t options) {
+ spv_context context = spvContextCreate(env);
+ const libspirv::AssemblyGrammar grammar(context);
+ if (!grammar.isValid()) {
+ spvContextDestroy(context);
+ return "";
+ }
+
+ // Generate friendly names for Ids if requested.
+ std::unique_ptr<libspirv::FriendlyNameMapper> friendly_mapper;
+ libspirv::NameMapper name_mapper = libspirv::GetTrivialNameMapper();
+ if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
+ friendly_mapper.reset(
+ new libspirv::FriendlyNameMapper(context, code, wordCount));
+ name_mapper = friendly_mapper->GetNameMapper();
+ }
+
+ // Now disassemble!
+ Disassembler disassembler(grammar, options, name_mapper);
+ WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
+ spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
+ DisassembleTargetInstruction, nullptr);
+
+ spv_text text = nullptr;
+ std::string output;
+ if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
+ output.assign(text->str, text->str + text->length);
+ // Drop trailing newline characters.
+ while (!output.empty() && output.back() == '\n') output.pop_back();
+ }
+ spvTextDestroy(text);
+ spvContextDestroy(context);
+
+ return output;
+}
diff --git a/source/disassemble.h b/source/disassemble.h
new file mode 100644
index 00000000..b833dd07
--- /dev/null
+++ b/source/disassemble.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_DISASSEMBLE_H_
+#define SPIRV_TOOLS_DISASSEMBLE_H_
+
+#include <string>
+
+#include "spirv-tools/libspirv.h"
+
+namespace spvtools {
+
+// Decodes the given SPIR-V instruction binary representation to its assembly
+// text. The context is inferred from the provided module binary. The options
+// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
+// be stored into *text. Any error will be written into *diagnostic if
+// diagnostic is non-null.
+std::string spvInstructionBinaryToText(const spv_target_env env,
+ const uint32_t* inst_binary,
+ const size_t inst_word_count,
+ const uint32_t* binary,
+ const size_t word_count,
+ const uint32_t options);
+
+} // namespace spvtools
+
+#endif // SPIRV_TOOLS_DISASSEMBLE_H_
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index 77ee133a..d2f4b33d 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -15,9 +15,12 @@
#include "basic_block.h"
#include "function.h"
#include "module.h"
+#include "reflect.h"
#include "make_unique.h"
+#include <ostream>
+
namespace spvtools {
namespace ir {
@@ -155,5 +158,15 @@ uint32_t BasicBlock::ContinueBlockIdIfAny() const {
return cbid;
}
+std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
+ block.ForEachInst([&str](const ir::Instruction* inst) {
+ str << *inst;
+ if (!IsTerminatorInst(inst->opcode())) {
+ str << std::endl;
+ }
+ });
+ return str;
+}
+
} // namespace ir
} // namespace spvtools
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h
index cfff9021..2e27a4b5 100644
--- a/source/opt/basic_block.h
+++ b/source/opt/basic_block.h
@@ -148,6 +148,7 @@ class BasicBlock {
// Returns the terminator instruction. Assumes the terminator exists.
Instruction* terminator() { return &*tail(); }
+ const Instruction* terminator() const { return &*ctail(); }
// Returns true if this basic block exits this function and returns to its
// caller.
@@ -165,6 +166,9 @@ class BasicBlock {
InstructionList insts_;
};
+// Pretty-prints |block| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const BasicBlock& block);
+
inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
: function_(nullptr), label_(std::move(label)) {}
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 4fc87291..d8e43892 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -16,6 +16,8 @@
#include "make_unique.h"
+#include <ostream>
+
namespace spvtools {
namespace ir {
@@ -75,5 +77,15 @@ void Function::ForEachParam(const std::function<void(const Instruction*)>& f,
->ForEachInst(f, run_on_debug_line_insts);
}
+std::ostream& operator<<(std::ostream& str, const Function& func) {
+ func.ForEachInst([&str](const ir::Instruction* inst) {
+ str << *inst;
+ if (inst->opcode() != SpvOpFunctionEnd) {
+ str << std::endl;
+ }
+ });
+ return str;
+}
+
} // namespace ir
} // namespace spvtools
diff --git a/source/opt/function.h b/source/opt/function.h
index 8caa3e3f..0da62a8f 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -112,6 +112,9 @@ class Function {
std::unique_ptr<Instruction> end_inst_;
};
+// Pretty-prints |func| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const Function& func);
+
inline Function::Function(std::unique_ptr<Instruction> def_inst)
: module_(nullptr), def_inst_(std::move(def_inst)), end_inst_() {}
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 2b83ca11..8321561d 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -14,6 +14,7 @@
#include <initializer_list>
+#include "disassemble.h"
#include "fold.h"
#include "instruction.h"
#include "ir_context.h"
@@ -476,5 +477,27 @@ bool Instruction::IsFoldable() const {
return opt::IsFoldableType(type);
}
+std::string Instruction::PrettyPrint(uint32_t options) const {
+ // Convert the module to binary.
+ std::vector<uint32_t> module_binary;
+ context()->module()->ToBinary(&module_binary, /* skip_nop = */ false);
+
+ // Convert the instruction to binary. This is used to identify the correct
+ // stream of words to output from the module.
+ std::vector<uint32_t> inst_binary;
+ ToBinaryWithoutAttachedDebugInsts(&inst_binary);
+
+ // Do not generate a header.
+ return spvInstructionBinaryToText(
+ context()->grammar().target_env(), inst_binary.data(), inst_binary.size(),
+ module_binary.data(), module_binary.size(),
+ options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+}
+
+std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst) {
+ str << inst.PrettyPrint();
+ return str;
+}
+
} // namespace ir
} // namespace spvtools
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 61de1f9e..30b3d78e 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -351,6 +351,15 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Spec constant.
inline bool IsConstant() const;
+ // Pretty-prints |inst|.
+ //
+ // Provides the disassembly of a specific instruction. Utilizes |inst|'s
+ // context to provide the correct interpretation of types, constants, etc.
+ //
+ // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
+ // is always added to |options|.
+ std::string PrettyPrint(uint32_t options = 0u) const;
+
private:
// Returns the total count of result type id and result id.
uint32_t TypeResultIdCount() const {
@@ -388,6 +397,14 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
friend InstructionList;
};
+// Pretty-prints |inst| to |str| and returns |str|.
+//
+// Provides the disassembly of a specific instruction. Utilizes |inst|'s context
+// to provide the correct interpretation of types, constants, etc.
+//
+// Disassembly uses raw ids (not pretty printed names).
+std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst);
+
inline bool Instruction::operator==(const Instruction& other) const {
return unique_id() == other.unique_id();
}
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 98e14331..6602ddf1 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -391,6 +391,9 @@ class IRContext {
return feature_mgr_.get();
}
+ // Returns the grammar for this context.
+ const libspirv::AssemblyGrammar& grammar() const { return grammar_; }
+
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index ba31384e..fea49ef3 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -16,6 +16,7 @@
#include <algorithm>
#include <cstring>
+#include <ostream>
#include "operand.h"
#include "reflect.h"
@@ -158,5 +159,15 @@ uint32_t Module::GetExtInstImportId(const char* extstr) {
return 0;
}
+std::ostream& operator<<(std::ostream& str, const Module& module) {
+ module.ForEachInst([&str](const ir::Instruction* inst) {
+ str << *inst;
+ if (inst->opcode() != SpvOpFunctionEnd) {
+ str << std::endl;
+ }
+ });
+ return str;
+}
+
} // namespace ir
} // namespace spvtools
diff --git a/source/opt/module.h b/source/opt/module.h
index 99ed3a26..163c4e30 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -273,6 +273,9 @@ class Module {
std::vector<std::unique_ptr<Function>> functions_;
};
+// Pretty-prints |module| to |str|. Returns |str|.
+std::ostream& operator<<(std::ostream& str, const Module& module);
+
inline void Module::AddCapability(std::unique_ptr<Instruction> c) {
capabilities_.push_back(std::move(c));
}
diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp
index 43be818f..177b45b9 100644
--- a/test/opt/module_test.cpp
+++ b/test/opt/module_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <sstream>
#include <vector>
#include "gmock/gmock.h"
@@ -26,10 +27,10 @@
namespace {
+using ::testing::Eq;
using spvtest::GetIdBound;
using spvtools::ir::IRContext;
using spvtools::ir::Module;
-using ::testing::Eq;
TEST(ModuleTest, SetIdBound) {
Module m;
@@ -46,7 +47,8 @@ TEST(ModuleTest, SetIdBound) {
// Returns an IRContext owning the module formed by assembling the given text,
// then loading the result.
inline std::unique_ptr<IRContext> BuildModule(std::string text) {
- return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+ return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
}
TEST(ModuleTest, ComputeIdBound) {
@@ -74,4 +76,68 @@ TEST(ModuleTest, ComputeIdBound) {
->ComputeIdBound());
}
+TEST(ModuleTest, OstreamOperator) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpName %7 "restrict"
+OpDecorate %8 Restrict
+%9 = OpTypeVoid
+%10 = OpTypeInt 32 0
+%11 = OpTypeStruct %10 %10
+%12 = OpTypePointer Function %10
+%13 = OpTypePointer Function %11
+%14 = OpConstant %10 0
+%15 = OpConstant %10 1
+%7 = OpTypeFunction %9
+%1 = OpFunction %9 None %7
+%2 = OpLabel
+%8 = OpVariable %13 Function
+%3 = OpAccessChain %12 %8 %14
+%4 = OpLoad %10 %3
+%5 = OpAccessChain %12 %8 %15
+%6 = OpLoad %10 %5
+OpReturn
+OpFunctionEnd)";
+
+ std::string s;
+ std::ostringstream str(s);
+ str << *BuildModule(text)->module();
+ EXPECT_EQ(text, str.str());
+}
+
+TEST(ModuleTest, OstreamOperatorInt64) {
+ const std::string text = R"(OpCapability Shader
+OpCapability Linkage
+OpCapability Int64
+OpMemoryModel Logical GLSL450
+OpName %7 "restrict"
+OpDecorate %5 Restrict
+%9 = OpTypeVoid
+%10 = OpTypeInt 64 0
+%11 = OpTypeStruct %10 %10
+%12 = OpTypePointer Function %10
+%13 = OpTypePointer Function %11
+%14 = OpConstant %10 0
+%15 = OpConstant %10 1
+%16 = OpConstant %10 4294967297
+%7 = OpTypeFunction %9
+%1 = OpFunction %9 None %7
+%2 = OpLabel
+%5 = OpVariable %12 Function
+%6 = OpLoad %10 %5
+OpSelectionMerge %3 None
+OpSwitch %6 %3 4294967297 %4
+%4 = OpLabel
+OpBranch %3
+%3 = OpLabel
+OpReturn
+OpFunctionEnd)";
+
+ std::string s;
+ std::ostringstream str(s);
+ str << *BuildModule(text)->module();
+ EXPECT_EQ(text, str.str());
+}
+
} // anonymous namespace
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index a2ed459b..de7bf471 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -31,9 +31,9 @@ AUTHORS = ['The Khronos Group Inc.',
'LunarG Inc.',
'Google Inc.',
'Pierre Moreau']
-CURRENT_YEAR='2017'
+CURRENT_YEAR='2018'
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017)'
+YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2018)'
COPYRIGHT_RE = re.compile(
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))