Create an iterator class for in-memory representation.

This commit is contained in:
Lei Zhang 2016-08-10 10:02:28 -04:00
parent f949e321f2
commit 645ee1d9e0
9 changed files with 486 additions and 29 deletions

View File

@ -34,6 +34,7 @@
#include "basic_block.h"
#include "instruction.h"
#include "iterator.h"
namespace spvtools {
namespace ir {
@ -43,6 +44,9 @@ class Module;
// A SPIR-V function.
class Function {
public:
using iterator = UptrVectorIterator<BasicBlock>;
using const_iterator = UptrVectorIterator<BasicBlock, true>;
// Creates a function instance declared by the given instruction |def_inst|.
Function(std::unique_ptr<Instruction> def_inst)
: module_(nullptr),
@ -55,11 +59,10 @@ class Function {
inline void AddParameter(std::unique_ptr<Instruction> p);
// Appends a basic block to this function.
inline void AddBasicBlock(std::unique_ptr<BasicBlock> b);
const std::vector<std::unique_ptr<BasicBlock>>& basic_blocks() const {
return blocks_;
}
std::vector<std::unique_ptr<BasicBlock>>& basic_blocks() { return blocks_; }
iterator begin() { return iterator(&blocks_, blocks_.begin()); }
iterator end() { return iterator(&blocks_, blocks_.end()); }
const_iterator cbegin() { return const_iterator(&blocks_, blocks_.cbegin()); }
const_iterator cend() { return const_iterator(&blocks_, blocks_.cend()); }
// Runs the given function |f| on each instruction in this basic block.
void ForEachInst(const std::function<void(Instruction*)>& f);

View File

@ -106,11 +106,9 @@ void IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
// Resolves internal references among the module, functions, basic blocks, etc.
// This function should be called after adding all instructions.
void IrLoader::EndModule() {
for (auto& function : module_->functions()) {
for (auto& bb : function->basic_blocks()) {
bb->SetParent(function.get());
}
function->SetParent(module_);
for (auto& function : *module_) {
for (auto& bb : function) bb.SetParent(&function);
function.SetParent(module_);
}
}

171
source/opt/iterator.h Normal file
View File

@ -0,0 +1,171 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#ifndef LIBSPIRV_OPT_ITERATOR_H_
#define LIBSPIRV_OPT_ITERATOR_H_
#include <memory>
#include <type_traits>
#include <vector>
namespace spvtools {
namespace ir {
// An ad hoc iterator class for std::vector<std::unique_ptr<|ValueType|>>. The
// purpose of this iterator class is to provide transparent access to those
// std::unique_ptr managed elements in the vector, behaving like we are using
// std::vector<|ValueType|>.
template <typename ValueType, bool IsConst = false>
class UptrVectorIterator {
public:
using pointer =
typename std::conditional<IsConst, const ValueType*, ValueType*>::type;
using reference =
typename std::conditional<IsConst, const ValueType&, ValueType&>::type;
// Type aliases. We need to apply constness properly if |IsConst| is true.
using Uptr = std::unique_ptr<ValueType>;
using UptrVector = typename std::conditional<IsConst, const std::vector<Uptr>,
std::vector<Uptr>>::type;
using UnderlyingIterator =
typename std::conditional<IsConst, typename UptrVector::const_iterator,
typename UptrVector::iterator>::type;
// Creates a new iterator from the given |container| and its raw iterator
// |it|.
UptrVectorIterator(UptrVector* container, const UnderlyingIterator& it)
: container_(container), iterator_(it) {}
UptrVectorIterator(const UptrVectorIterator&) = default;
UptrVectorIterator& operator=(const UptrVectorIterator&) = default;
inline UptrVectorIterator& operator++();
inline UptrVectorIterator operator++(int);
inline UptrVectorIterator& operator--();
inline UptrVectorIterator operator--(int);
reference operator*() const { return **iterator_; }
pointer operator->() { return (*iterator_).get(); }
reference operator[](ptrdiff_t index) { return **(iterator_ + index); }
inline bool operator==(const UptrVectorIterator& that) const;
inline bool operator!=(const UptrVectorIterator& that) const;
inline ptrdiff_t operator-(const UptrVectorIterator& that) const;
inline bool operator<(const UptrVectorIterator& that) const;
// Inserts the given |value| to the position pointed to by this iterator
// and returns an iterator to the newly iserted |value|.
// If the underlying vector changes capacity, all previous iterators will be
// invalidated. Otherwise, those previous iterators pointing to after the
// insertion point will be invalidated.
inline UptrVectorIterator InsertBefore(Uptr value);
private:
UptrVector* container_; // The container we are manipulating.
UnderlyingIterator iterator_; // The raw iterator from the container.
};
// Handy class for a (begin, end) iterator pair.
template <typename IteratorType>
class IteratorRange {
public:
IteratorRange(IteratorType b, IteratorType e) : begin_(b), end_(e) {}
IteratorType begin() const { return begin_; }
IteratorType end() const { return end_; }
bool empty() const { return begin_ == end_; }
size_t size() const { return end_ - begin_; }
private:
IteratorType begin_;
IteratorType end_;
};
template <typename VT, bool IC>
inline UptrVectorIterator<VT, IC>& UptrVectorIterator<VT, IC>::operator++() {
++iterator_;
return *this;
}
template <typename VT, bool IC>
inline UptrVectorIterator<VT, IC> UptrVectorIterator<VT, IC>::operator++(int) {
auto it = *this;
++(*this);
return it;
}
template <typename VT, bool IC>
inline UptrVectorIterator<VT, IC>& UptrVectorIterator<VT, IC>::operator--() {
--iterator_;
return *this;
}
template <typename VT, bool IC>
inline UptrVectorIterator<VT, IC> UptrVectorIterator<VT, IC>::operator--(int) {
auto it = *this;
--(*this);
return it;
}
template <typename VT, bool IC>
inline bool UptrVectorIterator<VT, IC>::operator==(
const UptrVectorIterator& that) const {
return container_ == that.container_ && iterator_ == that.iterator_;
}
template <typename VT, bool IC>
inline bool UptrVectorIterator<VT, IC>::operator!=(
const UptrVectorIterator& that) const {
return !(*this == that);
}
template <typename VT, bool IC>
inline ptrdiff_t UptrVectorIterator<VT, IC>::operator-(
const UptrVectorIterator& that) const {
assert(container_ == that.container_);
return iterator_ - that.iterator_;
}
template <typename VT, bool IC>
inline bool UptrVectorIterator<VT, IC>::operator<(
const UptrVectorIterator& that) const {
assert(container_ == that.container_);
return iterator_ < that.iterator_;
}
template <typename VT, bool IC>
inline UptrVectorIterator<VT, IC> UptrVectorIterator<VT, IC>::InsertBefore(
Uptr value) {
auto index = iterator_ - container_->begin();
container_->insert(iterator_, std::move(value));
return UptrVectorIterator(container_, container_->begin() + index);
}
} // namespace ir
} // namespace spvtools
#endif // LIBSPIRV_OPT_ITERATOR_H_

View File

@ -34,6 +34,7 @@
#include "function.h"
#include "instruction.h"
#include "iterator.h"
namespace spvtools {
namespace ir {
@ -51,6 +52,11 @@ struct ModuleHeader {
// serves as the backbone of optimization transformations.
class Module {
public:
using iterator = UptrVectorIterator<Function>;
using const_iterator = UptrVectorIterator<Function, true>;
using inst_iterator = UptrVectorIterator<Instruction>;
using const_inst_iterator = UptrVectorIterator<Instruction, true>;
// Creates an empty module with zero'd header.
Module() : header_({}) {}
@ -85,22 +91,29 @@ class Module {
// module.
std::vector<Instruction*> GetTypes();
std::vector<const Instruction*> GetTypes() const;
// Returns the constant-defining instructions.
// Returns a vector of pointers to constant-creation instructions in this
// module.
std::vector<Instruction*> GetConstants();
const std::vector<std::unique_ptr<Instruction>>& debugs() const {
return debugs_;
}
std::vector<std::unique_ptr<Instruction>>& debugs() { return debugs_; }
const std::vector<std::unique_ptr<Instruction>>& annotations() const {
return annotations_;
}
std::vector<std::unique_ptr<Instruction>>& annotations() {
return annotations_;
}
const std::vector<std::unique_ptr<Function>>& functions() const {
return functions_;
}
std::vector<std::unique_ptr<Function>>& functions() { return functions_; }
// Iterators for debug instructions (excluding OpLine & OpNoLine) contained in
// this module.
inline inst_iterator debug_begin();
inline inst_iterator debug_end();
inline IteratorRange<inst_iterator> debugs();
inline IteratorRange<const_inst_iterator> debugs() const;
// Clears all debug instructions (excluding OpLine & OpNoLine).
void debug_clear() { debugs_.clear(); }
// Iterators for annotation instructions contained in this module.
IteratorRange<inst_iterator> annotations();
IteratorRange<const_inst_iterator> annotations() const;
// Iterators for functions contained in this module.
iterator begin() { return iterator(&functions_, functions_.begin()); }
iterator end() { return iterator(&functions_, functions_.end()); }
inline const_iterator cbegin() const;
inline const_iterator cend() const;
// Invokes function |f| on all instructions in this module.
void ForEachInst(const std::function<void(Instruction*)>& f);
@ -176,6 +189,44 @@ inline void Module::AddFunction(std::unique_ptr<Function> f) {
functions_.emplace_back(std::move(f));
}
inline Module::inst_iterator Module::debug_begin() {
return inst_iterator(&debugs_, debugs_.begin());
}
inline Module::inst_iterator Module::debug_end() {
return inst_iterator(&debugs_, debugs_.end());
}
inline IteratorRange<Module::inst_iterator> Module::debugs() {
return IteratorRange<inst_iterator>(inst_iterator(&debugs_, debugs_.begin()),
inst_iterator(&debugs_, debugs_.end()));
}
inline IteratorRange<Module::const_inst_iterator> Module::debugs() const {
return IteratorRange<const_inst_iterator>(
const_inst_iterator(&debugs_, debugs_.cbegin()),
const_inst_iterator(&debugs_, debugs_.cend()));
}
inline IteratorRange<Module::inst_iterator> Module::annotations() {
return IteratorRange<inst_iterator>(
inst_iterator(&annotations_, annotations_.begin()),
inst_iterator(&annotations_, annotations_.end()));
}
inline IteratorRange<Module::const_inst_iterator> Module::annotations() const {
return IteratorRange<const_inst_iterator>(
const_inst_iterator(&annotations_, annotations_.cbegin()),
const_inst_iterator(&annotations_, annotations_.cend()));
}
inline Module::const_iterator Module::cbegin() const {
return const_iterator(&functions_, functions_.cbegin());
}
inline Module::const_iterator Module::cend() const {
return const_iterator(&functions_, functions_.cend());
}
} // namespace ir
} // namespace spvtools

View File

@ -39,7 +39,7 @@ namespace opt {
bool StripDebugInfoPass::Process(ir::Module* module) {
bool modified = !module->debugs().empty();
module->debugs().clear();
module->debug_clear();
module->ForEachInst([&modified](ir::Instruction* inst) {
modified |= !inst->dbg_line_insts().empty();

View File

@ -53,10 +53,11 @@ ForwardPointer* TypeManager::GetForwardPointer(uint32_t index) const {
void TypeManager::AnalyzeTypes(const spvtools::ir::Module& module) {
for (const auto* inst : module.GetTypes()) RecordIfTypeDefinition(*inst);
for (const auto& inst : module.annotations()) AttachIfTypeDecoration(*inst);
for (const auto& inst : module.annotations()) AttachIfTypeDecoration(inst);
}
Type* TypeManager::RecordIfTypeDefinition(const spvtools::ir::Instruction& inst) {
Type* TypeManager::RecordIfTypeDefinition(
const spvtools::ir::Instruction& inst) {
if (!spvtools::ir::IsTypeInst(inst.opcode())) return nullptr;
Type* type = nullptr;

View File

@ -73,3 +73,8 @@ add_spvtools_unittest(TARGET type_manager
SRCS test_type_manager.cpp
LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET iterator
SRCS test_iterator.cpp
LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
)

228
test/opt/test_iterator.cpp Normal file
View File

@ -0,0 +1,228 @@
// Copyright (c) 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
// https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
#include <memory>
#include <vector>
#include "gmock/gmock.h"
#include "opt/iterator.h"
namespace {
using namespace spvtools;
using ::testing::ContainerEq;
TEST(Iterator, IncrementDeref) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> it(&data, data.begin());
ir::UptrVectorIterator<int> end(&data, data.end());
EXPECT_EQ(*data[0], *it);
for (int i = 1; i < count; ++i) {
EXPECT_NE(end, it);
EXPECT_EQ(*data[i], *(++it));
}
EXPECT_EQ(end, ++it);
}
TEST(Iterator, DecrementDeref) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> begin(&data, data.begin());
ir::UptrVectorIterator<int> it(&data, data.end());
for (int i = count - 1; i >= 0; --i) {
EXPECT_NE(begin, it);
EXPECT_EQ(*data[i], *(--it));
}
EXPECT_EQ(begin, it);
}
TEST(Iterator, PostIncrementDeref) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> it(&data, data.begin());
ir::UptrVectorIterator<int> end(&data, data.end());
for (int i = 0; i < count; ++i) {
EXPECT_NE(end, it);
EXPECT_EQ(*data[i], *(it++));
}
EXPECT_EQ(end, it);
}
TEST(Iterator, PostDecrementDeref) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> begin(&data, data.begin());
ir::UptrVectorIterator<int> end(&data, data.end());
ir::UptrVectorIterator<int> it(&data, data.end());
EXPECT_EQ(end, it--);
for (int i = count - 1; i >= 1; --i) {
EXPECT_EQ(*data[i], *(it--));
}
// Decrementing .begin() is undefined behavior.
EXPECT_EQ(*data[0], *it);
}
TEST(Iterator, Access) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> it(&data, data.begin());
for (int i = 0; i < count; ++i) EXPECT_EQ(*data[i], it[i]);
}
TEST(Iterator, Comparison) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
}
ir::UptrVectorIterator<int> it(&data, data.begin());
ir::UptrVectorIterator<int> end(&data, data.end());
for (int i = 0; i < count; ++i, ++it) EXPECT_TRUE(it < end);
EXPECT_EQ(end, it);
}
TEST(Iterator, InsertBeginEnd) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
std::vector<int> expected;
std::vector<int> actual;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
expected.push_back(i);
}
// Insert at the beginning
expected.insert(expected.begin(), -100);
ir::UptrVectorIterator<int> begin(&data, data.begin());
auto insert_point = begin.InsertBefore(std::unique_ptr<int>(new int(-100)));
for (int i = 0; i < count + 1; ++i) {
actual.push_back(*(insert_point++));
}
EXPECT_THAT(actual, ContainerEq(expected));
// Insert at the end
expected.push_back(-42);
expected.push_back(-36);
expected.push_back(-77);
ir::UptrVectorIterator<int> end(&data, data.end());
end = end.InsertBefore(std::unique_ptr<int>(new int(-77)));
end = end.InsertBefore(std::unique_ptr<int>(new int(-36)));
end = end.InsertBefore(std::unique_ptr<int>(new int(-42)));
actual.clear();
begin = ir::UptrVectorIterator<int>(&data, data.begin());
for (int i = 0; i < count + 4; ++i) {
actual.push_back(*(begin++));
}
EXPECT_THAT(actual, ContainerEq(expected));
}
TEST(Iterator, InsertMiddle) {
const int count = 100;
std::vector<std::unique_ptr<int>> data;
std::vector<int> expected;
std::vector<int> actual;
for (int i = 0; i < count; ++i) {
data.emplace_back(new int(i));
expected.push_back(i);
}
const int insert_pos = 42;
expected.insert(expected.begin() + insert_pos, -100);
expected.insert(expected.begin() + insert_pos, -42);
ir::UptrVectorIterator<int> it(&data, data.begin());
for (int i = 0; i < insert_pos; ++i) ++it;
it = it.InsertBefore(std::unique_ptr<int>(new int(-100)));
it = it.InsertBefore(std::unique_ptr<int>(new int(-42)));
auto begin = ir::UptrVectorIterator<int>(&data, data.begin());
for (int i = 0; i < count + 2; ++i) {
actual.push_back(*(begin++));
}
EXPECT_THAT(actual, ContainerEq(expected));
}
TEST(IteratorRange, Interface) {
const uint32_t count = 100;
std::vector<std::unique_ptr<uint32_t>> data;
for (uint32_t i = 0; i < count; ++i) {
data.emplace_back(new uint32_t(i));
}
auto b = ir::UptrVectorIterator<uint32_t>(&data, data.begin());
auto e = ir::UptrVectorIterator<uint32_t>(&data, data.end());
auto range = ir::IteratorRange<decltype(b)>(b, e);
EXPECT_EQ(b, range.begin());
EXPECT_EQ(e, range.end());
EXPECT_FALSE(range.empty());
EXPECT_EQ(count, range.size());
EXPECT_EQ(0u, *range.begin());
EXPECT_EQ(99u, *(--range.end()));
// IteratorRange itself is immutable.
++b, --e;
EXPECT_EQ(count, range.size());
++range.begin(), --range.end();
EXPECT_EQ(count, range.size());
}
} // anonymous namespace

View File

@ -65,7 +65,7 @@ class DuplicateInstPass : public opt::Pass {
const char* name() const override { return "DuplicateInst"; }
bool Process(ir::Module* module) override {
std::unique_ptr<ir::Instruction> inst(
new ir::Instruction(*module->debugs().back()));
new ir::Instruction(*(--module->debug_end())));
module->AddDebugInst(std::move(inst));
return true;
}