Add MessageConsumer to PassManager, Pass, and analysis interfaces.
Also convert some uses of assert() in optimization code to use
SPIRV_ASSERT(), SPIRV_UNIMPLEMENTED(), or SPIRV_UNREACHABLE()
accordingly.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index a045ce1..e999e6a 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -143,6 +143,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp
${CMAKE_CURRENT_SOURCE_DIR}/instruction.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/message.cpp
${CMAKE_CURRENT_SOURCE_DIR}/name_mapper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/opcode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/operand.cpp
diff --git a/source/message.cpp b/source/message.cpp
new file mode 100644
index 0000000..9d28c8c
--- /dev/null
+++ b/source/message.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2016 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.
+
+#include "message.h"
+
+#include <sstream>
+
+namespace spvtools {
+
+std::string StringifyMessage(MessageLevel level, const char* source,
+ const spv_position_t& position,
+ const char* message) {
+ const char* level_string = nullptr;
+ switch (level) {
+ case MessageLevel::Fatal:
+ level_string = "fatal";
+ break;
+ case MessageLevel::InternalError:
+ level_string = "internal error";
+ break;
+ case MessageLevel::Error:
+ level_string = "error";
+ break;
+ case MessageLevel::Warning:
+ level_string = "warning";
+ break;
+ case MessageLevel::Info:
+ level_string = "info";
+ break;
+ case MessageLevel::Debug:
+ level_string = "debug";
+ break;
+ }
+ std::ostringstream oss;
+ oss << level_string << ": ";
+ if (source) oss << source << ":";
+ oss << position.line << ":" << position.column << ":";
+ oss << position.index << ": ";
+ if (message) oss << message;
+ oss << "\n";
+ return oss.str();
+}
+
+} // namespace spvtools
diff --git a/source/message.h b/source/message.h
index 68e4cd7..7af4297 100644
--- a/source/message.h
+++ b/source/message.h
@@ -16,6 +16,7 @@
#define SPIRV_TOOLS_MESSAGE_H_
#include <functional>
+#include <string>
#include "spirv-tools/libspirv.h"
@@ -42,6 +43,17 @@
const spv_position_t& /* position */, const char* /* message */
)>;
+// A message consumer that ignores all messages.
+inline void IgnoreMessage(MessageLevel, const char*, const spv_position_t&,
+ const char*) {}
+
+// A helper function to compose and return a string from the message in the
+// following format:
+// "<level>: <source>:<line>:<column>:<index>: <message>"
+std::string StringifyMessage(MessageLevel level, const char* source,
+ const spv_position_t& position,
+ const char* message);
+
} // namespace spvtools
#endif // SPIRV_TOOLS_MESSAGE_H_
diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp
index e829c5f..19178eb 100644
--- a/source/opt/def_use_manager.cpp
+++ b/source/opt/def_use_manager.cpp
@@ -14,10 +14,10 @@
#include "def_use_manager.h"
-#include <cassert>
#include <functional>
#include "instruction.h"
+#include "log.h"
#include "module.h"
#include "reflect.h"
@@ -80,11 +80,10 @@
return &iter->second;
}
-std::vector<ir::Instruction*> DefUseManager::GetAnnotations(
- uint32_t id) const {
+std::vector<ir::Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
std::vector<ir::Instruction*> annos;
const auto* uses = GetUses(id);
- if (!uses) return annos;
+ if (!uses) return annos;
for (const auto& c : *uses) {
if (ir::IsAnnotationInst(c.inst->opcode())) {
annos.push_back(c.inst);
@@ -121,11 +120,13 @@
if (it->inst->type_id() != 0 && it->operand_index == 0) {
it->inst->SetResultType(after);
} else if (it->inst->type_id() == 0) {
- assert(false &&
- "Result type id considered as using while the instruction "
- "doesn't have a result type id.");
+ SPIRV_ASSERT(consumer_, false,
+ "Result type id considered as use while the instruction "
+ "doesn't have a result type id.");
+ (void)consumer_; // Makes the compiler happy for release build.
} else {
- assert(false && "Trying Setting the result id which is immutable.");
+ SPIRV_ASSERT(consumer_, false,
+ "Trying setting the immutable result id.");
}
} else {
// Update an in-operand.
diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h
index 23f098a..41c405a 100644
--- a/source/opt/def_use_manager.h
+++ b/source/opt/def_use_manager.h
@@ -21,6 +21,7 @@
#include <vector>
#include "instruction.h"
+#include "message.h"
#include "module.h"
namespace spvtools {
@@ -45,7 +46,15 @@
using IdToDefMap = std::unordered_map<uint32_t, ir::Instruction*>;
using IdToUsesMap = std::unordered_map<uint32_t, UseList>;
- inline explicit DefUseManager(ir::Module* module) { AnalyzeDefUse(module); }
+ // Constructs a def-use manager from the given |module|. All internal messages
+ // will be communicated to the outside via the given message |consumer|. This
+ // instance only keeps a reference to the |consumer|, so the |consumer| should
+ // outlive this instance.
+ DefUseManager(const MessageConsumer& consumer, ir::Module* module)
+ : consumer_(consumer) {
+ AnalyzeDefUse(module);
+ }
+
DefUseManager(const DefUseManager&) = delete;
DefUseManager(DefUseManager&&) = delete;
DefUseManager& operator=(const DefUseManager&) = delete;
@@ -106,8 +115,9 @@
// Erases the records that a given instruction uses its operand ids.
void EraseUseRecordsOfOperandIds(const ir::Instruction* inst);
- IdToDefMap id_to_def_; // Mapping from ids to their definitions
- IdToUsesMap id_to_uses_; // Mapping from ids to their uses
+ const MessageConsumer& consumer_; // Message consumer.
+ IdToDefMap id_to_def_; // Mapping from ids to their definitions
+ IdToUsesMap id_to_uses_; // Mapping from ids to their uses
// Mapping from instructions to the ids used in the instructions generating
// the result ids.
InstToUsedIdsMap inst_to_used_ids_;
diff --git a/source/opt/eliminate_dead_constant_pass.cpp b/source/opt/eliminate_dead_constant_pass.cpp
index 15f1e44..c7a921f 100644
--- a/source/opt/eliminate_dead_constant_pass.cpp
+++ b/source/opt/eliminate_dead_constant_pass.cpp
@@ -19,13 +19,14 @@
#include <unordered_set>
#include "def_use_manager.h"
+#include "log.h"
#include "reflect.h"
namespace spvtools {
namespace opt {
bool EliminateDeadConstantPass::Process(ir::Module* module) {
- analysis::DefUseManager def_use(module);
+ analysis::DefUseManager def_use(consumer(), module);
std::unordered_set<ir::Instruction*> working_list;
// Traverse all the instructions to get the initial set of dead constants as
// working list and count number of real uses for constants. Uses in
@@ -73,7 +74,7 @@
}
// The number of uses should never be less then 0, so it can not be
// less than 1 before it decreases.
- assert(use_counts[def_inst] > 0);
+ SPIRV_ASSERT(consumer(), use_counts[def_inst] > 0);
--use_counts[def_inst];
if (!use_counts[def_inst]) {
working_list.insert(def_inst);
diff --git a/source/opt/eliminate_dead_constant_pass.h b/source/opt/eliminate_dead_constant_pass.h
index fa09dba..bc24eba 100644
--- a/source/opt/eliminate_dead_constant_pass.h
+++ b/source/opt/eliminate_dead_constant_pass.h
@@ -28,6 +28,8 @@
// OpSpecConstantOp.
class EliminateDeadConstantPass : public Pass {
public:
+ explicit EliminateDeadConstantPass(const MessageConsumer& c) : Pass(c) {}
+
const char* name() const override { return "eliminate-dead-const"; }
bool Process(ir::Module*) override;
};
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
index 1d5ffc2..6adb497 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -242,6 +242,15 @@
}
} // anonymous namespace
+FoldSpecConstantOpAndCompositePass::FoldSpecConstantOpAndCompositePass(
+ const MessageConsumer& c)
+ : Pass(c),
+ max_id_(0),
+ module_(nullptr),
+ def_use_mgr_(nullptr),
+ type_mgr_(nullptr),
+ id_to_const_val_() {}
+
bool FoldSpecConstantOpAndCompositePass::ProcessImpl(ir::Module* module) {
bool modified = false;
// Traverse through all the constant defining instructions. For Normal
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h
index e7205f3..8b913a3 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.h
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.h
@@ -50,12 +50,8 @@
// TODO(qining): Add support for the operations listed above.
class FoldSpecConstantOpAndCompositePass : public Pass {
public:
- FoldSpecConstantOpAndCompositePass()
- : max_id_(0),
- module_(nullptr),
- def_use_mgr_(nullptr),
- type_mgr_(nullptr),
- id_to_const_val_() {}
+ explicit FoldSpecConstantOpAndCompositePass(const MessageConsumer& c);
+
const char* name() const override { return "fold-spec-const-op-composite"; }
bool Process(ir::Module* module) override {
Initialize(module);
@@ -66,8 +62,8 @@
// Initializes the type manager, def-use manager and get the maximal id used
// in the module.
void Initialize(ir::Module* module) {
- type_mgr_.reset(new analysis::TypeManager(*module));
- def_use_mgr_.reset(new analysis::DefUseManager(module));
+ type_mgr_.reset(new analysis::TypeManager(consumer(), *module));
+ def_use_mgr_.reset(new analysis::DefUseManager(consumer(), module));
for (const auto& id_def : def_use_mgr_->id_to_defs()) {
max_id_ = std::max(max_id_, id_def.first);
}
diff --git a/source/opt/freeze_spec_constant_value_pass.h b/source/opt/freeze_spec_constant_value_pass.h
index efe98e7..8b21463 100644
--- a/source/opt/freeze_spec_constant_value_pass.h
+++ b/source/opt/freeze_spec_constant_value_pass.h
@@ -31,6 +31,8 @@
// other spec constants defined by OpSpecConstantComposite or OpSpecConstantOp.
class FreezeSpecConstantValuePass : public Pass {
public:
+ explicit FreezeSpecConstantValuePass(const MessageConsumer& c) : Pass(c) {}
+
const char* name() const override { return "freeze-spec-const"; }
bool Process(ir::Module*) override;
};
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index 3066a87..09106a3 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -14,8 +14,7 @@
#include "ir_loader.h"
-#include <cassert>
-
+#include "log.h"
#include "reflect.h"
namespace spvtools {
@@ -34,28 +33,28 @@
// Handle function and basic block boundaries first, then normal
// instructions.
if (opcode == SpvOpFunction) {
- assert(function_ == nullptr);
- assert(block_ == nullptr);
+ SPIRV_ASSERT(consumer_, function_ == nullptr);
+ SPIRV_ASSERT(consumer_, block_ == nullptr);
function_.reset(new Function(std::move(spv_inst)));
} else if (opcode == SpvOpFunctionEnd) {
- assert(function_ != nullptr);
- assert(block_ == nullptr);
+ SPIRV_ASSERT(consumer_, function_ != nullptr);
+ SPIRV_ASSERT(consumer_, block_ == nullptr);
function_->SetFunctionEnd(std::move(spv_inst));
module_->AddFunction(std::move(function_));
function_ = nullptr;
} else if (opcode == SpvOpLabel) {
- assert(function_ != nullptr);
- assert(block_ == nullptr);
+ SPIRV_ASSERT(consumer_, function_ != nullptr);
+ SPIRV_ASSERT(consumer_, block_ == nullptr);
block_.reset(new BasicBlock(std::move(spv_inst)));
} else if (IsTerminatorInst(opcode)) {
- assert(function_ != nullptr);
- assert(block_ != nullptr);
+ SPIRV_ASSERT(consumer_, function_ != nullptr);
+ SPIRV_ASSERT(consumer_, block_ != nullptr);
block_->AddInstruction(std::move(spv_inst));
function_->AddBasicBlock(std::move(block_));
block_ = nullptr;
} else {
if (function_ == nullptr) { // Outside function definition
- assert(block_ == nullptr);
+ SPIRV_ASSERT(consumer_, block_ == nullptr);
if (opcode == SpvOpCapability) {
module_->AddCapability(std::move(spv_inst));
} else if (opcode == SpvOpExtension) {
@@ -78,11 +77,12 @@
opcode == SpvOpUndef) {
module_->AddGlobalValue(std::move(spv_inst));
} else {
- assert(0 && "unhandled inst type outside function defintion");
+ SPIRV_UNIMPLEMENTED(consumer_,
+ "unhandled inst type outside function defintion");
}
} else {
if (block_ == nullptr) { // Inside function but outside blocks
- assert(opcode == SpvOpFunctionParameter);
+ SPIRV_ASSERT(consumer_, opcode == SpvOpFunctionParameter);
function_->AddParameter(std::move(spv_inst));
} else {
block_->AddInstruction(std::move(spv_inst));
diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h
index 6795a5d..ce971d0 100644
--- a/source/opt/ir_loader.h
+++ b/source/opt/ir_loader.h
@@ -19,6 +19,7 @@
#include "basic_block.h"
#include "instruction.h"
+#include "message.h"
#include "module.h"
#include "spirv-tools/libspirv.h"
@@ -36,7 +37,11 @@
class IrLoader {
public:
// Instantiates a builder to construct the given |module| gradually.
- IrLoader(Module* module) : module_(module) {}
+ // All internal messages will be communicated to the outside via the given
+ // message |consumer|. This instance only keeps a reference to the |consumer|,
+ // so the |consumer| should outlive this instance.
+ IrLoader(const MessageConsumer& consumer, Module* module)
+ : consumer_(consumer), module_(module) {}
// Sets the fields in the module's header to the given parameters.
void SetModuleHeader(uint32_t magic, uint32_t version, uint32_t generator,
@@ -54,6 +59,8 @@
void EndModule();
private:
+ // Consumer for communicating messages to outside.
+ const MessageConsumer& consumer_;
// The module to be built.
Module* module_;
// The current Function under construction.
diff --git a/source/opt/libspirv.cpp b/source/opt/libspirv.cpp
index eabc260..47be681 100644
--- a/source/opt/libspirv.cpp
+++ b/source/opt/libspirv.cpp
@@ -83,7 +83,7 @@
spv_diagnostic diagnostic = nullptr;
auto module = MakeUnique<ir::Module>();
- ir::IrLoader loader(module.get());
+ ir::IrLoader loader(context_->consumer, module.get());
spv_result_t status =
spvBinaryParse(context_, &loader, binary.data(), binary.size(),
diff --git a/source/opt/null_pass.h b/source/opt/null_pass.h
index 0f87c61..7e10791 100644
--- a/source/opt/null_pass.h
+++ b/source/opt/null_pass.h
@@ -23,6 +23,9 @@
// A null pass that does nothing.
class NullPass : public Pass {
+ public:
+ explicit NullPass(const MessageConsumer& c) : Pass(c) {}
+
const char* name() const override { return "null"; }
bool Process(ir::Module*) override { return false; }
};
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 22df24e..dadb53a 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -17,6 +17,7 @@
#include <memory>
+#include "message.h"
#include "module.h"
namespace spvtools {
@@ -26,11 +27,24 @@
// and all analysis and transformation is done via the Process() method.
class Pass {
public:
+ // Constructs a new pass with the given message consumer, which will be
+ // invoked every time there is a message to be communicated to the outside.
+ //
+ // This pass just keeps a reference to the message consumer; so the message
+ // consumer should outlive this pass.
+ explicit Pass(const MessageConsumer& c) : consumer_(c) {}
+
// Returns a descriptive name for this pass.
virtual const char* name() const = 0;
+ // Returns the reference to the message consumer for this pass.
+ const MessageConsumer& consumer() const { return consumer_; }
+
// Processes the given |module| and returns true if the given |module| is
// modified for optimization.
virtual bool Process(ir::Module* module) = 0;
+
+ private:
+ const MessageConsumer& consumer_; // Message consumer.
};
} // namespace opt
diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h
index 6242a46..58c0537 100644
--- a/source/opt/pass_manager.h
+++ b/source/opt/pass_manager.h
@@ -15,10 +15,11 @@
#ifndef LIBSPIRV_OPT_PASS_MANAGER_H_
#define LIBSPIRV_OPT_PASS_MANAGER_H_
-#include <cassert>
#include <memory>
#include <vector>
+#include "log.h"
+#include "message.h"
#include "module.h"
#include "passes.h"
@@ -27,32 +28,35 @@
// The pass manager, responsible for tracking and running passes.
// Clients should first call AddPass() to add passes and then call Run()
-// to run on a module. Passes are executed in the exact order of added.
-//
-// TODO(antiagainst): The pass manager is fairly simple right now. Eventually it
-// should support pass dependency, common functionality (like def-use analysis)
-// sharing, etc.
+// to run on a module. Passes are executed in the exact order of addition.
class PassManager {
public:
- // Adds a pass.
+ // Constructs a pass manager with the given message consumer.
+ explicit PassManager(MessageConsumer c) : consumer_(std::move(c)) {}
+
+ // Adds an externally constructed pass.
void AddPass(std::unique_ptr<Pass> pass) {
passes_.push_back(std::move(pass));
}
- // Uses the argument to construct a pass instance of type PassT, and adds the
- // pass instance to this pass manger.
- template <typename PassT, typename... Args>
+ // Uses the argument |args| to construct a pass instance of type |T|, and adds
+ // the pass instance to this pass manger. The pass added will use this pass
+ // manager's message consumer.
+ template <typename T, typename... Args>
void AddPass(Args&&... args) {
- passes_.emplace_back(new PassT(std::forward<Args>(args)...));
+ passes_.emplace_back(new T(consumer_, std::forward<Args>(args)...));
}
// Returns the number of passes added.
uint32_t NumPasses() const { return static_cast<uint32_t>(passes_.size()); }
// Returns a pointer to the |index|th pass added.
Pass* GetPass(uint32_t index) const {
- assert(index < passes_.size() && "index out of bound");
+ SPIRV_ASSERT(consumer_, index < passes_.size(), "index out of bound");
return passes_[index].get();
}
+ // Returns the message consumer.
+ const MessageConsumer& consumer() const { return consumer_; }
+
// Runs all passes on the given |module|.
void Run(ir::Module* module) {
bool modified = false;
@@ -64,6 +68,8 @@
}
private:
+ // Consumer for messages.
+ MessageConsumer consumer_;
// A vector of passes. Order matters.
std::vector<std::unique_ptr<Pass>> passes_;
};
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 9ea304d..68db8bf 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -14,8 +14,8 @@
#include "set_spec_constant_default_value_pass.h"
-#include <cstring>
#include <cctype>
+#include <cstring>
#include <string>
#include <tuple>
#include <unordered_map>
@@ -164,8 +164,8 @@
const uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
bool modified = false;
- analysis::DefUseManager def_use_mgr(module);
- analysis::TypeManager type_mgr(*module);
+ analysis::DefUseManager def_use_mgr(consumer(), module);
+ analysis::TypeManager type_mgr(consumer(), *module);
// Scan through all the annotation instructions to find 'OpDecorate SpecId'
// instructions. Then extract the decoration target of those instructions.
// The decoration targets should be spec constant defining instructions with
diff --git a/source/opt/set_spec_constant_default_value_pass.h b/source/opt/set_spec_constant_default_value_pass.h
index 0958f19..7762050 100644
--- a/source/opt/set_spec_constant_default_value_pass.h
+++ b/source/opt/set_spec_constant_default_value_pass.h
@@ -34,11 +34,12 @@
using SpecIdToInstMap = std::unordered_map<uint32_t, ir::Instruction*>;
// Constructs a pass instance with a map from spec ids to default values.
- explicit SetSpecConstantDefaultValuePass(
- const SpecIdToValueStrMap& default_values)
- : Pass(), spec_id_to_value_(default_values) {}
- explicit SetSpecConstantDefaultValuePass(SpecIdToValueStrMap&& default_values)
- : Pass(), spec_id_to_value_(std::move(default_values)) {}
+ SetSpecConstantDefaultValuePass(const MessageConsumer& c,
+ const SpecIdToValueStrMap& default_values)
+ : Pass(c), spec_id_to_value_(default_values) {}
+ SetSpecConstantDefaultValuePass(const MessageConsumer& c,
+ SpecIdToValueStrMap&& default_values)
+ : Pass(c), spec_id_to_value_(std::move(default_values)) {}
const char* name() const override { return "set-spec-const-default-value"; }
bool Process(ir::Module*) override;
diff --git a/source/opt/strip_debug_info_pass.h b/source/opt/strip_debug_info_pass.h
index 2bf3512..9a8d27c 100644
--- a/source/opt/strip_debug_info_pass.h
+++ b/source/opt/strip_debug_info_pass.h
@@ -25,6 +25,8 @@
// Section 3.32.2 of the SPIR-V spec).
class StripDebugInfoPass : public Pass {
public:
+ explicit StripDebugInfoPass(const MessageConsumer& c) : Pass(c) {}
+
const char* name() const override { return "strip-debug"; }
bool Process(ir::Module* module) override;
};
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index f55cecc..ce4b676 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -13,8 +13,8 @@
// limitations under the License.
#include <algorithm>
-#include <cassert>
+#include "log.h"
#include "reflect.h"
#include "type_manager.h"
@@ -161,16 +161,17 @@
type = new NamedBarrier();
break;
default:
- assert(0 && "unhandled type found");
+ SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
break;
}
uint32_t id = inst.result_id();
if (id == 0) {
- assert(inst.opcode() == SpvOpTypeForwardPointer &&
- "instruction without result id found");
+ SPIRV_ASSERT(consumer_, inst.opcode() == SpvOpTypeForwardPointer,
+ "instruction without result id found");
} else {
- assert(type != nullptr && "type should not be nullptr at this point");
+ SPIRV_ASSERT(consumer_, type != nullptr,
+ "type should not be nullptr at this point");
id_to_type_[id].reset(type);
type_to_id_[type] = id;
}
@@ -204,16 +205,16 @@
if (Struct* st = target_type->AsStruct()) {
st->AddMemeberDecoration(index, std::move(data));
} else {
- assert(0 && "OpMemberDecorate on non-struct type");
+ SPIRV_UNIMPLEMENTED(consumer_, "OpMemberDecorate non-struct type");
}
} break;
case SpvOpDecorationGroup:
case SpvOpGroupDecorate:
case SpvOpGroupMemberDecorate:
- assert(0 && "unhandled decoration");
+ SPIRV_UNIMPLEMENTED(consumer_, "unhandled decoration");
break;
default:
- assert(0 && "unreachable");
+ SPIRV_UNREACHABLE(consumer_);
break;
}
}
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
index a8a8155..48fa212 100644
--- a/source/opt/type_manager.h
+++ b/source/opt/type_manager.h
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "message.h"
#include "module.h"
#include "types.h"
@@ -33,7 +34,13 @@
using TypeToIdMap = std::unordered_map<const Type*, uint32_t>;
using ForwardPointerVector = std::vector<std::unique_ptr<ForwardPointer>>;
- inline explicit TypeManager(const spvtools::ir::Module& module);
+ // Constructs a type manager from the given |module|. All internal messages
+ // will be communicated to the outside via the given message |consumer|.
+ // This instance only keeps a reference to the |consumer|, so the |consumer|
+ // should outlive this instance.
+ TypeManager(const MessageConsumer& consumer,
+ const spvtools::ir::Module& module);
+
TypeManager(const TypeManager&) = delete;
TypeManager(TypeManager&&) = delete;
TypeManager& operator=(const TypeManager&) = delete;
@@ -64,6 +71,7 @@
// given instruction is not a decoration instruction or not decorating a type.
void AttachIfTypeDecoration(const spvtools::ir::Instruction& inst);
+ const MessageConsumer& consumer_; // Message consumer.
IdToTypeMap id_to_type_; // Mapping from ids to their type representations.
TypeToIdMap type_to_id_; // Mapping from types to their defining ids.
ForwardPointerVector forward_pointers_; // All forward pointer declarations.
@@ -72,7 +80,9 @@
std::unordered_set<ForwardPointer*> unresolved_forward_pointers_;
};
-inline TypeManager::TypeManager(const spvtools::ir::Module& module) {
+inline TypeManager::TypeManager(const spvtools::MessageConsumer& consumer,
+ const spvtools::ir::Module& module)
+ : consumer_(consumer) {
AnalyzeTypes(module);
}
diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp
index 02a5c9c..72fb3cd 100644
--- a/source/opt/unify_const_pass.cpp
+++ b/source/opt/unify_const_pass.cpp
@@ -105,7 +105,7 @@
bool UnifyConstantPass::Process(ir::Module* module) {
bool modified = false;
ResultIdTrie defined_constants;
- analysis::DefUseManager def_use_mgr(module);
+ analysis::DefUseManager def_use_mgr(consumer(), module);
for (ir::Instruction& inst : module->types_values()) {
// Do not handle the instruction when there are decorations upon the result
diff --git a/source/opt/unify_const_pass.h b/source/opt/unify_const_pass.h
index 6debaad..db17b69 100644
--- a/source/opt/unify_const_pass.h
+++ b/source/opt/unify_const_pass.h
@@ -36,6 +36,8 @@
// 3) NaN in float point format with different bit patterns are not unified.
class UnifyConstantPass : public Pass {
public:
+ explicit UnifyConstantPass(const MessageConsumer& c) : Pass(c) {}
+
const char* name() const override { return "unify-const"; }
bool Process(ir::Module*) override;
};
@@ -43,4 +45,4 @@
} // namespace opt
} // namespace spvtools
-#endif // LIBSPIRV_OPT_UNIFY_CONSTANT_PASS_H_
+#endif // LIBSPIRV_OPT_UNIFY_CONSTANT_PASS_H_
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
index ec8d569..436ed42 100644
--- a/test/opt/pass_fixture.h
+++ b/test/opt/pass_fixture.h
@@ -15,6 +15,7 @@
#ifndef LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
#define LIBSPIRV_TEST_OPT_PASS_FIXTURE_H_
+#include <iostream>
#include <string>
#include <tuple>
#include <vector>
@@ -39,7 +40,9 @@
class PassTest : public TestT {
public:
PassTest()
- : tools_(SPV_ENV_UNIVERSAL_1_1), manager_(new opt::PassManager()) {}
+ : consumer_(IgnoreMessage),
+ tools_(SPV_ENV_UNIVERSAL_1_1),
+ manager_(new opt::PassManager(consumer_)) {}
// Runs the given |pass| on the binary assembled from the |assembly|, and
// disassebles the optimized binary. Returns a tuple of disassembly string
@@ -70,7 +73,7 @@
template <typename PassT, typename... Args>
std::tuple<std::string, bool> SinglePassRunAndDisassemble(
const std::string& assembly, bool skip_nop, Args&&... args) {
- auto pass = MakeUnique<PassT>(std::forward<Args>(args)...);
+ auto pass = MakeUnique<PassT>(consumer_, std::forward<Args>(args)...);
return OptimizeAndDisassemble(pass.get(), assembly, skip_nop);
}
@@ -98,7 +101,7 @@
}
// Renews the pass manager, including clearing all previously added passes.
- void RenewPassManger() { manager_.reset(new opt::PassManager()); }
+ void RenewPassManger() { manager_.reset(new opt::PassManager(consumer_)); }
// Runs the passes added thus far using a pass manager on the binary assembled
// from the |original| assembly, and checks whether the optimized binary can
@@ -121,6 +124,7 @@
}
private:
+ MessageConsumer consumer_; // Message consumer.
SpvTools tools_; // An instance for calling SPIRV-Tools functionalities.
std::unique_ptr<opt::PassManager> manager_; // The pass manager.
};
diff --git a/test/opt/test_def_use.cpp b/test/opt/test_def_use.cpp
index 2f29c08..412bf64 100644
--- a/test/opt/test_def_use.cpp
+++ b/test/opt/test_def_use.cpp
@@ -132,7 +132,7 @@
ASSERT_NE(nullptr, module);
// Analyze def and use.
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
CheckDef(tc.du, manager.id_to_defs());
CheckUse(tc.du, manager.id_to_uses());
@@ -513,7 +513,7 @@
ASSERT_NE(nullptr, module);
// Analyze def and use.
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
// Do the substitution.
for (const auto& candiate : tc.candidates) {
@@ -815,7 +815,7 @@
ASSERT_NE(nullptr, module);
// Analyze def and use.
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
// Do the substitution.
for (const auto id : tc.ids_to_kill) manager.KillDef(id);
@@ -1065,7 +1065,7 @@
ASSERT_NE(nullptr, module);
// Analyze def and use.
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
// Do a bunch replacements.
manager.ReplaceAllUsesWith(9, 900); // to unused id
@@ -1188,7 +1188,7 @@
ASSERT_NE(nullptr, module);
// Analyze the instructions.
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
for (ir::Instruction& inst : tc.insts) {
manager.AnalyzeInstDefUse(&inst);
}
@@ -1312,7 +1312,7 @@
// KillInst
uint32_t index = 0;
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
module->ForEachInst([&index, &tc, &manager](ir::Instruction* inst) {
if (tc.indices_for_inst_to_kill.count(index) != 0) {
manager.KillInst(inst);
@@ -1419,7 +1419,7 @@
ASSERT_NE(nullptr, module);
// Get annotations
- opt::analysis::DefUseManager manager(module.get());
+ opt::analysis::DefUseManager manager(IgnoreMessage, module.get());
auto insts = manager.GetAnnotations(tc.id);
// Check
diff --git a/test/opt/test_line_debug_info.cpp b/test/opt/test_line_debug_info.cpp
index 81eed2b..3e86ed0 100644
--- a/test/opt/test_line_debug_info.cpp
+++ b/test/opt/test_line_debug_info.cpp
@@ -22,6 +22,8 @@
// A pass turning all none debug line instructions into Nop.
class NopifyPass : public opt::Pass {
public:
+ explicit NopifyPass(const MessageConsumer& c) : opt::Pass(c) {}
+
const char* name() const override { return "NopifyPass"; }
bool Process(ir::Module* module) override {
module->ForEachInst([](ir::Instruction* inst) { inst->ToNop(); },
diff --git a/test/opt/test_pass_manager.cpp b/test/opt/test_pass_manager.cpp
index cf653c2..b365ad2 100644
--- a/test/opt/test_pass_manager.cpp
+++ b/test/opt/test_pass_manager.cpp
@@ -29,23 +29,25 @@
// A null pass whose construtors accept arguments
class NullPassWithArgs : public opt::NullPass {
public:
- NullPassWithArgs(uint32_t) : NullPass() {}
- NullPassWithArgs(std::string) : NullPass() {}
- NullPassWithArgs(const std::vector<int>&) : NullPass() {}
- NullPassWithArgs(const std::vector<int>&, uint32_t) : NullPass() {}
+ NullPassWithArgs(const MessageConsumer& c, uint32_t) : NullPass(c) {}
+ NullPassWithArgs(const MessageConsumer& c, std::string) : NullPass(c) {}
+ NullPassWithArgs(const MessageConsumer& c, const std::vector<int>&)
+ : NullPass(c) {}
+ NullPassWithArgs(const MessageConsumer& c, const std::vector<int>&, uint32_t)
+ : NullPass(c) {}
const char* name() const override { return "null-with-args"; }
};
TEST(PassManager, Interface) {
- opt::PassManager manager;
+ opt::PassManager manager(IgnoreMessage);
EXPECT_EQ(0u, manager.NumPasses());
manager.AddPass<opt::StripDebugInfoPass>();
EXPECT_EQ(1u, manager.NumPasses());
EXPECT_STREQ("strip-debug", manager.GetPass(0)->name());
- manager.AddPass(MakeUnique<opt::NullPass>());
+ manager.AddPass(MakeUnique<opt::NullPass>(IgnoreMessage));
EXPECT_EQ(2u, manager.NumPasses());
EXPECT_STREQ("strip-debug", manager.GetPass(0)->name());
EXPECT_STREQ("null", manager.GetPass(1)->name());
@@ -72,6 +74,9 @@
// A pass that appends an OpNop instruction to the debug section.
class AppendOpNopPass : public opt::Pass {
+ public:
+ explicit AppendOpNopPass(const MessageConsumer& c) : opt::Pass(c) {}
+
const char* name() const override { return "AppendOpNop"; }
bool Process(ir::Module* module) override {
auto inst = MakeUnique<ir::Instruction>();
@@ -84,7 +89,9 @@
// section.
class AppendMultipleOpNopPass : public opt::Pass {
public:
- AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {}
+ AppendMultipleOpNopPass(const MessageConsumer& c, uint32_t num_nop)
+ : opt::Pass(c), num_nop_(num_nop) {}
+
const char* name() const override { return "AppendOpNop"; }
bool Process(ir::Module* module) override {
for (uint32_t i = 0; i < num_nop_; i++) {
@@ -100,6 +107,9 @@
// A pass that duplicates the last instruction in the debug section.
class DuplicateInstPass : public opt::Pass {
+ public:
+ explicit DuplicateInstPass(const MessageConsumer& c) : opt::Pass(c) {}
+
const char* name() const override { return "DuplicateInst"; }
bool Process(ir::Module* module) override {
auto inst = MakeUnique<ir::Instruction>(*(--module->debug_end()));
@@ -135,7 +145,9 @@
// A pass that appends an OpTypeVoid instruction that uses a given id.
class AppendTypeVoidInstPass : public opt::Pass {
public:
- AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {}
+ AppendTypeVoidInstPass(const MessageConsumer& c, uint32_t result_id)
+ : opt::Pass(c), result_id_(result_id) {}
+
const char* name() const override { return "AppendTypeVoidInstPass"; }
bool Process(ir::Module* module) override {
auto inst = MakeUnique<ir::Instruction>(SpvOpTypeVoid, 0, result_id_,
@@ -152,14 +164,14 @@
ir::Module module;
EXPECT_THAT(GetIdBound(module), Eq(0u));
- opt::PassManager manager;
+ opt::PassManager manager(IgnoreMessage);
manager.Run(&module);
manager.AddPass<AppendOpNopPass>();
// With no ID changes, the ID bound does not change.
EXPECT_THAT(GetIdBound(module), Eq(0u));
// Now we force an Id of 100 to be used.
- manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(100));
+ manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(IgnoreMessage, 100));
EXPECT_THAT(GetIdBound(module), Eq(0u));
manager.Run(&module);
// The Id has been updated automatically, even though the pass
@@ -167,12 +179,12 @@
EXPECT_THAT(GetIdBound(module), Eq(101u));
// Try one more time!
- manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(200));
+ manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(IgnoreMessage, 200));
manager.Run(&module);
EXPECT_THAT(GetIdBound(module), Eq(201u));
// Add another pass, but which uses a lower Id.
- manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(10));
+ manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(IgnoreMessage, 10));
manager.Run(&module);
// The Id stays high.
EXPECT_THAT(GetIdBound(module), Eq(201u));
diff --git a/test/opt/test_type_manager.cpp b/test/opt/test_type_manager.cpp
index 7120a81..1fb2776 100644
--- a/test/opt/test_type_manager.cpp
+++ b/test/opt/test_type_manager.cpp
@@ -90,7 +90,7 @@
std::unique_ptr<ir::Module> module =
SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
- opt::analysis::TypeManager manager(*module);
+ opt::analysis::TypeManager manager(IgnoreMessage, *module);
EXPECT_EQ(type_id_strs.size(), manager.NumTypes());
EXPECT_EQ(2u, manager.NumForwardPointers());
@@ -120,7 +120,7 @@
)";
std::unique_ptr<ir::Module> module =
SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
- opt::analysis::TypeManager manager(*module);
+ opt::analysis::TypeManager manager(IgnoreMessage, *module);
ASSERT_EQ(7u, manager.NumTypes());
ASSERT_EQ(0u, manager.NumForwardPointers());
@@ -170,7 +170,7 @@
)";
std::unique_ptr<ir::Module> module =
SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
- opt::analysis::TypeManager manager(*module);
+ opt::analysis::TypeManager manager(IgnoreMessage, *module);
ASSERT_EQ(10u, manager.NumTypes());
ASSERT_EQ(0u, manager.NumForwardPointers());
@@ -208,7 +208,7 @@
)";
std::unique_ptr<ir::Module> module =
SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
- opt::analysis::TypeManager manager(*module);
+ opt::analysis::TypeManager manager(IgnoreMessage, *module);
ASSERT_EQ(5u, manager.NumTypes());
ASSERT_EQ(0u, manager.NumForwardPointers());
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 0e3be79..d5784e1 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -16,6 +16,7 @@
#include <iostream>
#include <vector>
+#include "message.h"
#include "source/opt/ir_loader.h"
#include "source/opt/libspirv.hpp"
#include "source/opt/pass_manager.h"
@@ -62,7 +63,11 @@
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1;
- opt::PassManager pass_manager;
+ opt::PassManager pass_manager([](MessageLevel level, const char* source,
+ const spv_position_t& position,
+ const char* message) {
+ std::cerr << StringifyMessage(level, source, position, message);
+ });
for (int argi = 1; argi < argc; ++argi) {
const char* cur_arg = argv[argi];