Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2022-02-06 08:04:37 -08:00
parent 56dec59c8e
commit e844bbdb03
10 changed files with 2932 additions and 27 deletions

View File

@ -1 +1 @@
"v2022.2-dev", "SPIRV-Tools v2022.2-dev 2e9ea79f27f42b1ea49e66ce7ba0a5c1ab75ea81"
"v2022.2-dev", "SPIRV-Tools v2022.2-dev 6875e96bcbc938fb6a208e1c5c630a32bfeef49d"

2658
3rdparty/spirv-tools/source/diff/diff.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

48
3rdparty/spirv-tools/source/diff/diff.h vendored Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2022 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_DIFF_DIFF_H_
#define SOURCE_DIFF_DIFF_H_
#include "source/opt/ir_context.h"
namespace spvtools {
namespace diff {
struct Options {
bool ignore_set_binding = false;
bool ignore_location = false;
bool indent = false;
bool no_header = false;
bool color_output = false;
bool dump_id_map = false;
};
// Given two SPIR-V modules, this function outputs the textual diff of their
// assembly in `out`. The diff is *semantic*, so that the ordering of certain
// instructions wouldn't matter.
//
// The output is a disassembly of src, with diff(1)-style + and - lines that
// show how the src is changed into dst. To make this disassembly
// self-consistent, the ids that are output are all in the space of the src
// module; e.g. any + lines (showing instructions from the dst module) have
// their ids mapped to the matched instruction in the src module (or a new id
// allocated in the src module if unmatched).
spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out,
Options options);
} // namespace diff
} // namespace spvtools
#endif // SOURCE_DIFF_DIFF_H_

195
3rdparty/spirv-tools/source/diff/lcs.h vendored Normal file
View File

@ -0,0 +1,195 @@
// Copyright (c) 2022 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_DIFF_LCS_H_
#define SOURCE_DIFF_LCS_H_
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <functional>
#include <vector>
namespace spvtools {
namespace diff {
// The result of a diff.
using DiffMatch = std::vector<bool>;
// Helper class to find the longest common subsequence between two function
// bodies.
template <typename Sequence>
class LongestCommonSubsequence {
public:
LongestCommonSubsequence(const Sequence& src, const Sequence& dst)
: src_(src),
dst_(dst),
table_(src.size(), std::vector<DiffMatchEntry>(dst.size())) {}
// Given two sequences, it creates a matching between them. The elements are
// simply marked as matched in src and dst, with any unmatched element in src
// implying a removal and any unmatched element in dst implying an addition.
//
// Returns the length of the longest common subsequence.
template <typename T>
size_t Get(std::function<bool(T src_elem, T dst_elem)> match,
DiffMatch* src_match_result, DiffMatch* dst_match_result);
private:
template <typename T>
size_t CalculateLCS(size_t src_start, size_t dst_start,
std::function<bool(T src_elem, T dst_elem)> match);
void RetrieveMatch(DiffMatch* src_match_result, DiffMatch* dst_match_result);
bool IsInBound(size_t src_index, size_t dst_index) {
return src_index < src_.size() && dst_index < dst_.size();
}
bool IsCalculated(size_t src_index, size_t dst_index) {
assert(IsInBound(src_index, dst_index));
return table_[src_index][dst_index].valid;
}
size_t GetMemoizedLength(size_t src_index, size_t dst_index) {
if (!IsInBound(src_index, dst_index)) {
return 0;
}
assert(IsCalculated(src_index, dst_index));
return table_[src_index][dst_index].best_match_length;
}
bool IsMatched(size_t src_index, size_t dst_index) {
assert(IsCalculated(src_index, dst_index));
return table_[src_index][dst_index].matched;
}
const Sequence& src_;
const Sequence& dst_;
struct DiffMatchEntry {
size_t best_match_length = 0;
// Whether src[i] and dst[j] matched. This is an optimization to avoid
// calling the `match` function again when walking the LCS table.
bool matched = false;
// Use for the recursive algorithm to know if the contents of this entry are
// valid.
bool valid = false;
};
std::vector<std::vector<DiffMatchEntry>> table_;
};
template <typename Sequence>
template <typename T>
size_t LongestCommonSubsequence<Sequence>::Get(
std::function<bool(T src_elem, T dst_elem)> match,
DiffMatch* src_match_result, DiffMatch* dst_match_result) {
size_t best_match_length = CalculateLCS(0, 0, match);
RetrieveMatch(src_match_result, dst_match_result);
return best_match_length;
}
template <typename Sequence>
template <typename T>
size_t LongestCommonSubsequence<Sequence>::CalculateLCS(
size_t src_start, size_t dst_start,
std::function<bool(T src_elem, T dst_elem)> match) {
// The LCS algorithm is simple. Given sequences s and d, with a:b depicting a
// range in python syntax:
//
// lcs(s[i:], d[j:]) =
// lcs(s[i+1:], d[j+1:]) + 1 if s[i] == d[j]
// max(lcs(s[i+1:], d[j:]), lcs(s[i:], d[j+1:])) o.w.
//
// Once the LCS table is filled according to the above, it can be walked and
// the best match retrieved.
//
// This is a recursive function with memoization, which avoids filling table
// entries where unnecessary. This makes the best case O(N) instead of
// O(N^2).
// To avoid unnecessary recursion on long sequences, process a whole strip of
// matching elements in one go.
size_t src_cur = src_start;
size_t dst_cur = dst_start;
while (IsInBound(src_cur, dst_cur) && !IsCalculated(src_cur, dst_cur) &&
match(src_[src_cur], dst_[dst_cur])) {
++src_cur;
++dst_cur;
}
// We've reached a pair of elements that don't match. Recursively determine
// which one should be left unmatched.
size_t best_match_length = 0;
if (IsInBound(src_cur, dst_cur)) {
if (IsCalculated(src_cur, dst_cur)) {
best_match_length = GetMemoizedLength(src_cur, dst_cur);
} else {
best_match_length = std::max(CalculateLCS(src_cur + 1, dst_cur, match),
CalculateLCS(src_cur, dst_cur + 1, match));
// Fill the table with this information
DiffMatchEntry& entry = table_[src_cur][dst_cur];
assert(!entry.valid);
entry.best_match_length = best_match_length;
entry.valid = true;
}
}
// Go over the matched strip and update the table as well.
assert(src_cur - src_start == dst_cur - dst_start);
size_t contiguous_match_len = src_cur - src_start;
for (size_t i = 0; i < contiguous_match_len; ++i) {
--src_cur;
--dst_cur;
assert(IsInBound(src_cur, dst_cur));
DiffMatchEntry& entry = table_[src_cur][dst_cur];
assert(!entry.valid);
entry.best_match_length = ++best_match_length;
entry.matched = true;
entry.valid = true;
}
return best_match_length;
}
template <typename Sequence>
void LongestCommonSubsequence<Sequence>::RetrieveMatch(
DiffMatch* src_match_result, DiffMatch* dst_match_result) {
src_match_result->clear();
dst_match_result->clear();
src_match_result->resize(src_.size(), false);
dst_match_result->resize(dst_.size(), false);
size_t src_cur = 0;
size_t dst_cur = 0;
while (IsInBound(src_cur, dst_cur)) {
if (IsMatched(src_cur, dst_cur)) {
(*src_match_result)[src_cur++] = true;
(*dst_match_result)[dst_cur++] = true;
continue;
}
if (GetMemoizedLength(src_cur + 1, dst_cur) >=
GetMemoizedLength(src_cur, dst_cur + 1)) {
++src_cur;
} else {
++dst_cur;
}
}
}
} // namespace diff
} // namespace spvtools
#endif // SOURCE_DIFF_LCS_H_

View File

@ -87,6 +87,12 @@ struct Operand {
spv_operand_type_t type; // Type of this logical operand.
OperandData words; // Binary segments of this logical operand.
uint32_t AsId() const {
assert(spvIsIdType(type));
assert(words.size() == 1);
return words[0];
}
// Returns a string operand as a std::string.
std::string AsString() const {
assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
@ -95,7 +101,10 @@ struct Operand {
// Returns a literal integer operand as a uint64_t
uint64_t AsLiteralUint64() const {
assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER);
assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER ||
type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER ||
type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER ||
type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER);
assert(1 <= words.size());
assert(words.size() <= 2);
uint64_t result = 0;
@ -294,6 +303,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
inline void SetInOperands(OperandList&& new_operands);
// Sets the result type id.
inline void SetResultType(uint32_t ty_id);
inline bool HasResultType() const { return has_type_id_; }
// Sets the result id
inline void SetResultId(uint32_t res_id);
inline bool HasResultId() const { return has_result_id_; }

View File

@ -24,6 +24,7 @@
#include "source/opt/reflect.h"
#include "source/opt/types.h"
#include "source/util/make_unique.h"
#include "types.h"
static const uint32_t kDebugValueOperandValueIndex = 5;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
@ -395,7 +396,7 @@ bool ScalarReplacementPass::CreateReplacementVariables(
if (!components_used || components_used->count(elem)) {
CreateVariable(*id, inst, elem, replacements);
} else {
replacements->push_back(CreateNullConstant(*id));
replacements->push_back(GetUndef(*id));
}
elem++;
});
@ -406,8 +407,8 @@ bool ScalarReplacementPass::CreateReplacementVariables(
CreateVariable(type->GetSingleWordInOperand(0u), inst, i,
replacements);
} else {
replacements->push_back(
CreateNullConstant(type->GetSingleWordInOperand(0u)));
uint32_t element_type_id = type->GetSingleWordInOperand(0);
replacements->push_back(GetUndef(element_type_id));
}
}
break;
@ -429,6 +430,10 @@ bool ScalarReplacementPass::CreateReplacementVariables(
replacements->end();
}
Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) {
return get_def_use_mgr()->GetDef(Type2Undef(type_id));
}
void ScalarReplacementPass::TransferAnnotations(
const Instruction* source, std::vector<Instruction*>* replacements) {
// Only transfer invariant and restrict decorations on the variable. There are
@ -981,20 +986,6 @@ ScalarReplacementPass::GetUsedComponents(Instruction* inst) {
return result;
}
Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
const analysis::Type* type = type_mgr->GetType(type_id);
const analysis::Constant* null_const = const_mgr->GetConstant(type, {});
Instruction* null_inst =
const_mgr->GetDefiningInstruction(null_const, type_id);
if (null_inst != nullptr) {
context()->UpdateDefUse(null_inst);
}
return null_inst;
}
uint64_t ScalarReplacementPass::GetMaxLegalIndex(
const Instruction* var_inst) const {
assert(var_inst->opcode() == SpvOpVariable &&

View File

@ -23,14 +23,14 @@
#include <vector>
#include "source/opt/function.h"
#include "source/opt/pass.h"
#include "source/opt/mem_pass.h"
#include "source/opt/type_manager.h"
namespace spvtools {
namespace opt {
// Documented in optimizer.hpp
class ScalarReplacementPass : public Pass {
class ScalarReplacementPass : public MemPass {
private:
static const uint32_t kDefaultLimit = 100;
@ -234,10 +234,8 @@ class ScalarReplacementPass : public Pass {
std::unique_ptr<std::unordered_set<int64_t>> GetUsedComponents(
Instruction* inst);
// Returns an instruction defining a null constant with type |type_id|. If
// one already exists, it is returned. Otherwise a new one is created.
// Returns |nullptr| if the new constant could not be created.
Instruction* CreateNullConstant(uint32_t type_id);
// Returns an instruction defining an undefined value type |type_id|.
Instruction* GetUndef(uint32_t type_id);
// Maps storage type to a pointer type enclosing that type.
std::unordered_map<uint32_t, uint32_t> pointee_to_pointer_;

View File

@ -235,6 +235,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
DefineParameterlessCase(PipeStorage);
DefineParameterlessCase(NamedBarrier);
DefineParameterlessCase(AccelerationStructureNV);
DefineParameterlessCase(RayQueryKHR);
#undef DefineParameterlessCase
case Type::kInteger:
typeInst = MakeUnique<Instruction>(
@ -527,6 +528,7 @@ Type* TypeManager::RebuildType(const Type& type) {
DefineNoSubtypeCase(PipeStorage);
DefineNoSubtypeCase(NamedBarrier);
DefineNoSubtypeCase(AccelerationStructureNV);
DefineNoSubtypeCase(RayQueryKHR);
#undef DefineNoSubtypeCase
case Type::kVector: {
const Vector* vec_ty = type.AsVector();

View File

@ -96,7 +96,8 @@ class Type {
kNamedBarrier,
kAccelerationStructureNV,
kCooperativeMatrixNV,
kRayQueryKHR
kRayQueryKHR,
kLast
};
Type(Kind k) : kind_(k) {}

View File

@ -24,7 +24,9 @@ namespace spvtools {
void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst,
const spv_parsed_operand_t& operand) {
if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER &&
operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER)
operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER &&
operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER &&
operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER)
return;
if (operand.num_words < 1) return;
// TODO(dneto): Support more than 64-bits at a time.