mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Re-format files in source, source/opt, source/util, source/val and tools.
NFC. This just makes sure every file is formatted following the formatting definition in .clang-format. Re-formatted with: $ clang-format -i $(find source tools include -name '*.cpp') $ clang-format -i $(find source tools include -name '*.h')
This commit is contained in:
parent
f32d11f74b
commit
d2938e4842
@ -179,12 +179,11 @@ class Parser {
|
||||
word_index(0),
|
||||
endian(),
|
||||
requires_endian_conversion(false) {
|
||||
|
||||
// Temporary storage for parser state within a single instruction.
|
||||
// Most instructions require fewer than 25 words or operands.
|
||||
operands.reserve(25);
|
||||
endian_converted_words.reserve(25);
|
||||
expected_operands.reserve(25);
|
||||
// Temporary storage for parser state within a single instruction.
|
||||
// Most instructions require fewer than 25 words or operands.
|
||||
operands.reserve(25);
|
||||
endian_converted_words.reserve(25);
|
||||
expected_operands.reserve(25);
|
||||
}
|
||||
State() : State(0, 0, nullptr) {}
|
||||
const uint32_t* words; // Words in the binary SPIR-V module.
|
||||
@ -310,7 +309,8 @@ spv_result_t Parser::parseInstruction() {
|
||||
// own operands depending on the selected extended instruction.
|
||||
_.expected_operands.clear();
|
||||
for (auto i = 0; i < opcode_desc->numTypes; i++)
|
||||
_.expected_operands.push_back(opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]);
|
||||
_.expected_operands.push_back(
|
||||
opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]);
|
||||
|
||||
while (_.word_index < inst_offset + inst_word_count) {
|
||||
const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
|
||||
@ -323,7 +323,8 @@ spv_result_t Parser::parseInstruction() {
|
||||
<< inst_word_count << ".";
|
||||
}
|
||||
|
||||
spv_operand_type_t type = spvTakeFirstMatchableOperand(&_.expected_operands);
|
||||
spv_operand_type_t type =
|
||||
spvTakeFirstMatchableOperand(&_.expected_operands);
|
||||
|
||||
if (auto error =
|
||||
parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
|
||||
@ -355,7 +356,8 @@ spv_result_t Parser::parseInstruction() {
|
||||
// word.
|
||||
assert(!_.requires_endian_conversion ||
|
||||
(inst_word_count == _.endian_converted_words.size()));
|
||||
assert(_.requires_endian_conversion || (_.endian_converted_words.size() == 1));
|
||||
assert(_.requires_endian_conversion ||
|
||||
(_.endian_converted_words.size() == 1));
|
||||
|
||||
recordNumberType(inst_offset, &inst);
|
||||
|
||||
@ -430,8 +432,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
// Save the result ID to type ID mapping.
|
||||
// In the grammar, type ID always appears before result ID.
|
||||
if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end())
|
||||
return diagnostic(SPV_ERROR_INVALID_ID) << "Id " << inst->result_id
|
||||
<< " is defined more than once";
|
||||
return diagnostic(SPV_ERROR_INVALID_ID)
|
||||
<< "Id " << inst->result_id << " is defined more than once";
|
||||
// Record it.
|
||||
// A regular value maps to its type. Some instructions (e.g. OpLabel)
|
||||
// have no type Id, and will map to 0. The result Id for a
|
||||
@ -477,8 +479,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
|
||||
assert(SpvOpSpecConstantOp == opcode);
|
||||
if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
|
||||
return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": "
|
||||
<< word;
|
||||
return diagnostic()
|
||||
<< "Invalid " << spvOperandTypeStr(type) << ": " << word;
|
||||
}
|
||||
spv_opcode_desc opcode_entry = nullptr;
|
||||
if (grammar_.lookupOpcode(SpvOp(word), &opcode_entry)) {
|
||||
@ -581,8 +583,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
const spv_ext_inst_type_t ext_inst_type =
|
||||
spvExtInstImportTypeGet(string);
|
||||
if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
|
||||
return diagnostic() << "Invalid extended instruction import '"
|
||||
<< string << "'";
|
||||
return diagnostic()
|
||||
<< "Invalid extended instruction import '" << string << "'";
|
||||
}
|
||||
// We must have parsed a valid result ID. It's a condition
|
||||
// of the grammar, and we only accept non-zero result Ids.
|
||||
@ -620,9 +622,9 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
|
||||
|
||||
spv_operand_desc entry;
|
||||
if (grammar_.lookupOperand(type, word, &entry)) {
|
||||
return diagnostic() << "Invalid "
|
||||
<< spvOperandTypeStr(parsed_operand.type)
|
||||
<< " operand: " << word;
|
||||
return diagnostic()
|
||||
<< "Invalid " << spvOperandTypeStr(parsed_operand.type)
|
||||
<< " operand: " << word;
|
||||
}
|
||||
// Prepare to accept operands to this operand, if needed.
|
||||
spvPushOperandTypes(entry->operandTypes, expected_operands);
|
||||
|
139
source/cfa.h
139
source/cfa.h
@ -17,9 +17,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
@ -36,12 +36,12 @@ using std::vector;
|
||||
namespace spvtools {
|
||||
|
||||
// Control Flow Analysis of control flow graphs of basic block nodes |BB|.
|
||||
template<class BB> class CFA {
|
||||
template <class BB>
|
||||
class CFA {
|
||||
using bb_ptr = BB*;
|
||||
using cbb_ptr = const BB*;
|
||||
using bb_iter = typename std::vector<BB*>::const_iterator;
|
||||
using get_blocks_func =
|
||||
std::function<const std::vector<BB*>*(const BB*)>;
|
||||
using get_blocks_func = std::function<const std::vector<BB*>*(const BB*)>;
|
||||
|
||||
struct block_info {
|
||||
cbb_ptr block; ///< pointer to the block
|
||||
@ -50,15 +50,16 @@ template<class BB> class CFA {
|
||||
|
||||
/// Returns true if a block with @p id is found in the @p work_list vector
|
||||
///
|
||||
/// @param[in] work_list Set of blocks visited in the the depth first traversal
|
||||
/// @param[in] work_list Set of blocks visited in the the depth first
|
||||
/// traversal
|
||||
/// of the CFG
|
||||
/// @param[in] id The ID of the block being checked
|
||||
///
|
||||
/// @return true if the edge work_list.back().block->id() => id is a back-edge
|
||||
static bool FindInWorkList(
|
||||
const std::vector<block_info>& work_list, uint32_t id);
|
||||
static bool FindInWorkList(const std::vector<block_info>& work_list,
|
||||
uint32_t id);
|
||||
|
||||
public:
|
||||
public:
|
||||
/// @brief Depth first traversal starting from the \p entry BasicBlock
|
||||
///
|
||||
/// This function performs a depth first traversal from the \p entry
|
||||
@ -75,15 +76,16 @@ public:
|
||||
/// CFG following postorder traversal semantics
|
||||
/// @param[in] backedge A function that will be called when a backedge is
|
||||
/// encountered during a traversal
|
||||
/// NOTE: The @p successor_func and predecessor_func each return a pointer to a
|
||||
/// NOTE: The @p successor_func and predecessor_func each return a pointer to
|
||||
/// a
|
||||
/// collection such that iterators to that collection remain valid for the
|
||||
/// lifetime of the algorithm.
|
||||
static void DepthFirstTraversal(const BB* entry,
|
||||
get_blocks_func successor_func,
|
||||
std::function<void(cbb_ptr)> preorder,
|
||||
std::function<void(cbb_ptr)> postorder,
|
||||
std::function<void(cbb_ptr, cbb_ptr)> backedge);
|
||||
|
||||
static void DepthFirstTraversal(
|
||||
const BB* entry, get_blocks_func successor_func,
|
||||
std::function<void(cbb_ptr)> preorder,
|
||||
std::function<void(cbb_ptr)> postorder,
|
||||
std::function<void(cbb_ptr, cbb_ptr)> backedge);
|
||||
|
||||
/// @brief Calculates dominator edges for a set of blocks
|
||||
///
|
||||
/// Computes dominators using the algorithm of Cooper, Harvey, and Kennedy
|
||||
@ -92,54 +94,58 @@ public:
|
||||
/// The algorithm assumes there is a unique root node (a node without
|
||||
/// predecessors), and it is therefore at the end of the postorder vector.
|
||||
///
|
||||
/// This function calculates the dominator edges for a set of blocks in the CFG.
|
||||
/// This function calculates the dominator edges for a set of blocks in the
|
||||
/// CFG.
|
||||
/// Uses the dominator algorithm by Cooper et al.
|
||||
///
|
||||
/// @param[in] postorder A vector of blocks in post order traversal order
|
||||
/// @param[in] postorder A vector of blocks in post order traversal
|
||||
/// order
|
||||
/// in a CFG
|
||||
/// @param[in] predecessor_func Function used to get the predecessor nodes of a
|
||||
/// @param[in] predecessor_func Function used to get the predecessor nodes of
|
||||
/// a
|
||||
/// block
|
||||
///
|
||||
/// @return the dominator tree of the graph, as a vector of pairs of nodes.
|
||||
/// The first node in the pair is a node in the graph. The second node in the
|
||||
/// pair is its immediate dominator in the sense of Cooper et.al., where a block
|
||||
/// without predecessors (such as the root node) is its own immediate dominator.
|
||||
/// pair is its immediate dominator in the sense of Cooper et.al., where a
|
||||
/// block
|
||||
/// without predecessors (such as the root node) is its own immediate
|
||||
/// dominator.
|
||||
static vector<pair<BB*, BB*>> CalculateDominators(
|
||||
const vector<cbb_ptr>& postorder, get_blocks_func predecessor_func);
|
||||
const vector<cbb_ptr>& postorder, get_blocks_func predecessor_func);
|
||||
|
||||
// Computes a minimal set of root nodes required to traverse, in the forward
|
||||
// direction, the CFG represented by the given vector of blocks, and successor
|
||||
// and predecessor functions. When considering adding two nodes, each having
|
||||
// predecessors, favour using the one that appears earlier on the input blocks
|
||||
// list.
|
||||
static std::vector<BB*> TraversalRoots(
|
||||
const std::vector<BB*>& blocks,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func);
|
||||
static std::vector<BB*> TraversalRoots(const std::vector<BB*>& blocks,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func);
|
||||
|
||||
static void ComputeAugmentedCFG(
|
||||
std::vector<BB*>& ordered_blocks,
|
||||
BB* pseudo_entry_block,
|
||||
BB* pseudo_exit_block,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>* augmented_successors_map,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>* augmented_predecessors_map,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func);
|
||||
std::vector<BB*>& ordered_blocks, BB* pseudo_entry_block,
|
||||
BB* pseudo_exit_block,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>* augmented_successors_map,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>*
|
||||
augmented_predecessors_map,
|
||||
get_blocks_func succ_func, get_blocks_func pred_func);
|
||||
};
|
||||
|
||||
template<class BB> bool CFA<BB>::FindInWorkList(const vector<block_info>& work_list,
|
||||
uint32_t id) {
|
||||
template <class BB>
|
||||
bool CFA<BB>::FindInWorkList(const vector<block_info>& work_list, uint32_t id) {
|
||||
for (const auto b : work_list) {
|
||||
if (b.block->id() == id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class BB> void CFA<BB>::DepthFirstTraversal(const BB* entry,
|
||||
get_blocks_func successor_func,
|
||||
function<void(cbb_ptr)> preorder,
|
||||
function<void(cbb_ptr)> postorder,
|
||||
function<void(cbb_ptr, cbb_ptr)> backedge) {
|
||||
template <class BB>
|
||||
void CFA<BB>::DepthFirstTraversal(const BB* entry,
|
||||
get_blocks_func successor_func,
|
||||
function<void(cbb_ptr)> preorder,
|
||||
function<void(cbb_ptr)> postorder,
|
||||
function<void(cbb_ptr, cbb_ptr)> backedge) {
|
||||
unordered_set<uint32_t> processed;
|
||||
|
||||
/// NOTE: work_list is the sequence of nodes from the root node to the node
|
||||
@ -147,7 +153,7 @@ template<class BB> void CFA<BB>::DepthFirstTraversal(const BB* entry,
|
||||
vector<block_info> work_list;
|
||||
work_list.reserve(10);
|
||||
|
||||
work_list.push_back({ entry, begin(*successor_func(entry)) });
|
||||
work_list.push_back({entry, begin(*successor_func(entry))});
|
||||
preorder(entry);
|
||||
processed.insert(entry->id());
|
||||
|
||||
@ -156,8 +162,7 @@ template<class BB> void CFA<BB>::DepthFirstTraversal(const BB* entry,
|
||||
if (top.iter == end(*successor_func(top.block))) {
|
||||
postorder(top.block);
|
||||
work_list.pop_back();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BB* child = *top.iter;
|
||||
top.iter++;
|
||||
if (FindInWorkList(work_list, child->id())) {
|
||||
@ -166,16 +171,16 @@ template<class BB> void CFA<BB>::DepthFirstTraversal(const BB* entry,
|
||||
if (processed.count(child->id()) == 0) {
|
||||
preorder(child);
|
||||
work_list.emplace_back(
|
||||
block_info{ child, begin(*successor_func(child)) });
|
||||
block_info{child, begin(*successor_func(child))});
|
||||
processed.insert(child->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class BB>
|
||||
template <class BB>
|
||||
vector<pair<BB*, BB*>> CFA<BB>::CalculateDominators(
|
||||
const vector<cbb_ptr>& postorder, get_blocks_func predecessor_func) {
|
||||
const vector<cbb_ptr>& postorder, get_blocks_func predecessor_func) {
|
||||
struct block_detail {
|
||||
size_t dominator; ///< The index of blocks's dominator in post order array
|
||||
size_t postorder_index; ///< The index of the block in the post order array
|
||||
@ -184,7 +189,7 @@ vector<pair<BB*, BB*>> CFA<BB>::CalculateDominators(
|
||||
|
||||
unordered_map<cbb_ptr, block_detail> idoms;
|
||||
for (size_t i = 0; i < postorder.size(); i++) {
|
||||
idoms[postorder[i]] = { undefined_dom, i };
|
||||
idoms[postorder[i]] = {undefined_dom, i};
|
||||
}
|
||||
idoms[postorder.back()].dominator = idoms[postorder.back()].postorder_index;
|
||||
|
||||
@ -196,10 +201,10 @@ vector<pair<BB*, BB*>> CFA<BB>::CalculateDominators(
|
||||
// Find the first processed/reachable predecessor that is reachable
|
||||
// in the forward traversal.
|
||||
auto res = find_if(begin(predecessors), end(predecessors),
|
||||
[&idoms, undefined_dom](BB* pred) {
|
||||
return idoms.count(pred) &&
|
||||
idoms[pred].dominator != undefined_dom;
|
||||
});
|
||||
[&idoms, undefined_dom](BB* pred) {
|
||||
return idoms.count(pred) &&
|
||||
idoms[pred].dominator != undefined_dom;
|
||||
});
|
||||
if (res == end(predecessors)) continue;
|
||||
const BB* idom = *res;
|
||||
size_t idom_idx = idoms[idom].postorder_index;
|
||||
@ -236,17 +241,16 @@ vector<pair<BB*, BB*>> CFA<BB>::CalculateDominators(
|
||||
for (auto idom : idoms) {
|
||||
// NOTE: performing a const cast for convenient usage with
|
||||
// UpdateImmediateDominators
|
||||
out.push_back({ const_cast<BB*>(get<0>(idom)),
|
||||
const_cast<BB*>(postorder[get<1>(idom).dominator]) });
|
||||
out.push_back({const_cast<BB*>(get<0>(idom)),
|
||||
const_cast<BB*>(postorder[get<1>(idom).dominator])});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<class BB>
|
||||
std::vector<BB*> CFA<BB>::TraversalRoots(
|
||||
const std::vector<BB*>& blocks,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func) {
|
||||
template <class BB>
|
||||
std::vector<BB*> CFA<BB>::TraversalRoots(const std::vector<BB*>& blocks,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func) {
|
||||
// The set of nodes which have been visited from any of the roots so far.
|
||||
std::unordered_set<const BB*> visited;
|
||||
|
||||
@ -254,11 +258,10 @@ std::vector<BB*> CFA<BB>::TraversalRoots(
|
||||
auto ignore_block = [](const BB*) {};
|
||||
auto ignore_blocks = [](const BB*, const BB*) {};
|
||||
|
||||
|
||||
auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block,
|
||||
&ignore_blocks](const BB* entry) {
|
||||
DepthFirstTraversal(
|
||||
entry, succ_func, mark_visited, ignore_block, ignore_blocks);
|
||||
&ignore_blocks](const BB* entry) {
|
||||
DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block,
|
||||
ignore_blocks);
|
||||
};
|
||||
|
||||
std::vector<BB*> result;
|
||||
@ -283,15 +286,13 @@ std::vector<BB*> CFA<BB>::TraversalRoots(
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class BB>
|
||||
template <class BB>
|
||||
void CFA<BB>::ComputeAugmentedCFG(
|
||||
std::vector<BB*>& ordered_blocks,
|
||||
BB* pseudo_entry_block, BB* pseudo_exit_block,
|
||||
std::vector<BB*>& ordered_blocks, BB* pseudo_entry_block,
|
||||
BB* pseudo_exit_block,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>* augmented_successors_map,
|
||||
std::unordered_map<const BB*, std::vector<BB*>>* augmented_predecessors_map,
|
||||
get_blocks_func succ_func,
|
||||
get_blocks_func pred_func) {
|
||||
|
||||
get_blocks_func succ_func, get_blocks_func pred_func) {
|
||||
// Compute the successors of the pseudo-entry block, and
|
||||
// the predecessors of the pseudo exit block.
|
||||
auto sources = TraversalRoots(ordered_blocks, succ_func, pred_func);
|
||||
@ -308,7 +309,7 @@ void CFA<BB>::ComputeAugmentedCFG(
|
||||
// constraint when A is a loop header that points to itself as its
|
||||
// own continue target, and B is the latch block for the loop.
|
||||
std::vector<BB*> reversed_blocks(ordered_blocks.rbegin(),
|
||||
ordered_blocks.rend());
|
||||
ordered_blocks.rend());
|
||||
auto sinks = TraversalRoots(reversed_blocks, pred_func, succ_func);
|
||||
|
||||
// Wire up the pseudo entry block.
|
||||
@ -332,6 +333,6 @@ void CFA<BB>::ComputeAugmentedCFG(
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace spvtools
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPVTOOLS_CFA_H_
|
||||
|
@ -38,9 +38,9 @@ struct MarkvCodecOptions {
|
||||
// |bits| is a textual representation of the MARK-V bit sequence used to encode
|
||||
// the instruction (char '0' for 0, char '1' for 1).
|
||||
// |comment| contains all logs generated while processing the instruction.
|
||||
using MarkvDebugConsumer = std::function<bool(
|
||||
const std::vector<uint32_t>& words, const std::string& bits,
|
||||
const std::string& comment)>;
|
||||
using MarkvDebugConsumer =
|
||||
std::function<bool(const std::vector<uint32_t>& words,
|
||||
const std::string& bits, const std::string& comment)>;
|
||||
|
||||
// Logging callback. Called often (if decoder reads a single bit, the log
|
||||
// consumer will receive 1 character string with that bit).
|
||||
@ -54,26 +54,20 @@ using MarkvLogConsumer = std::function<void(const std::string& snippet)>;
|
||||
// Encodes the given SPIR-V binary to MARK-V binary.
|
||||
// |log_consumer| is optional (pass MarkvLogConsumer() to disable).
|
||||
// |debug_consumer| is optional (pass MarkvDebugConsumer() to disable).
|
||||
spv_result_t SpirvToMarkv(spv_const_context context,
|
||||
const std::vector<uint32_t>& spirv,
|
||||
const MarkvCodecOptions& options,
|
||||
const MarkvModel& markv_model,
|
||||
MessageConsumer message_consumer,
|
||||
MarkvLogConsumer log_consumer,
|
||||
MarkvDebugConsumer debug_consumer,
|
||||
std::vector<uint8_t>* markv);
|
||||
spv_result_t SpirvToMarkv(
|
||||
spv_const_context context, const std::vector<uint32_t>& spirv,
|
||||
const MarkvCodecOptions& options, const MarkvModel& markv_model,
|
||||
MessageConsumer message_consumer, MarkvLogConsumer log_consumer,
|
||||
MarkvDebugConsumer debug_consumer, std::vector<uint8_t>* markv);
|
||||
|
||||
// Decodes a SPIR-V binary from the given MARK-V binary.
|
||||
// |log_consumer| is optional (pass MarkvLogConsumer() to disable).
|
||||
// |debug_consumer| is optional (pass MarkvDebugConsumer() to disable).
|
||||
spv_result_t MarkvToSpirv(spv_const_context context,
|
||||
const std::vector<uint8_t>& markv,
|
||||
const MarkvCodecOptions& options,
|
||||
const MarkvModel& markv_model,
|
||||
MessageConsumer message_consumer,
|
||||
MarkvLogConsumer log_consumer,
|
||||
MarkvDebugConsumer debug_consumer,
|
||||
std::vector<uint32_t>* spirv);
|
||||
spv_result_t MarkvToSpirv(
|
||||
spv_const_context context, const std::vector<uint8_t>& markv,
|
||||
const MarkvCodecOptions& options, const MarkvModel& markv_model,
|
||||
MessageConsumer message_consumer, MarkvLogConsumer log_consumer,
|
||||
MarkvDebugConsumer debug_consumer, std::vector<uint32_t>* spirv);
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,8 +19,8 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv/1.2/spirv.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "spirv/1.2/spirv.h"
|
||||
#include "util/huffman_codec.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -30,14 +30,17 @@ namespace spvtools {
|
||||
// codecs used by the compression algorithm.
|
||||
class MarkvModel {
|
||||
public:
|
||||
MarkvModel() : operand_chunk_lengths_(
|
||||
static_cast<size_t>(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES), 0) {}
|
||||
MarkvModel()
|
||||
: operand_chunk_lengths_(
|
||||
static_cast<size_t>(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES), 0) {}
|
||||
|
||||
uint32_t model_type() const { return model_type_; }
|
||||
uint32_t model_version() const { return model_version_; }
|
||||
|
||||
uint32_t opcode_chunk_length() const { return opcode_chunk_length_; }
|
||||
uint32_t num_operands_chunk_length() const { return num_operands_chunk_length_; }
|
||||
uint32_t num_operands_chunk_length() const {
|
||||
return num_operands_chunk_length_;
|
||||
}
|
||||
uint32_t mtf_rank_chunk_length() const { return mtf_rank_chunk_length_; }
|
||||
|
||||
uint32_t u64_chunk_length() const { return u64_chunk_length_; }
|
||||
@ -46,8 +49,8 @@ class MarkvModel {
|
||||
|
||||
// Returns a codec for common opcode_and_num_operands words for the given
|
||||
// previous opcode. May return nullptr if the codec doesn't exist.
|
||||
const spvutils::HuffmanCodec<uint64_t>* GetOpcodeAndNumOperandsMarkovHuffmanCodec(
|
||||
uint32_t prev_opcode) const {
|
||||
const spvutils::HuffmanCodec<uint64_t>*
|
||||
GetOpcodeAndNumOperandsMarkovHuffmanCodec(uint32_t prev_opcode) const {
|
||||
if (prev_opcode == SpvOpNop)
|
||||
return opcode_and_num_operands_huffman_codec_.get();
|
||||
|
||||
@ -65,8 +68,7 @@ class MarkvModel {
|
||||
uint32_t opcode, uint32_t operand_index) const {
|
||||
const auto it = non_id_word_huffman_codecs_.find(
|
||||
std::pair<uint32_t, uint32_t>(opcode, operand_index));
|
||||
if (it == non_id_word_huffman_codecs_.end())
|
||||
return nullptr;
|
||||
if (it == non_id_word_huffman_codecs_.end()) return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
@ -77,8 +79,7 @@ class MarkvModel {
|
||||
uint32_t opcode, uint32_t operand_index) const {
|
||||
const auto it = id_descriptor_huffman_codecs_.find(
|
||||
std::pair<uint32_t, uint32_t>(opcode, operand_index));
|
||||
if (it == id_descriptor_huffman_codecs_.end())
|
||||
return nullptr;
|
||||
if (it == id_descriptor_huffman_codecs_.end()) return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
@ -88,8 +89,7 @@ class MarkvModel {
|
||||
const spvutils::HuffmanCodec<std::string>* GetLiteralStringHuffmanCodec(
|
||||
uint32_t opcode) const {
|
||||
const auto it = literal_string_huffman_codecs_.find(opcode);
|
||||
if (it == literal_string_huffman_codecs_.end())
|
||||
return nullptr;
|
||||
if (it == literal_string_huffman_codecs_.end()) return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
@ -106,9 +106,7 @@ class MarkvModel {
|
||||
}
|
||||
|
||||
// Sets model type.
|
||||
void SetModelType(uint32_t in_model_type) {
|
||||
model_type_ = in_model_type;
|
||||
}
|
||||
void SetModelType(uint32_t in_model_type) { model_type_ = in_model_type; }
|
||||
|
||||
// Sets model version.
|
||||
void SetModelVersion(uint32_t in_model_version) {
|
||||
@ -137,12 +135,14 @@ class MarkvModel {
|
||||
// Huffman codecs for non-id single-word operand values.
|
||||
// The map key is pair <opcode, operand_index>.
|
||||
std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>> non_id_word_huffman_codecs_;
|
||||
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>>
|
||||
non_id_word_huffman_codecs_;
|
||||
|
||||
// Huffman codecs for id descriptors. The map key is pair
|
||||
// <opcode, operand_index>.
|
||||
std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>> id_descriptor_huffman_codecs_;
|
||||
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>>
|
||||
id_descriptor_huffman_codecs_;
|
||||
|
||||
// Set of all descriptors which have a coding scheme in any of
|
||||
// id_descriptor_huffman_codecs_.
|
||||
@ -160,7 +160,7 @@ class MarkvModel {
|
||||
std::vector<uint32_t> operand_chunk_lengths_;
|
||||
|
||||
uint32_t opcode_chunk_length_ = 7;
|
||||
uint32_t num_operands_chunk_length_ = 3;
|
||||
uint32_t num_operands_chunk_length_ = 3;
|
||||
uint32_t mtf_rank_chunk_length_ = 5;
|
||||
|
||||
uint32_t u64_chunk_length_ = 8;
|
||||
|
@ -23,8 +23,7 @@
|
||||
namespace libspirv {
|
||||
|
||||
std::string GetExtensionString(const spv_parsed_instruction_t* inst) {
|
||||
if (inst->opcode != SpvOpExtension)
|
||||
return "ERROR_not_op_extension";
|
||||
if (inst->opcode != SpvOpExtension) return "ERROR_not_op_extension";
|
||||
|
||||
assert(inst->num_operands == 1);
|
||||
|
||||
@ -37,9 +36,8 @@ std::string GetExtensionString(const spv_parsed_instruction_t* inst) {
|
||||
|
||||
std::string ExtensionSetToString(const ExtensionSet& extensions) {
|
||||
std::stringstream ss;
|
||||
extensions.ForEach([&ss](Extension ext) {
|
||||
ss << ExtensionToString(ext) << " ";
|
||||
});
|
||||
extensions.ForEach(
|
||||
[&ss](Extension ext) { ss << ExtensionToString(ext) << " "; });
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
@ -40,15 +40,14 @@ uint32_t HashU32Array(const std::vector<uint32_t>& words) {
|
||||
|
||||
uint32_t IdDescriptorCollection::ProcessInstruction(
|
||||
const spv_parsed_instruction_t& inst) {
|
||||
if (!inst.result_id)
|
||||
return 0;
|
||||
if (!inst.result_id) return 0;
|
||||
|
||||
assert(words_.empty());
|
||||
words_.push_back(inst.words[0]);
|
||||
|
||||
for (size_t operand_index = 0; operand_index < inst.num_operands;
|
||||
++operand_index) {
|
||||
const auto &operand = inst.operands[operand_index];
|
||||
const auto& operand = inst.operands[operand_index];
|
||||
if (spvIsIdType(operand.type)) {
|
||||
const uint32_t id = inst.words[operand.offset];
|
||||
const auto it = id_to_descriptor_.find(id);
|
||||
|
@ -28,9 +28,7 @@ namespace libspirv {
|
||||
// were substituted with previously computed descriptors.
|
||||
class IdDescriptorCollection {
|
||||
public:
|
||||
IdDescriptorCollection() {
|
||||
words_.reserve(16);
|
||||
}
|
||||
IdDescriptorCollection() { words_.reserve(16); }
|
||||
|
||||
// Computes descriptor for the result id of the given instruction and
|
||||
// registers it in id_to_descriptor_. Returns the computed descriptor.
|
||||
@ -41,8 +39,7 @@ class IdDescriptorCollection {
|
||||
// Returns a previously computed descriptor id.
|
||||
uint32_t GetDescriptor(uint32_t id) const {
|
||||
const auto it = id_to_descriptor_.find(id);
|
||||
if (it == id_to_descriptor_.end())
|
||||
return 0;
|
||||
if (it == id_to_descriptor_.end()) return 0;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@ -56,4 +53,3 @@ class IdDescriptorCollection {
|
||||
} // namespace libspirv
|
||||
|
||||
#endif // LIBSPIRV_ID_DESCRIPTOR_H_
|
||||
|
||||
|
@ -497,7 +497,7 @@ static spv_result_t GetImportExportPairs(
|
||||
// Ignore if the targeted symbol is a built-in
|
||||
bool is_built_in = false;
|
||||
for (const auto& id_decoration :
|
||||
decoration_manager.GetDecorationsFor(id, false)) {
|
||||
decoration_manager.GetDecorationsFor(id, false)) {
|
||||
if (id_decoration->GetSingleWordInOperand(1u) == SpvDecorationBuiltIn) {
|
||||
is_built_in = true;
|
||||
break;
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
#include "name_mapper.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -234,26 +234,30 @@ spv_result_t FriendlyNameMapper::ParseInstruction(
|
||||
}
|
||||
} break;
|
||||
case SpvOpTypeVector:
|
||||
SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
|
||||
NameForId(inst.words[2]));
|
||||
SaveName(result_id,
|
||||
std::string("v") + to_string(inst.words[3]) +
|
||||
NameForId(inst.words[2]));
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
|
||||
NameForId(inst.words[2]));
|
||||
SaveName(result_id,
|
||||
std::string("mat") + to_string(inst.words[3]) +
|
||||
NameForId(inst.words[2]));
|
||||
break;
|
||||
case SpvOpTypeArray:
|
||||
SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
|
||||
"_" + NameForId(inst.words[3]));
|
||||
SaveName(result_id,
|
||||
std::string("_arr_") + NameForId(inst.words[2]) + "_" +
|
||||
NameForId(inst.words[3]));
|
||||
break;
|
||||
case SpvOpTypeRuntimeArray:
|
||||
SaveName(result_id,
|
||||
std::string("_runtimearr_") + NameForId(inst.words[2]));
|
||||
break;
|
||||
case SpvOpTypePointer:
|
||||
SaveName(result_id, std::string("_ptr_") +
|
||||
NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
inst.words[2]) +
|
||||
"_" + NameForId(inst.words[3]));
|
||||
SaveName(result_id,
|
||||
std::string("_ptr_") +
|
||||
NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
inst.words[2]) +
|
||||
"_" + NameForId(inst.words[3]));
|
||||
break;
|
||||
case SpvOpTypePipe:
|
||||
SaveName(result_id,
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "assembly_grammar.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
@ -41,7 +41,8 @@ NameMapper GetTrivialNameMapper();
|
||||
// - If an Id has a debug name (via OpName), then that will be used when
|
||||
// possible.
|
||||
// - Well known scalar types map to friendly names. For example,
|
||||
// OpTypeVoid should be %void. Scalar types map to their names in OpenCL when
|
||||
// OpTypeVoid should be %void. Scalar types map to their names in OpenCL
|
||||
// when
|
||||
// there is a correspondence, and otherwise as follows:
|
||||
// - unsigned integer type of n bits map to "u" followed by n
|
||||
// - signed integer type of n bits map to "i" followed by n
|
||||
|
@ -147,7 +147,7 @@ spv_result_t spvOpcodeTableValueLookup(const spv_opcode_table table,
|
||||
return lhs.opcode < rhs.opcode;
|
||||
};
|
||||
auto it = std::lower_bound(beg, end, value, comp);
|
||||
if (it!=end && it->opcode == opcode) {
|
||||
if (it != end && it->opcode == opcode) {
|
||||
*pEntry = it;
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -177,13 +177,14 @@ const char* spvOpcodeString(const SpvOp opcode) {
|
||||
// previous ones.
|
||||
|
||||
const auto beg = kOpcodeTableEntries_1_2;
|
||||
const auto end = kOpcodeTableEntries_1_2 + ARRAY_SIZE(kOpcodeTableEntries_1_2);
|
||||
const auto end =
|
||||
kOpcodeTableEntries_1_2 + ARRAY_SIZE(kOpcodeTableEntries_1_2);
|
||||
spv_opcode_desc_t value{"", opcode, 0, nullptr, 0, {}, 0, 0};
|
||||
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
|
||||
return lhs.opcode < rhs.opcode;
|
||||
};
|
||||
auto it = std::lower_bound(beg, end, value, comp);
|
||||
if (it!=end && it->opcode == opcode) {
|
||||
if (it != end && it->opcode == opcode) {
|
||||
return it->name;
|
||||
}
|
||||
|
||||
|
@ -36,29 +36,25 @@ const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId,
|
||||
uint32_t storageClass) {
|
||||
bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
|
||||
const ir::Instruction* varInst = get_def_use_mgr()->GetDef(varId);
|
||||
const SpvOp op = varInst->opcode();
|
||||
if (op != SpvOpVariable)
|
||||
return false;
|
||||
if (op != SpvOpVariable) return false;
|
||||
const uint32_t varTypeId = varInst->type_id();
|
||||
const ir::Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
|
||||
if (varTypeInst->opcode() != SpvOpTypePointer)
|
||||
return false;
|
||||
if (varTypeInst->opcode() != SpvOpTypePointer) return false;
|
||||
return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
|
||||
storageClass;
|
||||
storageClass;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
|
||||
return IsVarOfStorage(varId, SpvStorageClassFunction) ||
|
||||
(IsVarOfStorage(varId, SpvStorageClassPrivate) && private_like_local_);
|
||||
return IsVarOfStorage(varId, SpvStorageClassFunction) ||
|
||||
(IsVarOfStorage(varId, SpvStorageClassPrivate) && private_like_local_);
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddStores(uint32_t ptrId) {
|
||||
const analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
|
||||
if (uses == nullptr)
|
||||
return;
|
||||
if (uses == nullptr) return;
|
||||
for (const auto u : *uses) {
|
||||
const SpvOp op = u.inst->opcode();
|
||||
switch (op) {
|
||||
@ -72,8 +68,7 @@ void AggressiveDCEPass::AddStores(uint32_t ptrId) {
|
||||
// If default, assume it stores eg frexp, modf, function call
|
||||
case SpvOpStore:
|
||||
default: {
|
||||
if (!IsLive(u.inst))
|
||||
AddToWorklist(u.inst);
|
||||
if (!IsLive(u.inst)) AddToWorklist(u.inst);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@ -88,17 +83,16 @@ bool AggressiveDCEPass::IsCombinatorExt(ir::Instruction* inst) const {
|
||||
if (inst->GetSingleWordInOperand(kExtInstSetIdInIndx) == glsl_std_450_id_) {
|
||||
uint32_t op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx);
|
||||
return combinator_ops_glsl_std_450_.find(op) !=
|
||||
combinator_ops_glsl_std_450_.end();
|
||||
}
|
||||
else
|
||||
combinator_ops_glsl_std_450_.end();
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -117,11 +111,9 @@ bool AggressiveDCEPass::KillInstIfTargetDead(ir::Instruction* inst) {
|
||||
|
||||
void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
|
||||
// Only process locals
|
||||
if (!IsLocalVar(varId))
|
||||
return;
|
||||
if (!IsLocalVar(varId)) return;
|
||||
// Return if already processed
|
||||
if (live_local_vars_.find(varId) != live_local_vars_.end())
|
||||
return;
|
||||
if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
|
||||
// Mark all stores to varId as live
|
||||
AddStores(varId);
|
||||
// Cache varId as processed
|
||||
@ -129,20 +121,17 @@ void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::IsStructuredIfHeader(ir::BasicBlock* bp,
|
||||
ir::Instruction** mergeInst, ir::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId) {
|
||||
ir::Instruction** mergeInst,
|
||||
ir::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId) {
|
||||
auto ii = bp->end();
|
||||
--ii;
|
||||
if (ii->opcode() != SpvOpBranchConditional)
|
||||
return false;
|
||||
if (ii == bp->begin())
|
||||
return false;
|
||||
if (ii->opcode() != SpvOpBranchConditional) return false;
|
||||
if (ii == bp->begin()) return false;
|
||||
if (branchInst != nullptr) *branchInst = &*ii;
|
||||
--ii;
|
||||
if (ii->opcode() != SpvOpSelectionMerge)
|
||||
return false;
|
||||
if (mergeInst != nullptr)
|
||||
*mergeInst = &*ii;
|
||||
if (ii->opcode() != SpvOpSelectionMerge) return false;
|
||||
if (mergeInst != nullptr) *mergeInst = &*ii;
|
||||
if (mergeBlockId != nullptr)
|
||||
*mergeBlockId =
|
||||
ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
|
||||
@ -150,7 +139,7 @@ bool AggressiveDCEPass::IsStructuredIfHeader(ir::BasicBlock* bp,
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ComputeBlock2HeaderMaps(
|
||||
std::list<ir::BasicBlock*>& structuredOrder) {
|
||||
std::list<ir::BasicBlock*>& structuredOrder) {
|
||||
block2headerMerge_.clear();
|
||||
block2headerBranch_.clear();
|
||||
std::stack<ir::Instruction*> currentMergeInst;
|
||||
@ -180,16 +169,15 @@ void AggressiveDCEPass::ComputeBlock2HeaderMaps(
|
||||
|
||||
void AggressiveDCEPass::ComputeInst2BlockMap(ir::Function* func) {
|
||||
for (auto& blk : *func) {
|
||||
blk.ForEachInst([&blk,this](ir::Instruction* ip) {
|
||||
inst2block_[ip] = &blk;
|
||||
});
|
||||
blk.ForEachInst(
|
||||
[&blk, this](ir::Instruction* ip) { inst2block_[ip] = &blk; });
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(
|
||||
new ir::Instruction(SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newBranch);
|
||||
bp->AddInstruction(std::move(newBranch));
|
||||
}
|
||||
@ -209,7 +197,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
func_is_entry_point_ = false;
|
||||
private_stores_.clear();
|
||||
// Stacks to keep track of when we are inside an if-construct. When not
|
||||
// immediately inside an in-construct, we must assume all branches are live.
|
||||
// immediately inside an in-construct, we must assume all branches are live.
|
||||
std::stack<bool> assume_branches_live;
|
||||
std::stack<uint32_t> currentMergeBlockId;
|
||||
// Push sentinel values on stack for when outside of any control flow.
|
||||
@ -225,7 +213,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
switch (op) {
|
||||
case SpvOpStore: {
|
||||
uint32_t varId;
|
||||
(void) GetPtr(&*ii, &varId);
|
||||
(void)GetPtr(&*ii, &varId);
|
||||
// Mark stores as live if their variable is not function scope
|
||||
// and is not private scope. Remember private stores for possible
|
||||
// later inclusion
|
||||
@ -236,8 +224,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
} break;
|
||||
case SpvOpExtInst: {
|
||||
// eg. GLSL frexp, modf
|
||||
if (!IsCombinatorExt(&*ii))
|
||||
AddToWorklist(&*ii);
|
||||
if (!IsCombinatorExt(&*ii)) AddToWorklist(&*ii);
|
||||
} break;
|
||||
case SpvOpLoopMerge: {
|
||||
// Assume loops live (for now)
|
||||
@ -254,22 +241,18 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
assume_branches_live.push(!is_structured_if);
|
||||
currentMergeBlockId.push(
|
||||
ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx));
|
||||
if (!is_structured_if)
|
||||
AddToWorklist(&*ii);
|
||||
if (!is_structured_if) AddToWorklist(&*ii);
|
||||
} break;
|
||||
case SpvOpBranch:
|
||||
case SpvOpBranchConditional: {
|
||||
if (assume_branches_live.top())
|
||||
AddToWorklist(&*ii);
|
||||
if (assume_branches_live.top()) AddToWorklist(&*ii);
|
||||
} break;
|
||||
default: {
|
||||
// Function calls, atomics, function params, function returns, etc.
|
||||
// TODO(greg-lunarg): function calls live only if write to non-local
|
||||
if (!IsCombinator(op))
|
||||
AddToWorklist(&*ii);
|
||||
if (!IsCombinator(op)) AddToWorklist(&*ii);
|
||||
// Remember function calls
|
||||
if (op == SpvOpFunctionCall)
|
||||
call_in_func_ = true;
|
||||
if (op == SpvOpFunctionCall) call_in_func_ = true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@ -287,23 +270,20 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
private_like_local_ = func_is_entry_point_ && !call_in_func_;
|
||||
// If privates are not like local, add their stores to worklist
|
||||
if (!private_like_local_)
|
||||
for (auto& ps : private_stores_)
|
||||
AddToWorklist(ps);
|
||||
for (auto& ps : private_stores_) AddToWorklist(ps);
|
||||
// Add OpGroupDecorates to worklist because they are a pain to remove
|
||||
// ids from.
|
||||
// TODO(greg-lunarg): Handle dead ids in OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations()) {
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
AddToWorklist(&ai);
|
||||
if (ai.opcode() == SpvOpGroupDecorate) AddToWorklist(&ai);
|
||||
}
|
||||
// Perform closure on live instruction set.
|
||||
// Perform closure on live instruction set.
|
||||
while (!worklist_.empty()) {
|
||||
ir::Instruction* liveInst = worklist_.front();
|
||||
// Add all operand instructions if not already live
|
||||
liveInst->ForEachInId([this](const uint32_t* iid) {
|
||||
ir::Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (!IsLive(inInst))
|
||||
AddToWorklist(inInst);
|
||||
if (!IsLive(inInst)) AddToWorklist(inInst);
|
||||
});
|
||||
// If in a structured if construct, add the controlling conditional branch
|
||||
// and its merge. Any containing if construct is marked live when the
|
||||
@ -317,7 +297,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// If local load, add all variable's stores if variable not already live
|
||||
if (liveInst->opcode() == SpvOpLoad) {
|
||||
uint32_t varId;
|
||||
(void) GetPtr(liveInst, &varId);
|
||||
(void)GetPtr(liveInst, &varId);
|
||||
ProcessLoad(varId);
|
||||
}
|
||||
// If function call, treat as if it loads from all pointer arguments
|
||||
@ -326,7 +306,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// Skip non-ptr args
|
||||
if (!IsPtr(*iid)) return;
|
||||
uint32_t varId;
|
||||
(void) GetPtr(*iid, &varId);
|
||||
(void)GetPtr(*iid, &varId);
|
||||
ProcessLoad(varId);
|
||||
});
|
||||
}
|
||||
@ -340,8 +320,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// at the end of an if-header, which indicate a dead if.
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
|
||||
if (IsLive(&*ii))
|
||||
continue;
|
||||
if (IsLive(&*ii)) continue;
|
||||
if (IsBranch(ii->opcode()) &&
|
||||
!IsStructuredIfHeader(*bi, nullptr, nullptr, nullptr))
|
||||
continue;
|
||||
@ -352,27 +331,23 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
// This must be done before killing the instructions, otherwise there are
|
||||
// dead objects in the def/use database.
|
||||
for (auto& di : get_module()->debugs2()) {
|
||||
if (di.opcode() != SpvOpName)
|
||||
continue;
|
||||
if (KillInstIfTargetDead(&di))
|
||||
modified = true;
|
||||
if (di.opcode() != SpvOpName) continue;
|
||||
if (KillInstIfTargetDead(&di)) modified = true;
|
||||
}
|
||||
for (auto& ai : get_module()->annotations()) {
|
||||
if (ai.opcode() != SpvOpDecorate && ai.opcode() != SpvOpDecorateId)
|
||||
continue;
|
||||
if (KillInstIfTargetDead(&ai))
|
||||
modified = true;
|
||||
if (KillInstIfTargetDead(&ai)) modified = true;
|
||||
}
|
||||
// Kill dead instructions and remember dead blocks
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
|
||||
uint32_t mergeBlockId = 0;
|
||||
for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
|
||||
if (dead_insts_.find(&*ii) == dead_insts_.end())
|
||||
continue;
|
||||
if (dead_insts_.find(&*ii) == dead_insts_.end()) continue;
|
||||
// If dead instruction is selection merge, remember merge block
|
||||
// for new branch at end of block
|
||||
if (ii->opcode() == SpvOpSelectionMerge)
|
||||
mergeBlockId =
|
||||
mergeBlockId =
|
||||
ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
|
||||
context()->KillInst(&*ii);
|
||||
modified = true;
|
||||
@ -384,8 +359,7 @@ bool AggressiveDCEPass::AggressiveDCE(ir::Function* func) {
|
||||
AddBranch(mergeBlockId, *bi);
|
||||
for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
++bi;
|
||||
}
|
||||
}
|
||||
@ -412,7 +386,7 @@ void AggressiveDCEPass::Initialize(ir::IRContext* c) {
|
||||
}
|
||||
|
||||
Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
// Current functionality assumes shader capability
|
||||
// Current functionality assumes shader capability
|
||||
// TODO(greg-lunarg): Handle additional capabilities
|
||||
if (!get_module()->HasCapability(SpvCapabilityShader))
|
||||
return Status::SuccessWithoutChange;
|
||||
@ -421,15 +395,12 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
if (get_module()->HasCapability(SpvCapabilityAddresses))
|
||||
return Status::SuccessWithoutChange;
|
||||
// If any extensions in the module are not explicitly supported,
|
||||
// return unmodified.
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
// return unmodified.
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Initialize combinator whitelists
|
||||
InitCombinatorSets();
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return AggressiveDCE(fp);
|
||||
};
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return AggressiveDCE(fp); };
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
@ -443,258 +414,246 @@ Pass::Status AggressiveDCEPass::Process(ir::IRContext* c) {
|
||||
|
||||
void AggressiveDCEPass::InitCombinatorSets() {
|
||||
combinator_ops_shader_ = {
|
||||
SpvOpNop,
|
||||
SpvOpUndef,
|
||||
SpvOpVariable,
|
||||
SpvOpImageTexelPointer,
|
||||
SpvOpLoad,
|
||||
SpvOpAccessChain,
|
||||
SpvOpInBoundsAccessChain,
|
||||
SpvOpArrayLength,
|
||||
SpvOpVectorExtractDynamic,
|
||||
SpvOpVectorInsertDynamic,
|
||||
SpvOpVectorShuffle,
|
||||
SpvOpCompositeConstruct,
|
||||
SpvOpCompositeExtract,
|
||||
SpvOpCompositeInsert,
|
||||
SpvOpCopyObject,
|
||||
SpvOpTranspose,
|
||||
SpvOpSampledImage,
|
||||
SpvOpImageSampleImplicitLod,
|
||||
SpvOpImageSampleExplicitLod,
|
||||
SpvOpImageSampleDrefImplicitLod,
|
||||
SpvOpImageSampleDrefExplicitLod,
|
||||
SpvOpImageSampleProjImplicitLod,
|
||||
SpvOpImageSampleProjExplicitLod,
|
||||
SpvOpImageSampleProjDrefImplicitLod,
|
||||
SpvOpImageSampleProjDrefExplicitLod,
|
||||
SpvOpImageFetch,
|
||||
SpvOpImageGather,
|
||||
SpvOpImageDrefGather,
|
||||
SpvOpImageRead,
|
||||
SpvOpImage,
|
||||
SpvOpConvertFToU,
|
||||
SpvOpConvertFToS,
|
||||
SpvOpConvertSToF,
|
||||
SpvOpConvertUToF,
|
||||
SpvOpUConvert,
|
||||
SpvOpSConvert,
|
||||
SpvOpFConvert,
|
||||
SpvOpQuantizeToF16,
|
||||
SpvOpBitcast,
|
||||
SpvOpSNegate,
|
||||
SpvOpFNegate,
|
||||
SpvOpIAdd,
|
||||
SpvOpFAdd,
|
||||
SpvOpISub,
|
||||
SpvOpFSub,
|
||||
SpvOpIMul,
|
||||
SpvOpFMul,
|
||||
SpvOpUDiv,
|
||||
SpvOpSDiv,
|
||||
SpvOpFDiv,
|
||||
SpvOpUMod,
|
||||
SpvOpSRem,
|
||||
SpvOpSMod,
|
||||
SpvOpFRem,
|
||||
SpvOpFMod,
|
||||
SpvOpVectorTimesScalar,
|
||||
SpvOpMatrixTimesScalar,
|
||||
SpvOpVectorTimesMatrix,
|
||||
SpvOpMatrixTimesVector,
|
||||
SpvOpMatrixTimesMatrix,
|
||||
SpvOpOuterProduct,
|
||||
SpvOpDot,
|
||||
SpvOpIAddCarry,
|
||||
SpvOpISubBorrow,
|
||||
SpvOpUMulExtended,
|
||||
SpvOpSMulExtended,
|
||||
SpvOpAny,
|
||||
SpvOpAll,
|
||||
SpvOpIsNan,
|
||||
SpvOpIsInf,
|
||||
SpvOpLogicalEqual,
|
||||
SpvOpLogicalNotEqual,
|
||||
SpvOpLogicalOr,
|
||||
SpvOpLogicalAnd,
|
||||
SpvOpLogicalNot,
|
||||
SpvOpSelect,
|
||||
SpvOpIEqual,
|
||||
SpvOpINotEqual,
|
||||
SpvOpUGreaterThan,
|
||||
SpvOpSGreaterThan,
|
||||
SpvOpUGreaterThanEqual,
|
||||
SpvOpSGreaterThanEqual,
|
||||
SpvOpULessThan,
|
||||
SpvOpSLessThan,
|
||||
SpvOpULessThanEqual,
|
||||
SpvOpSLessThanEqual,
|
||||
SpvOpFOrdEqual,
|
||||
SpvOpFUnordEqual,
|
||||
SpvOpFOrdNotEqual,
|
||||
SpvOpFUnordNotEqual,
|
||||
SpvOpFOrdLessThan,
|
||||
SpvOpFUnordLessThan,
|
||||
SpvOpFOrdGreaterThan,
|
||||
SpvOpFUnordGreaterThan,
|
||||
SpvOpFOrdLessThanEqual,
|
||||
SpvOpFUnordLessThanEqual,
|
||||
SpvOpFOrdGreaterThanEqual,
|
||||
SpvOpFUnordGreaterThanEqual,
|
||||
SpvOpShiftRightLogical,
|
||||
SpvOpShiftRightArithmetic,
|
||||
SpvOpShiftLeftLogical,
|
||||
SpvOpBitwiseOr,
|
||||
SpvOpBitwiseXor,
|
||||
SpvOpBitwiseAnd,
|
||||
SpvOpNot,
|
||||
SpvOpBitFieldInsert,
|
||||
SpvOpBitFieldSExtract,
|
||||
SpvOpBitFieldUExtract,
|
||||
SpvOpBitReverse,
|
||||
SpvOpBitCount,
|
||||
SpvOpDPdx,
|
||||
SpvOpDPdy,
|
||||
SpvOpFwidth,
|
||||
SpvOpDPdxFine,
|
||||
SpvOpDPdyFine,
|
||||
SpvOpFwidthFine,
|
||||
SpvOpDPdxCoarse,
|
||||
SpvOpDPdyCoarse,
|
||||
SpvOpFwidthCoarse,
|
||||
SpvOpPhi,
|
||||
SpvOpImageSparseSampleImplicitLod,
|
||||
SpvOpImageSparseSampleExplicitLod,
|
||||
SpvOpImageSparseSampleDrefImplicitLod,
|
||||
SpvOpImageSparseSampleDrefExplicitLod,
|
||||
SpvOpImageSparseSampleProjImplicitLod,
|
||||
SpvOpImageSparseSampleProjExplicitLod,
|
||||
SpvOpImageSparseSampleProjDrefImplicitLod,
|
||||
SpvOpImageSparseSampleProjDrefExplicitLod,
|
||||
SpvOpImageSparseFetch,
|
||||
SpvOpImageSparseGather,
|
||||
SpvOpImageSparseDrefGather,
|
||||
SpvOpImageSparseTexelsResident,
|
||||
SpvOpImageSparseRead,
|
||||
SpvOpSizeOf
|
||||
// TODO(dneto): Add instructions enabled by ImageQuery
|
||||
SpvOpNop,
|
||||
SpvOpUndef,
|
||||
SpvOpVariable,
|
||||
SpvOpImageTexelPointer,
|
||||
SpvOpLoad,
|
||||
SpvOpAccessChain,
|
||||
SpvOpInBoundsAccessChain,
|
||||
SpvOpArrayLength,
|
||||
SpvOpVectorExtractDynamic,
|
||||
SpvOpVectorInsertDynamic,
|
||||
SpvOpVectorShuffle,
|
||||
SpvOpCompositeConstruct,
|
||||
SpvOpCompositeExtract,
|
||||
SpvOpCompositeInsert,
|
||||
SpvOpCopyObject,
|
||||
SpvOpTranspose,
|
||||
SpvOpSampledImage,
|
||||
SpvOpImageSampleImplicitLod,
|
||||
SpvOpImageSampleExplicitLod,
|
||||
SpvOpImageSampleDrefImplicitLod,
|
||||
SpvOpImageSampleDrefExplicitLod,
|
||||
SpvOpImageSampleProjImplicitLod,
|
||||
SpvOpImageSampleProjExplicitLod,
|
||||
SpvOpImageSampleProjDrefImplicitLod,
|
||||
SpvOpImageSampleProjDrefExplicitLod,
|
||||
SpvOpImageFetch,
|
||||
SpvOpImageGather,
|
||||
SpvOpImageDrefGather,
|
||||
SpvOpImageRead,
|
||||
SpvOpImage,
|
||||
SpvOpConvertFToU,
|
||||
SpvOpConvertFToS,
|
||||
SpvOpConvertSToF,
|
||||
SpvOpConvertUToF,
|
||||
SpvOpUConvert,
|
||||
SpvOpSConvert,
|
||||
SpvOpFConvert,
|
||||
SpvOpQuantizeToF16,
|
||||
SpvOpBitcast,
|
||||
SpvOpSNegate,
|
||||
SpvOpFNegate,
|
||||
SpvOpIAdd,
|
||||
SpvOpFAdd,
|
||||
SpvOpISub,
|
||||
SpvOpFSub,
|
||||
SpvOpIMul,
|
||||
SpvOpFMul,
|
||||
SpvOpUDiv,
|
||||
SpvOpSDiv,
|
||||
SpvOpFDiv,
|
||||
SpvOpUMod,
|
||||
SpvOpSRem,
|
||||
SpvOpSMod,
|
||||
SpvOpFRem,
|
||||
SpvOpFMod,
|
||||
SpvOpVectorTimesScalar,
|
||||
SpvOpMatrixTimesScalar,
|
||||
SpvOpVectorTimesMatrix,
|
||||
SpvOpMatrixTimesVector,
|
||||
SpvOpMatrixTimesMatrix,
|
||||
SpvOpOuterProduct,
|
||||
SpvOpDot,
|
||||
SpvOpIAddCarry,
|
||||
SpvOpISubBorrow,
|
||||
SpvOpUMulExtended,
|
||||
SpvOpSMulExtended,
|
||||
SpvOpAny,
|
||||
SpvOpAll,
|
||||
SpvOpIsNan,
|
||||
SpvOpIsInf,
|
||||
SpvOpLogicalEqual,
|
||||
SpvOpLogicalNotEqual,
|
||||
SpvOpLogicalOr,
|
||||
SpvOpLogicalAnd,
|
||||
SpvOpLogicalNot,
|
||||
SpvOpSelect,
|
||||
SpvOpIEqual,
|
||||
SpvOpINotEqual,
|
||||
SpvOpUGreaterThan,
|
||||
SpvOpSGreaterThan,
|
||||
SpvOpUGreaterThanEqual,
|
||||
SpvOpSGreaterThanEqual,
|
||||
SpvOpULessThan,
|
||||
SpvOpSLessThan,
|
||||
SpvOpULessThanEqual,
|
||||
SpvOpSLessThanEqual,
|
||||
SpvOpFOrdEqual,
|
||||
SpvOpFUnordEqual,
|
||||
SpvOpFOrdNotEqual,
|
||||
SpvOpFUnordNotEqual,
|
||||
SpvOpFOrdLessThan,
|
||||
SpvOpFUnordLessThan,
|
||||
SpvOpFOrdGreaterThan,
|
||||
SpvOpFUnordGreaterThan,
|
||||
SpvOpFOrdLessThanEqual,
|
||||
SpvOpFUnordLessThanEqual,
|
||||
SpvOpFOrdGreaterThanEqual,
|
||||
SpvOpFUnordGreaterThanEqual,
|
||||
SpvOpShiftRightLogical,
|
||||
SpvOpShiftRightArithmetic,
|
||||
SpvOpShiftLeftLogical,
|
||||
SpvOpBitwiseOr,
|
||||
SpvOpBitwiseXor,
|
||||
SpvOpBitwiseAnd,
|
||||
SpvOpNot,
|
||||
SpvOpBitFieldInsert,
|
||||
SpvOpBitFieldSExtract,
|
||||
SpvOpBitFieldUExtract,
|
||||
SpvOpBitReverse,
|
||||
SpvOpBitCount,
|
||||
SpvOpDPdx,
|
||||
SpvOpDPdy,
|
||||
SpvOpFwidth,
|
||||
SpvOpDPdxFine,
|
||||
SpvOpDPdyFine,
|
||||
SpvOpFwidthFine,
|
||||
SpvOpDPdxCoarse,
|
||||
SpvOpDPdyCoarse,
|
||||
SpvOpFwidthCoarse,
|
||||
SpvOpPhi,
|
||||
SpvOpImageSparseSampleImplicitLod,
|
||||
SpvOpImageSparseSampleExplicitLod,
|
||||
SpvOpImageSparseSampleDrefImplicitLod,
|
||||
SpvOpImageSparseSampleDrefExplicitLod,
|
||||
SpvOpImageSparseSampleProjImplicitLod,
|
||||
SpvOpImageSparseSampleProjExplicitLod,
|
||||
SpvOpImageSparseSampleProjDrefImplicitLod,
|
||||
SpvOpImageSparseSampleProjDrefExplicitLod,
|
||||
SpvOpImageSparseFetch,
|
||||
SpvOpImageSparseGather,
|
||||
SpvOpImageSparseDrefGather,
|
||||
SpvOpImageSparseTexelsResident,
|
||||
SpvOpImageSparseRead,
|
||||
SpvOpSizeOf
|
||||
// TODO(dneto): Add instructions enabled by ImageQuery
|
||||
};
|
||||
|
||||
// Find supported extension instruction set ids
|
||||
glsl_std_450_id_ = get_module()->GetExtInstImportId("GLSL.std.450");
|
||||
|
||||
combinator_ops_glsl_std_450_ = {
|
||||
GLSLstd450Round,
|
||||
GLSLstd450RoundEven,
|
||||
GLSLstd450Trunc,
|
||||
GLSLstd450FAbs,
|
||||
GLSLstd450SAbs,
|
||||
GLSLstd450FSign,
|
||||
GLSLstd450SSign,
|
||||
GLSLstd450Floor,
|
||||
GLSLstd450Ceil,
|
||||
GLSLstd450Fract,
|
||||
GLSLstd450Radians,
|
||||
GLSLstd450Degrees,
|
||||
GLSLstd450Sin,
|
||||
GLSLstd450Cos,
|
||||
GLSLstd450Tan,
|
||||
GLSLstd450Asin,
|
||||
GLSLstd450Acos,
|
||||
GLSLstd450Atan,
|
||||
GLSLstd450Sinh,
|
||||
GLSLstd450Cosh,
|
||||
GLSLstd450Tanh,
|
||||
GLSLstd450Asinh,
|
||||
GLSLstd450Acosh,
|
||||
GLSLstd450Atanh,
|
||||
GLSLstd450Atan2,
|
||||
GLSLstd450Pow,
|
||||
GLSLstd450Exp,
|
||||
GLSLstd450Log,
|
||||
GLSLstd450Exp2,
|
||||
GLSLstd450Log2,
|
||||
GLSLstd450Sqrt,
|
||||
GLSLstd450InverseSqrt,
|
||||
GLSLstd450Determinant,
|
||||
GLSLstd450MatrixInverse,
|
||||
GLSLstd450ModfStruct,
|
||||
GLSLstd450FMin,
|
||||
GLSLstd450UMin,
|
||||
GLSLstd450SMin,
|
||||
GLSLstd450FMax,
|
||||
GLSLstd450UMax,
|
||||
GLSLstd450SMax,
|
||||
GLSLstd450FClamp,
|
||||
GLSLstd450UClamp,
|
||||
GLSLstd450SClamp,
|
||||
GLSLstd450FMix,
|
||||
GLSLstd450IMix,
|
||||
GLSLstd450Step,
|
||||
GLSLstd450SmoothStep,
|
||||
GLSLstd450Fma,
|
||||
GLSLstd450FrexpStruct,
|
||||
GLSLstd450Ldexp,
|
||||
GLSLstd450PackSnorm4x8,
|
||||
GLSLstd450PackUnorm4x8,
|
||||
GLSLstd450PackSnorm2x16,
|
||||
GLSLstd450PackUnorm2x16,
|
||||
GLSLstd450PackHalf2x16,
|
||||
GLSLstd450PackDouble2x32,
|
||||
GLSLstd450UnpackSnorm2x16,
|
||||
GLSLstd450UnpackUnorm2x16,
|
||||
GLSLstd450UnpackHalf2x16,
|
||||
GLSLstd450UnpackSnorm4x8,
|
||||
GLSLstd450UnpackUnorm4x8,
|
||||
GLSLstd450UnpackDouble2x32,
|
||||
GLSLstd450Length,
|
||||
GLSLstd450Distance,
|
||||
GLSLstd450Cross,
|
||||
GLSLstd450Normalize,
|
||||
GLSLstd450FaceForward,
|
||||
GLSLstd450Reflect,
|
||||
GLSLstd450Refract,
|
||||
GLSLstd450FindILsb,
|
||||
GLSLstd450FindSMsb,
|
||||
GLSLstd450FindUMsb,
|
||||
GLSLstd450InterpolateAtCentroid,
|
||||
GLSLstd450InterpolateAtSample,
|
||||
GLSLstd450InterpolateAtOffset,
|
||||
GLSLstd450NMin,
|
||||
GLSLstd450NMax,
|
||||
GLSLstd450NClamp
|
||||
};
|
||||
combinator_ops_glsl_std_450_ = {GLSLstd450Round,
|
||||
GLSLstd450RoundEven,
|
||||
GLSLstd450Trunc,
|
||||
GLSLstd450FAbs,
|
||||
GLSLstd450SAbs,
|
||||
GLSLstd450FSign,
|
||||
GLSLstd450SSign,
|
||||
GLSLstd450Floor,
|
||||
GLSLstd450Ceil,
|
||||
GLSLstd450Fract,
|
||||
GLSLstd450Radians,
|
||||
GLSLstd450Degrees,
|
||||
GLSLstd450Sin,
|
||||
GLSLstd450Cos,
|
||||
GLSLstd450Tan,
|
||||
GLSLstd450Asin,
|
||||
GLSLstd450Acos,
|
||||
GLSLstd450Atan,
|
||||
GLSLstd450Sinh,
|
||||
GLSLstd450Cosh,
|
||||
GLSLstd450Tanh,
|
||||
GLSLstd450Asinh,
|
||||
GLSLstd450Acosh,
|
||||
GLSLstd450Atanh,
|
||||
GLSLstd450Atan2,
|
||||
GLSLstd450Pow,
|
||||
GLSLstd450Exp,
|
||||
GLSLstd450Log,
|
||||
GLSLstd450Exp2,
|
||||
GLSLstd450Log2,
|
||||
GLSLstd450Sqrt,
|
||||
GLSLstd450InverseSqrt,
|
||||
GLSLstd450Determinant,
|
||||
GLSLstd450MatrixInverse,
|
||||
GLSLstd450ModfStruct,
|
||||
GLSLstd450FMin,
|
||||
GLSLstd450UMin,
|
||||
GLSLstd450SMin,
|
||||
GLSLstd450FMax,
|
||||
GLSLstd450UMax,
|
||||
GLSLstd450SMax,
|
||||
GLSLstd450FClamp,
|
||||
GLSLstd450UClamp,
|
||||
GLSLstd450SClamp,
|
||||
GLSLstd450FMix,
|
||||
GLSLstd450IMix,
|
||||
GLSLstd450Step,
|
||||
GLSLstd450SmoothStep,
|
||||
GLSLstd450Fma,
|
||||
GLSLstd450FrexpStruct,
|
||||
GLSLstd450Ldexp,
|
||||
GLSLstd450PackSnorm4x8,
|
||||
GLSLstd450PackUnorm4x8,
|
||||
GLSLstd450PackSnorm2x16,
|
||||
GLSLstd450PackUnorm2x16,
|
||||
GLSLstd450PackHalf2x16,
|
||||
GLSLstd450PackDouble2x32,
|
||||
GLSLstd450UnpackSnorm2x16,
|
||||
GLSLstd450UnpackUnorm2x16,
|
||||
GLSLstd450UnpackHalf2x16,
|
||||
GLSLstd450UnpackSnorm4x8,
|
||||
GLSLstd450UnpackUnorm4x8,
|
||||
GLSLstd450UnpackDouble2x32,
|
||||
GLSLstd450Length,
|
||||
GLSLstd450Distance,
|
||||
GLSLstd450Cross,
|
||||
GLSLstd450Normalize,
|
||||
GLSLstd450FaceForward,
|
||||
GLSLstd450Reflect,
|
||||
GLSLstd450Refract,
|
||||
GLSLstd450FindILsb,
|
||||
GLSLstd450FindSMsb,
|
||||
GLSLstd450FindUMsb,
|
||||
GLSLstd450InterpolateAtCentroid,
|
||||
GLSLstd450InterpolateAtSample,
|
||||
GLSLstd450InterpolateAtOffset,
|
||||
GLSLstd450NMin,
|
||||
GLSLstd450NMax,
|
||||
GLSLstd450NClamp};
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -26,20 +26,19 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class AggressiveDCEPass : public MemPass {
|
||||
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
AggressiveDCEPass();
|
||||
const char* name() const override { return "eliminate-dead-code-aggressive"; }
|
||||
@ -102,9 +101,9 @@ class AggressiveDCEPass : public MemPass {
|
||||
|
||||
// If |bp| is structured if header block, return true and set |branchInst|
|
||||
// to the conditional branch and |mergeBlockId| to the merge block.
|
||||
bool IsStructuredIfHeader(ir::BasicBlock* bp,
|
||||
ir::Instruction** mergeInst, ir::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId);
|
||||
bool IsStructuredIfHeader(ir::BasicBlock* bp, ir::Instruction** mergeInst,
|
||||
ir::Instruction** branchInst,
|
||||
uint32_t* mergeBlockId);
|
||||
|
||||
// Initialize block2branch_ and block2merge_ using |structuredOrder| to
|
||||
// order blocks.
|
||||
@ -120,7 +119,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
// and block terminating instructions as live. Recursively mark the values
|
||||
// they use. When complete, delete any non-live instructions. Return true
|
||||
// if the function has been modified.
|
||||
//
|
||||
//
|
||||
// Note: This function does not delete useless control structures. All
|
||||
// existing control structures will remain. This can leave not-insignificant
|
||||
// sequences of ultimately useless code.
|
||||
@ -160,7 +159,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
// Map from block's label id to block.
|
||||
std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
|
||||
|
||||
// Map from block to its structured successor blocks. See
|
||||
// Map from block to its structured successor blocks. See
|
||||
// ComputeStructuredSuccessors() for definition.
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
block2structured_succs_;
|
||||
@ -200,4 +199,3 @@ class AggressiveDCEPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_AGGRESSIVE_DCE_PASS_H_
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
#include "block_merge_pass.h"
|
||||
|
||||
#include "iterator.h"
|
||||
#include "ir_context.h"
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -27,10 +27,8 @@ bool BlockMergePass::HasMultipleRefs(uint32_t labId) {
|
||||
int rcnt = 0;
|
||||
for (const auto u : *uses) {
|
||||
// Don't count OpName
|
||||
if (u.inst->opcode() == SpvOpName)
|
||||
continue;
|
||||
if (rcnt == 1)
|
||||
return true;
|
||||
if (u.inst->opcode() == SpvOpName) continue;
|
||||
if (rcnt == 1) return true;
|
||||
++rcnt;
|
||||
}
|
||||
return false;
|
||||
@ -52,7 +50,7 @@ void BlockMergePass::KillInstAndName(ir::Instruction* inst) {
|
||||
|
||||
bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ) {
|
||||
for (auto bi = func->begin(); bi != func->end();) {
|
||||
// Do not merge loop header blocks, at least for now.
|
||||
if (bi->IsLoopHeader()) {
|
||||
++bi;
|
||||
@ -81,14 +79,13 @@ bool BlockMergePass::MergeBlocks(ir::Function* func) {
|
||||
context()->KillInst(br);
|
||||
auto sbi = bi;
|
||||
for (; sbi != func->end(); ++sbi)
|
||||
if (sbi->id() == labId)
|
||||
break;
|
||||
if (sbi->id() == labId) break;
|
||||
// If bi is sbi's only predecessor, it dominates sbi and thus
|
||||
// sbi must follow bi in func's ordering.
|
||||
assert(sbi != func->end());
|
||||
bi->AddInstructions(&*sbi);
|
||||
KillInstAndName(sbi->GetLabelInst());
|
||||
(void) sbi.Erase();
|
||||
(void)sbi.Erase();
|
||||
// reprocess block
|
||||
modified = true;
|
||||
}
|
||||
@ -105,8 +102,8 @@ void BlockMergePass::Initialize(ir::IRContext* c) {
|
||||
bool BlockMergePass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -115,12 +112,9 @@ bool BlockMergePass::AllExtensionsSupported() const {
|
||||
|
||||
Pass::Status BlockMergePass::ProcessImpl() {
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return MergeBlocks(fp);
|
||||
};
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return MergeBlocks(fp); };
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
@ -135,31 +129,30 @@ Pass::Status BlockMergePass::Process(ir::IRContext* c) {
|
||||
void BlockMergePass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -26,9 +26,9 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -68,4 +68,3 @@ class BlockMergePass : public Pass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_BLOCK_MERGE_PASS_H_
|
||||
|
||||
|
@ -27,8 +27,8 @@ namespace {
|
||||
spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic,
|
||||
uint32_t version, uint32_t generator,
|
||||
uint32_t id_bound, uint32_t reserved) {
|
||||
reinterpret_cast<ir::IrLoader*>(builder)
|
||||
->SetModuleHeader(magic, version, generator, id_bound, reserved);
|
||||
reinterpret_cast<ir::IrLoader*>(builder)->SetModuleHeader(
|
||||
magic, version, generator, id_bound, reserved);
|
||||
return SPV_SUCCESS;
|
||||
};
|
||||
|
||||
|
@ -27,9 +27,9 @@ namespace spvtools {
|
||||
// specifies number of words in |binary|. The |binary| will be decoded
|
||||
// according to the given target |env|. Returns nullptr if erors occur and
|
||||
// sends the errors to |consumer|.
|
||||
std::unique_ptr<ir::Module> BuildModule(
|
||||
spv_target_env env, MessageConsumer consumer, const uint32_t* binary,
|
||||
size_t size);
|
||||
std::unique_ptr<ir::Module> BuildModule(spv_target_env env,
|
||||
MessageConsumer consumer,
|
||||
const uint32_t* binary, size_t size);
|
||||
|
||||
// Builds and returns an ir::Module from the given SPIR-V assembly |text|.
|
||||
// The |text| will be encoded according to the given target |env|. Returns
|
||||
|
@ -574,31 +574,21 @@ void CommonUniformElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -24,23 +24,23 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "decoration_manager.h"
|
||||
#include "module.h"
|
||||
#include "basic_block.h"
|
||||
#include "pass.h"
|
||||
#include "decoration_manager.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class CommonUniformElimPass : public Pass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
CommonUniformElimPass();
|
||||
const char* name() const override { return "eliminate-common-uniform"; }
|
||||
@ -72,7 +72,8 @@ class CommonUniformElimPass : public Pass {
|
||||
// Given an OpAccessChain instruction, return true
|
||||
// if the accessed variable belongs to a volatile
|
||||
// decorated object or member of a struct type
|
||||
bool IsAccessChainToVolatileStructType(const ir::Instruction &AccessChainInst);
|
||||
bool IsAccessChainToVolatileStructType(
|
||||
const ir::Instruction& AccessChainInst);
|
||||
|
||||
// Given an OpLoad instruction, return true if
|
||||
// OpLoad has a Volatile Memory Access flag or if
|
||||
@ -96,15 +97,14 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Replace all instances of load's id with replId and delete load
|
||||
// and its access chain, if any
|
||||
void ReplaceAndDeleteLoad(ir::Instruction* loadInst,
|
||||
uint32_t replId,
|
||||
ir::Instruction* ptrInst);
|
||||
void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId,
|
||||
ir::Instruction* ptrInst);
|
||||
|
||||
// For the (constant index) access chain ptrInst, create an
|
||||
// equivalent load and extract
|
||||
void GenACLoadRepl(const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
uint32_t* resultId);
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
|
||||
uint32_t* resultId);
|
||||
|
||||
// Return true if all indices are constant
|
||||
bool IsConstantIndexAccessChain(ir::Instruction* acp);
|
||||
@ -132,14 +132,15 @@ class CommonUniformElimPass : public Pass {
|
||||
// TODO(dnovillo): This pass computes structured order slightly different
|
||||
// than the implementation in class Pass. Can this be re-factored?
|
||||
void ComputeStructuredOrder(ir::Function* func,
|
||||
std::list<ir::BasicBlock*>* order);
|
||||
std::list<ir::BasicBlock*>* order);
|
||||
|
||||
// Eliminate loads of uniform variables which have previously been loaded.
|
||||
// If first load is in control flow, move it to first block of function.
|
||||
// Most effective if preceded by UniformAccessChainRemoval().
|
||||
bool CommonUniformLoadElimination(ir::Function* func);
|
||||
|
||||
// Eliminate loads of uniform sampler and image variables which have previously
|
||||
// Eliminate loads of uniform sampler and image variables which have
|
||||
// previously
|
||||
// been loaded in the same block for types whose loads cannot cross blocks.
|
||||
bool CommonUniformLoadElimBlock(ir::Function* func);
|
||||
|
||||
@ -150,11 +151,11 @@ class CommonUniformElimPass : public Pass {
|
||||
bool CommonExtractElimination(ir::Function* func);
|
||||
|
||||
// For function |func|, first change all uniform constant index
|
||||
// access chain loads into equivalent composite extracts. Then consolidate
|
||||
// access chain loads into equivalent composite extracts. Then consolidate
|
||||
// identical uniform loads into one uniform load. Finally, consolidate
|
||||
// identical uniform extracts into one uniform extract. This may require
|
||||
// moving a load or extract to a point which dominates all uses.
|
||||
// Return true if func is modified.
|
||||
// Return true if func is modified.
|
||||
//
|
||||
// This pass requires the function to have structured control flow ie shader
|
||||
// capability. It also requires logical addressing ie Addresses capability
|
||||
@ -185,8 +186,9 @@ class CommonUniformElimPass : public Pass {
|
||||
|
||||
// Map of extract composite ids to map of indices to insts
|
||||
// TODO(greg-lunarg): Consider std::vector.
|
||||
std::unordered_map<uint32_t, std::unordered_map<uint32_t,
|
||||
std::list<ir::Instruction*>>> comp2idx2inst_;
|
||||
std::unordered_map<uint32_t,
|
||||
std::unordered_map<uint32_t, std::list<ir::Instruction*>>>
|
||||
comp2idx2inst_;
|
||||
|
||||
// Extensions supported by this pass.
|
||||
std::unordered_set<std::string> extensions_whitelist_;
|
||||
@ -201,4 +203,3 @@ class CommonUniformElimPass : public Pass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_SSAMEM_PASS_H_
|
||||
|
||||
|
@ -30,35 +30,38 @@ Pass::Status CompactIdsPass::Process(ir::IRContext* c) {
|
||||
bool modified = false;
|
||||
std::unordered_map<uint32_t, uint32_t> result_id_mapping;
|
||||
|
||||
c->module()->ForEachInst([&result_id_mapping, &modified] (Instruction* inst) {
|
||||
auto operand = inst->begin();
|
||||
while (operand != inst->end()) {
|
||||
const auto type = operand->type;
|
||||
if (spvIsIdType(type)) {
|
||||
assert(operand->words.size() == 1);
|
||||
uint32_t& id = operand->words[0];
|
||||
auto it = result_id_mapping.find(id);
|
||||
if (it == result_id_mapping.end()) {
|
||||
const uint32_t new_id =
|
||||
static_cast<uint32_t>(result_id_mapping.size()) + 1;
|
||||
const auto insertion_result = result_id_mapping.emplace(id, new_id);
|
||||
it = insertion_result.first;
|
||||
assert(insertion_result.second);
|
||||
}
|
||||
if (id != it->second) {
|
||||
modified = true;
|
||||
id = it->second;
|
||||
// Update data cached in the instruction object.
|
||||
if (type == SPV_OPERAND_TYPE_RESULT_ID) {
|
||||
inst->SetResultId(id);
|
||||
} else if (type == SPV_OPERAND_TYPE_TYPE_ID) {
|
||||
inst->SetResultType(id);
|
||||
c->module()->ForEachInst(
|
||||
[&result_id_mapping, &modified](Instruction* inst) {
|
||||
auto operand = inst->begin();
|
||||
while (operand != inst->end()) {
|
||||
const auto type = operand->type;
|
||||
if (spvIsIdType(type)) {
|
||||
assert(operand->words.size() == 1);
|
||||
uint32_t& id = operand->words[0];
|
||||
auto it = result_id_mapping.find(id);
|
||||
if (it == result_id_mapping.end()) {
|
||||
const uint32_t new_id =
|
||||
static_cast<uint32_t>(result_id_mapping.size()) + 1;
|
||||
const auto insertion_result =
|
||||
result_id_mapping.emplace(id, new_id);
|
||||
it = insertion_result.first;
|
||||
assert(insertion_result.second);
|
||||
}
|
||||
if (id != it->second) {
|
||||
modified = true;
|
||||
id = it->second;
|
||||
// Update data cached in the instruction object.
|
||||
if (type == SPV_OPERAND_TYPE_RESULT_ID) {
|
||||
inst->SetResultId(id);
|
||||
} else if (type == SPV_OPERAND_TYPE_TYPE_ID) {
|
||||
inst->SetResultType(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
++operand;
|
||||
}
|
||||
}
|
||||
++operand;
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
true);
|
||||
|
||||
if (modified)
|
||||
c->SetIdBound(static_cast<uint32_t>(result_id_mapping.size() + 1));
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_COMPACT_IDS_PASS_H_
|
||||
#define LIBSPIRV_OPT_COMPACT_IDS_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -29,7 +29,7 @@ const uint32_t kBranchCondTrueLabIdInIdx = 1;
|
||||
const uint32_t kBranchCondFalseLabIdInIdx = 2;
|
||||
const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
|
||||
bool condIsConst;
|
||||
@ -45,14 +45,11 @@ bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) {
|
||||
} break;
|
||||
case SpvOpLogicalNot: {
|
||||
bool negVal;
|
||||
condIsConst = GetConstCondition(cInst->GetSingleWordInOperand(0),
|
||||
&negVal);
|
||||
if (condIsConst)
|
||||
*condVal = !negVal;
|
||||
} break;
|
||||
default: {
|
||||
condIsConst = false;
|
||||
condIsConst =
|
||||
GetConstCondition(cInst->GetSingleWordInOperand(0), &negVal);
|
||||
if (condIsConst) *condVal = !negVal;
|
||||
} break;
|
||||
default: { condIsConst = false; } break;
|
||||
}
|
||||
return condIsConst;
|
||||
}
|
||||
@ -63,13 +60,11 @@ bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) {
|
||||
ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
if (!typeInst || (typeInst->opcode() != SpvOpTypeInt)) return false;
|
||||
// TODO(greg-lunarg): Support non-32 bit ints
|
||||
if (typeInst->GetSingleWordInOperand(0) != 32)
|
||||
return false;
|
||||
if (typeInst->GetSingleWordInOperand(0) != 32) return false;
|
||||
if (sInst->opcode() == SpvOpConstant) {
|
||||
*selVal = sInst->GetSingleWordInOperand(0);
|
||||
return true;
|
||||
}
|
||||
else if (sInst->opcode() == SpvOpConstantNull) {
|
||||
} else if (sInst->opcode() == SpvOpConstantNull) {
|
||||
*selVal = 0;
|
||||
return true;
|
||||
}
|
||||
@ -77,46 +72,47 @@ bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) {
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::AddBranch(uint32_t labelId, ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(
|
||||
new ir::Instruction(SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newBranch);
|
||||
bp->AddInstruction(std::move(newBranch));
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::AddSelectionMerge(uint32_t labelId,
|
||||
ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newMerge(
|
||||
new ir::Instruction(SpvOpSelectionMerge, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0}}}));
|
||||
ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newMerge(new ir::Instruction(
|
||||
SpvOpSelectionMerge, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newMerge);
|
||||
bp->AddInstruction(std::move(newMerge));
|
||||
}
|
||||
|
||||
void DeadBranchElimPass::AddBranchConditional(uint32_t condId,
|
||||
uint32_t trueLabId, uint32_t falseLabId, ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newBranchCond(
|
||||
new ir::Instruction(SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {condId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {trueLabId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {falseLabId}}}));
|
||||
uint32_t trueLabId,
|
||||
uint32_t falseLabId,
|
||||
ir::BasicBlock* bp) {
|
||||
std::unique_ptr<ir::Instruction> newBranchCond(new ir::Instruction(
|
||||
SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {condId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {trueLabId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {falseLabId}}}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newBranchCond);
|
||||
bp->AddInstruction(std::move(newBranchCond));
|
||||
}
|
||||
|
||||
bool DeadBranchElimPass::GetSelectionBranch(ir::BasicBlock* bp,
|
||||
ir::Instruction** branchInst, ir::Instruction** mergeInst,
|
||||
uint32_t *condId) {
|
||||
ir::Instruction** branchInst,
|
||||
ir::Instruction** mergeInst,
|
||||
uint32_t* condId) {
|
||||
auto ii = bp->end();
|
||||
--ii;
|
||||
*branchInst = &*ii;
|
||||
if (ii == bp->begin())
|
||||
return false;
|
||||
if (ii == bp->begin()) return false;
|
||||
--ii;
|
||||
*mergeInst = &*ii;
|
||||
if ((*mergeInst)->opcode() != SpvOpSelectionMerge)
|
||||
return false;
|
||||
if ((*mergeInst)->opcode() != SpvOpSelectionMerge) return false;
|
||||
// SPIR-V says the terminator for an OpSelectionMerge must be
|
||||
// either a conditional branch or a switch.
|
||||
assert((*branchInst)->opcode() == SpvOpBranchConditional ||
|
||||
@ -128,8 +124,7 @@ bool DeadBranchElimPass::GetSelectionBranch(ir::BasicBlock* bp,
|
||||
|
||||
bool DeadBranchElimPass::HasNonPhiNonBackedgeRef(uint32_t labelId) {
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(labelId);
|
||||
if (uses == nullptr)
|
||||
return false;
|
||||
if (uses == nullptr) return false;
|
||||
for (auto u : *uses) {
|
||||
if (u.inst->opcode() != SpvOpPhi &&
|
||||
backedges_.find(u.inst) == backedges_.end())
|
||||
@ -147,24 +142,22 @@ void DeadBranchElimPass::ComputeBackEdges(
|
||||
visited.insert((*bi)->id());
|
||||
auto ii = (*bi)->end();
|
||||
--ii;
|
||||
switch(ii->opcode()) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpBranch: {
|
||||
const uint32_t labId = ii->GetSingleWordInOperand(
|
||||
kBranchTargetLabIdInIdx);
|
||||
if (visited.find(labId) != visited.end())
|
||||
backedges_.insert(&*ii);
|
||||
const uint32_t labId =
|
||||
ii->GetSingleWordInOperand(kBranchTargetLabIdInIdx);
|
||||
if (visited.find(labId) != visited.end()) backedges_.insert(&*ii);
|
||||
} break;
|
||||
case SpvOpBranchConditional: {
|
||||
const uint32_t tLabId = ii->GetSingleWordInOperand(
|
||||
kBranchCondTrueLabIdInIdx);
|
||||
const uint32_t tLabId =
|
||||
ii->GetSingleWordInOperand(kBranchCondTrueLabIdInIdx);
|
||||
if (visited.find(tLabId) != visited.end()) {
|
||||
backedges_.insert(&*ii);
|
||||
break;
|
||||
}
|
||||
const uint32_t fLabId = ii->GetSingleWordInOperand(
|
||||
kBranchCondFalseLabIdInIdx);
|
||||
if (visited.find(fLabId) != visited.end())
|
||||
backedges_.insert(&*ii);
|
||||
const uint32_t fLabId =
|
||||
ii->GetSingleWordInOperand(kBranchCondFalseLabIdInIdx);
|
||||
if (visited.find(fLabId) != visited.end()) backedges_.insert(&*ii);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
@ -181,53 +174,45 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
bool modified = false;
|
||||
for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
|
||||
// Skip blocks that are already in the elimination set
|
||||
if (elimBlocks.find(*bi) != elimBlocks.end())
|
||||
continue;
|
||||
if (elimBlocks.find(*bi) != elimBlocks.end()) continue;
|
||||
// Skip blocks that don't have conditional branch preceded
|
||||
// by OpSelectionMerge
|
||||
ir::Instruction* br;
|
||||
ir::Instruction* mergeInst;
|
||||
uint32_t condId;
|
||||
if (!GetSelectionBranch(*bi, &br, &mergeInst, &condId))
|
||||
continue;
|
||||
if (!GetSelectionBranch(*bi, &br, &mergeInst, &condId)) continue;
|
||||
|
||||
// If constant condition/selector, replace conditional branch/switch
|
||||
// with unconditional branch and delete merge
|
||||
uint32_t liveLabId;
|
||||
if (br->opcode() == SpvOpBranchConditional) {
|
||||
bool condVal;
|
||||
if (!GetConstCondition(condId, &condVal))
|
||||
continue;
|
||||
liveLabId = (condVal == true) ?
|
||||
br->GetSingleWordInOperand(kBranchCondTrueLabIdInIdx) :
|
||||
br->GetSingleWordInOperand(kBranchCondFalseLabIdInIdx);
|
||||
}
|
||||
else {
|
||||
if (!GetConstCondition(condId, &condVal)) continue;
|
||||
liveLabId = (condVal == true)
|
||||
? br->GetSingleWordInOperand(kBranchCondTrueLabIdInIdx)
|
||||
: br->GetSingleWordInOperand(kBranchCondFalseLabIdInIdx);
|
||||
} else {
|
||||
assert(br->opcode() == SpvOpSwitch);
|
||||
// Search switch operands for selector value, set liveLabId to
|
||||
// corresponding label, use default if not found
|
||||
uint32_t selVal;
|
||||
if (!GetConstInteger(condId, &selVal))
|
||||
continue;
|
||||
if (!GetConstInteger(condId, &selVal)) continue;
|
||||
uint32_t icnt = 0;
|
||||
uint32_t caseVal;
|
||||
br->ForEachInOperand(
|
||||
[&icnt,&caseVal,&selVal,&liveLabId](const uint32_t* idp) {
|
||||
if (icnt == 1) {
|
||||
// Start with default label
|
||||
liveLabId = *idp;
|
||||
}
|
||||
else if (icnt > 1) {
|
||||
if (icnt % 2 == 0) {
|
||||
caseVal = *idp;
|
||||
}
|
||||
else {
|
||||
if (caseVal == selVal)
|
||||
[&icnt, &caseVal, &selVal, &liveLabId](const uint32_t* idp) {
|
||||
if (icnt == 1) {
|
||||
// Start with default label
|
||||
liveLabId = *idp;
|
||||
}
|
||||
}
|
||||
++icnt;
|
||||
});
|
||||
} else if (icnt > 1) {
|
||||
if (icnt % 2 == 0) {
|
||||
caseVal = *idp;
|
||||
} else {
|
||||
if (caseVal == selVal) liveLabId = *idp;
|
||||
}
|
||||
}
|
||||
++icnt;
|
||||
});
|
||||
}
|
||||
|
||||
const uint32_t mergeLabId =
|
||||
@ -258,36 +243,30 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
KillAllInsts(*dbi);
|
||||
elimBlocks.insert(*dbi);
|
||||
++dbi;
|
||||
if (dbi == structuredOrder.end())
|
||||
break;
|
||||
if (dbi == structuredOrder.end()) break;
|
||||
dLabId = (*dbi)->id();
|
||||
}
|
||||
|
||||
// If last block reached, look for next dead branch
|
||||
if (dbi == structuredOrder.end())
|
||||
continue;
|
||||
if (dbi == structuredOrder.end()) continue;
|
||||
|
||||
// Create set of dead predecessors in preparation for phi update.
|
||||
// Add the header block if the live branch is not the merge block.
|
||||
std::unordered_set<ir::BasicBlock*> deadPreds(elimBlocks);
|
||||
if (liveLabId != dLabId)
|
||||
deadPreds.insert(*bi);
|
||||
if (liveLabId != dLabId) deadPreds.insert(*bi);
|
||||
|
||||
// Update phi instructions in terminating block.
|
||||
for (auto pii = (*dbi)->begin(); ; ++pii) {
|
||||
for (auto pii = (*dbi)->begin();; ++pii) {
|
||||
// Skip NoOps, break at end of phis
|
||||
SpvOp op = pii->opcode();
|
||||
if (op == SpvOpNop)
|
||||
continue;
|
||||
if (op != SpvOpPhi)
|
||||
break;
|
||||
if (op == SpvOpNop) continue;
|
||||
if (op != SpvOpPhi) break;
|
||||
// Count phi's live predecessors with lcnt and remember last one
|
||||
// with lidx.
|
||||
uint32_t lcnt = 0;
|
||||
uint32_t lidx = 0;
|
||||
uint32_t icnt = 0;
|
||||
pii->ForEachInId(
|
||||
[&deadPreds,&icnt,&lcnt,&lidx,this](uint32_t* idp) {
|
||||
pii->ForEachInId([&deadPreds, &icnt, &lcnt, &lidx, this](uint32_t* idp) {
|
||||
if (icnt % 2 == 1) {
|
||||
if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) {
|
||||
++lcnt;
|
||||
@ -300,8 +279,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
uint32_t replId;
|
||||
if (lcnt == 1) {
|
||||
replId = pii->GetSingleWordInOperand(lidx);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Otherwise create new phi eliminating dead predecessor entries
|
||||
assert(lcnt > 1);
|
||||
replId = TakeNextId();
|
||||
@ -309,20 +287,19 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
icnt = 0;
|
||||
uint32_t lastId;
|
||||
pii->ForEachInId(
|
||||
[&deadPreds,&icnt,&phi_in_opnds,&lastId,this](uint32_t* idp) {
|
||||
if (icnt % 2 == 1) {
|
||||
if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) {
|
||||
phi_in_opnds.push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {lastId}});
|
||||
phi_in_opnds.push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*idp}});
|
||||
}
|
||||
}
|
||||
else {
|
||||
lastId = *idp;
|
||||
}
|
||||
++icnt;
|
||||
});
|
||||
[&deadPreds, &icnt, &phi_in_opnds, &lastId, this](uint32_t* idp) {
|
||||
if (icnt % 2 == 1) {
|
||||
if (deadPreds.find(cfg()->block(*idp)) == deadPreds.end()) {
|
||||
phi_in_opnds.push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {lastId}});
|
||||
phi_in_opnds.push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*idp}});
|
||||
}
|
||||
} else {
|
||||
lastId = *idp;
|
||||
}
|
||||
++icnt;
|
||||
});
|
||||
std::unique_ptr<ir::Instruction> newPhi(new ir::Instruction(
|
||||
SpvOpPhi, pii->type_id(), replId, phi_in_opnds));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newPhi);
|
||||
@ -337,7 +314,7 @@ bool DeadBranchElimPass::EliminateDeadBranches(ir::Function* func) {
|
||||
}
|
||||
|
||||
// Erase dead blocks
|
||||
for (auto ebi = func->begin(); ebi != func->end(); )
|
||||
for (auto ebi = func->begin(); ebi != func->end();)
|
||||
if (elimBlocks.find(&*ebi) != elimBlocks.end())
|
||||
ebi = ebi.Erase();
|
||||
else
|
||||
@ -355,8 +332,8 @@ void DeadBranchElimPass::Initialize(ir::IRContext* c) {
|
||||
bool DeadBranchElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -364,7 +341,7 @@ bool DeadBranchElimPass::AllExtensionsSupported() const {
|
||||
}
|
||||
|
||||
Pass::Status DeadBranchElimPass::ProcessImpl() {
|
||||
// Current functionality assumes structured control flow.
|
||||
// Current functionality assumes structured control flow.
|
||||
// TODO(greg-lunarg): Handle non-structured control-flow.
|
||||
if (!get_module()->HasCapability(SpvCapabilityShader))
|
||||
return Status::SuccessWithoutChange;
|
||||
@ -372,11 +349,9 @@ Pass::Status DeadBranchElimPass::ProcessImpl() {
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return EliminateDeadBranches(fp);
|
||||
@ -395,31 +370,30 @@ Pass::Status DeadBranchElimPass::Process(ir::IRContext* module) {
|
||||
void DeadBranchElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
#ifndef LIBSPIRV_OPT_DEAD_BRANCH_ELIM_PASS_H_
|
||||
#define LIBSPIRV_OPT_DEAD_BRANCH_ELIM_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
@ -27,20 +26,19 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class DeadBranchElimPass : public MemPass {
|
||||
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
DeadBranchElimPass();
|
||||
const char* name() const override { return "eliminate-dead-branches"; }
|
||||
@ -64,13 +62,13 @@ class DeadBranchElimPass : public MemPass {
|
||||
// Add conditional branch of |condId|, |trueLabId| and |falseLabId| to end
|
||||
// of block |bp|.
|
||||
void AddBranchConditional(uint32_t condId, uint32_t trueLabId,
|
||||
uint32_t falseLabId, ir::BasicBlock* bp);
|
||||
uint32_t falseLabId, ir::BasicBlock* bp);
|
||||
|
||||
// If block |bp| contains conditional branch or switch preceeded by an
|
||||
// OpSelctionMerge, return true and return branch and merge instructions
|
||||
// in |branchInst| and |mergeInst| and the conditional id in |condId|.
|
||||
// in |branchInst| and |mergeInst| and the conditional id in |condId|.
|
||||
bool GetSelectionBranch(ir::BasicBlock* bp, ir::Instruction** branchInst,
|
||||
ir::Instruction** mergeInst, uint32_t *condId);
|
||||
ir::Instruction** mergeInst, uint32_t* condId);
|
||||
|
||||
// Return true if |labelId| has any non-phi, non-backedge references
|
||||
bool HasNonPhiNonBackedgeRef(uint32_t labelId);
|
||||
@ -106,4 +104,3 @@ class DeadBranchElimPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_DEAD_BRANCH_ELIM_PASS_H_
|
||||
|
||||
|
@ -24,7 +24,8 @@ namespace opt {
|
||||
Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
// The algorithm will compute the reference count for every global variable.
|
||||
// Anything with a reference count of 0 will then be deleted. For variables
|
||||
// that might have references that are not explicit in this context, we use the
|
||||
// that might have references that are not explicit in this context, we use
|
||||
// the
|
||||
// value kMustKeep as the reference count.
|
||||
InitializeProcessing(c);
|
||||
|
||||
@ -61,7 +62,7 @@ Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
count = std::count_if(
|
||||
uses->begin(), uses->end(), [](const analysis::Use& u) {
|
||||
return (!ir::IsAnnotationInst(u.inst->opcode()) &&
|
||||
u.inst->opcode() != SpvOpName);
|
||||
u.inst->opcode() != SpvOpName);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -85,7 +86,7 @@ Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
|
||||
void DeadVariableElimination::DeleteVariable(uint32_t result_id) {
|
||||
ir::Instruction* inst = get_def_use_mgr()->GetDef(result_id);
|
||||
assert(inst->opcode() == SpvOpVariable &&
|
||||
"Should not be trying to delete anything other than an OpVariable.");
|
||||
"Should not be trying to delete anything other than an OpVariable.");
|
||||
|
||||
// Look for an initializer that references another variable. We need to know
|
||||
// if that variable can be deleted after the reference is removed.
|
||||
|
@ -15,8 +15,8 @@
|
||||
#ifndef SPIRV_TOOLS_DEAD_VARIABLE_ELIMINATION_H
|
||||
#define SPIRV_TOOLS_DEAD_VARIABLE_ELIMINATION_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <climits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "decoration_manager.h"
|
||||
#include "mem_pass.h"
|
||||
|
@ -29,8 +29,9 @@ void DecorationManager::RemoveDecorationsFrom(uint32_t id, bool keep_linkage) {
|
||||
case SpvOpDecorate:
|
||||
case SpvOpDecorateId:
|
||||
case SpvOpMemberDecorate:
|
||||
if (!(keep_linkage && inst->GetSingleWordInOperand(1u) ==
|
||||
SpvDecorationLinkageAttributes))
|
||||
if (!(keep_linkage &&
|
||||
inst->GetSingleWordInOperand(1u) ==
|
||||
SpvDecorationLinkageAttributes))
|
||||
inst->ToNop();
|
||||
break;
|
||||
case SpvOpGroupDecorate:
|
||||
@ -229,9 +230,9 @@ std::vector<T> DecorationManager::InternalGetDecorationsFor(
|
||||
return decorations;
|
||||
}
|
||||
|
||||
void DecorationManager::ForEachDecoration(uint32_t id,
|
||||
uint32_t decoration,
|
||||
std::function<void(const ir::Instruction&)> f) const {
|
||||
void DecorationManager::ForEachDecoration(
|
||||
uint32_t id, uint32_t decoration,
|
||||
std::function<void(const ir::Instruction&)> f) const {
|
||||
auto decoration_list = id_to_decoration_insts_.find(id);
|
||||
if (decoration_list != id_to_decoration_insts_.end()) {
|
||||
for (const ir::Instruction* inst : decoration_list->second) {
|
||||
|
@ -31,8 +31,7 @@ void DefUseManager::AnalyzeInstDef(ir::Instruction* inst) {
|
||||
ClearInst(iter->second);
|
||||
}
|
||||
id_to_def_[def_id] = inst;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ClearInst(inst);
|
||||
}
|
||||
}
|
||||
@ -46,16 +45,16 @@ void DefUseManager::AnalyzeInstUse(ir::Instruction* inst) {
|
||||
for (uint32_t i = 0; i < inst->NumOperands(); ++i) {
|
||||
switch (inst->GetOperand(i).type) {
|
||||
// For any id type but result id type
|
||||
case SPV_OPERAND_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||
case SPV_OPERAND_TYPE_SCOPE_ID: {
|
||||
uint32_t use_id = inst->GetSingleWordOperand(i);
|
||||
id_to_uses_[use_id].push_back({ inst, i });
|
||||
inst_to_used_ids_[inst].push_back(use_id);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
case SPV_OPERAND_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_TYPE_ID:
|
||||
case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
|
||||
case SPV_OPERAND_TYPE_SCOPE_ID: {
|
||||
uint32_t use_id = inst->GetSingleWordOperand(i);
|
||||
id_to_uses_[use_id].push_back({inst, i});
|
||||
inst_to_used_ids_[inst].push_back(use_id);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +100,6 @@ std::vector<ir::Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
|
||||
return annos;
|
||||
}
|
||||
|
||||
|
||||
void DefUseManager::AnalyzeDefUse(ir::Module* module) {
|
||||
if (!module) return;
|
||||
module->ForEachInst(std::bind(&DefUseManager::AnalyzeInstDefUse, this,
|
||||
|
@ -49,9 +49,7 @@ class DefUseManager {
|
||||
// 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(ir::Module* module) {
|
||||
AnalyzeDefUse(module);
|
||||
}
|
||||
DefUseManager(ir::Module* module) { AnalyzeDefUse(module); }
|
||||
|
||||
DefUseManager(const DefUseManager&) = delete;
|
||||
DefUseManager(DefUseManager&&) = delete;
|
||||
@ -105,8 +103,8 @@ class DefUseManager {
|
||||
// structures in this class. Does nothing if |module| is nullptr.
|
||||
void AnalyzeDefUse(ir::Module* module);
|
||||
|
||||
IdToDefMap id_to_def_; // Mapping from ids to their definitions
|
||||
IdToUsesMap id_to_uses_; // Mapping from ids to their uses
|
||||
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 instruction.
|
||||
InstToUsedIdsMap inst_to_used_ids_;
|
||||
};
|
||||
|
@ -19,9 +19,9 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "log.h"
|
||||
#include "reflect.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -36,7 +36,8 @@ Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
for (auto* c : constants) {
|
||||
uint32_t const_id = c->result_id();
|
||||
size_t count = 0;
|
||||
if (analysis::UseList* uses = irContext->get_def_use_mgr()->GetUses(const_id)) {
|
||||
if (analysis::UseList* uses =
|
||||
irContext->get_def_use_mgr()->GetUses(const_id)) {
|
||||
count =
|
||||
std::count_if(uses->begin(), uses->end(), [](const analysis::Use& u) {
|
||||
return !(ir::IsAnnotationInst(u.inst->opcode()) ||
|
||||
@ -68,7 +69,8 @@ Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
continue;
|
||||
}
|
||||
uint32_t operand_id = inst->GetSingleWordInOperand(i);
|
||||
ir::Instruction* def_inst = irContext->get_def_use_mgr()->GetDef(operand_id);
|
||||
ir::Instruction* def_inst =
|
||||
irContext->get_def_use_mgr()->GetDef(operand_id);
|
||||
// If the use_count does not have any count for the def_inst,
|
||||
// def_inst must not be a constant, and should be ignored here.
|
||||
if (!use_counts.count(def_inst)) {
|
||||
@ -94,7 +96,8 @@ Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
|
||||
// constants.
|
||||
std::unordered_set<ir::Instruction*> dead_others;
|
||||
for (auto* dc : dead_consts) {
|
||||
if (analysis::UseList* uses = irContext->get_def_use_mgr()->GetUses(dc->result_id())) {
|
||||
if (analysis::UseList* uses =
|
||||
irContext->get_def_use_mgr()->GetUses(dc->result_id())) {
|
||||
for (const auto& u : *uses) {
|
||||
if (ir::IsAnnotationInst(u.inst->opcode()) ||
|
||||
ir::IsDebug1Inst(u.inst->opcode()) ||
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_
|
||||
#define LIBSPIRV_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -16,9 +16,9 @@
|
||||
#include "ir_context.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_FLATTEN_DECORATION_PASS_H_
|
||||
#define LIBSPIRV_OPT_FLATTEN_DECORATION_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -15,8 +15,8 @@
|
||||
#ifndef LIBSPIRV_UTIL_FOLD_H_
|
||||
#define LIBSPIRV_UTIL_FOLD_H_
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "constants.h"
|
||||
#include "def_use_manager.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
@ -19,8 +19,8 @@
|
||||
#include <tuple>
|
||||
|
||||
#include "constants.h"
|
||||
#include "make_unique.h"
|
||||
#include "ir_context.h"
|
||||
#include "make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -245,7 +245,8 @@ std::vector<uint32_t> OperateVectors(
|
||||
FoldSpecConstantOpAndCompositePass::FoldSpecConstantOpAndCompositePass()
|
||||
: max_id_(0), type_mgr_(nullptr), id_to_const_val_() {}
|
||||
|
||||
Pass::Status FoldSpecConstantOpAndCompositePass::Process(ir::IRContext* irContext) {
|
||||
Pass::Status FoldSpecConstantOpAndCompositePass::Process(
|
||||
ir::IRContext* irContext) {
|
||||
Initialize(irContext);
|
||||
return ProcessImpl(irContext);
|
||||
}
|
||||
|
@ -21,10 +21,10 @@
|
||||
|
||||
#include "constants.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "type_manager.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_
|
||||
#define LIBSPIRV_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -64,8 +64,9 @@ class Function {
|
||||
// Returns function's id
|
||||
inline uint32_t result_id() const { return def_inst_->result_id(); }
|
||||
|
||||
// // Returns function's type id
|
||||
// inline uint32_t type_id() const { return def_inst_->GetSingleWordInOperand(1u); }
|
||||
// // Returns function's type id
|
||||
// inline uint32_t type_id() const { return
|
||||
// def_inst_->GetSingleWordInOperand(1u); }
|
||||
|
||||
// Returns function's return type id
|
||||
inline uint32_t type_id() const { return def_inst_->type_id(); }
|
||||
|
@ -31,13 +31,13 @@ bool InlineExhaustivePass::InlineExhaustive(ir::Function* func) {
|
||||
GenInlineCode(&newBlocks, &newVars, ii, bi);
|
||||
// If call block is replaced with more than one block, point
|
||||
// succeeding phis at new last block.
|
||||
if (newBlocks.size() > 1)
|
||||
UpdateSucceedingPhis(newBlocks);
|
||||
if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks);
|
||||
// Replace old calling block with new block(s).
|
||||
bi = bi.Erase();
|
||||
bi = bi.InsertBefore(&newBlocks);
|
||||
// Insert new function variables.
|
||||
if (newVars.size() > 0) func->begin()->begin().InsertBefore(std::move(newVars));
|
||||
if (newVars.size() > 0)
|
||||
func->begin()->begin().InsertBefore(std::move(newVars));
|
||||
// Restart inlining at beginning of calling block.
|
||||
ii = bi->begin();
|
||||
modified = true;
|
||||
|
@ -20,19 +20,18 @@
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "inline_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class InlineExhaustivePass : public InlinePass {
|
||||
|
||||
public:
|
||||
InlineExhaustivePass();
|
||||
Status Process(ir::IRContext* c) override;
|
||||
|
@ -21,9 +21,9 @@ namespace opt {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kTypePointerTypeIdInIdx = 1;
|
||||
const uint32_t kTypePointerTypeIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
|
||||
const ir::Instruction* typeInst = get_def_use_mgr()->GetDef(typeId);
|
||||
@ -33,17 +33,16 @@ bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
|
||||
case SpvOpTypeSampledImage:
|
||||
return true;
|
||||
case SpvOpTypePointer:
|
||||
return IsOpaqueType(typeInst->GetSingleWordInOperand(
|
||||
kTypePointerTypeIdInIdx));
|
||||
return IsOpaqueType(
|
||||
typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// TODO(greg-lunarg): Handle arrays containing opaque type
|
||||
if (typeInst->opcode() != SpvOpTypeStruct)
|
||||
return false;
|
||||
if (typeInst->opcode() != SpvOpTypeStruct) return false;
|
||||
// Return true if any member is opaque
|
||||
int ocnt = 0;
|
||||
typeInst->ForEachInId([&ocnt,this](const uint32_t* tid) {
|
||||
typeInst->ForEachInId([&ocnt, this](const uint32_t* tid) {
|
||||
if (ocnt == 0 && IsOpaqueType(*tid)) ++ocnt;
|
||||
});
|
||||
return ocnt > 0;
|
||||
@ -51,16 +50,14 @@ bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) {
|
||||
|
||||
bool InlineOpaquePass::HasOpaqueArgsOrReturn(const ir::Instruction* callInst) {
|
||||
// Check return type
|
||||
if (IsOpaqueType(callInst->type_id()))
|
||||
return true;
|
||||
if (IsOpaqueType(callInst->type_id())) return true;
|
||||
// Check args
|
||||
int icnt = 0;
|
||||
int ocnt = 0;
|
||||
callInst->ForEachInId([&icnt,&ocnt,this](const uint32_t *iid) {
|
||||
callInst->ForEachInId([&icnt, &ocnt, this](const uint32_t* iid) {
|
||||
if (icnt > 0) {
|
||||
const ir::Instruction* argInst = get_def_use_mgr()->GetDef(*iid);
|
||||
if (IsOpaqueType(argInst->type_id()))
|
||||
++ocnt;
|
||||
if (IsOpaqueType(argInst->type_id())) ++ocnt;
|
||||
}
|
||||
++icnt;
|
||||
});
|
||||
@ -79,13 +76,13 @@ bool InlineOpaquePass::InlineOpaque(ir::Function* func) {
|
||||
GenInlineCode(&newBlocks, &newVars, ii, bi);
|
||||
// If call block is replaced with more than one block, point
|
||||
// succeeding phis at new last block.
|
||||
if (newBlocks.size() > 1)
|
||||
UpdateSucceedingPhis(newBlocks);
|
||||
if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks);
|
||||
// Replace old calling block with new block(s).
|
||||
bi = bi.Erase();
|
||||
bi = bi.InsertBefore(&newBlocks);
|
||||
// Insert new function variables.
|
||||
if (newVars.size() > 0) func->begin()->begin().InsertBefore(std::move(newVars));
|
||||
if (newVars.size() > 0)
|
||||
func->begin()->begin().InsertBefore(std::move(newVars));
|
||||
// Restart inlining at beginning of calling block.
|
||||
ii = bi->begin();
|
||||
modified = true;
|
||||
@ -97,15 +94,11 @@ bool InlineOpaquePass::InlineOpaque(ir::Function* func) {
|
||||
return modified;
|
||||
}
|
||||
|
||||
void InlineOpaquePass::Initialize(ir::IRContext* c) {
|
||||
InitializeInline(c);
|
||||
};
|
||||
void InlineOpaquePass::Initialize(ir::IRContext* c) { InitializeInline(c); };
|
||||
|
||||
Pass::Status InlineOpaquePass::ProcessImpl() {
|
||||
// Do opaque inlining on each function in entry point call tree
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return InlineOpaque(fp);
|
||||
};
|
||||
ProcessFunction pfn = [this](ir::Function* fp) { return InlineOpaque(fp); };
|
||||
bool modified = ProcessEntryPointCallTree(pfn, get_module());
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
@ -20,19 +20,18 @@
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "inline_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class InlineOpaquePass : public InlinePass {
|
||||
|
||||
public:
|
||||
InlineOpaquePass();
|
||||
Status Process(ir::IRContext* c) override;
|
||||
|
@ -58,25 +58,26 @@ uint32_t InlinePass::AddPointerToType(uint32_t type_id,
|
||||
}
|
||||
|
||||
void InlinePass::AddBranch(uint32_t label_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
|
||||
SpvOpBranch, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
|
||||
(*block_ptr)->AddInstruction(std::move(newBranch));
|
||||
}
|
||||
|
||||
void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id,
|
||||
uint32_t false_id, std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
uint32_t false_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newBranch(new ir::Instruction(
|
||||
SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
|
||||
SpvOpBranchConditional, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
|
||||
(*block_ptr)->AddInstruction(std::move(newBranch));
|
||||
}
|
||||
|
||||
void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newLoopMerge(new ir::Instruction(
|
||||
SpvOpLoopMerge, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
|
||||
@ -88,8 +89,9 @@ void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
std::unique_ptr<ir::Instruction> newStore(new ir::Instruction(
|
||||
SpvOpStore, 0, 0, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
|
||||
SpvOpStore, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
|
||||
(*block_ptr)->AddInstruction(std::move(newStore));
|
||||
}
|
||||
|
||||
@ -108,11 +110,9 @@ std::unique_ptr<ir::Instruction> InlinePass::NewLabel(uint32_t label_id) {
|
||||
}
|
||||
|
||||
uint32_t InlinePass::GetFalseId() {
|
||||
if (false_id_ != 0)
|
||||
return false_id_;
|
||||
if (false_id_ != 0) return false_id_;
|
||||
false_id_ = get_module()->GetGlobalValue(SpvOpConstantFalse);
|
||||
if (false_id_ != 0)
|
||||
return false_id_;
|
||||
if (false_id_ != 0) return false_id_;
|
||||
uint32_t boolId = get_module()->GetGlobalValue(SpvOpTypeBool);
|
||||
if (boolId == 0) {
|
||||
boolId = TakeNextId();
|
||||
@ -124,8 +124,7 @@ uint32_t InlinePass::GetFalseId() {
|
||||
}
|
||||
|
||||
void InlinePass::MapParams(
|
||||
ir::Function* calleeFn,
|
||||
ir::BasicBlock::iterator call_inst_itr,
|
||||
ir::Function* calleeFn, ir::BasicBlock::iterator call_inst_itr,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller) {
|
||||
int param_idx = 0;
|
||||
calleeFn->ForEachParam(
|
||||
@ -186,8 +185,8 @@ void InlinePass::CloneSameBlockOps(
|
||||
std::unordered_map<uint32_t, uint32_t>* postCallSB,
|
||||
std::unordered_map<uint32_t, ir::Instruction*>* preCallSB,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr) {
|
||||
(*inst)
|
||||
->ForEachInId([&postCallSB, &preCallSB, &block_ptr, this](uint32_t* iid) {
|
||||
(*inst)->ForEachInId(
|
||||
[&postCallSB, &preCallSB, &block_ptr, this](uint32_t* iid) {
|
||||
const auto mapItr = (*postCallSB).find(*iid);
|
||||
if (mapItr == (*postCallSB).end()) {
|
||||
const auto mapItr2 = (*preCallSB).find(*iid);
|
||||
@ -242,11 +241,9 @@ void InlinePass::GenInlineCode(
|
||||
|
||||
// Create set of callee result ids. Used to detect forward references
|
||||
std::unordered_set<uint32_t> callee_result_ids;
|
||||
calleeFn->ForEachInst([&callee_result_ids](
|
||||
const ir::Instruction* cpi) {
|
||||
calleeFn->ForEachInst([&callee_result_ids](const ir::Instruction* cpi) {
|
||||
const uint32_t rid = cpi->result_id();
|
||||
if (rid != 0)
|
||||
callee_result_ids.insert(rid);
|
||||
if (rid != 0) callee_result_ids.insert(rid);
|
||||
});
|
||||
|
||||
// If the caller is in a single-block loop, and the callee has multiple
|
||||
@ -333,8 +330,7 @@ void InlinePass::GenInlineCode(
|
||||
}
|
||||
new_blk_ptr->AddInstruction(std::move(cp_inst));
|
||||
}
|
||||
if (caller_is_loop_header &&
|
||||
callee_begins_with_structured_header) {
|
||||
if (caller_is_loop_header && callee_begins_with_structured_header) {
|
||||
// We can't place both the caller's merge instruction and another
|
||||
// merge instruction in the same block. So split the calling block.
|
||||
// Insert an unconditional branch to a new guard block. Later,
|
||||
@ -366,8 +362,8 @@ void InlinePass::GenInlineCode(
|
||||
singleTripLoopHeaderId = this->TakeNextId();
|
||||
AddBranch(singleTripLoopHeaderId, &new_blk_ptr);
|
||||
new_blocks->push_back(std::move(new_blk_ptr));
|
||||
new_blk_ptr.reset(new ir::BasicBlock(NewLabel(
|
||||
singleTripLoopHeaderId)));
|
||||
new_blk_ptr.reset(
|
||||
new ir::BasicBlock(NewLabel(singleTripLoopHeaderId)));
|
||||
returnLabelId = this->TakeNextId();
|
||||
singleTripLoopContinueId = this->TakeNextId();
|
||||
AddLoopMerge(returnLabelId, singleTripLoopContinueId, &new_blk_ptr);
|
||||
@ -475,8 +471,7 @@ void InlinePass::GenInlineCode(
|
||||
uint32_t nid;
|
||||
if (mapItr != callee2caller.end()) {
|
||||
nid = mapItr->second;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
nid = this->TakeNextId();
|
||||
callee2caller[rid] = nid;
|
||||
}
|
||||
@ -530,15 +525,14 @@ void InlinePass::UpdateSucceedingPhis(
|
||||
const auto lastBlk = new_blocks.end() - 1;
|
||||
const uint32_t firstId = (*firstBlk)->id();
|
||||
const uint32_t lastId = (*lastBlk)->id();
|
||||
(*lastBlk)->ForEachSuccessorLabel(
|
||||
[&firstId, &lastId, this](uint32_t succ) {
|
||||
ir::BasicBlock* sbp = this->id2block_[succ];
|
||||
sbp->ForEachPhiInst([&firstId, &lastId](ir::Instruction* phi) {
|
||||
phi->ForEachInId([&firstId, &lastId](uint32_t* id) {
|
||||
if (*id == firstId) *id = lastId;
|
||||
});
|
||||
});
|
||||
(*lastBlk)->ForEachSuccessorLabel([&firstId, &lastId, this](uint32_t succ) {
|
||||
ir::BasicBlock* sbp = this->id2block_[succ];
|
||||
sbp->ForEachPhiInst([&firstId, &lastId](ir::Instruction* phi) {
|
||||
phi->ForEachInId([&firstId, &lastId](uint32_t* id) {
|
||||
if (*id == firstId) *id = lastId;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
@ -547,7 +541,7 @@ bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
for (auto& blk : *func) {
|
||||
auto terminal_ii = blk.cend();
|
||||
--terminal_ii;
|
||||
if (terminal_ii->opcode() == SpvOpReturn ||
|
||||
if (terminal_ii->opcode() == SpvOpReturn ||
|
||||
terminal_ii->opcode() == SpvOpReturnValue) {
|
||||
if (seenReturn) {
|
||||
multipleReturns = true;
|
||||
@ -559,7 +553,6 @@ bool InlinePass::HasMultipleReturns(ir::Function* func) {
|
||||
return multipleReturns;
|
||||
}
|
||||
|
||||
|
||||
void InlinePass::ComputeStructuredSuccessors(ir::Function* func) {
|
||||
// If header, make merge block first successor.
|
||||
for (auto& blk : *func) {
|
||||
@ -584,8 +577,7 @@ InlinePass::GetBlocksFunction InlinePass::StructuredSuccessorsFunction() {
|
||||
bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
|
||||
// If control not structured, do not do loop/return analysis
|
||||
// TODO: Analyze returns in non-structured control flow
|
||||
if (!get_module()->HasCapability(SpvCapabilityShader))
|
||||
return false;
|
||||
if (!get_module()->HasCapability(SpvCapabilityShader)) return false;
|
||||
// Compute structured block order. This order has the property
|
||||
// that dominators are before all blocks they dominate and merge blocks
|
||||
// are after all blocks that are in the control constructs of their header.
|
||||
@ -594,32 +586,30 @@ bool InlinePass::HasNoReturnInLoop(ir::Function* func) {
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
std::list<const ir::BasicBlock*> structuredOrder;
|
||||
spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
&*func->begin(), StructuredSuccessorsFunction(), ignore_block,
|
||||
[&](cbb_ptr b) { structuredOrder.push_front(b); }, ignore_edge);
|
||||
&*func->begin(), StructuredSuccessorsFunction(), ignore_block,
|
||||
[&](cbb_ptr b) { structuredOrder.push_front(b); }, ignore_edge);
|
||||
// Search for returns in loops. Only need to track outermost loop
|
||||
bool return_in_loop = false;
|
||||
uint32_t outerLoopMergeId = 0;
|
||||
for (auto& blk : structuredOrder) {
|
||||
// Exiting current outer loop
|
||||
if (blk->id() == outerLoopMergeId)
|
||||
outerLoopMergeId = 0;
|
||||
if (blk->id() == outerLoopMergeId) outerLoopMergeId = 0;
|
||||
// Return block
|
||||
auto terminal_ii = blk->cend();
|
||||
--terminal_ii;
|
||||
if (terminal_ii->opcode() == SpvOpReturn ||
|
||||
if (terminal_ii->opcode() == SpvOpReturn ||
|
||||
terminal_ii->opcode() == SpvOpReturnValue) {
|
||||
if (outerLoopMergeId != 0) {
|
||||
return_in_loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (terminal_ii != blk->cbegin()) {
|
||||
} else if (terminal_ii != blk->cbegin()) {
|
||||
auto merge_ii = terminal_ii;
|
||||
--merge_ii;
|
||||
// Entering outermost loop
|
||||
if (merge_ii->opcode() == SpvOpLoopMerge && outerLoopMergeId == 0)
|
||||
outerLoopMergeId = merge_ii->GetSingleWordOperand(
|
||||
kSpvLoopMergeMergeBlockId);
|
||||
outerLoopMergeId =
|
||||
merge_ii->GetSingleWordOperand(kSpvLoopMergeMergeBlockId);
|
||||
}
|
||||
}
|
||||
return !return_in_loop;
|
||||
@ -633,14 +623,12 @@ void InlinePass::AnalyzeReturns(ir::Function* func) {
|
||||
}
|
||||
multi_return_funcs_.insert(func->result_id());
|
||||
// If multiple returns, see if any are in a loop
|
||||
if (HasNoReturnInLoop(func))
|
||||
no_return_in_loop_.insert(func->result_id());
|
||||
if (HasNoReturnInLoop(func)) no_return_in_loop_.insert(func->result_id());
|
||||
}
|
||||
|
||||
bool InlinePass::IsInlinableFunction(ir::Function* func) {
|
||||
// We can only inline a function if it has blocks.
|
||||
if (func->cbegin() == func->cend())
|
||||
return false;
|
||||
if (func->cbegin() == func->cend()) return false;
|
||||
// Do not inline functions with returns in loops. Currently early return
|
||||
// functions are inlined by wrapping them in a one trip loop and implementing
|
||||
// the returns as a branch to the loop's merge block. However, this can only
|
||||
@ -671,8 +659,7 @@ void InlinePass::InitializeInline(ir::IRContext* c) {
|
||||
id2block_[blk.id()] = &blk;
|
||||
}
|
||||
// Compute inlinability
|
||||
if (IsInlinableFunction(&fn))
|
||||
inlinable_.insert(fn.result_id());
|
||||
if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -18,10 +18,10 @@
|
||||
#define LIBSPIRV_OPT_INLINE_PASS_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
@ -32,12 +32,11 @@ namespace opt {
|
||||
|
||||
// See optimizer.hpp for documentation.
|
||||
class InlinePass : public Pass {
|
||||
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
InlinePass();
|
||||
virtual ~InlinePass() = default;
|
||||
@ -54,8 +53,8 @@ class InlinePass : public Pass {
|
||||
void AddBranch(uint32_t labelId, std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
|
||||
// Add conditional branch to end of block |block_ptr|.
|
||||
void AddBranchCond(uint32_t cond_id, uint32_t true_id,
|
||||
uint32_t false_id, std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id,
|
||||
std::unique_ptr<ir::BasicBlock>* block_ptr);
|
||||
|
||||
// Add unconditional branch to labelId to end of block block_ptr.
|
||||
void AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
|
||||
@ -77,8 +76,7 @@ class InlinePass : public Pass {
|
||||
uint32_t GetFalseId();
|
||||
|
||||
// Map callee params to caller args
|
||||
void MapParams(ir::Function* calleeFn,
|
||||
ir::BasicBlock::iterator call_inst_itr,
|
||||
void MapParams(ir::Function* calleeFn, ir::BasicBlock::iterator call_inst_itr,
|
||||
std::unordered_map<uint32_t, uint32_t>* callee2caller);
|
||||
|
||||
// Clone and map callee locals
|
||||
@ -133,11 +131,11 @@ class InlinePass : public Pass {
|
||||
// A block's structured successors are the blocks it branches to
|
||||
// together with its declared merge block if it has one.
|
||||
// When order matters, the merge block always appears first.
|
||||
// This assures correct depth first search in the presence of early
|
||||
// This assures correct depth first search in the presence of early
|
||||
// returns and kills. If the successor vector contain duplicates
|
||||
// if the merge block, they are safely ignored by DFS.
|
||||
void ComputeStructuredSuccessors(ir::Function* func);
|
||||
|
||||
|
||||
// Return function to return ordered structure successors for a given block
|
||||
// Assumes ComputeStructuredSuccessors() has been called.
|
||||
GetBlocksFunction StructuredSuccessorsFunction();
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
#include "insert_extract_elim.h"
|
||||
|
||||
#include "iterator.h"
|
||||
#include "ir_context.h"
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -28,12 +28,11 @@ const uint32_t kExtractCompositeIdInIdx = 0;
|
||||
const uint32_t kInsertObjectIdInIdx = 0;
|
||||
const uint32_t kInsertCompositeIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool InsertExtractElimPass::ExtInsMatch(const ir::Instruction* extInst,
|
||||
const ir::Instruction* insInst) const {
|
||||
if (extInst->NumInOperands() != insInst->NumInOperands() - 1)
|
||||
return false;
|
||||
const ir::Instruction* insInst) const {
|
||||
if (extInst->NumInOperands() != insInst->NumInOperands() - 1) return false;
|
||||
uint32_t numIdx = extInst->NumInOperands() - 1;
|
||||
for (uint32_t i = 0; i < numIdx; ++i)
|
||||
if (extInst->GetSingleWordInOperand(i + 1) !=
|
||||
@ -42,10 +41,9 @@ bool InsertExtractElimPass::ExtInsMatch(const ir::Instruction* extInst,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InsertExtractElimPass::ExtInsConflict(const ir::Instruction* extInst,
|
||||
const ir::Instruction* insInst) const {
|
||||
if (extInst->NumInOperands() == insInst->NumInOperands() - 1)
|
||||
return false;
|
||||
bool InsertExtractElimPass::ExtInsConflict(
|
||||
const ir::Instruction* extInst, const ir::Instruction* insInst) const {
|
||||
if (extInst->NumInOperands() == insInst->NumInOperands() - 1) return false;
|
||||
uint32_t extNumIdx = extInst->NumInOperands() - 1;
|
||||
uint32_t insNumIdx = insInst->NumInOperands() - 2;
|
||||
uint32_t numIdx = std::min(extNumIdx, insNumIdx);
|
||||
@ -71,8 +69,7 @@ bool InsertExtractElimPass::EliminateInsertExtract(ir::Function* func) {
|
||||
ir::Instruction* cinst = get_def_use_mgr()->GetDef(cid);
|
||||
uint32_t replId = 0;
|
||||
while (cinst->opcode() == SpvOpCompositeInsert) {
|
||||
if (ExtInsConflict(&*ii, cinst))
|
||||
break;
|
||||
if (ExtInsConflict(&*ii, cinst)) break;
|
||||
if (ExtInsMatch(&*ii, cinst)) {
|
||||
replId = cinst->GetSingleWordInOperand(kInsertObjectIdInIdx);
|
||||
break;
|
||||
@ -96,14 +93,12 @@ bool InsertExtractElimPass::EliminateInsertExtract(ir::Function* func) {
|
||||
for (; i <= compIdx; i++) {
|
||||
uint32_t compId = cinst->GetSingleWordInOperand(i);
|
||||
ir::Instruction* compInst = get_def_use_mgr()->GetDef(compId);
|
||||
if (compInst->type_id() != (*ii).type_id())
|
||||
break;
|
||||
if (compInst->type_id() != (*ii).type_id()) break;
|
||||
}
|
||||
if (i > compIdx)
|
||||
replId = cinst->GetSingleWordInOperand(compIdx);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
replId = cinst->GetSingleWordInOperand(compIdx);
|
||||
}
|
||||
}
|
||||
@ -132,8 +127,8 @@ void InsertExtractElimPass::Initialize(ir::IRContext* c) {
|
||||
bool InsertExtractElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -142,8 +137,7 @@ bool InsertExtractElimPass::AllExtensionsSupported() const {
|
||||
|
||||
Pass::Status InsertExtractElimPass::ProcessImpl() {
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return EliminateInsertExtract(fp);
|
||||
@ -162,31 +156,30 @@ Pass::Status InsertExtractElimPass::Process(ir::IRContext* c) {
|
||||
void InsertExtractElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
"SPV_KHR_variable_pointers",
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
#ifndef LIBSPIRV_OPT_INSERT_EXTRACT_ELIM_PASS_H_
|
||||
#define LIBSPIRV_OPT_INSERT_EXTRACT_ELIM_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
@ -26,9 +25,9 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -42,16 +41,16 @@ class InsertExtractElimPass : public Pass {
|
||||
|
||||
private:
|
||||
// Return true if indices of extract |extInst| and insert |insInst| match
|
||||
bool ExtInsMatch(
|
||||
const ir::Instruction* extInst, const ir::Instruction* insInst) const;
|
||||
bool ExtInsMatch(const ir::Instruction* extInst,
|
||||
const ir::Instruction* insInst) const;
|
||||
|
||||
// Return true if indices of extract |extInst| and insert |insInst| conflict,
|
||||
// specifically, if the insert changes bits specified by the extract, but
|
||||
// changes either more bits or less bits than the extract specifies,
|
||||
// meaning the exact value being inserted cannot be used to replace
|
||||
// the extract.
|
||||
bool ExtInsConflict(
|
||||
const ir::Instruction* extInst, const ir::Instruction* insInst) const;
|
||||
bool ExtInsConflict(const ir::Instruction* extInst,
|
||||
const ir::Instruction* insInst) const;
|
||||
|
||||
// Return true if |typeId| is a vector type
|
||||
bool IsVectorType(uint32_t typeId);
|
||||
@ -79,4 +78,3 @@ class InsertExtractElimPass : public Pass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_INSERT_EXTRACT_ELIM_PASS_H_
|
||||
|
||||
|
@ -219,8 +219,8 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
|
||||
// Runs the given function |f| on all "in" operands
|
||||
inline void ForEachInOperand(const std::function<void(uint32_t*)>& f);
|
||||
inline void ForEachInOperand(const std::function<void(const uint32_t*)>& f)
|
||||
const;
|
||||
inline void ForEachInOperand(
|
||||
const std::function<void(const uint32_t*)>& f) const;
|
||||
|
||||
// Returns true if any operands can be labels
|
||||
inline bool HasLabels() const;
|
||||
@ -308,9 +308,9 @@ inline void Instruction::ForEachInst(
|
||||
inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_)
|
||||
if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
|
||||
if (type_id_ != 0u)
|
||||
type_id_ = GetSingleWordOperand(0u);
|
||||
if (result_id_ != 0u) result_id_ = GetSingleWordOperand(type_id_ == 0u ? 0u : 1u);
|
||||
if (type_id_ != 0u) type_id_ = GetSingleWordOperand(0u);
|
||||
if (result_id_ != 0u)
|
||||
result_id_ = GetSingleWordOperand(type_id_ == 0u ? 0u : 1u);
|
||||
}
|
||||
|
||||
inline void Instruction::ForEachId(
|
||||
@ -347,7 +347,7 @@ inline void Instruction::ForEachInId(
|
||||
}
|
||||
|
||||
inline void Instruction::ForEachInOperand(
|
||||
const std::function<void(uint32_t*)>& f) {
|
||||
const std::function<void(uint32_t*)>& f) {
|
||||
for (auto& opnd : operands_) {
|
||||
switch (opnd.type) {
|
||||
case SPV_OPERAND_TYPE_RESULT_ID:
|
||||
|
@ -17,7 +17,6 @@
|
||||
namespace spvtools {
|
||||
namespace ir {
|
||||
|
||||
|
||||
InstructionList::iterator InstructionList::iterator::InsertBefore(
|
||||
std::vector<std::unique_ptr<Instruction>>&& list) {
|
||||
Instruction* first_node = list.front().get();
|
||||
|
@ -24,7 +24,8 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
|
||||
}
|
||||
}
|
||||
|
||||
void IRContext::InvalidateAnalysesExceptFor(IRContext::Analysis preserved_analyses) {
|
||||
void IRContext::InvalidateAnalysesExceptFor(
|
||||
IRContext::Analysis preserved_analyses) {
|
||||
uint32_t analyses_to_invalidate = valid_analyses_ & (~preserved_analyses);
|
||||
if (analyses_to_invalidate & kAnalysisDefUse) {
|
||||
def_use_mgr_.reset(nullptr);
|
||||
@ -75,7 +76,7 @@ bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
||||
} else if (use.inst->type_id() == 0) {
|
||||
SPIRV_ASSERT(consumer_, false,
|
||||
"Result type id considered as use while the instruction "
|
||||
"doesn't have a result type id.");
|
||||
"doesn't have a result type id.");
|
||||
(void)consumer_; // Makes the compiler happy for release build.
|
||||
} else {
|
||||
SPIRV_ASSERT(consumer_, false,
|
||||
|
@ -123,21 +123,23 @@ class IteratorRange {
|
||||
|
||||
// Returns a (begin, end) iterator pair for the given iterators.
|
||||
// The iterators must belong to the same container.
|
||||
template<typename IteratorType>
|
||||
inline IteratorRange<IteratorType> make_range(IteratorType& begin, IteratorType& end) {
|
||||
template <typename IteratorType>
|
||||
inline IteratorRange<IteratorType> make_range(IteratorType& begin,
|
||||
IteratorType& end) {
|
||||
return {begin, end};
|
||||
}
|
||||
|
||||
// Returns a (begin, end) iterator pair for the given iterators.
|
||||
// The iterators must belong to the same container.
|
||||
template<typename IteratorType>
|
||||
inline IteratorRange<IteratorType> make_range(IteratorType&& begin, IteratorType&& end) {
|
||||
template <typename IteratorType>
|
||||
inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
|
||||
IteratorType&& end) {
|
||||
return {begin, end};
|
||||
}
|
||||
|
||||
// Returns a (begin, end) iterator pair for the given container.
|
||||
template<typename ValueType,
|
||||
class IteratorType = UptrVectorIterator<ValueType>>
|
||||
template <typename ValueType,
|
||||
class IteratorType = UptrVectorIterator<ValueType>>
|
||||
inline IteratorRange<IteratorType> make_range(
|
||||
std::vector<std::unique_ptr<ValueType>>& container) {
|
||||
return {IteratorType(&container, container.begin()),
|
||||
|
@ -28,7 +28,7 @@ const uint32_t kAccessChainPtrIdInIdx = 0;
|
||||
const uint32_t kConstantValueInIdx = 0;
|
||||
const uint32_t kTypeIntWidthInIdx = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
void LocalAccessChainConvertPass::DeleteIfUseless(ir::Instruction* inst) {
|
||||
const uint32_t resId = inst->result_id();
|
||||
@ -40,21 +40,17 @@ void LocalAccessChainConvertPass::DeleteIfUseless(ir::Instruction* inst) {
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::BuildAndAppendInst(
|
||||
SpvOp opcode,
|
||||
uint32_t typeId,
|
||||
uint32_t resultId,
|
||||
SpvOp opcode, uint32_t typeId, uint32_t resultId,
|
||||
const std::vector<ir::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
std::unique_ptr<ir::Instruction> newInst(new ir::Instruction(
|
||||
opcode, typeId, resultId, in_opnds));
|
||||
std::unique_ptr<ir::Instruction> newInst(
|
||||
new ir::Instruction(opcode, typeId, resultId, in_opnds));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*newInst);
|
||||
newInsts->emplace_back(std::move(newInst));
|
||||
}
|
||||
|
||||
uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad(
|
||||
const ir::Instruction* ptrInst,
|
||||
uint32_t* varId,
|
||||
uint32_t* varPteTypeId,
|
||||
const ir::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
const uint32_t ldResultId = TakeNextId();
|
||||
*varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx);
|
||||
@ -62,20 +58,20 @@ uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad(
|
||||
assert(varInst->opcode() == SpvOpVariable);
|
||||
*varPteTypeId = GetPointeeTypeId(varInst);
|
||||
BuildAndAppendInst(SpvOpLoad, *varPteTypeId, ldResultId,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}}, newInsts);
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}},
|
||||
newInsts);
|
||||
return ldResultId;
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<ir::Operand>* in_opnds) {
|
||||
const ir::Instruction* ptrInst, std::vector<ir::Operand>* in_opnds) {
|
||||
uint32_t iidIdx = 0;
|
||||
ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t *iid) {
|
||||
ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) {
|
||||
if (iidIdx > 0) {
|
||||
const ir::Instruction* cInst = get_def_use_mgr()->GetDef(*iid);
|
||||
uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx);
|
||||
in_opnds->push_back(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}});
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}});
|
||||
}
|
||||
++iidIdx;
|
||||
});
|
||||
@ -84,12 +80,11 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
|
||||
uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
|
||||
// Build and append load of variable in ptrInst
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
const uint32_t ldResultId = BuildAndAppendVarLoad(ptrInst, &varId,
|
||||
&varPteTypeId, newInsts);
|
||||
const uint32_t ldResultId =
|
||||
BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts);
|
||||
|
||||
// Build and append Extract
|
||||
const uint32_t extResultId = TakeNextId();
|
||||
@ -103,30 +98,28 @@ uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement(
|
||||
}
|
||||
|
||||
void LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
|
||||
const ir::Instruction* ptrInst,
|
||||
uint32_t valId,
|
||||
const ir::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts) {
|
||||
|
||||
// Build and append load of variable in ptrInst
|
||||
uint32_t varId;
|
||||
uint32_t varPteTypeId;
|
||||
const uint32_t ldResultId = BuildAndAppendVarLoad(ptrInst, &varId,
|
||||
&varPteTypeId, newInsts);
|
||||
const uint32_t ldResultId =
|
||||
BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts);
|
||||
|
||||
// Build and append Insert
|
||||
const uint32_t insResultId = TakeNextId();
|
||||
std::vector<ir::Operand> ins_in_opnds =
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
|
||||
std::vector<ir::Operand> ins_in_opnds = {
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}};
|
||||
AppendConstantOperands(ptrInst, &ins_in_opnds);
|
||||
BuildAndAppendInst(
|
||||
SpvOpCompositeInsert, varPteTypeId, insResultId, ins_in_opnds, newInsts);
|
||||
BuildAndAppendInst(SpvOpCompositeInsert, varPteTypeId, insResultId,
|
||||
ins_in_opnds, newInsts);
|
||||
|
||||
// Build and append Store
|
||||
BuildAndAppendInst(SpvOpStore, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}},
|
||||
newInsts);
|
||||
BuildAndAppendInst(SpvOpStore, 0, 0,
|
||||
{{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}},
|
||||
newInsts);
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
|
||||
@ -144,8 +137,7 @@ bool LocalAccessChainConvertPass::IsConstantIndexAccessChain(
|
||||
}
|
||||
|
||||
bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end())
|
||||
return true;
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
|
||||
assert(uses != nullptr);
|
||||
for (auto u : *uses) {
|
||||
@ -164,36 +156,36 @@ void LocalAccessChainConvertPass::FindTargetVars(ir::Function* func) {
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpStore:
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId))
|
||||
case SpvOpStore:
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) break;
|
||||
const SpvOp op = ptrInst->opcode();
|
||||
// Rule out variables with non-supported refs eg function calls
|
||||
if (!HasOnlySupportedRefs(varId)) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
// Rule out variables with nested access chains
|
||||
// TODO(): Convert nested access chains
|
||||
if (IsNonPtrAccessChain(op) &&
|
||||
ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) !=
|
||||
varId) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
// Rule out variables accessed with non-constant indices
|
||||
if (!IsConstantIndexAccessChain(ptrInst)) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
const SpvOp op = ptrInst->opcode();
|
||||
// Rule out variables with non-supported refs eg function calls
|
||||
if (!HasOnlySupportedRefs(varId)) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
// Rule out variables with nested access chains
|
||||
// TODO(): Convert nested access chains
|
||||
if (IsNonPtrAccessChain(op) &&
|
||||
ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx) != varId) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
// Rule out variables accessed with non-constant indices
|
||||
if (!IsConstantIndexAccessChain(ptrInst)) {
|
||||
seen_non_target_vars_.insert(varId);
|
||||
seen_target_vars_.erase(varId);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,42 +199,37 @@ bool LocalAccessChainConvertPass::ConvertLocalAccessChains(ir::Function* func) {
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode()))
|
||||
case SpvOpLoad: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
uint32_t replId = GenAccessChainLoadReplacement(ptrInst, &newInsts);
|
||||
ReplaceAndDeleteLoad(&*ii, replId);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
++ii;
|
||||
modified = true;
|
||||
} break;
|
||||
case SpvOpStore: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode())) break;
|
||||
if (!IsTargetVar(varId)) break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
uint32_t valId = ii->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
GenAccessChainStoreReplacement(ptrInst, valId, &newInsts);
|
||||
context()->KillInst(&*ii);
|
||||
DeleteIfUseless(ptrInst);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
++ii;
|
||||
++ii;
|
||||
modified = true;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
if (!IsTargetVar(varId))
|
||||
break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
uint32_t replId =
|
||||
GenAccessChainLoadReplacement(ptrInst, &newInsts);
|
||||
ReplaceAndDeleteLoad(&*ii, replId);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
++ii;
|
||||
modified = true;
|
||||
} break;
|
||||
case SpvOpStore: {
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsNonPtrAccessChain(ptrInst->opcode()))
|
||||
break;
|
||||
if (!IsTargetVar(varId))
|
||||
break;
|
||||
std::vector<std::unique_ptr<ir::Instruction>> newInsts;
|
||||
uint32_t valId = ii->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
GenAccessChainStoreReplacement(ptrInst, valId, &newInsts);
|
||||
context()->KillInst(&*ii);
|
||||
DeleteIfUseless(ptrInst);
|
||||
++ii;
|
||||
ii = ii.InsertBefore(std::move(newInsts));
|
||||
++ii;
|
||||
++ii;
|
||||
modified = true;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,8 +253,8 @@ void LocalAccessChainConvertPass::Initialize(ir::IRContext* c) {
|
||||
bool LocalAccessChainConvertPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -285,11 +272,9 @@ Pass::Status LocalAccessChainConvertPass::ProcessImpl() {
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return ConvertLocalAccessChains(fp);
|
||||
@ -308,32 +293,22 @@ Pass::Status LocalAccessChainConvertPass::Process(ir::IRContext* c) {
|
||||
void LocalAccessChainConvertPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
#ifndef LIBSPIRV_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_
|
||||
#define LIBSPIRV_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
@ -27,8 +26,8 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -59,38 +58,40 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
|
||||
// Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|.
|
||||
// Append to |newInsts|.
|
||||
void BuildAndAppendInst(SpvOp opcode, uint32_t typeId, uint32_t resultId,
|
||||
const std::vector<ir::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
void BuildAndAppendInst(
|
||||
SpvOp opcode, uint32_t typeId, uint32_t resultId,
|
||||
const std::vector<ir::Operand>& in_opnds,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
|
||||
// Build load of variable in |ptrInst| and append to |newInsts|.
|
||||
// Return var in |varId| and its pointee type in |varPteTypeId|.
|
||||
uint32_t BuildAndAppendVarLoad(const ir::Instruction* ptrInst,
|
||||
uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
uint32_t BuildAndAppendVarLoad(
|
||||
const ir::Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
|
||||
// Append literal integer operands to |in_opnds| corresponding to constant
|
||||
// integer operands from access chain |ptrInst|. Assumes all indices in
|
||||
// access chains are OpConstant.
|
||||
void AppendConstantOperands( const ir::Instruction* ptrInst,
|
||||
std::vector<ir::Operand>* in_opnds);
|
||||
void AppendConstantOperands(const ir::Instruction* ptrInst,
|
||||
std::vector<ir::Operand>* in_opnds);
|
||||
|
||||
// Create a load/insert/store equivalent to a store of
|
||||
// |valId| through (constant index) access chaing |ptrInst|.
|
||||
// Append to |newInsts|.
|
||||
void GenAccessChainStoreReplacement(const ir::Instruction* ptrInst,
|
||||
uint32_t valId,
|
||||
void GenAccessChainStoreReplacement(
|
||||
const ir::Instruction* ptrInst, uint32_t valId,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
|
||||
// For the (constant index) access chain |ptrInst|, create an
|
||||
// equivalent load and extract. Append to |newInsts|.
|
||||
uint32_t GenAccessChainLoadReplacement(const ir::Instruction* ptrInst,
|
||||
uint32_t GenAccessChainLoadReplacement(
|
||||
const ir::Instruction* ptrInst,
|
||||
std::vector<std::unique_ptr<ir::Instruction>>* newInsts);
|
||||
|
||||
// Return true if all indices of access chain |acp| are OpConstant integers
|
||||
bool IsConstantIndexAccessChain(const ir::Instruction* acp) const;
|
||||
|
||||
// Identify all function scope variables of target type which are
|
||||
// Identify all function scope variables of target type which are
|
||||
// accessed only with loads, stores and access chains with constant
|
||||
// indices. Convert all loads and stores of such variables into equivalent
|
||||
// loads, stores, extracts and inserts. This unifies access to these
|
||||
@ -122,4 +123,3 @@ class LocalAccessChainConvertPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_
|
||||
|
||||
|
@ -25,11 +25,10 @@ namespace {
|
||||
|
||||
const uint32_t kStoreValIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end())
|
||||
return true;
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
|
||||
assert(uses != nullptr);
|
||||
for (auto u : *uses) {
|
||||
@ -54,83 +53,74 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
pinned_vars_.clear();
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpStore: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId))
|
||||
continue;
|
||||
if (!HasOnlySupportedRefs(varId))
|
||||
continue;
|
||||
// Register the store
|
||||
if (ptrInst->opcode() == SpvOpVariable) {
|
||||
// if not pinned, look for WAW
|
||||
if (pinned_vars_.find(varId) == pinned_vars_.end()) {
|
||||
case SpvOpStore: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) continue;
|
||||
if (!HasOnlySupportedRefs(varId)) continue;
|
||||
// Register the store
|
||||
if (ptrInst->opcode() == SpvOpVariable) {
|
||||
// if not pinned, look for WAW
|
||||
if (pinned_vars_.find(varId) == pinned_vars_.end()) {
|
||||
auto si = var2store_.find(varId);
|
||||
if (si != var2store_.end()) {
|
||||
context()->KillInst(si->second);
|
||||
}
|
||||
}
|
||||
var2store_[varId] = &*ii;
|
||||
} else {
|
||||
assert(IsNonPtrAccessChain(ptrInst->opcode()));
|
||||
var2store_.erase(varId);
|
||||
}
|
||||
pinned_vars_.erase(varId);
|
||||
var2load_.erase(varId);
|
||||
} break;
|
||||
case SpvOpLoad: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) continue;
|
||||
if (!HasOnlySupportedRefs(varId)) continue;
|
||||
// Look for previous store or load
|
||||
uint32_t replId = 0;
|
||||
if (ptrInst->opcode() == SpvOpVariable) {
|
||||
auto si = var2store_.find(varId);
|
||||
if (si != var2store_.end()) {
|
||||
context()->KillInst(si->second);
|
||||
replId = si->second->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
} else {
|
||||
auto li = var2load_.find(varId);
|
||||
if (li != var2load_.end()) {
|
||||
replId = li->second->result_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
var2store_[varId] = &*ii;
|
||||
}
|
||||
else {
|
||||
assert(IsNonPtrAccessChain(ptrInst->opcode()));
|
||||
var2store_.erase(varId);
|
||||
}
|
||||
pinned_vars_.erase(varId);
|
||||
var2load_.erase(varId);
|
||||
} break;
|
||||
case SpvOpLoad: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId))
|
||||
continue;
|
||||
if (!HasOnlySupportedRefs(varId))
|
||||
continue;
|
||||
// Look for previous store or load
|
||||
uint32_t replId = 0;
|
||||
if (ptrInst->opcode() == SpvOpVariable) {
|
||||
auto si = var2store_.find(varId);
|
||||
if (si != var2store_.end()) {
|
||||
replId = si->second->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
if (replId != 0) {
|
||||
// replace load's result id and delete load
|
||||
ReplaceAndDeleteLoad(&*ii, replId);
|
||||
modified = true;
|
||||
} else {
|
||||
if (ptrInst->opcode() == SpvOpVariable)
|
||||
var2load_[varId] = &*ii; // register load
|
||||
pinned_vars_.insert(varId);
|
||||
}
|
||||
else {
|
||||
auto li = var2load_.find(varId);
|
||||
if (li != var2load_.end()) {
|
||||
replId = li->second->result_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (replId != 0) {
|
||||
// replace load's result id and delete load
|
||||
ReplaceAndDeleteLoad(&*ii, replId);
|
||||
modified = true;
|
||||
}
|
||||
else {
|
||||
if (ptrInst->opcode() == SpvOpVariable)
|
||||
var2load_[varId] = &*ii; // register load
|
||||
pinned_vars_.insert(varId);
|
||||
}
|
||||
} break;
|
||||
case SpvOpFunctionCall: {
|
||||
// Conservatively assume all locals are redefined for now.
|
||||
// TODO(): Handle more optimally
|
||||
var2store_.clear();
|
||||
var2load_.clear();
|
||||
pinned_vars_.clear();
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
} break;
|
||||
case SpvOpFunctionCall: {
|
||||
// Conservatively assume all locals are redefined for now.
|
||||
// TODO(): Handle more optimally
|
||||
var2store_.clear();
|
||||
var2load_.clear();
|
||||
pinned_vars_.clear();
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Go back and delete useless stores in block
|
||||
// TODO(greg-lunarg): Consider moving DCE into separate pass
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpStore)
|
||||
continue;
|
||||
if (IsLiveStore(&*ii))
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpStore) continue;
|
||||
if (IsLiveStore(&*ii)) continue;
|
||||
DCEInst(&*ii);
|
||||
}
|
||||
}
|
||||
@ -154,8 +144,8 @@ void LocalSingleBlockLoadStoreElimPass::Initialize(ir::IRContext* c) {
|
||||
bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -170,12 +160,10 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// If any extensions in the module are not explicitly supported,
|
||||
// return unmodified.
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
// return unmodified.
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return LocalSingleBlockLoadStoreElim(fp);
|
||||
@ -194,29 +182,20 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::Process(ir::IRContext* c) {
|
||||
void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,18 +17,17 @@
|
||||
#ifndef LIBSPIRV_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_
|
||||
#define LIBSPIRV_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -98,4 +97,3 @@ class LocalSingleBlockLoadStoreElimPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_
|
||||
|
||||
|
@ -27,11 +27,10 @@ namespace {
|
||||
|
||||
const uint32_t kStoreValIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
bool LocalSingleStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end())
|
||||
return true;
|
||||
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
|
||||
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
|
||||
assert(uses != nullptr);
|
||||
for (auto u : *uses) {
|
||||
@ -55,41 +54,40 @@ void LocalSingleStoreElimPass::SingleStoreAnalyze(ir::Function* func) {
|
||||
uint32_t instIdx = 0;
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii, ++instIdx) {
|
||||
switch (ii->opcode()) {
|
||||
case SpvOpStore: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (non_ssa_vars_.find(varId) != non_ssa_vars_.end())
|
||||
continue;
|
||||
if (ptrInst->opcode() != SpvOpVariable) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
ssa_var2store_.erase(varId);
|
||||
continue;
|
||||
}
|
||||
// Verify target type and function storage class
|
||||
if (!IsTargetVar(varId)) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
continue;
|
||||
}
|
||||
if (!HasOnlySupportedRefs(varId)) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
continue;
|
||||
}
|
||||
// Ignore variables with multiple stores
|
||||
if (ssa_var2store_.find(varId) != ssa_var2store_.end()) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
ssa_var2store_.erase(varId);
|
||||
continue;
|
||||
}
|
||||
// Remember pointer to variable's store and it's
|
||||
// ordinal position in block
|
||||
ssa_var2store_[varId] = &*ii;
|
||||
store2idx_[&*ii] = instIdx;
|
||||
store2blk_[&*ii] = &*bi;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
case SpvOpStore: {
|
||||
// Verify store variable is target type
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
if (non_ssa_vars_.find(varId) != non_ssa_vars_.end()) continue;
|
||||
if (ptrInst->opcode() != SpvOpVariable) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
ssa_var2store_.erase(varId);
|
||||
continue;
|
||||
}
|
||||
// Verify target type and function storage class
|
||||
if (!IsTargetVar(varId)) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
continue;
|
||||
}
|
||||
if (!HasOnlySupportedRefs(varId)) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
continue;
|
||||
}
|
||||
// Ignore variables with multiple stores
|
||||
if (ssa_var2store_.find(varId) != ssa_var2store_.end()) {
|
||||
non_ssa_vars_.insert(varId);
|
||||
ssa_var2store_.erase(varId);
|
||||
continue;
|
||||
}
|
||||
// Remember pointer to variable's store and it's
|
||||
// ordinal position in block
|
||||
ssa_var2store_[varId] = &*ii;
|
||||
store2idx_[&*ii] = instIdx;
|
||||
store2blk_[&*ii] = &*bi;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,8 +96,7 @@ LocalSingleStoreElimPass::GetBlocksFunction
|
||||
LocalSingleStoreElimPass::AugmentedCFGSuccessorsFunction() const {
|
||||
return [this](const ir::BasicBlock* block) {
|
||||
auto asmi = augmented_successors_map_.find(block);
|
||||
if (asmi != augmented_successors_map_.end())
|
||||
return &(*asmi).second;
|
||||
if (asmi != augmented_successors_map_.end()) return &(*asmi).second;
|
||||
auto smi = successors_map_.find(block);
|
||||
return &(*smi).second;
|
||||
};
|
||||
@ -109,8 +106,7 @@ LocalSingleStoreElimPass::GetBlocksFunction
|
||||
LocalSingleStoreElimPass::AugmentedCFGPredecessorsFunction() const {
|
||||
return [this](const ir::BasicBlock* block) {
|
||||
auto apmi = augmented_predecessors_map_.find(block);
|
||||
if (apmi != augmented_predecessors_map_.end())
|
||||
return &(*apmi).second;
|
||||
if (apmi != augmented_predecessors_map_.end()) return &(*apmi).second;
|
||||
auto pmi = predecessors_map_.find(block);
|
||||
return &(*pmi).second;
|
||||
};
|
||||
@ -134,43 +130,36 @@ void LocalSingleStoreElimPass::CalculateImmediateDominators(
|
||||
augmented_predecessors_map_.clear();
|
||||
successors_map_[cfg()->pseudo_exit_block()] = {};
|
||||
predecessors_map_[cfg()->pseudo_entry_block()] = {};
|
||||
auto succ_func = [this](const ir::BasicBlock* b)
|
||||
{ return &successors_map_[b]; };
|
||||
auto pred_func = [this](const ir::BasicBlock* b)
|
||||
{ return &predecessors_map_[b]; };
|
||||
auto succ_func = [this](const ir::BasicBlock* b) {
|
||||
return &successors_map_[b];
|
||||
};
|
||||
auto pred_func = [this](const ir::BasicBlock* b) {
|
||||
return &predecessors_map_[b];
|
||||
};
|
||||
CFA<ir::BasicBlock>::ComputeAugmentedCFG(
|
||||
ordered_blocks,
|
||||
cfg()->pseudo_entry_block(),
|
||||
cfg()->pseudo_exit_block(),
|
||||
&augmented_successors_map_,
|
||||
&augmented_predecessors_map_,
|
||||
succ_func,
|
||||
pred_func);
|
||||
ordered_blocks, cfg()->pseudo_entry_block(), cfg()->pseudo_exit_block(),
|
||||
&augmented_successors_map_, &augmented_predecessors_map_, succ_func,
|
||||
pred_func);
|
||||
// Compute Dominators
|
||||
vector<const ir::BasicBlock*> postorder;
|
||||
auto ignore_block = [](cbb_ptr) {};
|
||||
auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
|
||||
spvtools::CFA<ir::BasicBlock>::DepthFirstTraversal(
|
||||
ordered_blocks[0], AugmentedCFGSuccessorsFunction(),
|
||||
ignore_block, [&](cbb_ptr b) { postorder.push_back(b); },
|
||||
ignore_edge);
|
||||
ordered_blocks[0], AugmentedCFGSuccessorsFunction(), ignore_block,
|
||||
[&](cbb_ptr b) { postorder.push_back(b); }, ignore_edge);
|
||||
auto edges = spvtools::CFA<ir::BasicBlock>::CalculateDominators(
|
||||
postorder, AugmentedCFGPredecessorsFunction());
|
||||
postorder, AugmentedCFGPredecessorsFunction());
|
||||
idom_.clear();
|
||||
for (auto edge : edges)
|
||||
idom_[edge.first] = edge.second;
|
||||
for (auto edge : edges) idom_[edge.first] = edge.second;
|
||||
}
|
||||
|
||||
bool LocalSingleStoreElimPass::Dominates(
|
||||
ir::BasicBlock* blk0, uint32_t idx0,
|
||||
ir::BasicBlock* blk1, uint32_t idx1) {
|
||||
if (blk0 == blk1)
|
||||
return idx0 <= idx1;
|
||||
bool LocalSingleStoreElimPass::Dominates(ir::BasicBlock* blk0, uint32_t idx0,
|
||||
ir::BasicBlock* blk1, uint32_t idx1) {
|
||||
if (blk0 == blk1) return idx0 <= idx1;
|
||||
ir::BasicBlock* b = blk1;
|
||||
while (idom_[b] != b) {
|
||||
b = idom_[b];
|
||||
if (b == blk0)
|
||||
return true;
|
||||
if (b == blk0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -181,20 +170,17 @@ bool LocalSingleStoreElimPass::SingleStoreProcess(ir::Function* func) {
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
uint32_t instIdx = 0;
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii, ++instIdx) {
|
||||
if (ii->opcode() != SpvOpLoad)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpLoad) continue;
|
||||
uint32_t varId;
|
||||
ir::Instruction* ptrInst = GetPtr(&*ii, &varId);
|
||||
// Skip access chain loads
|
||||
if (ptrInst->opcode() != SpvOpVariable)
|
||||
continue;
|
||||
if (ptrInst->opcode() != SpvOpVariable) continue;
|
||||
const auto vsi = ssa_var2store_.find(varId);
|
||||
if (vsi == ssa_var2store_.end())
|
||||
continue;
|
||||
if (non_ssa_vars_.find(varId) != non_ssa_vars_.end())
|
||||
continue;
|
||||
if (vsi == ssa_var2store_.end()) continue;
|
||||
if (non_ssa_vars_.find(varId) != non_ssa_vars_.end()) continue;
|
||||
// store must dominate load
|
||||
if (!Dominates(store2blk_[vsi->second], store2idx_[vsi->second], &*bi, instIdx))
|
||||
if (!Dominates(store2blk_[vsi->second], store2idx_[vsi->second], &*bi,
|
||||
instIdx))
|
||||
continue;
|
||||
// Use store value as replacement id
|
||||
uint32_t replId = vsi->second->GetSingleWordInOperand(kStoreValIdInIdx);
|
||||
@ -210,10 +196,8 @@ bool LocalSingleStoreElimPass::SingleStoreDCE() {
|
||||
bool modified = false;
|
||||
for (auto v : ssa_var2store_) {
|
||||
// check that it hasn't already been DCE'd
|
||||
if (v.second->opcode() != SpvOpStore)
|
||||
continue;
|
||||
if (non_ssa_vars_.find(v.first) != non_ssa_vars_.end())
|
||||
continue;
|
||||
if (v.second->opcode() != SpvOpStore) continue;
|
||||
if (non_ssa_vars_.find(v.first) != non_ssa_vars_.end()) continue;
|
||||
if (!IsLiveStore(v.second)) {
|
||||
DCEInst(v.second);
|
||||
modified = true;
|
||||
@ -225,8 +209,7 @@ bool LocalSingleStoreElimPass::SingleStoreDCE() {
|
||||
bool LocalSingleStoreElimPass::LocalSingleStoreElim(ir::Function* func) {
|
||||
bool modified = false;
|
||||
SingleStoreAnalyze(func);
|
||||
if (ssa_var2store_.empty())
|
||||
return false;
|
||||
if (ssa_var2store_.empty()) return false;
|
||||
modified |= SingleStoreProcess(func);
|
||||
modified |= SingleStoreDCE();
|
||||
return modified;
|
||||
@ -258,8 +241,8 @@ void LocalSingleStoreElimPass::Initialize(ir::IRContext* irContext) {
|
||||
bool LocalSingleStoreElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -274,11 +257,9 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process all entry point functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return LocalSingleStoreElim(fp);
|
||||
@ -297,29 +278,20 @@ Pass::Status LocalSingleStoreElimPass::Process(ir::IRContext* irContext) {
|
||||
void LocalSingleStoreElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
#ifndef LIBSPIRV_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_
|
||||
#define LIBSPIRV_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
@ -27,8 +26,8 @@
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -58,7 +57,7 @@ class LocalSingleStoreElimPass : public MemPass {
|
||||
void SingleStoreAnalyze(ir::Function* func);
|
||||
|
||||
using GetBlocksFunction =
|
||||
std::function<const std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
std::function<const std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
/// Returns the block successors function for the augmented CFG.
|
||||
GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
|
||||
@ -70,11 +69,11 @@ class LocalSingleStoreElimPass : public MemPass {
|
||||
// in idom_. Entries for augmented CFG (pseudo blocks) are not created.
|
||||
// TODO(dnovillo): Move to new CFG class.
|
||||
void CalculateImmediateDominators(ir::Function* func);
|
||||
|
||||
|
||||
// Return true if instruction in |blk0| at ordinal position |idx0|
|
||||
// dominates instruction in |blk1| at position |idx1|.
|
||||
bool Dominates(ir::BasicBlock* blk0, uint32_t idx0,
|
||||
ir::BasicBlock* blk1, uint32_t idx1);
|
||||
bool Dominates(ir::BasicBlock* blk0, uint32_t idx0, ir::BasicBlock* blk1,
|
||||
uint32_t idx1);
|
||||
|
||||
// For each load of an SSA variable in |func|, replace all uses of
|
||||
// the load with the value stored if the store dominates the load.
|
||||
@ -123,19 +122,19 @@ class LocalSingleStoreElimPass : public MemPass {
|
||||
|
||||
// CFG Predecessors
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
predecessors_map_;
|
||||
predecessors_map_;
|
||||
|
||||
// CFG Successors
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
successors_map_;
|
||||
successors_map_;
|
||||
|
||||
// CFG Augmented Predecessors
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
augmented_predecessors_map_;
|
||||
augmented_predecessors_map_;
|
||||
|
||||
// CFG Augmented Successors
|
||||
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
|
||||
augmented_successors_map_;
|
||||
augmented_successors_map_;
|
||||
|
||||
// Immediate Dominator Map
|
||||
// If block has no idom it points to itself.
|
||||
@ -149,4 +148,3 @@ class LocalSingleStoreElimPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_
|
||||
|
||||
|
@ -16,27 +16,24 @@
|
||||
|
||||
#include "local_ssa_elim_pass.h"
|
||||
|
||||
#include "iterator.h"
|
||||
#include "cfa.h"
|
||||
#include "iterator.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
bool LocalMultiStoreElimPass::EliminateMultiStoreLocal(ir::Function* func) {
|
||||
// Add Phi instructions to the function.
|
||||
if (InsertPhiInstructions(func) == Status::SuccessWithoutChange)
|
||||
return false;
|
||||
if (InsertPhiInstructions(func) == Status::SuccessWithoutChange) return false;
|
||||
|
||||
// Remove all target variable stores.
|
||||
bool modified = false;
|
||||
for (auto bi = func->begin(); bi != func->end(); ++bi) {
|
||||
for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
|
||||
if (ii->opcode() != SpvOpStore)
|
||||
continue;
|
||||
if (ii->opcode() != SpvOpStore) continue;
|
||||
uint32_t varId;
|
||||
(void) GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId))
|
||||
continue;
|
||||
(void)GetPtr(&*ii, &varId);
|
||||
if (!IsTargetVar(varId)) continue;
|
||||
assert(!HasLoads(varId));
|
||||
DCEInst(&*ii);
|
||||
modified = true;
|
||||
@ -56,8 +53,8 @@ void LocalMultiStoreElimPass::Initialize(ir::IRContext* c) {
|
||||
bool LocalMultiStoreElimPass::AllExtensionsSupported() const {
|
||||
// If any extension not in whitelist, return false
|
||||
for (auto& ei : get_module()->extensions()) {
|
||||
const char* extName = reinterpret_cast<const char*>(
|
||||
&ei.GetInOperand(0).words[0]);
|
||||
const char* extName =
|
||||
reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
|
||||
if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
|
||||
return false;
|
||||
}
|
||||
@ -76,12 +73,10 @@ Pass::Status LocalMultiStoreElimPass::ProcessImpl() {
|
||||
// Do not process if module contains OpGroupDecorate. Additional
|
||||
// support required in KillNamesAndDecorates().
|
||||
// TODO(greg-lunarg): Add support for OpGroupDecorate
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate)
|
||||
return Status::SuccessWithoutChange;
|
||||
for (auto& ai : get_module()->annotations())
|
||||
if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange;
|
||||
// Do not process if any disallowed extensions are enabled
|
||||
if (!AllExtensionsSupported())
|
||||
return Status::SuccessWithoutChange;
|
||||
if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
|
||||
// Process functions
|
||||
ProcessFunction pfn = [this](ir::Function* fp) {
|
||||
return EliminateMultiStoreLocal(fp);
|
||||
@ -100,32 +95,22 @@ Pass::Status LocalMultiStoreElimPass::Process(ir::IRContext* c) {
|
||||
void LocalMultiStoreElimPass::InitExtensions() {
|
||||
extensions_whitelist_.clear();
|
||||
extensions_whitelist_.insert({
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax",
|
||||
"SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot",
|
||||
"SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float",
|
||||
"SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote",
|
||||
"SPV_KHR_16bit_storage",
|
||||
"SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview",
|
||||
"SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2",
|
||||
"SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough",
|
||||
"SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16",
|
||||
"SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
"SPV_AMD_shader_explicit_vertex_parameter",
|
||||
"SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
|
||||
"SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
|
||||
"SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
|
||||
"SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
|
||||
"SPV_KHR_multiview", "SPV_NVX_multiview_per_view_attributes",
|
||||
"SPV_NV_viewport_array2", "SPV_NV_stereo_view_rendering",
|
||||
"SPV_NV_sample_mask_override_coverage",
|
||||
"SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
|
||||
"SPV_KHR_storage_buffer_storage_class",
|
||||
// SPV_KHR_variable_pointers
|
||||
// Currently do not support extended pointer expressions
|
||||
"SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
|
||||
"SPV_KHR_shader_atomic_counter_ops",
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -17,18 +17,17 @@
|
||||
#ifndef LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
|
||||
#define LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "basic_block.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "module.h"
|
||||
#include "mem_pass.h"
|
||||
#include "module.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -38,8 +37,8 @@ class LocalMultiStoreElimPass : public MemPass {
|
||||
using cbb_ptr = const ir::BasicBlock*;
|
||||
|
||||
public:
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
using GetBlocksFunction =
|
||||
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
|
||||
|
||||
LocalMultiStoreElimPass();
|
||||
const char* name() const override { return "eliminate-local-multi-store"; }
|
||||
@ -70,4 +69,3 @@ class LocalMultiStoreElimPass : public MemPass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_LOCAL_SSA_ELIM_PASS_H_
|
||||
|
||||
|
@ -626,7 +626,8 @@ Pass::Status MemPass::InsertPhiInstructions(ir::Function* func) {
|
||||
}
|
||||
// If variable is not defined, use undef
|
||||
if (replId == 0) {
|
||||
replId = Type2Undef(GetPointeeTypeId(get_def_use_mgr()->GetDef(varId)));
|
||||
replId =
|
||||
Type2Undef(GetPointeeTypeId(get_def_use_mgr()->GetDef(varId)));
|
||||
}
|
||||
// Replace load's id with the last stored value id for variable
|
||||
// and delete load. Kill any names or decorates using id before
|
||||
@ -720,7 +721,7 @@ void MemPass::RemovePhiOperands(
|
||||
assert(i % 2 == 0 && i < phi->NumOperands() - 1 &&
|
||||
"malformed Phi arguments");
|
||||
|
||||
ir::BasicBlock *in_block = cfg()->block(phi->GetSingleWordOperand(i + 1));
|
||||
ir::BasicBlock* in_block = cfg()->block(phi->GetSingleWordOperand(i + 1));
|
||||
if (reachable_blocks.find(in_block) == reachable_blocks.end()) {
|
||||
// If the incoming block is unreachable, remove both operands as this
|
||||
// means that the |phi| has lost an incoming edge.
|
||||
@ -730,7 +731,7 @@ void MemPass::RemovePhiOperands(
|
||||
|
||||
// In all other cases, the operand must be kept but may need to be changed.
|
||||
uint32_t arg_id = phi->GetSingleWordOperand(i);
|
||||
ir::BasicBlock *def_block = def_block_[arg_id];
|
||||
ir::BasicBlock* def_block = def_block_[arg_id];
|
||||
if (def_block &&
|
||||
reachable_blocks.find(def_block_[arg_id]) == reachable_blocks.end()) {
|
||||
// If the current |phi| argument was defined in an unreachable block, it
|
||||
@ -824,10 +825,8 @@ bool MemPass::RemoveUnreachableBlocks(ir::Function* func) {
|
||||
// If the block is reachable and has Phi instructions, remove all
|
||||
// operands from its Phi instructions that reference unreachable blocks.
|
||||
// If the block has no Phi instructions, this is a no-op.
|
||||
block.ForEachPhiInst(
|
||||
[&block, &reachable_blocks, this](ir::Instruction* phi) {
|
||||
RemovePhiOperands(phi, reachable_blocks);
|
||||
});
|
||||
block.ForEachPhiInst([&block, &reachable_blocks, this](
|
||||
ir::Instruction* phi) { RemovePhiOperands(phi, reachable_blocks); });
|
||||
}
|
||||
|
||||
// Erase unreachable blocks.
|
||||
@ -869,4 +868,3 @@ void MemPass::InitializeCFGCleanup(ir::IRContext* c) {
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -242,4 +242,3 @@ class MemPass : public Pass {
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // LIBSPIRV_OPT_OPT_PASS_H_
|
||||
|
||||
|
@ -323,7 +323,7 @@ inline IteratorRange<Module::inst_iterator> Module::ext_inst_imports() {
|
||||
}
|
||||
|
||||
inline IteratorRange<Module::const_inst_iterator> Module::ext_inst_imports()
|
||||
const {
|
||||
const {
|
||||
return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end());
|
||||
}
|
||||
|
||||
@ -380,7 +380,7 @@ inline IteratorRange<Module::inst_iterator> Module::execution_modes() {
|
||||
}
|
||||
|
||||
inline IteratorRange<Module::const_inst_iterator> Module::execution_modes()
|
||||
const {
|
||||
const {
|
||||
return make_range(execution_modes_.begin(), execution_modes_.end());
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,9 @@ namespace opt {
|
||||
class NullPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "null"; }
|
||||
Status Process(ir::IRContext*) override { return Status::SuccessWithoutChange; }
|
||||
Status Process(ir::IRContext*) override {
|
||||
return Status::SuccessWithoutChange;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -87,7 +87,7 @@ class Pass {
|
||||
|
||||
// Returns a pointer to the CFG for current module. TODO(dnovillo): This
|
||||
// should belong in IRContext.
|
||||
ir::CFG *cfg() const { return cfg_.get(); }
|
||||
ir::CFG* cfg() const { return cfg_.get(); }
|
||||
|
||||
// Add to |todo| all ids of functions called in |func|.
|
||||
void AddCalls(ir::Function* func, std::queue<uint32_t>* todo);
|
||||
@ -111,8 +111,8 @@ class Pass {
|
||||
const std::unordered_map<uint32_t, ir::Function*>& id2function,
|
||||
std::queue<uint32_t>* roots);
|
||||
|
||||
|
||||
// Run the pass on the given |module|. Returns Status::Failure if errors occur when
|
||||
// Run the pass on the given |module|. Returns Status::Failure if errors occur
|
||||
// when
|
||||
// processing. Returns the corresponding Status::Success if processing is
|
||||
// successful to indicate whether changes are made to the module. If there
|
||||
// were any changes it will also invalidate the analyses in the IRContext
|
||||
|
@ -22,8 +22,8 @@
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "ir_context.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
// A single header to include all passes.
|
||||
|
||||
#include "aggressive_dead_code_elim_pass.h"
|
||||
#include "block_merge_pass.h"
|
||||
#include "cfg_cleanup_pass.h"
|
||||
#include "common_uniform_elim_pass.h"
|
||||
@ -24,22 +25,21 @@
|
||||
#include "dead_branch_elim_pass.h"
|
||||
#include "dead_variable_elimination.h"
|
||||
#include "eliminate_dead_constant_pass.h"
|
||||
#include "eliminate_dead_functions_pass.h"
|
||||
#include "flatten_decoration_pass.h"
|
||||
#include "fold_spec_constant_op_and_composite_pass.h"
|
||||
#include "freeze_spec_constant_value_pass.h"
|
||||
#include "inline_exhaustive_pass.h"
|
||||
#include "inline_opaque_pass.h"
|
||||
#include "insert_extract_elim.h"
|
||||
#include "local_access_chain_convert_pass.h"
|
||||
#include "local_single_block_elim_pass.h"
|
||||
#include "local_single_store_elim_pass.h"
|
||||
#include "local_ssa_elim_pass.h"
|
||||
#include "freeze_spec_constant_value_pass.h"
|
||||
#include "local_access_chain_convert_pass.h"
|
||||
#include "aggressive_dead_code_elim_pass.h"
|
||||
#include "null_pass.h"
|
||||
#include "set_spec_constant_default_value_pass.h"
|
||||
#include "strength_reduction_pass.h"
|
||||
#include "strip_debug_info_pass.h"
|
||||
#include "unify_const_pass.h"
|
||||
#include "eliminate_dead_functions_pass.h"
|
||||
|
||||
#endif // LIBSPIRV_OPT_PASSES_H_
|
||||
|
@ -23,8 +23,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "decoration_manager.h"
|
||||
#include "opcode.h"
|
||||
#include "ir_context.h"
|
||||
#include "opcode.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -46,11 +46,13 @@ Pass::Status RemoveDuplicatesPass::Process(ir::IRContext* irContext) {
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool RemoveDuplicatesPass::RemoveDuplicateCapabilities(ir::IRContext* irContext) const {
|
||||
bool RemoveDuplicatesPass::RemoveDuplicateCapabilities(
|
||||
ir::IRContext* irContext) const {
|
||||
bool modified = false;
|
||||
|
||||
std::unordered_set<uint32_t> capabilities;
|
||||
for (auto i = irContext->capability_begin(); i != irContext->capability_end();) {
|
||||
for (auto i = irContext->capability_begin();
|
||||
i != irContext->capability_end();) {
|
||||
auto res = capabilities.insert(i->GetSingleWordOperand(0u));
|
||||
|
||||
if (res.second) {
|
||||
@ -66,8 +68,8 @@ bool RemoveDuplicatesPass::RemoveDuplicateCapabilities(ir::IRContext* irContext)
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool
|
||||
RemoveDuplicatesPass::RemoveDuplicatesExtInstImports(ir::IRContext* irContext) const {
|
||||
bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports(
|
||||
ir::IRContext* irContext) const {
|
||||
bool modified = false;
|
||||
|
||||
std::unordered_map<std::string, SpvId> extInstImports;
|
||||
@ -90,8 +92,8 @@ RemoveDuplicatesPass::RemoveDuplicatesExtInstImports(ir::IRContext* irContext) c
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool RemoveDuplicatesPass::RemoveDuplicateTypes(ir::IRContext* irContext,
|
||||
DecorationManager& decManager) const {
|
||||
bool RemoveDuplicatesPass::RemoveDuplicateTypes(
|
||||
ir::IRContext* irContext, DecorationManager& decManager) const {
|
||||
bool modified = false;
|
||||
|
||||
std::vector<Instruction> visitedTypes;
|
||||
@ -142,7 +144,8 @@ bool RemoveDuplicatesPass::RemoveDuplicateDecorations(
|
||||
std::vector<const Instruction*> visitedDecorations;
|
||||
|
||||
opt::analysis::DecorationManager decorationManager(irContext->module());
|
||||
for (auto i = irContext->annotation_begin(); i != irContext->annotation_end();) {
|
||||
for (auto i = irContext->annotation_begin();
|
||||
i != irContext->annotation_end();) {
|
||||
// Is the current decoration equal to one of the decorations we have aready
|
||||
// visited?
|
||||
bool alreadyVisited = false;
|
||||
|
@ -19,9 +19,9 @@
|
||||
|
||||
#include "decoration_manager.h"
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -44,7 +44,7 @@ class RemoveDuplicatesPass : public Pass {
|
||||
bool RemoveDuplicateCapabilities(ir::IRContext* irContext) const;
|
||||
bool RemoveDuplicatesExtInstImports(ir::IRContext* irContext) const;
|
||||
bool RemoveDuplicateTypes(ir::IRContext* irContext,
|
||||
analysis::DecorationManager& decManager) const;
|
||||
analysis::DecorationManager& decManager) const;
|
||||
bool RemoveDuplicateDecorations(ir::IRContext* irContext) const;
|
||||
};
|
||||
|
||||
|
@ -21,12 +21,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "make_unique.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "type_manager.h"
|
||||
#include "types.h"
|
||||
#include "util/parse_number.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -188,7 +188,8 @@ ir::Instruction* GetSpecIdTargetFromDecorationGroup(
|
||||
}
|
||||
};
|
||||
|
||||
Pass::Status SetSpecConstantDefaultValuePass::Process(ir::IRContext* irContext) {
|
||||
Pass::Status SetSpecConstantDefaultValuePass::Process(
|
||||
ir::IRContext* irContext) {
|
||||
// The operand index of decoration target in an OpDecorate instruction.
|
||||
const uint32_t kTargetIdOperandIndex = 0;
|
||||
// The operand index of the decoration literal in an OpDecorate instruction.
|
||||
@ -254,8 +255,8 @@ Pass::Status SetSpecConstantDefaultValuePass::Process(ir::IRContext* irContext)
|
||||
// Gets the string of the default value and parses it to bit pattern
|
||||
// with the type of the spec constant.
|
||||
const std::string& default_value_str = iter->second;
|
||||
bit_pattern = ParseDefaultValueStr(default_value_str.c_str(),
|
||||
type_mgr.GetType(spec_inst->type_id()));
|
||||
bit_pattern = ParseDefaultValueStr(
|
||||
default_value_str.c_str(), type_mgr.GetType(spec_inst->type_id()));
|
||||
|
||||
} else {
|
||||
// Search for the new bit-pattern-form default value for this spec id.
|
||||
|
@ -19,9 +19,9 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -38,17 +38,22 @@ class SetSpecConstantDefaultValuePass : public Pass {
|
||||
// in the form of string.
|
||||
explicit SetSpecConstantDefaultValuePass(
|
||||
const SpecIdToValueStrMap& default_values)
|
||||
: spec_id_to_value_str_(default_values), spec_id_to_value_bit_pattern_() {}
|
||||
: spec_id_to_value_str_(default_values),
|
||||
spec_id_to_value_bit_pattern_() {}
|
||||
explicit SetSpecConstantDefaultValuePass(SpecIdToValueStrMap&& default_values)
|
||||
: spec_id_to_value_str_(std::move(default_values)), spec_id_to_value_bit_pattern_() {}
|
||||
: spec_id_to_value_str_(std::move(default_values)),
|
||||
spec_id_to_value_bit_pattern_() {}
|
||||
|
||||
// Constructs a pass instance with a map from spec ids to default values in
|
||||
// the form of bit pattern.
|
||||
explicit SetSpecConstantDefaultValuePass(
|
||||
const SpecIdToValueBitPatternMap& default_values)
|
||||
: spec_id_to_value_str_(), spec_id_to_value_bit_pattern_(default_values) {}
|
||||
explicit SetSpecConstantDefaultValuePass(SpecIdToValueBitPatternMap&& default_values)
|
||||
: spec_id_to_value_str_(), spec_id_to_value_bit_pattern_(std::move(default_values)) {}
|
||||
: spec_id_to_value_str_(),
|
||||
spec_id_to_value_bit_pattern_(default_values) {}
|
||||
explicit SetSpecConstantDefaultValuePass(
|
||||
SpecIdToValueBitPatternMap&& default_values)
|
||||
: spec_id_to_value_str_(),
|
||||
spec_id_to_value_bit_pattern_(std::move(default_values)) {}
|
||||
|
||||
const char* name() const override { return "set-spec-const-default-value"; }
|
||||
Status Process(ir::IRContext*) override;
|
||||
@ -96,7 +101,8 @@ class SetSpecConstantDefaultValuePass : public Pass {
|
||||
|
||||
// The mapping from spec ids to their string-form default values to be set.
|
||||
const SpecIdToValueStrMap spec_id_to_value_str_;
|
||||
// The mapping from spec ids to their bitpattern-form default values to be set.
|
||||
// The mapping from spec ids to their bitpattern-form default values to be
|
||||
// set.
|
||||
const SpecIdToValueBitPatternMap spec_id_to_value_bit_pattern_;
|
||||
};
|
||||
|
||||
|
@ -21,9 +21,9 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "log.h"
|
||||
#include "reflect.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace {
|
||||
// Count the number of trailing zeros in the binary representation of
|
||||
|
@ -16,9 +16,9 @@
|
||||
#define LIBSPIRV_OPT_STRENGTH_REDUCTION_PASS_H_
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -34,8 +34,10 @@ class StrengthReductionPass : public Pass {
|
||||
// Returns true if something changed.
|
||||
bool ReplaceMultiplyByPowerOf2(ir::BasicBlock::iterator*);
|
||||
|
||||
// Scan the types and constants in the module looking for the the integer types that we are
|
||||
// interested in. The shift operation needs a small unsigned integer. We need to find
|
||||
// Scan the types and constants in the module looking for the the integer
|
||||
// types that we are
|
||||
// interested in. The shift operation needs a small unsigned integer. We
|
||||
// need to find
|
||||
// them or create them. We do not want duplicates.
|
||||
void FindIntTypesAndConstants();
|
||||
|
||||
|
@ -19,7 +19,8 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status StripDebugInfoPass::Process(ir::IRContext* irContext) {
|
||||
bool modified = !irContext->debugs1().empty() || !irContext->debugs2().empty() ||
|
||||
bool modified = !irContext->debugs1().empty() ||
|
||||
!irContext->debugs2().empty() ||
|
||||
!irContext->debugs3().empty();
|
||||
irContext->debug_clear();
|
||||
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_STRIP_DEBUG_INFO_PASS_H_
|
||||
#define LIBSPIRV_OPT_STRIP_DEBUG_INFO_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -42,10 +42,10 @@ bool CompareTwoVectors(const U32VecVec a, const U32VecVec b) {
|
||||
b_ptrs.push_back(&b[i]);
|
||||
}
|
||||
|
||||
const auto cmp =
|
||||
[](const std::vector<uint32_t>* m, const std::vector<uint32_t>* n) {
|
||||
return m->front() < n->front();
|
||||
};
|
||||
const auto cmp = [](const std::vector<uint32_t>* m,
|
||||
const std::vector<uint32_t>* n) {
|
||||
return m->front() < n->front();
|
||||
};
|
||||
|
||||
std::sort(a_ptrs.begin(), a_ptrs.end(), cmp);
|
||||
std::sort(b_ptrs.begin(), b_ptrs.end(), cmp);
|
||||
|
@ -18,8 +18,8 @@
|
||||
#include <utility>
|
||||
|
||||
#include "def_use_manager.h"
|
||||
#include "make_unique.h"
|
||||
#include "ir_context.h"
|
||||
#include "make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -15,9 +15,9 @@
|
||||
#ifndef LIBSPIRV_OPT_UNIFY_CONSTANT_PASS_H_
|
||||
#define LIBSPIRV_OPT_UNIFY_CONSTANT_PASS_H_
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "module.h"
|
||||
#include "pass.h"
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
@ -14,7 +14,8 @@
|
||||
|
||||
#include "print.h"
|
||||
|
||||
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || defined(SPIRV_FREEBSD)
|
||||
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
|
||||
defined(SPIRV_FREEBSD)
|
||||
namespace libspirv {
|
||||
|
||||
clr::reset::operator const char*() { return "\x1b[0m"; }
|
||||
@ -35,8 +36,7 @@ clr::blue::operator const char*() { return "\x1b[34m"; }
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
static void SetConsoleForegroundColorPrimary(HANDLE hConsole, WORD color)
|
||||
{
|
||||
static void SetConsoleForegroundColorPrimary(HANDLE hConsole, WORD color) {
|
||||
// Get screen buffer information from console handle
|
||||
CONSOLE_SCREEN_BUFFER_INFO bufInfo;
|
||||
GetConsoleScreenBufferInfo(hConsole, &bufInfo);
|
||||
@ -48,8 +48,7 @@ static void SetConsoleForegroundColorPrimary(HANDLE hConsole, WORD color)
|
||||
SetConsoleTextAttribute(hConsole, color);
|
||||
}
|
||||
|
||||
static void SetConsoleForegroundColor(WORD color)
|
||||
{
|
||||
static void SetConsoleForegroundColor(WORD color) {
|
||||
SetConsoleForegroundColorPrimary(GetStdHandle(STD_OUTPUT_HANDLE), color);
|
||||
SetConsoleForegroundColorPrimary(GetStdHandle(STD_ERROR_HANDLE), color);
|
||||
}
|
||||
|
@ -22,10 +22,6 @@ const char* kBuildVersions[] = {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
const char* spvSoftwareVersionString() {
|
||||
return kBuildVersions[0];
|
||||
}
|
||||
const char* spvSoftwareVersionString() { return kBuildVersions[0]; }
|
||||
|
||||
const char* spvSoftwareVersionDetailsString() {
|
||||
return kBuildVersions[1];
|
||||
}
|
||||
const char* spvSoftwareVersionDetailsString() { return kBuildVersions[1]; }
|
||||
|
@ -25,16 +25,16 @@
|
||||
#include "diagnostic.h"
|
||||
#include "enum_string_mapping.h"
|
||||
#include "extensions.h"
|
||||
#include "instruction.h"
|
||||
#include "id_descriptor.h"
|
||||
#include "instruction.h"
|
||||
#include "opcode.h"
|
||||
#include "operand.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
#include "spirv_endian.h"
|
||||
#include "spirv_validator_options.h"
|
||||
#include "validate.h"
|
||||
#include "val/instruction.h"
|
||||
#include "val/validation_state.h"
|
||||
#include "validate.h"
|
||||
|
||||
using libspirv::IdDescriptorCollection;
|
||||
using libspirv::Instruction;
|
||||
@ -54,10 +54,10 @@ class StatsAggregator {
|
||||
}
|
||||
|
||||
// Collects header statistics and sets correct id_bound.
|
||||
spv_result_t ProcessHeader(
|
||||
spv_endianness_t /* endian */, uint32_t /* magic */,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t /* schema */) {
|
||||
spv_result_t ProcessHeader(spv_endianness_t /* endian */,
|
||||
uint32_t /* magic */, uint32_t version,
|
||||
uint32_t generator, uint32_t id_bound,
|
||||
uint32_t /* schema */) {
|
||||
vstate_->setIdBound(id_bound);
|
||||
++stats_->version_hist[version];
|
||||
++stats_->generator_hist[generator];
|
||||
@ -68,9 +68,9 @@ class StatsAggregator {
|
||||
// then procession the instruction to collect stats.
|
||||
spv_result_t ProcessInstruction(const spv_parsed_instruction_t* inst) {
|
||||
const spv_result_t validation_result =
|
||||
spvtools::ValidateInstructionAndUpdateValidationState(vstate_.get(), inst);
|
||||
if (validation_result != SPV_SUCCESS)
|
||||
return validation_result;
|
||||
spvtools::ValidateInstructionAndUpdateValidationState(vstate_.get(),
|
||||
inst);
|
||||
if (validation_result != SPV_SUCCESS) return validation_result;
|
||||
|
||||
ProcessOpcode();
|
||||
ProcessCapability();
|
||||
@ -106,8 +106,9 @@ class StatsAggregator {
|
||||
id_descriptors_.GetDescriptor(inst.word(operand.offset));
|
||||
if (descriptor) {
|
||||
++stats_->id_descriptor_hist[descriptor];
|
||||
++stats_->operand_slot_id_descriptor_hist[
|
||||
std::pair<uint32_t, uint32_t>(inst.opcode(), index)][descriptor];
|
||||
++stats_
|
||||
->operand_slot_id_descriptor_hist[std::pair<uint32_t, uint32_t>(
|
||||
inst.opcode(), index)][descriptor];
|
||||
}
|
||||
}
|
||||
++index;
|
||||
@ -168,8 +169,8 @@ class StatsAggregator {
|
||||
uint32_t index = 0;
|
||||
for (const auto& operand : inst.operands()) {
|
||||
if (operand.num_words == 1 && !spvIsIdType(operand.type)) {
|
||||
++stats_->operand_slot_non_id_words_hist[std::pair<uint32_t, uint32_t>(
|
||||
inst.opcode(), index)][inst.word(operand.offset)];
|
||||
++stats_->operand_slot_non_id_words_hist[std::pair<uint32_t, uint32_t>(
|
||||
inst.opcode(), index)][inst.word(operand.offset)];
|
||||
}
|
||||
++index;
|
||||
}
|
||||
@ -205,13 +206,14 @@ class StatsAggregator {
|
||||
|
||||
if (inst_it != vstate_->ordered_instructions().rend()) {
|
||||
const SpvOp prev_opcode = inst_it->opcode();
|
||||
++stats_->opcode_and_num_operands_markov_hist[prev_opcode][
|
||||
opcode_and_num_operands];
|
||||
++stats_->opcode_and_num_operands_markov_hist[prev_opcode]
|
||||
[opcode_and_num_operands];
|
||||
}
|
||||
|
||||
auto step_it = stats_->opcode_markov_hist.begin();
|
||||
for (; inst_it != vstate_->ordered_instructions().rend() &&
|
||||
step_it != stats_->opcode_markov_hist.end(); ++inst_it, ++step_it) {
|
||||
step_it != stats_->opcode_markov_hist.end();
|
||||
++inst_it, ++step_it) {
|
||||
auto& hist = (*step_it)[inst_it->opcode()];
|
||||
++hist[opcode];
|
||||
}
|
||||
@ -260,9 +262,7 @@ class StatsAggregator {
|
||||
}
|
||||
}
|
||||
|
||||
SpirvStats* stats() {
|
||||
return stats_;
|
||||
}
|
||||
SpirvStats* stats() { return stats_; }
|
||||
|
||||
private:
|
||||
// Returns the current instruction (the one last processed by the validator).
|
||||
@ -276,18 +276,17 @@ class StatsAggregator {
|
||||
IdDescriptorCollection id_descriptors_;
|
||||
};
|
||||
|
||||
spv_result_t ProcessHeader(
|
||||
void* user_data, spv_endianness_t endian, uint32_t magic,
|
||||
uint32_t version, uint32_t generator, uint32_t id_bound,
|
||||
uint32_t schema) {
|
||||
spv_result_t ProcessHeader(void* user_data, spv_endianness_t endian,
|
||||
uint32_t magic, uint32_t version, uint32_t generator,
|
||||
uint32_t id_bound, uint32_t schema) {
|
||||
StatsAggregator* stats_aggregator =
|
||||
reinterpret_cast<StatsAggregator*>(user_data);
|
||||
return stats_aggregator->ProcessHeader(
|
||||
endian, magic, version, generator, id_bound, schema);
|
||||
return stats_aggregator->ProcessHeader(endian, magic, version, generator,
|
||||
id_bound, schema);
|
||||
}
|
||||
|
||||
spv_result_t ProcessInstruction(
|
||||
void* user_data, const spv_parsed_instruction_t* inst) {
|
||||
spv_result_t ProcessInstruction(void* user_data,
|
||||
const spv_parsed_instruction_t* inst) {
|
||||
StatsAggregator* stats_aggregator =
|
||||
reinterpret_cast<StatsAggregator*>(user_data);
|
||||
return stats_aggregator->ProcessInstruction(inst);
|
||||
@ -297,9 +296,9 @@ spv_result_t ProcessInstruction(
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
spv_result_t AggregateStats(
|
||||
const spv_context_t& context, const uint32_t* words, const size_t num_words,
|
||||
spv_diagnostic* pDiagnostic, SpirvStats* stats) {
|
||||
spv_result_t AggregateStats(const spv_context_t& context, const uint32_t* words,
|
||||
const size_t num_words, spv_diagnostic* pDiagnostic,
|
||||
SpirvStats* stats) {
|
||||
spv_const_binary_t binary = {words, num_words};
|
||||
|
||||
spv_endianness_t endian;
|
||||
@ -307,14 +306,14 @@ spv_result_t AggregateStats(
|
||||
if (spvBinaryEndianness(&binary, &endian)) {
|
||||
return libspirv::DiagnosticStream(position, context.consumer,
|
||||
SPV_ERROR_INVALID_BINARY)
|
||||
<< "Invalid SPIR-V magic number.";
|
||||
<< "Invalid SPIR-V magic number.";
|
||||
}
|
||||
|
||||
spv_header_t header;
|
||||
if (spvBinaryHeaderGet(&binary, endian, &header)) {
|
||||
return libspirv::DiagnosticStream(position, context.consumer,
|
||||
SPV_ERROR_INVALID_BINARY)
|
||||
<< "Invalid SPIR-V header.";
|
||||
<< "Invalid SPIR-V header.";
|
||||
}
|
||||
|
||||
StatsAggregator stats_aggregator(stats, &context);
|
||||
|
@ -69,15 +69,15 @@ struct SpirvStats {
|
||||
std::unordered_map<double, uint32_t> f64_constant_hist;
|
||||
|
||||
// Enum histogram, operand type -> operand value -> count.
|
||||
std::unordered_map<uint32_t,
|
||||
std::unordered_map<uint32_t, uint32_t>> enum_hist;
|
||||
std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>
|
||||
enum_hist;
|
||||
|
||||
// Histogram of all non-id single words.
|
||||
// pair<opcode, operand index> -> value -> count.
|
||||
// This is a generalization of enum_hist, also includes literal integers and
|
||||
// masks.
|
||||
std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::map<uint32_t, uint32_t>> operand_slot_non_id_words_hist;
|
||||
std::map<std::pair<uint32_t, uint32_t>, std::map<uint32_t, uint32_t>>
|
||||
operand_slot_non_id_words_hist;
|
||||
|
||||
// Historgam of descriptors generated by IdDescriptorCollection.
|
||||
// Descriptor -> count.
|
||||
@ -88,10 +88,11 @@ struct SpirvStats {
|
||||
|
||||
// Historgam of descriptors generated by IdDescriptorCollection for every
|
||||
// operand slot. pair<opcode, operand index> -> descriptor -> count.
|
||||
std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::map<uint32_t, uint32_t>> operand_slot_id_descriptor_hist;
|
||||
std::map<std::pair<uint32_t, uint32_t>, std::map<uint32_t, uint32_t>>
|
||||
operand_slot_id_descriptor_hist;
|
||||
|
||||
// Histogram of literal strings, sharded by opcodes, opcode -> string -> count.
|
||||
// Histogram of literal strings, sharded by opcodes, opcode -> string ->
|
||||
// count.
|
||||
// This is suboptimal if an opcode has multiple literal string operands,
|
||||
// as it wouldn't differentiate between operands.
|
||||
std::unordered_map<uint32_t, std::unordered_map<std::string, uint32_t>>
|
||||
@ -114,14 +115,15 @@ struct SpirvStats {
|
||||
// The size of the outer std::vector also serves as an input parameter,
|
||||
// determining how many steps will be collected.
|
||||
// I.e. do opcode_markov_hist.resize(1) to collect data for one step only.
|
||||
std::vector<std::unordered_map<uint32_t,
|
||||
std::unordered_map<uint32_t, uint32_t>>> opcode_markov_hist;
|
||||
std::vector<
|
||||
std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>>
|
||||
opcode_markov_hist;
|
||||
};
|
||||
|
||||
// Aggregates existing |stats| with new stats extracted from |binary|.
|
||||
spv_result_t AggregateStats(
|
||||
const spv_context_t& context, const uint32_t* words, const size_t num_words,
|
||||
spv_diagnostic* pDiagnostic, SpirvStats* stats);
|
||||
spv_result_t AggregateStats(const spv_context_t& context, const uint32_t* words,
|
||||
const size_t num_words, spv_diagnostic* pDiagnostic,
|
||||
SpirvStats* stats);
|
||||
|
||||
} // namespace libspirv
|
||||
|
||||
|
@ -37,12 +37,10 @@ struct validator_universal_limits_t {
|
||||
// Manages command line options passed to the SPIR-V Validator. New struct
|
||||
// members may be added for any new option.
|
||||
struct spv_validator_options_t {
|
||||
spv_validator_options_t()
|
||||
: universal_limits_(), relax_struct_store(false) {}
|
||||
spv_validator_options_t() : universal_limits_(), relax_struct_store(false) {}
|
||||
|
||||
validator_universal_limits_t universal_limits_;
|
||||
bool relax_struct_store;
|
||||
};
|
||||
|
||||
#endif // LIBSPIRV_SPIRV_VALIDATOR_OPTIONS_H_
|
||||
|
||||
|
@ -241,8 +241,8 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
|
||||
// and emits its corresponding number.
|
||||
spv_ext_inst_desc extInst;
|
||||
if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) {
|
||||
return context->diagnostic() << "Invalid extended instruction name '"
|
||||
<< textValue << "'.";
|
||||
return context->diagnostic()
|
||||
<< "Invalid extended instruction name '" << textValue << "'.";
|
||||
}
|
||||
spvInstructionAddWord(pInst, extInst->ext_inst);
|
||||
|
||||
@ -522,8 +522,8 @@ spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar,
|
||||
error = context->getWord(&opcodeName, &nextPosition);
|
||||
if (error) return context->diagnostic(error) << "Internal Error";
|
||||
if (!context->startsWithOp()) {
|
||||
return context->diagnostic() << "Invalid Opcode prefix '" << opcodeName
|
||||
<< "'.";
|
||||
return context->diagnostic()
|
||||
<< "Invalid Opcode prefix '" << opcodeName << "'.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,8 +533,8 @@ spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar,
|
||||
spv_opcode_desc opcodeEntry;
|
||||
error = grammar.lookupOpcode(pInstName, &opcodeEntry);
|
||||
if (error) {
|
||||
return context->diagnostic(error) << "Invalid Opcode name '" << opcodeName
|
||||
<< "'";
|
||||
return context->diagnostic(error)
|
||||
<< "Invalid Opcode name '" << opcodeName << "'";
|
||||
}
|
||||
if (opcodeEntry->hasResult && result_id.empty()) {
|
||||
return context->diagnostic()
|
||||
@ -556,7 +556,8 @@ spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar,
|
||||
spv_operand_pattern_t expectedOperands;
|
||||
expectedOperands.reserve(opcodeEntry->numTypes);
|
||||
for (auto i = 0; i < opcodeEntry->numTypes; i++)
|
||||
expectedOperands.push_back(opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]);
|
||||
expectedOperands.push_back(
|
||||
opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]);
|
||||
|
||||
while (!expectedOperands.empty()) {
|
||||
const spv_operand_type_t type = expectedOperands.back();
|
||||
@ -694,10 +695,11 @@ spv_result_t GetNumericIds(const libspirv::AssemblyGrammar& grammar,
|
||||
// Translates a given assembly language module into binary form.
|
||||
// If a diagnostic is generated, it is not yet marked as being
|
||||
// for a text-based input.
|
||||
spv_result_t spvTextToBinaryInternal(
|
||||
const libspirv::AssemblyGrammar& grammar,
|
||||
const spvtools::MessageConsumer& consumer, const spv_text text,
|
||||
const uint32_t options, spv_binary* pBinary) {
|
||||
spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar,
|
||||
const spvtools::MessageConsumer& consumer,
|
||||
const spv_text text,
|
||||
const uint32_t options,
|
||||
spv_binary* pBinary) {
|
||||
// The ids in this set will have the same values both in source and binary.
|
||||
// All other ids will be generated by filling in the gaps.
|
||||
std::set<uint32_t> ids_to_preserve;
|
||||
@ -770,15 +772,17 @@ spv_result_t spvTextToBinary(const spv_const_context context,
|
||||
const char* input_text,
|
||||
const size_t input_text_size, spv_binary* pBinary,
|
||||
spv_diagnostic* pDiagnostic) {
|
||||
return spvTextToBinaryWithOptions(
|
||||
context, input_text, input_text_size, SPV_BINARY_TO_TEXT_OPTION_NONE,
|
||||
pBinary, pDiagnostic);
|
||||
return spvTextToBinaryWithOptions(context, input_text, input_text_size,
|
||||
SPV_BINARY_TO_TEXT_OPTION_NONE, pBinary,
|
||||
pDiagnostic);
|
||||
}
|
||||
|
||||
spv_result_t spvTextToBinaryWithOptions(
|
||||
const spv_const_context context, const char* input_text,
|
||||
const size_t input_text_size, const uint32_t options, spv_binary* pBinary,
|
||||
spv_diagnostic* pDiagnostic) {
|
||||
spv_result_t spvTextToBinaryWithOptions(const spv_const_context context,
|
||||
const char* input_text,
|
||||
const size_t input_text_size,
|
||||
const uint32_t options,
|
||||
spv_binary* pBinary,
|
||||
spv_diagnostic* pDiagnostic) {
|
||||
spv_context_t hijack_context = *context;
|
||||
if (pDiagnostic) {
|
||||
*pDiagnostic = nullptr;
|
||||
|
@ -389,8 +389,7 @@ std::set<uint32_t> AssemblyContext::GetNumericIds() const {
|
||||
std::set<uint32_t> ids;
|
||||
for (const auto& kv : named_ids_) {
|
||||
uint32_t id;
|
||||
if (spvutils::ParseNumber(kv.first.c_str(), &id))
|
||||
ids.insert(id);
|
||||
if (spvutils::ParseNumber(kv.first.c_str(), &id)) ids.insert(id);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
@ -119,8 +119,12 @@ class AssemblyContext {
|
||||
public:
|
||||
AssemblyContext(spv_text text, const spvtools::MessageConsumer& consumer,
|
||||
std::set<uint32_t>&& ids_to_preserve = std::set<uint32_t>())
|
||||
: current_position_({}), consumer_(consumer), text_(text), bound_(1),
|
||||
next_id_(1), ids_to_preserve_(std::move(ids_to_preserve)) {}
|
||||
: current_position_({}),
|
||||
consumer_(consumer),
|
||||
text_(text),
|
||||
bound_(1),
|
||||
next_id_(1),
|
||||
ids_to_preserve_(std::move(ids_to_preserve)) {}
|
||||
|
||||
// Assigns a new integer value to the given text ID, or returns the previously
|
||||
// assigned integer value if the ID has been seen before.
|
||||
|
@ -121,15 +121,13 @@ bool ReadVariableWidthInternal(BitReaderInterface* reader, uint64_t* val,
|
||||
|
||||
while (payload_read + chunk_length < max_payload) {
|
||||
uint64_t bits = 0;
|
||||
if (reader->ReadBits(&bits, chunk_length) != chunk_length)
|
||||
return false;
|
||||
if (reader->ReadBits(&bits, chunk_length) != chunk_length) return false;
|
||||
|
||||
*val |= bits << payload_read;
|
||||
payload_read += chunk_length;
|
||||
|
||||
uint64_t more_to_come = 0;
|
||||
if (reader->ReadBits(&more_to_come, 1) != 1)
|
||||
return false;
|
||||
if (reader->ReadBits(&more_to_come, 1) != 1) return false;
|
||||
|
||||
if (!more_to_come) {
|
||||
return true;
|
||||
@ -139,8 +137,7 @@ bool ReadVariableWidthInternal(BitReaderInterface* reader, uint64_t* val,
|
||||
// Need to read the last chunk which may be truncated. No signal bit follows.
|
||||
uint64_t bits = 0;
|
||||
const size_t left_to_read = max_payload - payload_read;
|
||||
if (reader->ReadBits(&bits, left_to_read) != left_to_read)
|
||||
return false;
|
||||
if (reader->ReadBits(&bits, left_to_read) != left_to_read) return false;
|
||||
|
||||
*val |= bits << payload_read;
|
||||
return true;
|
||||
@ -255,26 +252,22 @@ void BitWriterInterface::WriteVariableWidthU8(uint8_t val,
|
||||
WriteVariableWidthUnsigned(this, val, chunk_length);
|
||||
}
|
||||
|
||||
void BitWriterInterface::WriteVariableWidthS64(int64_t val,
|
||||
size_t chunk_length,
|
||||
void BitWriterInterface::WriteVariableWidthS64(int64_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
void BitWriterInterface::WriteVariableWidthS32(int32_t val,
|
||||
size_t chunk_length,
|
||||
void BitWriterInterface::WriteVariableWidthS32(int32_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
void BitWriterInterface::WriteVariableWidthS16(int16_t val,
|
||||
size_t chunk_length,
|
||||
void BitWriterInterface::WriteVariableWidthS16(int16_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
void BitWriterInterface::WriteVariableWidthS8(int8_t val,
|
||||
size_t chunk_length,
|
||||
void BitWriterInterface::WriteVariableWidthS8(int8_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
@ -352,26 +345,22 @@ bool BitReaderInterface::ReadVariableWidthU8(uint8_t* val,
|
||||
return ReadVariableWidthUnsigned(this, val, chunk_length);
|
||||
}
|
||||
|
||||
bool BitReaderInterface::ReadVariableWidthS64(int64_t* val,
|
||||
size_t chunk_length,
|
||||
bool BitReaderInterface::ReadVariableWidthS64(int64_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
bool BitReaderInterface::ReadVariableWidthS32(int32_t* val,
|
||||
size_t chunk_length,
|
||||
bool BitReaderInterface::ReadVariableWidthS32(int32_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
bool BitReaderInterface::ReadVariableWidthS16(int16_t* val,
|
||||
size_t chunk_length,
|
||||
bool BitReaderInterface::ReadVariableWidthS16(int16_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
|
||||
bool BitReaderInterface::ReadVariableWidthS8(int8_t* val,
|
||||
size_t chunk_length,
|
||||
bool BitReaderInterface::ReadVariableWidthS8(int8_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent) {
|
||||
return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
|
||||
}
|
||||
@ -396,8 +385,7 @@ size_t BitReaderWord64::ReadBits(uint64_t* bits, size_t num_bits) {
|
||||
assert(is_little_endian && "Big-endian architecture support not implemented");
|
||||
if (!is_little_endian) return 0;
|
||||
|
||||
if (ReachedEnd())
|
||||
return 0;
|
||||
if (ReachedEnd()) return 0;
|
||||
|
||||
// Index of the current word.
|
||||
const size_t index = pos_ / 64;
|
||||
@ -431,17 +419,13 @@ size_t BitReaderWord64::ReadBits(uint64_t* bits, size_t num_bits) {
|
||||
return num_bits;
|
||||
}
|
||||
|
||||
bool BitReaderWord64::ReachedEnd() const {
|
||||
return pos_ >= buffer_.size() * 64;
|
||||
}
|
||||
bool BitReaderWord64::ReachedEnd() const { return pos_ >= buffer_.size() * 64; }
|
||||
|
||||
bool BitReaderWord64::OnlyZeroesLeft() const {
|
||||
if (ReachedEnd())
|
||||
return true;
|
||||
if (ReachedEnd()) return true;
|
||||
|
||||
const size_t index = pos_ / 64;
|
||||
if (index < buffer_.size() - 1)
|
||||
return false;
|
||||
if (index < buffer_.size() - 1) return false;
|
||||
|
||||
assert(index == buffer_.size() - 1);
|
||||
|
||||
|
@ -21,8 +21,8 @@
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace spvutils {
|
||||
@ -60,9 +60,7 @@ inline T GetLowerBits(T in, size_t num_bits) {
|
||||
// 2 -> 4
|
||||
// Motivation: -1 is 0xFF...FF what doesn't work very well with
|
||||
// WriteVariableWidth which prefers to have as many 0 bits as possible.
|
||||
inline uint64_t EncodeZigZag(int64_t val) {
|
||||
return (val << 1) ^ (val >> 63);
|
||||
}
|
||||
inline uint64_t EncodeZigZag(int64_t val) { return (val << 1) ^ (val >> 63); }
|
||||
|
||||
// Decodes signed integer encoded with EncodeZigZag.
|
||||
inline int64_t DecodeZigZag(uint64_t val) {
|
||||
@ -92,7 +90,8 @@ inline int64_t DecodeZigZag(uint64_t val) {
|
||||
inline uint64_t EncodeZigZag(int64_t val, size_t block_exponent) {
|
||||
assert(block_exponent < 64);
|
||||
const uint64_t uval = static_cast<uint64_t>(val >= 0 ? val : -val - 1);
|
||||
const uint64_t block_num = ((uval >> block_exponent) << 1) + (val >= 0 ? 0 : 1);
|
||||
const uint64_t block_num =
|
||||
((uval >> block_exponent) << 1) + (val >= 0 ? 0 : 1);
|
||||
const uint64_t pos = GetLowerBits(uval, block_exponent);
|
||||
return (block_num << block_exponent) + pos;
|
||||
}
|
||||
@ -139,13 +138,13 @@ std::vector<T> StreamToBuffer(std::string str) {
|
||||
std::vector<T> buffer;
|
||||
buffer.reserve(NumBitsToNumWords<sizeof(T)>(str.length()));
|
||||
for (int index = str_length - word_size; index >= 0; index -= word_size) {
|
||||
buffer.push_back(static_cast<T>(std::bitset<sizeof(T) * 8>(
|
||||
str, index, word_size).to_ullong()));
|
||||
buffer.push_back(static_cast<T>(
|
||||
std::bitset<sizeof(T) * 8>(str, index, word_size).to_ullong()));
|
||||
}
|
||||
const size_t suffix_length = str.length() % word_size;
|
||||
if (suffix_length != 0) {
|
||||
buffer.push_back(static_cast<T>(std::bitset<sizeof(T) * 8>(
|
||||
str, 0, suffix_length).to_ullong()));
|
||||
buffer.push_back(static_cast<T>(
|
||||
std::bitset<sizeof(T) * 8>(str, 0, suffix_length).to_ullong()));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@ -154,8 +153,7 @@ std::vector<T> StreamToBuffer(std::string str) {
|
||||
template <size_t N>
|
||||
inline std::string PadToWord(std::string&& str) {
|
||||
const size_t tail_length = str.size() % N;
|
||||
if (tail_length != 0)
|
||||
str += std::string(N - tail_length, '0');
|
||||
if (tail_length != 0) str += std::string(N - tail_length, '0');
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -174,7 +172,8 @@ inline std::bitset<N> StreamToBitset(std::string str) {
|
||||
|
||||
// Converts first |num_bits| of std::bitset to a left-to-right stream of bits.
|
||||
template <size_t N>
|
||||
inline std::string BitsetToStream(const std::bitset<N>& bits, size_t num_bits = N) {
|
||||
inline std::string BitsetToStream(const std::bitset<N>& bits,
|
||||
size_t num_bits = N) {
|
||||
std::string str = bits.to_string().substr(N - num_bits);
|
||||
std::reverse(str.begin(), str.end());
|
||||
return str;
|
||||
@ -237,14 +236,14 @@ class BitWriterInterface {
|
||||
void WriteVariableWidthU32(uint32_t val, size_t chunk_length);
|
||||
void WriteVariableWidthU16(uint16_t val, size_t chunk_length);
|
||||
void WriteVariableWidthU8(uint8_t val, size_t chunk_length);
|
||||
void WriteVariableWidthS64(
|
||||
int64_t val, size_t chunk_length, size_t zigzag_exponent);
|
||||
void WriteVariableWidthS32(
|
||||
int32_t val, size_t chunk_length, size_t zigzag_exponent);
|
||||
void WriteVariableWidthS16(
|
||||
int16_t val, size_t chunk_length, size_t zigzag_exponent);
|
||||
void WriteVariableWidthS8(
|
||||
int8_t val, size_t chunk_length, size_t zigzag_exponent);
|
||||
void WriteVariableWidthS64(int64_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
void WriteVariableWidthS32(int32_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
void WriteVariableWidthS16(int16_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
void WriteVariableWidthS8(int8_t val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
|
||||
// Writes |val| using fixed bit width. Bit width is determined by |max_val|:
|
||||
// max_val 0 -> bit width 1
|
||||
@ -262,14 +261,10 @@ class BitWriterInterface {
|
||||
virtual size_t GetNumBits() const = 0;
|
||||
|
||||
// Provides direct access to the buffer data if implemented.
|
||||
virtual const uint8_t* GetData() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const uint8_t* GetData() const { return nullptr; }
|
||||
|
||||
// Returns buffer size in bytes.
|
||||
size_t GetDataSizeBytes() const {
|
||||
return NumBitsToNumWords<8>(GetNumBits());
|
||||
}
|
||||
size_t GetDataSizeBytes() const { return NumBitsToNumWords<8>(GetNumBits()); }
|
||||
|
||||
// Generates and returns byte array containing written bits.
|
||||
virtual std::vector<uint8_t> GetDataCopy() const = 0;
|
||||
@ -286,9 +281,7 @@ class BitWriterWord64 : public BitWriterInterface {
|
||||
|
||||
void WriteBits(uint64_t bits, size_t num_bits) override;
|
||||
|
||||
size_t GetNumBits() const override {
|
||||
return end_;
|
||||
}
|
||||
size_t GetNumBits() const override { return end_; }
|
||||
|
||||
const uint8_t* GetData() const override {
|
||||
return reinterpret_cast<const uint8_t*>(buffer_.data());
|
||||
@ -300,9 +293,7 @@ class BitWriterWord64 : public BitWriterInterface {
|
||||
|
||||
// Returns written stream as std::string, padded with zeroes so that the
|
||||
// length is a multiple of 64.
|
||||
std::string GetStreamPadded64() const {
|
||||
return BufferToStream(buffer_);
|
||||
}
|
||||
std::string GetStreamPadded64() const { return BufferToStream(buffer_); }
|
||||
|
||||
// Sets callback to emit bit sequences after every write.
|
||||
void SetCallback(std::function<void(const std::string&)> callback) {
|
||||
@ -312,8 +303,7 @@ class BitWriterWord64 : public BitWriterInterface {
|
||||
protected:
|
||||
// Sends string generated from arguments to callback_ if defined.
|
||||
void EmitSequence(uint64_t bits, size_t num_bits) const {
|
||||
if (callback_)
|
||||
callback_(BitsToStream(bits, num_bits));
|
||||
if (callback_) callback_(BitsToStream(bits, num_bits));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -363,8 +353,7 @@ class BitReaderInterface {
|
||||
static_assert(sizeof(T) <= 64, "Type size too large");
|
||||
uint64_t bits = 0;
|
||||
const size_t num_read = ReadBits(&bits, sizeof(T) * 8);
|
||||
if (num_read != sizeof(T) * 8)
|
||||
return false;
|
||||
if (num_read != sizeof(T) * 8) return false;
|
||||
memcpy(val, &bits, sizeof(T));
|
||||
return true;
|
||||
}
|
||||
@ -384,9 +373,7 @@ class BitReaderInterface {
|
||||
// the buffer stream ends with padding zeroes, and would accept this as a
|
||||
// 'soft' EOF. Implementations of this class do not necessarily need to
|
||||
// implement this, default behavior can simply delegate to ReachedEnd().
|
||||
virtual bool OnlyZeroesLeft() const {
|
||||
return ReachedEnd();
|
||||
}
|
||||
virtual bool OnlyZeroesLeft() const { return ReachedEnd(); }
|
||||
|
||||
// Reads value encoded with WriteVariableWidthXXX (see BitWriterInterface).
|
||||
// Reader and writer must use the same |chunk_length| and variable type.
|
||||
@ -395,14 +382,14 @@ class BitReaderInterface {
|
||||
bool ReadVariableWidthU32(uint32_t* val, size_t chunk_length);
|
||||
bool ReadVariableWidthU16(uint16_t* val, size_t chunk_length);
|
||||
bool ReadVariableWidthU8(uint8_t* val, size_t chunk_length);
|
||||
bool ReadVariableWidthS64(
|
||||
int64_t* val, size_t chunk_length, size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS32(
|
||||
int32_t* val, size_t chunk_length, size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS16(
|
||||
int16_t* val, size_t chunk_length, size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS8(
|
||||
int8_t* val, size_t chunk_length, size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS64(int64_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS32(int32_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS16(int16_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
bool ReadVariableWidthS8(int8_t* val, size_t chunk_length,
|
||||
size_t zigzag_exponent);
|
||||
|
||||
// Reads value written by WriteFixedWidth (|max_val| needs to be the same).
|
||||
// Returns true on success, false if the bit stream ends prematurely.
|
||||
@ -428,9 +415,7 @@ class BitReaderWord64 : public BitReaderInterface {
|
||||
|
||||
size_t ReadBits(uint64_t* bits, size_t num_bits) override;
|
||||
|
||||
size_t GetNumReadBits() const override {
|
||||
return pos_;
|
||||
}
|
||||
size_t GetNumReadBits() const override { return pos_; }
|
||||
|
||||
bool ReachedEnd() const override;
|
||||
bool OnlyZeroesLeft() const override;
|
||||
@ -445,8 +430,7 @@ class BitReaderWord64 : public BitReaderInterface {
|
||||
protected:
|
||||
// Sends string generated from arguments to callback_ if defined.
|
||||
void EmitSequence(uint64_t bits, size_t num_bits) const {
|
||||
if (callback_)
|
||||
callback_(BitsToStream(bits, num_bits));
|
||||
if (callback_) callback_(BitsToStream(bits, num_bits));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -20,11 +20,11 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <tuple>
|
||||
@ -74,10 +74,10 @@ class HuffmanCodec {
|
||||
std::vector<uint32_t> queue_vector;
|
||||
queue_vector.reserve(hist.size());
|
||||
std::priority_queue<uint32_t, std::vector<uint32_t>,
|
||||
std::function<bool(uint32_t, uint32_t)>>
|
||||
queue(std::bind(&HuffmanCodec::LeftIsBigger, this,
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
std::move(queue_vector));
|
||||
std::function<bool(uint32_t, uint32_t)>>
|
||||
queue(std::bind(&HuffmanCodec::LeftIsBigger, this,
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
std::move(queue_vector));
|
||||
|
||||
// Put all leaves in the queue.
|
||||
for (const auto& pair : hist) {
|
||||
@ -153,11 +153,9 @@ class HuffmanCodec {
|
||||
|
||||
for (const Node& node : nodes_) {
|
||||
code << indent2 << "{";
|
||||
if (value_is_text)
|
||||
code << "\"";
|
||||
if (value_is_text) code << "\"";
|
||||
code << node.value;
|
||||
if (value_is_text)
|
||||
code << "\"";
|
||||
if (value_is_text) code << "\"";
|
||||
code << ", " << node.left << ", " << node.right << "},\n";
|
||||
}
|
||||
|
||||
@ -172,9 +170,7 @@ class HuffmanCodec {
|
||||
// Where w stands for the weight of the node.
|
||||
// Right tree branches appear above left branches. Taking the right path
|
||||
// adds 1 to the code, taking the left adds 0.
|
||||
void PrintTree(std::ostream& out) const {
|
||||
PrintTreeInternal(out, root_, 0);
|
||||
}
|
||||
void PrintTree(std::ostream& out) const { PrintTreeInternal(out, root_, 0); }
|
||||
|
||||
// Traverses the tree and prints the Huffman table: value, code
|
||||
// and optionally node weight for every leaf.
|
||||
@ -188,23 +184,20 @@ class HuffmanCodec {
|
||||
queue.pop();
|
||||
if (!RightOf(node) && !LeftOf(node)) {
|
||||
out << ValueOf(node);
|
||||
if (print_weights)
|
||||
out << " " << WeightOf(node);
|
||||
if (print_weights) out << " " << WeightOf(node);
|
||||
out << " " << code << std::endl;
|
||||
} else {
|
||||
if (LeftOf(node))
|
||||
queue.emplace(LeftOf(node), code + "0");
|
||||
if (LeftOf(node)) queue.emplace(LeftOf(node), code + "0");
|
||||
|
||||
if (RightOf(node))
|
||||
queue.emplace(RightOf(node), code + "1");
|
||||
if (RightOf(node)) queue.emplace(RightOf(node), code + "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the Huffman table. The table was built at at construction time,
|
||||
// this function just returns a const reference.
|
||||
const std::unordered_map<Val, std::pair<uint64_t, size_t>>&
|
||||
GetEncodingTable() const {
|
||||
const std::unordered_map<Val, std::pair<uint64_t, size_t>>& GetEncodingTable()
|
||||
const {
|
||||
return encoding_table_;
|
||||
}
|
||||
|
||||
@ -212,8 +205,7 @@ class HuffmanCodec {
|
||||
// |bits|. Returns false of |val| is not in the Huffman table.
|
||||
bool Encode(const Val& val, uint64_t* bits, size_t* num_bits) const {
|
||||
auto it = encoding_table_.find(val);
|
||||
if (it == encoding_table_.end())
|
||||
return false;
|
||||
if (it == encoding_table_.end()) return false;
|
||||
*bits = it->second.first;
|
||||
*num_bits = it->second.second;
|
||||
return true;
|
||||
@ -225,8 +217,8 @@ class HuffmanCodec {
|
||||
// |read_bit| has type bool func(bool* bit). When called, the next bit is
|
||||
// stored in |bit|. |read_bit| returns false if the stream terminates
|
||||
// prematurely.
|
||||
bool DecodeFromStream(
|
||||
const std::function<bool(bool*)>& read_bit, Val* val) const {
|
||||
bool DecodeFromStream(const std::function<bool(bool*)>& read_bit,
|
||||
Val* val) const {
|
||||
uint32_t node = root_;
|
||||
while (true) {
|
||||
assert(node);
|
||||
@ -237,8 +229,7 @@ class HuffmanCodec {
|
||||
}
|
||||
|
||||
bool go_right;
|
||||
if (!read_bit(&go_right))
|
||||
return false;
|
||||
if (!read_bit(&go_right)) return false;
|
||||
|
||||
if (go_right)
|
||||
node = RightOf(node);
|
||||
@ -246,35 +237,25 @@ class HuffmanCodec {
|
||||
node = LeftOf(node);
|
||||
}
|
||||
|
||||
assert (0);
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns value of the node referenced by |handle|.
|
||||
Val ValueOf(uint32_t node) const {
|
||||
return nodes_.at(node).value;
|
||||
}
|
||||
Val ValueOf(uint32_t node) const { return nodes_.at(node).value; }
|
||||
|
||||
// Returns left child of |node|.
|
||||
uint32_t LeftOf(uint32_t node) const {
|
||||
return nodes_.at(node).left;
|
||||
}
|
||||
uint32_t LeftOf(uint32_t node) const { return nodes_.at(node).left; }
|
||||
|
||||
// Returns right child of |node|.
|
||||
uint32_t RightOf(uint32_t node) const {
|
||||
return nodes_.at(node).right;
|
||||
}
|
||||
uint32_t RightOf(uint32_t node) const { return nodes_.at(node).right; }
|
||||
|
||||
// Returns weight of |node|.
|
||||
uint32_t WeightOf(uint32_t node) const {
|
||||
return nodes_.at(node).weight;
|
||||
}
|
||||
uint32_t WeightOf(uint32_t node) const { return nodes_.at(node).weight; }
|
||||
|
||||
// Returns id of |node|.
|
||||
uint32_t IdOf(uint32_t node) const {
|
||||
return nodes_.at(node).id;
|
||||
}
|
||||
uint32_t IdOf(uint32_t node) const { return nodes_.at(node).id; }
|
||||
|
||||
// Returns mutable reference to value of |node|.
|
||||
Val& MutableValueOf(uint32_t node) {
|
||||
@ -295,20 +276,16 @@ class HuffmanCodec {
|
||||
}
|
||||
|
||||
// Returns mutable reference to weight of |node|.
|
||||
uint32_t& MutableWeightOf(uint32_t node) {
|
||||
return nodes_.at(node).weight;
|
||||
}
|
||||
uint32_t& MutableWeightOf(uint32_t node) { return nodes_.at(node).weight; }
|
||||
|
||||
// Returns mutable reference to id of |node|.
|
||||
uint32_t& MutableIdOf(uint32_t node) {
|
||||
return nodes_.at(node).id;
|
||||
}
|
||||
uint32_t& MutableIdOf(uint32_t node) { return nodes_.at(node).id; }
|
||||
|
||||
// Returns true if |left| has bigger weight than |right|. Node ids are
|
||||
// used as tie-breaker.
|
||||
bool LeftIsBigger(uint32_t left, uint32_t right) const {
|
||||
if (WeightOf(left) == WeightOf(right)) {
|
||||
assert (IdOf(left) != IdOf(right));
|
||||
assert(IdOf(left) != IdOf(right));
|
||||
return IdOf(left) > IdOf(right);
|
||||
}
|
||||
return WeightOf(left) > WeightOf(right);
|
||||
@ -316,8 +293,7 @@ class HuffmanCodec {
|
||||
|
||||
// Prints subtree (helper function used by PrintTree).
|
||||
void PrintTreeInternal(std::ostream& out, uint32_t node, size_t depth) const {
|
||||
if (!node)
|
||||
return;
|
||||
if (!node) return;
|
||||
|
||||
const size_t kTextFieldWidth = 7;
|
||||
|
||||
@ -348,7 +324,7 @@ class HuffmanCodec {
|
||||
void CreateEncodingTable() {
|
||||
struct Context {
|
||||
Context(uint32_t in_node, uint64_t in_bits, size_t in_depth)
|
||||
: node(in_node), bits(in_bits), depth(in_depth) {}
|
||||
: node(in_node), bits(in_bits), depth(in_depth) {}
|
||||
uint32_t node;
|
||||
// Huffman tree depth cannot exceed 64 as histogramm counts are expected
|
||||
// to be positive and limited by numeric_limits<uint32_t>::max().
|
||||
@ -373,8 +349,7 @@ class HuffmanCodec {
|
||||
assert(insertion_result.second);
|
||||
(void)insertion_result;
|
||||
} else {
|
||||
if (LeftOf(node))
|
||||
queue.emplace(LeftOf(node), bits, depth + 1);
|
||||
if (LeftOf(node)) queue.emplace(LeftOf(node), bits, depth + 1);
|
||||
|
||||
if (RightOf(node))
|
||||
queue.emplace(RightOf(node), bits | (1ULL << depth), depth + 1);
|
||||
|
@ -272,7 +272,7 @@ bool IntrusiveList<NodeType>::empty() const {
|
||||
return sentinel_.NextNode() == nullptr;
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
void IntrusiveList<NodeType>::clear() {
|
||||
while (!empty()) {
|
||||
front().RemoveFromList();
|
||||
|
@ -110,7 +110,7 @@ template <class NodeType>
|
||||
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase()
|
||||
: next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(
|
||||
const IntrusiveNodeBase&) {
|
||||
next_node_ = nullptr;
|
||||
@ -118,7 +118,7 @@ inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(
|
||||
is_sentinel_ = false;
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
|
||||
const IntrusiveNodeBase&) {
|
||||
assert(!is_sentinel_);
|
||||
@ -128,7 +128,7 @@ inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(IntrusiveNodeBase&& that)
|
||||
: next_node_(nullptr),
|
||||
previous_node_(nullptr),
|
||||
@ -140,19 +140,19 @@ inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(IntrusiveNodeBase&& that)
|
||||
that.ReplaceWith(this);
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
IntrusiveNodeBase<NodeType>::~IntrusiveNodeBase() {
|
||||
assert(is_sentinel_ || !IsInAList());
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
|
||||
IntrusiveNodeBase&& that) {
|
||||
that.ReplaceWith(this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
inline bool IntrusiveNodeBase<NodeType>::IsInAList() const {
|
||||
return next_node_ != nullptr;
|
||||
}
|
||||
@ -199,7 +199,7 @@ template <class NodeType>
|
||||
inline void IntrusiveNodeBase<NodeType>::RemoveFromList() {
|
||||
assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
|
||||
assert(this->IsInAList() &&
|
||||
"Cannot remove a node from a list if it is not in a list.");
|
||||
"Cannot remove a node from a list if it is not in a list.");
|
||||
|
||||
this->next_node_->previous_node_ = this->previous_node_;
|
||||
this->previous_node_->next_node_ = this->next_node_;
|
||||
@ -207,16 +207,16 @@ inline void IntrusiveNodeBase<NodeType>::RemoveFromList() {
|
||||
this->previous_node_ = nullptr;
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) {
|
||||
if (this->is_sentinel_) {
|
||||
assert(target->IsEmptyList() &&
|
||||
"If target is not an empty list, the nodes in that list would not "
|
||||
"be linked to a sentinel.");
|
||||
"If target is not an empty list, the nodes in that list would not "
|
||||
"be linked to a sentinel.");
|
||||
} else {
|
||||
assert(IsInAList() && "The node being replaced must be in a list.");
|
||||
assert(!target->is_sentinel_ &&
|
||||
"Cannot turn a sentinel node into one that is not.");
|
||||
"Cannot turn a sentinel node into one that is not.");
|
||||
}
|
||||
|
||||
if (!this->IsEmptyList()) {
|
||||
@ -245,13 +245,13 @@ void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) {
|
||||
}
|
||||
}
|
||||
|
||||
template<class NodeType>
|
||||
template <class NodeType>
|
||||
bool IntrusiveNodeBase<NodeType>::IsEmptyList() {
|
||||
if (next_node_ == this) {
|
||||
assert(is_sentinel_ &&
|
||||
"None sentinel nodes should never point to themselves.");
|
||||
"None sentinel nodes should never point to themselves.");
|
||||
assert(previous_node_ == this &&
|
||||
"Inconsistency with the previous and next nodes.");
|
||||
"Inconsistency with the previous and next nodes.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -94,16 +94,16 @@ class MoveToFront {
|
||||
bool HasValue(const Val& value) const;
|
||||
|
||||
// Returns the number of elements in the move-to-front sequence.
|
||||
uint32_t GetSize() const {
|
||||
return SizeOf(root_);
|
||||
}
|
||||
uint32_t GetSize() const { return SizeOf(root_); }
|
||||
|
||||
protected:
|
||||
// Internal tree data structure uses handles instead of pointers. Leaves and
|
||||
// root parent reference a singleton under handle 0. Although dereferencing
|
||||
// a null pointer is not possible, inappropriate access to handle 0 would
|
||||
// cause an assertion. Handles are not garbage collected if value was deprecated
|
||||
// with DeprecateValue(). But handles are recycled when a node is repositioned.
|
||||
// cause an assertion. Handles are not garbage collected if value was
|
||||
// deprecated
|
||||
// with DeprecateValue(). But handles are recycled when a node is
|
||||
// repositioned.
|
||||
|
||||
// Internal tree data structure node.
|
||||
struct Node {
|
||||
@ -125,7 +125,8 @@ class MoveToFront {
|
||||
};
|
||||
|
||||
// Creates node and sets correct values. Non-NIL nodes should be created only
|
||||
// through this function. If the node with this value has been created previously
|
||||
// through this function. If the node with this value has been created
|
||||
// previously
|
||||
// and since orphaned, reuses the old node instead of creating a new one.
|
||||
uint32_t CreateNode(uint32_t timestamp, const Val& value) {
|
||||
uint32_t handle = static_cast<uint32_t>(nodes_.size());
|
||||
@ -137,7 +138,8 @@ class MoveToFront {
|
||||
node.timestamp = timestamp;
|
||||
node.value = value;
|
||||
node.size = 1;
|
||||
// Non-NIL nodes start with height 1 because their NIL children are leaves.
|
||||
// Non-NIL nodes start with height 1 because their NIL children are
|
||||
// leaves.
|
||||
node.height = 1;
|
||||
} else {
|
||||
// Reuse old node.
|
||||
@ -157,24 +159,16 @@ class MoveToFront {
|
||||
// ParentOf(LeftestDescendentOf(RightOf(node)))
|
||||
|
||||
// Returns value of the node referenced by |handle|.
|
||||
Val ValueOf(uint32_t node) const {
|
||||
return nodes_.at(node).value;
|
||||
}
|
||||
Val ValueOf(uint32_t node) const { return nodes_.at(node).value; }
|
||||
|
||||
// Returns left child of |node|.
|
||||
uint32_t LeftOf(uint32_t node) const {
|
||||
return nodes_.at(node).left;
|
||||
}
|
||||
uint32_t LeftOf(uint32_t node) const { return nodes_.at(node).left; }
|
||||
|
||||
// Returns right child of |node|.
|
||||
uint32_t RightOf(uint32_t node) const {
|
||||
return nodes_.at(node).right;
|
||||
}
|
||||
uint32_t RightOf(uint32_t node) const { return nodes_.at(node).right; }
|
||||
|
||||
// Returns parent of |node|.
|
||||
uint32_t ParentOf(uint32_t node) const {
|
||||
return nodes_.at(node).parent;
|
||||
}
|
||||
uint32_t ParentOf(uint32_t node) const { return nodes_.at(node).parent; }
|
||||
|
||||
// Returns timestamp of |node|.
|
||||
uint32_t TimestampOf(uint32_t node) const {
|
||||
@ -183,14 +177,10 @@ class MoveToFront {
|
||||
}
|
||||
|
||||
// Returns size of |node|.
|
||||
uint32_t SizeOf(uint32_t node) const {
|
||||
return nodes_.at(node).size;
|
||||
}
|
||||
uint32_t SizeOf(uint32_t node) const { return nodes_.at(node).size; }
|
||||
|
||||
// Returns height of |node|.
|
||||
uint32_t HeightOf(uint32_t node) const {
|
||||
return nodes_.at(node).height;
|
||||
}
|
||||
uint32_t HeightOf(uint32_t node) const { return nodes_.at(node).height; }
|
||||
|
||||
// Returns mutable reference to value of |node|.
|
||||
Val& MutableValueOf(uint32_t node) {
|
||||
@ -347,8 +337,7 @@ class MultiMoveToFront {
|
||||
// Removes |value| from all sequences which have it.
|
||||
void RemoveFromAll(const Val& value) {
|
||||
auto it = val_to_mtfs_.find(value);
|
||||
if (it == val_to_mtfs_.end())
|
||||
return;
|
||||
if (it == val_to_mtfs_.end()) return;
|
||||
|
||||
auto& mtfs_containing_value = it->second;
|
||||
for (uint64_t mtf : mtfs_containing_value) {
|
||||
@ -371,15 +360,12 @@ class MultiMoveToFront {
|
||||
}
|
||||
|
||||
// Returns size of |mtf| sequence.
|
||||
uint32_t GetSize(uint64_t mtf) {
|
||||
return GetMtf(mtf).GetSize();
|
||||
}
|
||||
uint32_t GetSize(uint64_t mtf) { return GetMtf(mtf).GetSize(); }
|
||||
|
||||
// Promotes |value| in all sequences which have it.
|
||||
void Promote(const Val& value) {
|
||||
const auto it = val_to_mtfs_.find(value);
|
||||
if (it == val_to_mtfs_.end())
|
||||
return;
|
||||
if (it == val_to_mtfs_.end()) return;
|
||||
|
||||
const auto& mtfs_containing_value = it->second;
|
||||
for (uint64_t mtf : mtfs_containing_value) {
|
||||
@ -426,8 +412,7 @@ class MultiMoveToFront {
|
||||
template <typename Val>
|
||||
bool MoveToFront<Val>::Insert(const Val& value) {
|
||||
auto it = value_to_node_.find(value);
|
||||
if (it != value_to_node_.end() && IsInTree(it->second))
|
||||
return false;
|
||||
if (it != value_to_node_.end() && IsInTree(it->second)) return false;
|
||||
|
||||
const uint32_t old_size = GetSize();
|
||||
(void)old_size;
|
||||
@ -445,14 +430,11 @@ bool MoveToFront<Val>::Insert(const Val& value) {
|
||||
template <typename Val>
|
||||
bool MoveToFront<Val>::Remove(const Val& value) {
|
||||
auto it = value_to_node_.find(value);
|
||||
if (it == value_to_node_.end())
|
||||
return false;
|
||||
if (it == value_to_node_.end()) return false;
|
||||
|
||||
if (!IsInTree(it->second))
|
||||
return false;
|
||||
if (!IsInTree(it->second)) return false;
|
||||
|
||||
if (last_accessed_value_ == value)
|
||||
last_accessed_value_valid_ = false;
|
||||
if (last_accessed_value_ == value) last_accessed_value_valid_ = false;
|
||||
|
||||
const uint32_t orphan = RemoveNode(it->second);
|
||||
(void)orphan;
|
||||
@ -494,8 +476,7 @@ bool MoveToFront<Val>::RankFromValue(const Val& value, uint32_t* rank) {
|
||||
uint32_t node = target;
|
||||
*rank = 1 + SizeOf(LeftOf(node));
|
||||
while (node) {
|
||||
if (IsRightChild(node))
|
||||
*rank += 1 + SizeOf(LeftOf(ParentOf(node)));
|
||||
if (IsRightChild(node)) *rank += 1 + SizeOf(LeftOf(ParentOf(node)));
|
||||
node = ParentOf(node);
|
||||
}
|
||||
|
||||
@ -532,8 +513,7 @@ bool MoveToFront<Val>::Promote(const Val& value) {
|
||||
}
|
||||
|
||||
const uint32_t old_size = GetSize();
|
||||
if (old_size == 1)
|
||||
return ValueOf(root_) == value;
|
||||
if (old_size == 1) return ValueOf(root_) == value;
|
||||
|
||||
const auto it = value_to_node_.find(value);
|
||||
if (it == value_to_node_.end()) {
|
||||
@ -663,8 +643,7 @@ void MoveToFront<Val>::InsertNode(uint32_t node) {
|
||||
// Added node to the right subtree.
|
||||
if (parent_balance > 1) {
|
||||
// Parent is right heavy, rotate left.
|
||||
if (BalanceOf(node) < 0)
|
||||
RotateRight(node);
|
||||
if (BalanceOf(node) < 0) RotateRight(node);
|
||||
parent = RotateLeft(parent);
|
||||
} else if (parent_balance == 0 || parent_balance == -1) {
|
||||
// Parent is balanced or left heavy, no need to balance further.
|
||||
@ -674,8 +653,7 @@ void MoveToFront<Val>::InsertNode(uint32_t node) {
|
||||
// Added node to the left subtree.
|
||||
if (parent_balance < -1) {
|
||||
// Parent is left heavy, rotate right.
|
||||
if (BalanceOf(node) > 0)
|
||||
RotateLeft(node);
|
||||
if (BalanceOf(node) > 0) RotateLeft(node);
|
||||
parent = RotateRight(parent);
|
||||
} else if (parent_balance == 0 || parent_balance == 1) {
|
||||
// Parent is balanced or right heavy, no need to balance further.
|
||||
@ -695,9 +673,11 @@ template <typename Val>
|
||||
uint32_t MoveToFront<Val>::RemoveNode(uint32_t node) {
|
||||
if (LeftOf(node) && RightOf(node)) {
|
||||
// If |node| has two children, then use another node as scapegoat and swap
|
||||
// their contents. We pick the scapegoat on the side of the tree which has more nodes.
|
||||
const uint32_t scapegoat = SizeOf(LeftOf(node)) >= SizeOf(RightOf(node)) ?
|
||||
RightestDescendantOf(LeftOf(node)) : LeftestDescendantOf(RightOf(node));
|
||||
// their contents. We pick the scapegoat on the side of the tree which has
|
||||
// more nodes.
|
||||
const uint32_t scapegoat = SizeOf(LeftOf(node)) >= SizeOf(RightOf(node))
|
||||
? RightestDescendantOf(LeftOf(node))
|
||||
: LeftestDescendantOf(RightOf(node));
|
||||
assert(scapegoat);
|
||||
std::swap(MutableValueOf(node), MutableValueOf(scapegoat));
|
||||
std::swap(MutableTimestampOf(node), MutableTimestampOf(scapegoat));
|
||||
@ -713,8 +693,7 @@ uint32_t MoveToFront<Val>::RemoveNode(uint32_t node) {
|
||||
uint32_t child = RightOf(node) ? RightOf(node) : LeftOf(node);
|
||||
|
||||
// Orphan |node| and reconnect parent and child.
|
||||
if (child)
|
||||
MutableParentOf(child) = parent;
|
||||
if (child) MutableParentOf(child) = parent;
|
||||
|
||||
if (parent) {
|
||||
if (LeftOf(parent) == node)
|
||||
@ -729,8 +708,7 @@ uint32_t MoveToFront<Val>::RemoveNode(uint32_t node) {
|
||||
UpdateNode(node);
|
||||
const uint32_t orphan = node;
|
||||
|
||||
if (root_ == node)
|
||||
root_ = child;
|
||||
if (root_ == node) root_ = child;
|
||||
|
||||
// Removal is finished. Start the balancing process.
|
||||
bool needs_rebalancing = true;
|
||||
@ -751,8 +729,7 @@ uint32_t MoveToFront<Val>::RemoveNode(uint32_t node) {
|
||||
if (parent_balance < -1) {
|
||||
// Parent is left heavy, rotate right.
|
||||
const uint32_t sibling = LeftOf(parent);
|
||||
if (BalanceOf(sibling) > 0)
|
||||
RotateLeft(sibling);
|
||||
if (BalanceOf(sibling) > 0) RotateLeft(sibling);
|
||||
parent = RotateRight(parent);
|
||||
}
|
||||
} else {
|
||||
@ -760,8 +737,7 @@ uint32_t MoveToFront<Val>::RemoveNode(uint32_t node) {
|
||||
if (parent_balance > 1) {
|
||||
// Parent is right heavy, rotate left.
|
||||
const uint32_t sibling = RightOf(parent);
|
||||
if (BalanceOf(sibling) < 0)
|
||||
RotateRight(sibling);
|
||||
if (BalanceOf(sibling) < 0) RotateRight(sibling);
|
||||
parent = RotateLeft(parent);
|
||||
}
|
||||
}
|
||||
@ -784,8 +760,7 @@ uint32_t MoveToFront<Val>::RotateLeft(const uint32_t node) {
|
||||
|
||||
// LeftOf(pivot) gets attached to node in place of pivot.
|
||||
MutableRightOf(node) = LeftOf(pivot);
|
||||
if (RightOf(node))
|
||||
MutableParentOf(RightOf(node)) = node;
|
||||
if (RightOf(node)) MutableParentOf(RightOf(node)) = node;
|
||||
|
||||
// Pivot gets attached to ParentOf(node) in place of node.
|
||||
MutableParentOf(pivot) = ParentOf(node);
|
||||
@ -815,8 +790,7 @@ uint32_t MoveToFront<Val>::RotateRight(const uint32_t node) {
|
||||
|
||||
// RightOf(pivot) gets attached to node in place of pivot.
|
||||
MutableLeftOf(node) = RightOf(pivot);
|
||||
if (LeftOf(node))
|
||||
MutableParentOf(LeftOf(node)) = node;
|
||||
if (LeftOf(node)) MutableParentOf(LeftOf(node)) = node;
|
||||
|
||||
// Pivot gets attached to ParentOf(node) in place of node.
|
||||
MutableParentOf(pivot) = ParentOf(node);
|
||||
|
@ -68,8 +68,8 @@ EncodeNumberStatus ParseAndEncodeIntegerNumber(
|
||||
const uint32_t bit_width = AssumedBitWidth(type);
|
||||
|
||||
if (bit_width > 64) {
|
||||
ErrorMsgStream(error_msg) << "Unsupported " << bit_width
|
||||
<< "-bit integer literals";
|
||||
ErrorMsgStream(error_msg)
|
||||
<< "Unsupported " << bit_width << "-bit integer literals";
|
||||
return EncodeNumberStatus::kUnsupported;
|
||||
}
|
||||
|
||||
@ -182,8 +182,8 @@ EncodeNumberStatus ParseAndEncodeFloatingPointNumber(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ErrorMsgStream(error_msg) << "Unsupported " << bit_width
|
||||
<< "-bit float literals";
|
||||
ErrorMsgStream(error_msg)
|
||||
<< "Unsupported " << bit_width << "-bit float literals";
|
||||
return EncodeNumberStatus::kUnsupported;
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ bool ParseNumber(const char* text, T* value_pointer) {
|
||||
static_assert(sizeof(T) > 1,
|
||||
"Single-byte types are not supported in this parse method");
|
||||
|
||||
if (!text) return false;
|
||||
if (!text) return false;
|
||||
std::istringstream text_stream(text);
|
||||
// Allow both decimal and hex input for integers.
|
||||
// It also allows octal input, but we don't care about that case.
|
||||
|
@ -118,7 +118,7 @@ const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const {
|
||||
|
||||
BasicBlock::DominatorIterator BasicBlock::pdom_begin() {
|
||||
return DominatorIterator(
|
||||
this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
|
||||
this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
|
||||
}
|
||||
|
||||
const BasicBlock::DominatorIterator BasicBlock::pdom_end() const {
|
||||
|
@ -108,7 +108,8 @@ class BasicBlock {
|
||||
void RegisterBranchInstruction(SpvOp branch_instruction);
|
||||
|
||||
/// Adds @p next BasicBlocks as successors of this BasicBlock
|
||||
void RegisterSuccessors(const std::vector<BasicBlock*>& next = std::vector<BasicBlock*>());
|
||||
void RegisterSuccessors(
|
||||
const std::vector<BasicBlock*>& next = std::vector<BasicBlock*>());
|
||||
|
||||
/// Returns true if the id of the BasicBlock matches
|
||||
bool operator==(const BasicBlock& other) const { return other.id_ == id_; }
|
||||
|
@ -19,9 +19,8 @@
|
||||
|
||||
namespace libspirv {
|
||||
|
||||
Construct::Construct(ConstructType construct_type,
|
||||
BasicBlock* entry, BasicBlock* exit,
|
||||
std::vector<Construct*> constructs)
|
||||
Construct::Construct(ConstructType construct_type, BasicBlock* entry,
|
||||
BasicBlock* exit, std::vector<Construct*> constructs)
|
||||
: type_(construct_type),
|
||||
corresponding_constructs_(constructs),
|
||||
entry_block_(entry),
|
||||
@ -38,11 +37,16 @@ std::vector<Construct*>& Construct::corresponding_constructs() {
|
||||
|
||||
bool ValidateConstructSize(ConstructType type, size_t size) {
|
||||
switch (type) {
|
||||
case ConstructType::kSelection: return size == 0;
|
||||
case ConstructType::kContinue: return size == 1;
|
||||
case ConstructType::kLoop: return size == 1;
|
||||
case ConstructType::kCase: return size >= 1;
|
||||
default: assert(1 == 0 && "Type not defined");
|
||||
case ConstructType::kSelection:
|
||||
return size == 0;
|
||||
case ConstructType::kContinue:
|
||||
return size == 1;
|
||||
case ConstructType::kLoop:
|
||||
return size == 1;
|
||||
case ConstructType::kCase:
|
||||
return size >= 1;
|
||||
default:
|
||||
assert(1 == 0 && "Type not defined");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -84,4 +84,3 @@ class Decoration {
|
||||
} // namespace libspirv
|
||||
|
||||
#endif /// LIBSPIRV_VAL_DECORATION_H_
|
||||
|
||||
|
@ -17,14 +17,14 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "cfa.h"
|
||||
#include "val/basic_block.h"
|
||||
#include "val/construct.h"
|
||||
#include "validate.h"
|
||||
#include "cfa.h"
|
||||
|
||||
using std::ignore;
|
||||
using std::list;
|
||||
@ -275,13 +275,9 @@ void Function::ComputeAugmentedCFG() {
|
||||
auto succ_func = [](const BasicBlock* b) { return b->successors(); };
|
||||
auto pred_func = [](const BasicBlock* b) { return b->predecessors(); };
|
||||
spvtools::CFA<BasicBlock>::ComputeAugmentedCFG(
|
||||
ordered_blocks_,
|
||||
&pseudo_entry_block_,
|
||||
&pseudo_exit_block_,
|
||||
&augmented_successors_map_,
|
||||
&augmented_predecessors_map_,
|
||||
succ_func,
|
||||
pred_func);
|
||||
ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
|
||||
&augmented_successors_map_, &augmented_predecessors_map_, succ_func,
|
||||
pred_func);
|
||||
};
|
||||
|
||||
Construct& Function::AddConstruct(const Construct& new_construct) {
|
||||
|
@ -183,7 +183,8 @@ class Function {
|
||||
GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
|
||||
/// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
|
||||
/// a loop header block to its continue target, if they are different blocks.
|
||||
GetBlocksFunction AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
|
||||
GetBlocksFunction
|
||||
AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
|
||||
/// Returns the block predecessors function for the augmented CFG.
|
||||
GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
|
||||
|
||||
|
@ -72,9 +72,7 @@ class Instruction {
|
||||
}
|
||||
|
||||
/// Provides direct access to the stored C instruction object.
|
||||
const spv_parsed_instruction_t& c_inst() const {
|
||||
return inst_;
|
||||
}
|
||||
const spv_parsed_instruction_t& c_inst() const { return inst_; }
|
||||
|
||||
// Casts the words belonging to the operand under |index| to |T| and returns.
|
||||
template <typename T>
|
||||
|
@ -358,15 +358,14 @@ class ValidationState_t {
|
||||
|
||||
// Provides detailed information on matrix type.
|
||||
// Returns false iff |id| is not matrix type.
|
||||
bool GetMatrixTypeInfo(
|
||||
uint32_t id, uint32_t* num_rows, uint32_t* num_cols,
|
||||
uint32_t* column_type, uint32_t* component_type) const;
|
||||
bool GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, uint32_t* num_cols,
|
||||
uint32_t* column_type, uint32_t* component_type) const;
|
||||
|
||||
// Collects struct member types into |member_types|.
|
||||
// Returns false iff not struct type or has no members.
|
||||
// Deletes prior contents of |member_types|.
|
||||
bool GetStructMemberTypes(
|
||||
uint32_t struct_type_id, std::vector<uint32_t>* member_types) const;
|
||||
bool GetStructMemberTypes(uint32_t struct_type_id,
|
||||
std::vector<uint32_t>* member_types) const;
|
||||
|
||||
// Returns true iff |id| is a type corresponding to the name of the function.
|
||||
// Only works for types not for objects.
|
||||
@ -393,8 +392,8 @@ class ValidationState_t {
|
||||
size_t operand_index) const;
|
||||
|
||||
// Provides information on pointer type. Returns false iff not pointer type.
|
||||
bool GetPointerTypeInfo(
|
||||
uint32_t id, uint32_t* data_type, uint32_t* storage_class) const;
|
||||
bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
|
||||
uint32_t* storage_class) const;
|
||||
|
||||
private:
|
||||
ValidationState_t(const ValidationState_t&);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user