Update MARK-V to version 1.01

Includes:
- Multi-sequence move-to-front
- Coding by id descriptor
- Statistical coding of non-id words
- Joint coding of opcode and num_operands

Removed explicit form Huffman codec constructor
- The standard use case for it is to be constructed from initializer list.

Using serialization for Huffman codecs
This commit is contained in:
Andrey Tuganov 2017-08-09 14:01:12 -04:00 committed by David Neto
parent 40e9c60ffe
commit b36acbec0e
12 changed files with 6667 additions and 596 deletions

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
add_library(SPIRV-Tools-comp markv_codec.cpp)
add_library(SPIRV-Tools-comp markv_codec.cpp markv_autogen.cpp)
spvtools_default_compile_options(SPIRV-Tools-comp)
target_include_directories(SPIRV-Tools-comp

View File

@ -0,0 +1,57 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "markv_autogen.h"
#include <algorithm>
#include <map>
#include <memory>
#include <vector>
#include <unordered_map>
#include "spirv/1.2/spirv.h"
using spvutils::HuffmanCodec;
namespace {
// Signals that the value is not in the coding scheme and a fallback method
// needs to be used.
const uint64_t kMarkvNoneOfTheAbove = GetMarkvNonOfTheAbove();
inline uint32_t CombineOpcodeAndNumOperands(uint32_t opcode,
uint32_t num_operands) {
return opcode | (num_operands << 16);
}
} // namespace
// The following file contains autogenerated statistical coding rules.
// Generated by running spirv-stats on representative corpus of shaders with
// flags:
// --codegen_opcode_and_num_operands_hist
// --codegen_opcode_and_num_operands_markov_huffman_codecs
// --codegen_literal_string_huffman_codecs
// --codegen_non_id_word_huffman_codecs
// --codegen_id_descriptor_huffman_codecs
//
// Example:
// find <SHADER_CORPUS_DIR> -type f -print0 | xargs -0 -s 2000000
// ~/SPIRV-Tools/build/tools/spirv-stats -v
// --codegen_opcode_and_num_operands_hist
// --codegen_opcode_and_num_operands_markov_huffman_codecs
// --codegen_literal_string_huffman_codecs --codegen_non_id_word_huffman_codecs
// --codegen_id_descriptor_huffman_codecs -o
// ~/SPIRV-Tools/source/comp/markv_autogen.inc
#include "markv_autogen.inc"

View File

@ -0,0 +1,45 @@
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef LIBSPIRV_COMP_MARKV_AUTOGEN_H_
#define LIBSPIRV_COMP_MARKV_AUTOGEN_H_
#include <map>
#include <memory>
#include <numeric>
#include "util/huffman_codec.h"
inline uint64_t GetMarkvNonOfTheAbove() {
// Magic number.
return 1111111111111111111;
}
std::map<uint64_t, uint32_t> GetOpcodeAndNumOperandsHist();
std::map<uint32_t, std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>>
GetOpcodeAndNumOperandsMarkovHuffmanCodecs();
std::map<uint32_t, std::unique_ptr<spvutils::HuffmanCodec<std::string>>>
GetLiteralStringHuffmanCodecs();
std::map<std::pair<uint32_t, uint32_t>,
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>>
GetNonIdWordHuffmanCodecs();
std::map<std::pair<uint32_t, uint32_t>,
std::unique_ptr<spvutils::HuffmanCodec<uint64_t>>>
GetIdDescriptorHuffmanCodecs();
#endif // LIBSPIRV_COMP_MARKV_AUTOGEN_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -328,3 +328,61 @@ bool spvIsIdType(spv_operand_type_t type) {
return false;
}
}
std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
SpvOp opcode) {
std::function<bool(unsigned index)> out;
switch (opcode) {
case SpvOpExecutionMode:
case SpvOpEntryPoint:
case SpvOpName:
case SpvOpMemberName:
case SpvOpSelectionMerge:
case SpvOpDecorate:
case SpvOpMemberDecorate:
case SpvOpTypeStruct:
case SpvOpBranch:
case SpvOpLoopMerge:
out = [](unsigned) { return true; };
break;
case SpvOpGroupDecorate:
case SpvOpGroupMemberDecorate:
case SpvOpBranchConditional:
case SpvOpSwitch:
out = [](unsigned index) { return index != 0; };
break;
case SpvOpFunctionCall:
// The Function parameter.
out = [](unsigned index) { return index == 2; };
break;
case SpvOpPhi:
out = [](unsigned index) { return index > 1; };
break;
case SpvOpEnqueueKernel:
// The Invoke parameter.
out = [](unsigned index) { return index == 8; };
break;
case SpvOpGetKernelNDrangeSubGroupCount:
case SpvOpGetKernelNDrangeMaxSubGroupSize:
// The Invoke parameter.
out = [](unsigned index) { return index == 3; };
break;
case SpvOpGetKernelWorkGroupSize:
case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
// The Invoke parameter.
out = [](unsigned index) { return index == 2; };
break;
case SpvOpTypeForwardPointer:
out = [](unsigned index) { return index == 0; };
break;
default:
out = [](unsigned) { return false; };
break;
}
return out;
}

View File

@ -16,6 +16,7 @@
#define LIBSPIRV_OPERAND_H_
#include <deque>
#include <functional>
#include "spirv-tools/libspirv.h"
#include "table.h"
@ -124,4 +125,11 @@ spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
// Is the operand an ID?
bool spvIsIdType(spv_operand_type_t type);
// Takes the opcode of an instruction and returns
// a function object that will return true if the index
// of the operand can be forward declared. This function will
// used in the SSA validation stage of the pipeline
std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
SpvOp opcode);
#endif // LIBSPIRV_OPERAND_H_

View File

@ -210,7 +210,7 @@ class HuffmanCodec {
// Encodes |val| and stores its Huffman code in the lower |num_bits| of
// |bits|. Returns false of |val| is not in the Huffman table.
bool Encode(const Val& val, uint64_t* bits, size_t* num_bits) {
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;
@ -225,7 +225,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) {
bool DecodeFromStream(
const std::function<bool(bool*)>& read_bit, Val* val) const {
uint32_t node = root_;
while (true) {
assert(node);

View File

@ -325,7 +325,7 @@ class MultiMoveToFront {
public:
// Inserts |value| to sequence with handle |mtf|.
// Returns false if |mtf| already has |value|.
bool Insert(uint32_t mtf, const Val& value) {
bool Insert(uint64_t mtf, const Val& value) {
if (GetMtf(mtf).Insert(value)) {
val_to_mtfs_[value].insert(mtf);
return true;
@ -335,7 +335,7 @@ class MultiMoveToFront {
// Removes |value| from sequence with handle |mtf|.
// Returns false if |mtf| doesn't have |value|.
bool Remove(uint32_t mtf, const Val& value) {
bool Remove(uint64_t mtf, const Val& value) {
if (GetMtf(mtf).Remove(value)) {
val_to_mtfs_[value].erase(mtf);
return true;
@ -351,7 +351,7 @@ class MultiMoveToFront {
return;
auto& mtfs_containing_value = it->second;
for (uint32_t mtf : mtfs_containing_value) {
for (uint64_t mtf : mtfs_containing_value) {
GetMtf(mtf).Remove(value);
}
@ -360,18 +360,18 @@ class MultiMoveToFront {
// Computes rank of |value| in sequence |mtf|.
// Returns false if |mtf| doesn't have |value|.
bool RankFromValue(uint32_t mtf, const Val& value, uint32_t* rank) {
bool RankFromValue(uint64_t mtf, const Val& value, uint32_t* rank) {
return GetMtf(mtf).RankFromValue(value, rank);
}
// Finds |value| with |rank| in sequence |mtf|.
// Returns false if |rank| is out of bounds.
bool ValueFromRank(uint32_t mtf, uint32_t rank, Val* value) {
bool ValueFromRank(uint64_t mtf, uint32_t rank, Val* value) {
return GetMtf(mtf).ValueFromRank(rank, value);
}
// Returns size of |mtf| sequence.
uint32_t GetSize(uint32_t mtf) {
uint32_t GetSize(uint64_t mtf) {
return GetMtf(mtf).GetSize();
}
@ -382,20 +382,20 @@ class MultiMoveToFront {
return;
const auto& mtfs_containing_value = it->second;
for (uint32_t mtf : mtfs_containing_value) {
for (uint64_t mtf : mtfs_containing_value) {
GetMtf(mtf).Promote(value);
}
}
// Inserts |value| in sequence |mtf| or promotes if it's already there.
void InsertOrPromote(uint32_t mtf, const Val& value) {
void InsertOrPromote(uint64_t mtf, const Val& value) {
if (!Insert(mtf, value)) {
GetMtf(mtf).Promote(value);
}
}
// Returns if |mtf| sequence has |value|.
bool HasValue(uint32_t mtf, const Val& value) {
bool HasValue(uint64_t mtf, const Val& value) {
return GetMtf(mtf).HasValue(value);
}
@ -403,7 +403,7 @@ class MultiMoveToFront {
// Returns actual MoveToFront object corresponding to |handle|.
// As multiple operations are often performed consecutively for the same
// sequence, the last returned value is cached.
MoveToFront<Val>& GetMtf(uint32_t handle) {
MoveToFront<Val>& GetMtf(uint64_t handle) {
if (!cached_mtf_ || cached_handle_ != handle) {
cached_handle_ = handle;
cached_mtf_ = &mtfs_[handle];
@ -413,13 +413,13 @@ class MultiMoveToFront {
}
// Container holding MoveToFront objects. Map key is sequence handle.
std::map<uint32_t, MoveToFront<Val>> mtfs_;
std::map<uint64_t, MoveToFront<Val>> mtfs_;
// Container mapping value to sequences which contain that value.
std::unordered_map<Val, std::set<uint32_t>> val_to_mtfs_;
std::unordered_map<Val, std::set<uint64_t>> val_to_mtfs_;
// Cache for the last accessed sequence.
uint32_t cached_handle_ = 0;
uint64_t cached_handle_ = 0;
MoveToFront<Val>* cached_mtf_ = nullptr;
};
@ -471,7 +471,15 @@ bool MoveToFront<Val>::RankFromValue(const Val& value, uint32_t* rank) {
}
const uint32_t old_size = GetSize();
(void)old_size;
if (old_size == 1) {
if (ValueOf(root_) == value) {
*rank = 1;
return true;
} else {
return false;
}
}
const auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) {
return false;
@ -524,7 +532,9 @@ bool MoveToFront<Val>::Promote(const Val& value) {
}
const uint32_t old_size = GetSize();
(void)old_size;
if (old_size == 1)
return ValueOf(root_) == value;
const auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) {
return false;
@ -561,6 +571,11 @@ bool MoveToFront<Val>::ValueFromRank(uint32_t rank, Val* value) {
return false;
}
if (old_size == 1) {
*value = ValueOf(root_);
return true;
}
const bool update_timestamp = (rank != 1);
uint32_t node = root_;

View File

@ -26,6 +26,7 @@
#include "instruction.h"
#include "message.h"
#include "opcode.h"
#include "operand.h"
#include "spirv_validator_options.h"
#include "spirv-tools/libspirv.h"
#include "val/function.h"
@ -3056,66 +3057,6 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
#undef TODO
#undef CASE
}
// This function takes the opcode of an instruction and returns
// a function object that will return true if the index
// of the operand can be forwarad declared. This function will
// used in the SSA validation stage of the pipeline
function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
function<bool(unsigned index)> out;
switch (opcode) {
case SpvOpExecutionMode:
case SpvOpEntryPoint:
case SpvOpName:
case SpvOpMemberName:
case SpvOpSelectionMerge:
case SpvOpDecorate:
case SpvOpMemberDecorate:
case SpvOpTypeStruct:
case SpvOpBranch:
case SpvOpLoopMerge:
out = [](unsigned) { return true; };
break;
case SpvOpGroupDecorate:
case SpvOpGroupMemberDecorate:
case SpvOpBranchConditional:
case SpvOpSwitch:
out = [](unsigned index) { return index != 0; };
break;
case SpvOpFunctionCall:
// The Function parameter.
out = [](unsigned index) { return index == 2; };
break;
case SpvOpPhi:
out = [](unsigned index) { return index > 1; };
break;
case SpvOpEnqueueKernel:
// The Invoke parameter.
out = [](unsigned index) { return index == 8; };
break;
case SpvOpGetKernelNDrangeSubGroupCount:
case SpvOpGetKernelNDrangeMaxSubGroupSize:
// The Invoke parameter.
out = [](unsigned index) { return index == 3; };
break;
case SpvOpGetKernelWorkGroupSize:
case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
// The Invoke parameter.
out = [](unsigned index) { return index == 2; };
break;
case SpvOpTypeForwardPointer:
out = [](unsigned index) { return index == 0; };
break;
default:
out = [](unsigned) { return false; };
break;
}
return out;
}
} // anonymous namespace
namespace libspirv {
@ -3214,7 +3155,7 @@ spv_result_t CheckIdDefinitionDominateUse(const ValidationState_t& _) {
spv_result_t IdPass(ValidationState_t& _,
const spv_parsed_instruction_t* inst) {
auto can_have_forward_declared_ids =
getCanBeForwardDeclaredFunction(static_cast<SpvOp>(inst->opcode));
spvOperandCanBeForwardDeclaredFunction(static_cast<SpvOp>(inst->opcode));
// Keep track of a result id defined by this instruction. 0 means it
// does not define an id.

View File

@ -165,6 +165,92 @@ void TestEncodeDecode(const std::string& original_text) {
spvMarkvBinaryDestroy(markv_binary);
}
void TestEncodeDecodeShaderMainBody(const std::string& body) {
const std::string prefix =
R"(
OpCapability Shader
OpCapability Int64
OpCapability Float64
%ext_inst = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
%void = OpTypeVoid
%func = OpTypeFunction %void
%bool = OpTypeBool
%f32 = OpTypeFloat 32
%u32 = OpTypeInt 32 0
%s32 = OpTypeInt 32 1
%f64 = OpTypeFloat 64
%u64 = OpTypeInt 64 0
%s64 = OpTypeInt 64 1
%boolvec2 = OpTypeVector %bool 2
%s32vec2 = OpTypeVector %s32 2
%u32vec2 = OpTypeVector %u32 2
%f32vec2 = OpTypeVector %f32 2
%f64vec2 = OpTypeVector %f64 2
%boolvec3 = OpTypeVector %bool 3
%u32vec3 = OpTypeVector %u32 3
%s32vec3 = OpTypeVector %s32 3
%f32vec3 = OpTypeVector %f32 3
%f64vec3 = OpTypeVector %f64 3
%boolvec4 = OpTypeVector %bool 4
%u32vec4 = OpTypeVector %u32 4
%s32vec4 = OpTypeVector %s32 4
%f32vec4 = OpTypeVector %f32 4
%f64vec4 = OpTypeVector %f64 4
%f32_0 = OpConstant %f32 0
%f32_1 = OpConstant %f32 1
%f32_2 = OpConstant %f32 2
%f32_3 = OpConstant %f32 3
%f32_4 = OpConstant %f32 4
%f32_pi = OpConstant %f32 3.14159
%s32_0 = OpConstant %s32 0
%s32_1 = OpConstant %s32 1
%s32_2 = OpConstant %s32 2
%s32_3 = OpConstant %s32 3
%s32_4 = OpConstant %s32 4
%s32_m1 = OpConstant %s32 -1
%u32_0 = OpConstant %u32 0
%u32_1 = OpConstant %u32 1
%u32_2 = OpConstant %u32 2
%u32_3 = OpConstant %u32 3
%u32_4 = OpConstant %u32 4
%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2
%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3
%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4
%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2
%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3
%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4
%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
%main = OpFunction %void None %func
%main_entry = OpLabel)";
const std::string suffix =
R"(
OpReturn
OpFunctionEnd)";
TestEncodeDecode(prefix + body + suffix);
}
TEST(Markv, U32Literal) {
TestEncodeDecode(R"(
OpCapability Shader
@ -322,6 +408,39 @@ OpFunctionEnd
)");
}
TEST(Markv, WithMultipleFunctions) {
TestEncodeDecode(R"(
OpCapability Addresses
OpCapability Kernel
OpCapability GenericPointer
OpCapability Linkage
OpMemoryModel Physical32 OpenCL
%f32 = OpTypeFloat 32
%one = OpConstant %f32 1
%void = OpTypeVoid
%void_func = OpTypeFunction %void
%f32_func = OpTypeFunction %f32 %f32
%sqr_plus_one = OpFunction %f32 None %f32_func
%x = OpFunctionParameter %f32
%100 = OpLabel
%x2 = OpFMul %f32 %x %x
%x2p1 = OpFunctionCall %f32 %plus_one %x2
OpReturnValue %x2p1
OpFunctionEnd
%plus_one = OpFunction %f32 None %f32_func
%y = OpFunctionParameter %f32
%200 = OpLabel
%yp1 = OpFAdd %f32 %y %one
OpReturnValue %yp1
OpFunctionEnd
%main = OpFunction %void None %void_func
%entry_main = OpLabel
%1p1 = OpFunctionCall %f32 %sqr_plus_one %one
OpReturn
OpFunctionEnd
)");
}
TEST(Markv, ForwardDeclaredId) {
TestEncodeDecode(R"(
OpCapability Addresses
@ -430,4 +549,304 @@ OpFunctionEnd
)");
}
TEST(Markv, F32Mul) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpFMul %f32 %f32_0 %f32_1
%val2 = OpFMul %f32 %f32_2 %f32_0
%val3 = OpFMul %f32 %f32_pi %f32_2
%val4 = OpFMul %f32 %f32_1 %f32_1
)");
}
TEST(Markv, U32Mul) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpIMul %u32 %u32_0 %u32_1
%val2 = OpIMul %u32 %u32_2 %u32_0
%val3 = OpIMul %u32 %u32_3 %u32_2
%val4 = OpIMul %u32 %u32_1 %u32_1
)");
}
TEST(Markv, S32Mul) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpIMul %s32 %s32_0 %s32_1
%val2 = OpIMul %s32 %s32_2 %s32_0
%val3 = OpIMul %s32 %s32_m1 %s32_2
%val4 = OpIMul %s32 %s32_1 %s32_1
)");
}
TEST(Markv, F32Add) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpFAdd %f32 %f32_0 %f32_1
%val2 = OpFAdd %f32 %f32_2 %f32_0
%val3 = OpFAdd %f32 %f32_pi %f32_2
%val4 = OpFAdd %f32 %f32_1 %f32_1
)");
}
TEST(Markv, U32Add) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpIAdd %u32 %u32_0 %u32_1
%val2 = OpIAdd %u32 %u32_2 %u32_0
%val3 = OpIAdd %u32 %u32_3 %u32_2
%val4 = OpIAdd %u32 %u32_1 %u32_1
)");
}
TEST(Markv, S32Add) {
TestEncodeDecodeShaderMainBody(R"(
%val1 = OpIAdd %s32 %s32_0 %s32_1
%val2 = OpIAdd %s32 %s32_2 %s32_0
%val3 = OpIAdd %s32 %s32_m1 %s32_2
%val4 = OpIAdd %s32 %s32_1 %s32_1
)");
}
TEST(Markv, F32Dot) {
TestEncodeDecodeShaderMainBody(R"(
%dot2_1 = OpDot %f32 %f32vec2_01 %f32vec2_12
%dot2_2 = OpDot %f32 %f32vec2_01 %f32vec2_01
%dot2_3 = OpDot %f32 %f32vec2_12 %f32vec2_12
%dot3_1 = OpDot %f32 %f32vec3_012 %f32vec3_123
%dot3_2 = OpDot %f32 %f32vec3_012 %f32vec3_012
%dot3_3 = OpDot %f32 %f32vec3_123 %f32vec3_123
%dot4_1 = OpDot %f32 %f32vec4_0123 %f32vec4_1234
%dot4_2 = OpDot %f32 %f32vec4_0123 %f32vec4_0123
%dot4_3 = OpDot %f32 %f32vec4_1234 %f32vec4_1234
)");
}
TEST(Markv, F32VectorCompositeConstruct) {
TestEncodeDecodeShaderMainBody(R"(
%cc1 = OpCompositeConstruct %f32vec4 %f32vec2_01 %f32vec2_12
%cc2 = OpCompositeConstruct %f32vec3 %f32vec2_01 %f32_2
%cc3 = OpCompositeConstruct %f32vec2 %f32_1 %f32_2
%cc4 = OpCompositeConstruct %f32vec4 %f32_1 %f32_2 %cc3
)");
}
TEST(Markv, U32VectorCompositeConstruct) {
TestEncodeDecodeShaderMainBody(R"(
%cc1 = OpCompositeConstruct %u32vec4 %u32vec2_01 %u32vec2_12
%cc2 = OpCompositeConstruct %u32vec3 %u32vec2_01 %u32_2
%cc3 = OpCompositeConstruct %u32vec2 %u32_1 %u32_2
%cc4 = OpCompositeConstruct %u32vec4 %u32_1 %u32_2 %cc3
)");
}
TEST(Markv, S32VectorCompositeConstruct) {
TestEncodeDecodeShaderMainBody(R"(
%cc1 = OpCompositeConstruct %u32vec4 %u32vec2_01 %u32vec2_12
%cc2 = OpCompositeConstruct %u32vec3 %u32vec2_01 %u32_2
%cc3 = OpCompositeConstruct %u32vec2 %u32_1 %u32_2
%cc4 = OpCompositeConstruct %u32vec4 %u32_1 %u32_2 %cc3
)");
}
TEST(Markv, F32VectorCompositeExtract) {
TestEncodeDecodeShaderMainBody(R"(
%f32vec4_3210 = OpCompositeConstruct %f32vec4 %f32_3 %f32_2 %f32_1 %f32_0
%f32vec3_013 = OpCompositeExtract %f32vec3 %f32vec4_0123 0 1 3
)");
}
TEST(Markv, F32VectorComparison) {
TestEncodeDecodeShaderMainBody(R"(
%f32vec4_3210 = OpCompositeConstruct %f32vec4 %f32_3 %f32_2 %f32_1 %f32_0
%c1 = OpFOrdEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c2 = OpFUnordEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c3 = OpFOrdNotEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c4 = OpFUnordNotEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c5 = OpFOrdLessThan %boolvec4 %f32vec4_0123 %f32vec4_3210
%c6 = OpFUnordLessThan %boolvec4 %f32vec4_0123 %f32vec4_3210
%c7 = OpFOrdGreaterThan %boolvec4 %f32vec4_0123 %f32vec4_3210
%c8 = OpFUnordGreaterThan %boolvec4 %f32vec4_0123 %f32vec4_3210
%c9 = OpFOrdLessThanEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c10 = OpFUnordLessThanEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c11 = OpFOrdGreaterThanEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
%c12 = OpFUnordGreaterThanEqual %boolvec4 %f32vec4_0123 %f32vec4_3210
)");
}
TEST(Markv, VectorShuffle) {
TestEncodeDecodeShaderMainBody(R"(
%f32vec4_3210 = OpCompositeConstruct %f32vec4 %f32_3 %f32_2 %f32_1 %f32_0
%sh1 = OpVectorShuffle %f32vec2 %f32vec4_0123 %f32vec4_3210 3 6
%sh2 = OpVectorShuffle %f32vec3 %f32vec2_01 %f32vec4_3210 0 3 4
)");
}
TEST(Markv, VectorTimesScalar) {
TestEncodeDecodeShaderMainBody(R"(
%f32vec4_3210 = OpCompositeConstruct %f32vec4 %f32_3 %f32_2 %f32_1 %f32_0
%res1 = OpVectorTimesScalar %f32vec4 %f32vec4_0123 %f32_2
%res2 = OpVectorTimesScalar %f32vec4 %f32vec4_3210 %f32_2
%res3 = OpVectorTimesScalar %u32vec3 %u32vec3_012 %u32_2
%res4 = OpVectorTimesScalar %s32vec2 %s32vec2_01 %s32_2
)");
}
TEST(Markv, SpirvSpecSample) {
TestEncodeDecode(R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %31 %33 %42 %57
OpExecutionMode %4 OriginLowerLeft
; Debug information
OpSource GLSL 450
OpName %4 "main"
OpName %9 "scale"
OpName %17 "S"
OpMemberName %17 0 "b"
OpMemberName %17 1 "v"
OpMemberName %17 2 "i"
OpName %18 "blockName"
OpMemberName %18 0 "s"
OpMemberName %18 1 "cond"
OpName %20 ""
OpName %31 "color"
OpName %33 "color1"
OpName %42 "color2"
OpName %48 "i"
OpName %57 "multiplier"
; Annotations (non-debug)
OpDecorate %15 ArrayStride 16
OpMemberDecorate %17 0 Offset 0
OpMemberDecorate %17 1 Offset 16
OpMemberDecorate %17 2 Offset 96
OpMemberDecorate %18 0 Offset 0
OpMemberDecorate %18 1 Offset 112
OpDecorate %18 Block
OpDecorate %20 DescriptorSet 0
OpDecorate %42 NoPerspective
; All types, variables, and constants
%2 = OpTypeVoid
%3 = OpTypeFunction %2 ; void ()
%6 = OpTypeFloat 32 ; 32-bit float
%7 = OpTypeVector %6 4 ; vec4
%8 = OpTypePointer Function %7 ; function-local vec4*
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstantComposite %7 %10 %10 %11 %10 ; vec4(1.0, 1.0, 2.0, 1.0)
%13 = OpTypeInt 32 0 ; 32-bit int, sign-less
%14 = OpConstant %13 5
%15 = OpTypeArray %7 %14
%16 = OpTypeInt 32 1
%17 = OpTypeStruct %13 %15 %16
%18 = OpTypeStruct %17 %13
%19 = OpTypePointer Uniform %18
%20 = OpVariable %19 Uniform
%21 = OpConstant %16 1
%22 = OpTypePointer Uniform %13
%25 = OpTypeBool
%26 = OpConstant %13 0
%30 = OpTypePointer Output %7
%31 = OpVariable %30 Output
%32 = OpTypePointer Input %7
%33 = OpVariable %32 Input
%35 = OpConstant %16 0
%36 = OpConstant %16 2
%37 = OpTypePointer Uniform %7
%42 = OpVariable %32 Input
%47 = OpTypePointer Function %16
%55 = OpConstant %16 4
%57 = OpVariable %32 Input
; All functions
%4 = OpFunction %2 None %3 ; main()
%5 = OpLabel
%9 = OpVariable %8 Function
%48 = OpVariable %47 Function
OpStore %9 %12
%23 = OpAccessChain %22 %20 %21 ; location of cond
%24 = OpLoad %13 %23 ; load 32-bit int from cond
%27 = OpINotEqual %25 %24 %26 ; convert to bool
OpSelectionMerge %29 None ; structured if
OpBranchConditional %27 %28 %41 ; if cond
%28 = OpLabel ; then
%34 = OpLoad %7 %33
%38 = OpAccessChain %37 %20 %35 %21 %36 ; s.v[2]
%39 = OpLoad %7 %38
%40 = OpFAdd %7 %34 %39
OpStore %31 %40
OpBranch %29
%41 = OpLabel ; else
%43 = OpLoad %7 %42
%44 = OpExtInst %7 %1 Sqrt %43 ; extended instruction sqrt
%45 = OpLoad %7 %9
%46 = OpFMul %7 %44 %45
OpStore %31 %46
OpBranch %29
%29 = OpLabel ; endif
OpStore %48 %35
OpBranch %49
%49 = OpLabel
OpLoopMerge %51 %52 None ; structured loop
OpBranch %53
%53 = OpLabel
%54 = OpLoad %16 %48
%56 = OpSLessThan %25 %54 %55 ; i < 4 ?
OpBranchConditional %56 %50 %51 ; body or break
%50 = OpLabel ; body
%58 = OpLoad %7 %57
%59 = OpLoad %7 %31
%60 = OpFMul %7 %59 %58
OpStore %31 %60
OpBranch %52
%52 = OpLabel ; continue target
%61 = OpLoad %16 %48
%62 = OpIAdd %16 %61 %21 ; ++i
OpStore %48 %62
OpBranch %49 ; loop back
%51 = OpLabel ; loop merge point
OpReturn
OpFunctionEnd
)");
}
TEST(Markv, SampleFromDeadBranchEliminationTest) {
TestEncodeDecode(R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%float_0 = OpConstant %float 0
%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
%float_1 = OpConstant %float 1
%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%main = OpFunction %void None %5
%17 = OpLabel
OpSelectionMerge %18 None
OpBranchConditional %true %19 %20
%19 = OpLabel
OpBranch %18
%20 = OpLabel
OpBranch %18
%18 = OpLabel
%21 = OpPhi %v4float %12 %19 %14 %20
OpStore %gl_FragColor %21
OpReturn
OpFunctionEnd
)");
}
} // namespace

View File

@ -23,14 +23,26 @@
#include "spirv/1.2/spirv.h"
#include "source/enum_string_mapping.h"
#include "source/comp/markv_autogen.h"
#include "source/opcode.h"
#include "source/operand.h"
#include "source/spirv_constant.h"
#include "source/util/huffman_codec.h"
using libspirv::SpirvStats;
using spvutils::HuffmanCodec;
namespace {
// Signals that the value is not in the coding scheme and a fallback method
// needs to be used.
const uint64_t kMarkvNoneOfTheAbove = GetMarkvNonOfTheAbove();
inline uint32_t CombineOpcodeAndNumOperands(uint32_t opcode,
uint32_t num_operands) {
return opcode | (num_operands << 16);
}
// Returns all SPIR-V v1.2 opcodes.
std::vector<uint32_t> GetAllOpcodes() {
return std::vector<uint32_t>({
@ -599,23 +611,30 @@ void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsHist(std::ostream& out) {
total += kv.second;
}
uint32_t left_out = 0;
for (const auto& kv : stats_.opcode_and_num_operands_hist) {
const uint32_t count = kv.second;
const double kFrequentEnoughToAnalyze = 0.001;
if (double(count) / double(total) < kFrequentEnoughToAnalyze) continue;
const uint32_t opcode_and_num_operands = kv.first;
const uint32_t opcode = opcode_and_num_operands & 0xFFFF;
const uint32_t num_operands = opcode_and_num_operands >> 16;
if (opcode == SpvOpTypeStruct)
if (opcode == SpvOpTypeStruct ||
double(count) / double(total) < kFrequentEnoughToAnalyze) {
left_out += count;
continue;
}
out << " { CombineOpcodeAndNumOperands(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << num_operands << "), " << count << " },\n";
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
// Heuristic.
const uint32_t none_of_the_above =
std::max(1, int(left_out + total * 0.01));
out << " { kMarkvNoneOfTheAbove, " << none_of_the_above << " },\n";
out << " });\n}\n";
}
@ -628,7 +647,7 @@ void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsMarkovHuffmanCodecs(
for (const auto& kv : stats_.opcode_and_num_operands_markov_hist) {
const uint32_t prev_opcode = kv.first;
const double kFrequentEnoughToAnalyze = 0.001;
const double kFrequentEnoughToAnalyze = 0.01;
if (opcode_freq_[prev_opcode] < kFrequentEnoughToAnalyze) continue;
const std::unordered_map<uint32_t, uint32_t>& hist = kv.second;
@ -638,10 +657,9 @@ void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsMarkovHuffmanCodecs(
total += pair.second;
}
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>({\n";
uint32_t left_out = 0;
std::map<uint64_t, uint32_t> processed_hist;
for (const auto& pair : hist) {
const uint32_t opcode_and_num_operands = pair.first;
const uint32_t opcode = opcode_and_num_operands & 0xFFFF;
@ -654,17 +672,25 @@ void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsMarkovHuffmanCodecs(
const double posterior_freq = double(count) / double(total);
if (opcode_freq_[opcode] < kFrequentEnoughToAnalyze &&
posterior_freq < kFrequentEnoughToAnalyze) continue;
total += count;
out << " { CombineOpcodeAndNumOperands(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << num_operands << "), " << count << " },\n";
posterior_freq < kFrequentEnoughToAnalyze) {
left_out += count;
continue;
}
processed_hist.emplace(CombineOpcodeAndNumOperands(opcode, num_operands),
count);
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
// Heuristic.
processed_hist.emplace(kMarkvNoneOfTheAbove,
std::max(1, int(left_out + total * 0.01)));
out << " }));\n" << std::endl;
HuffmanCodec<uint64_t> codec(processed_hist);
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>";
out << codec.SerializeToText(4);
out << ");\n" << std::endl;
out << " codecs.emplace(SpvOp" << GetOpcodeString(prev_opcode)
<< ", std::move(codec));\n";
out << " }\n\n";
@ -695,22 +721,31 @@ void StatsAnalyzer::WriteCodegenLiteralStringHuffmanCodecs(std::ostream& out) {
total += pair.second;
}
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<std::string>> "
<< "codec(new HuffmanCodec<std::string>({\n";
uint32_t left_out = 0;
std::map<std::string, uint32_t> processed_hist;
for (const auto& pair : hist) {
const uint32_t count = pair.second;
const double freq = double(count) / double(total);
const double kStringFrequentEnoughToAnalyze = 0.001;
if (freq < kStringFrequentEnoughToAnalyze) continue;
out << " { std::string(\"" << pair.first << "\"), " << count
<< " },\n";
if (freq < kStringFrequentEnoughToAnalyze) {
left_out += count;
continue;
}
processed_hist.emplace(pair.first, count);
}
out << " { std::string(\"kMarkvNoneOfTheAbove\"), "
<< 1 + int(total * 0.05) << " },\n";
// Heuristic.
processed_hist.emplace("kMarkvNoneOfTheAbove",
std::max(1, int(left_out + total * 0.01)));
out << " }));\n" << std::endl;
HuffmanCodec<std::string> codec(processed_hist);
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<std::string>> "
<< "codec(new HuffmanCodec<std::string>";
out << codec.SerializeToText(4);
out << ");\n" << std::endl;
out << " codecs.emplace(SpvOp" << spvOpcodeString(SpvOp(opcode))
<< ", std::move(codec));\n";
out << " }\n\n";
@ -741,21 +776,32 @@ void StatsAnalyzer::WriteCodegenNonIdWordHuffmanCodecs(std::ostream& out) {
total += pair.second;
}
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>({\n";
uint32_t left_out = 0;
std::map<uint64_t, uint32_t> processed_hist;
for (const auto& pair : hist) {
const uint32_t word = pair.first;
const uint32_t count = pair.second;
const double freq = double(count) / double(total);
const double kWordFrequentEnoughToAnalyze = 0.001;
if (freq < kWordFrequentEnoughToAnalyze) continue;
out << " { " << word << ", " << count << " },\n";
const double kWordFrequentEnoughToAnalyze = 0.01;
if (freq < kWordFrequentEnoughToAnalyze) {
left_out += count;
continue;
}
processed_hist.emplace(word, count);
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
// Heuristic.
processed_hist.emplace(kMarkvNoneOfTheAbove,
std::max(1, int(left_out + total * 0.01)));
out << " }));\n" << std::endl;
HuffmanCodec<uint64_t> codec(processed_hist);
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>";
out << codec.SerializeToText(4);
out << ");\n" << std::endl;
out << " codecs.emplace(std::pair<uint32_t, uint32_t>(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << index << "), std::move(codec));\n";
@ -778,31 +824,43 @@ void StatsAnalyzer::WriteCodegenIdDescriptorHuffmanCodecs(
const uint32_t opcode = opcode_and_index.first;
const uint32_t index = opcode_and_index.second;
const double kOpcodeFrequentEnoughToAnalyze = 0.001;
const double kOpcodeFrequentEnoughToAnalyze = 0.01;
if (opcode_freq_[opcode] < kOpcodeFrequentEnoughToAnalyze) continue;
const std::map<uint32_t, uint32_t>& hist = kv.second;
uint32_t total = 0;
for (const auto& pair : hist) {
total += pair.second;
}
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>({\n";
uint32_t left_out = 0;
std::map<uint64_t, uint32_t> processed_hist;
for (const auto& pair : hist) {
const uint32_t descriptor = pair.first;
const uint32_t count = pair.second;
const double freq = double(count) / double(total);
const double kDescriptorFrequentEnoughToAnalyze = 0.005;
if (freq < kDescriptorFrequentEnoughToAnalyze) continue;
out << " { " << descriptor << ", " << count << " },\n";
const double kDescriptorFrequentEnoughToAnalyze = 0.01;
if (freq < kDescriptorFrequentEnoughToAnalyze) {
left_out += count;
continue;
}
processed_hist.emplace(descriptor, count);
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
// Heuristic.
processed_hist.emplace(kMarkvNoneOfTheAbove,
std::max(1, int(left_out + total * 0.01)));
out << " }));\n" << std::endl;
HuffmanCodec<uint64_t> codec(processed_hist);
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<uint64_t>> "
<< "codec(new HuffmanCodec<uint64_t>";
out << codec.SerializeToText(4);
out << ");\n" << std::endl;
out << " codecs.emplace(std::pair<uint32_t, uint32_t>(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << index << "), std::move(codec));\n";