SPIRV-Tools/tools/stats/stats_analyzer.cpp
Andrey Tuganov 78cf86150e Add id descriptor feature to SPIR-V
Id descriptors are computed as a recursive hash of all instructions used
to define an id. Descriptors are invarint of actual id values and
the similar code in different files would produce the same descriptors.

Multiple ids can have the same descriptor. For example
%1 = OpConstant %u32 1
%2 = OpConstant %u32 1
would produce two ids with the same descriptor. But
%3 = OpConstant %s32 1
%4 = OpConstant %u32 2
would have descriptors different from %1 and %2.

Descriptors will be used as handles of move-to-front sequences in SPIR-V
compression.
2017-08-10 18:44:52 -04:00

814 lines
24 KiB
C++

// 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 "stats_analyzer.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>
#include "spirv/1.2/spirv.h"
#include "source/enum_string_mapping.h"
#include "source/opcode.h"
#include "source/operand.h"
#include "source/spirv_constant.h"
using libspirv::SpirvStats;
namespace {
// Returns all SPIR-V v1.2 opcodes.
std::vector<uint32_t> GetAllOpcodes() {
return std::vector<uint32_t>({
SpvOpNop,
SpvOpUndef,
SpvOpSourceContinued,
SpvOpSource,
SpvOpSourceExtension,
SpvOpName,
SpvOpMemberName,
SpvOpString,
SpvOpLine,
SpvOpExtension,
SpvOpExtInstImport,
SpvOpExtInst,
SpvOpMemoryModel,
SpvOpEntryPoint,
SpvOpExecutionMode,
SpvOpCapability,
SpvOpTypeVoid,
SpvOpTypeBool,
SpvOpTypeInt,
SpvOpTypeFloat,
SpvOpTypeVector,
SpvOpTypeMatrix,
SpvOpTypeImage,
SpvOpTypeSampler,
SpvOpTypeSampledImage,
SpvOpTypeArray,
SpvOpTypeRuntimeArray,
SpvOpTypeStruct,
SpvOpTypeOpaque,
SpvOpTypePointer,
SpvOpTypeFunction,
SpvOpTypeEvent,
SpvOpTypeDeviceEvent,
SpvOpTypeReserveId,
SpvOpTypeQueue,
SpvOpTypePipe,
SpvOpTypeForwardPointer,
SpvOpConstantTrue,
SpvOpConstantFalse,
SpvOpConstant,
SpvOpConstantComposite,
SpvOpConstantSampler,
SpvOpConstantNull,
SpvOpSpecConstantTrue,
SpvOpSpecConstantFalse,
SpvOpSpecConstant,
SpvOpSpecConstantComposite,
SpvOpSpecConstantOp,
SpvOpFunction,
SpvOpFunctionParameter,
SpvOpFunctionEnd,
SpvOpFunctionCall,
SpvOpVariable,
SpvOpImageTexelPointer,
SpvOpLoad,
SpvOpStore,
SpvOpCopyMemory,
SpvOpCopyMemorySized,
SpvOpAccessChain,
SpvOpInBoundsAccessChain,
SpvOpPtrAccessChain,
SpvOpArrayLength,
SpvOpGenericPtrMemSemantics,
SpvOpInBoundsPtrAccessChain,
SpvOpDecorate,
SpvOpMemberDecorate,
SpvOpDecorationGroup,
SpvOpGroupDecorate,
SpvOpGroupMemberDecorate,
SpvOpVectorExtractDynamic,
SpvOpVectorInsertDynamic,
SpvOpVectorShuffle,
SpvOpCompositeConstruct,
SpvOpCompositeExtract,
SpvOpCompositeInsert,
SpvOpCopyObject,
SpvOpTranspose,
SpvOpSampledImage,
SpvOpImageSampleImplicitLod,
SpvOpImageSampleExplicitLod,
SpvOpImageSampleDrefImplicitLod,
SpvOpImageSampleDrefExplicitLod,
SpvOpImageSampleProjImplicitLod,
SpvOpImageSampleProjExplicitLod,
SpvOpImageSampleProjDrefImplicitLod,
SpvOpImageSampleProjDrefExplicitLod,
SpvOpImageFetch,
SpvOpImageGather,
SpvOpImageDrefGather,
SpvOpImageRead,
SpvOpImageWrite,
SpvOpImage,
SpvOpImageQueryFormat,
SpvOpImageQueryOrder,
SpvOpImageQuerySizeLod,
SpvOpImageQuerySize,
SpvOpImageQueryLod,
SpvOpImageQueryLevels,
SpvOpImageQuerySamples,
SpvOpConvertFToU,
SpvOpConvertFToS,
SpvOpConvertSToF,
SpvOpConvertUToF,
SpvOpUConvert,
SpvOpSConvert,
SpvOpFConvert,
SpvOpQuantizeToF16,
SpvOpConvertPtrToU,
SpvOpSatConvertSToU,
SpvOpSatConvertUToS,
SpvOpConvertUToPtr,
SpvOpPtrCastToGeneric,
SpvOpGenericCastToPtr,
SpvOpGenericCastToPtrExplicit,
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,
SpvOpIsFinite,
SpvOpIsNormal,
SpvOpSignBitSet,
SpvOpLessOrGreater,
SpvOpOrdered,
SpvOpUnordered,
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,
SpvOpEmitVertex,
SpvOpEndPrimitive,
SpvOpEmitStreamVertex,
SpvOpEndStreamPrimitive,
SpvOpControlBarrier,
SpvOpMemoryBarrier,
SpvOpAtomicLoad,
SpvOpAtomicStore,
SpvOpAtomicExchange,
SpvOpAtomicCompareExchange,
SpvOpAtomicCompareExchangeWeak,
SpvOpAtomicIIncrement,
SpvOpAtomicIDecrement,
SpvOpAtomicIAdd,
SpvOpAtomicISub,
SpvOpAtomicSMin,
SpvOpAtomicUMin,
SpvOpAtomicSMax,
SpvOpAtomicUMax,
SpvOpAtomicAnd,
SpvOpAtomicOr,
SpvOpAtomicXor,
SpvOpPhi,
SpvOpLoopMerge,
SpvOpSelectionMerge,
SpvOpLabel,
SpvOpBranch,
SpvOpBranchConditional,
SpvOpSwitch,
SpvOpKill,
SpvOpReturn,
SpvOpReturnValue,
SpvOpUnreachable,
SpvOpLifetimeStart,
SpvOpLifetimeStop,
SpvOpGroupAsyncCopy,
SpvOpGroupWaitEvents,
SpvOpGroupAll,
SpvOpGroupAny,
SpvOpGroupBroadcast,
SpvOpGroupIAdd,
SpvOpGroupFAdd,
SpvOpGroupFMin,
SpvOpGroupUMin,
SpvOpGroupSMin,
SpvOpGroupFMax,
SpvOpGroupUMax,
SpvOpGroupSMax,
SpvOpReadPipe,
SpvOpWritePipe,
SpvOpReservedReadPipe,
SpvOpReservedWritePipe,
SpvOpReserveReadPipePackets,
SpvOpReserveWritePipePackets,
SpvOpCommitReadPipe,
SpvOpCommitWritePipe,
SpvOpIsValidReserveId,
SpvOpGetNumPipePackets,
SpvOpGetMaxPipePackets,
SpvOpGroupReserveReadPipePackets,
SpvOpGroupReserveWritePipePackets,
SpvOpGroupCommitReadPipe,
SpvOpGroupCommitWritePipe,
SpvOpEnqueueMarker,
SpvOpEnqueueKernel,
SpvOpGetKernelNDrangeSubGroupCount,
SpvOpGetKernelNDrangeMaxSubGroupSize,
SpvOpGetKernelWorkGroupSize,
SpvOpGetKernelPreferredWorkGroupSizeMultiple,
SpvOpRetainEvent,
SpvOpReleaseEvent,
SpvOpCreateUserEvent,
SpvOpIsValidEvent,
SpvOpSetUserEventStatus,
SpvOpCaptureEventProfilingInfo,
SpvOpGetDefaultQueue,
SpvOpBuildNDRange,
SpvOpImageSparseSampleImplicitLod,
SpvOpImageSparseSampleExplicitLod,
SpvOpImageSparseSampleDrefImplicitLod,
SpvOpImageSparseSampleDrefExplicitLod,
SpvOpImageSparseSampleProjImplicitLod,
SpvOpImageSparseSampleProjExplicitLod,
SpvOpImageSparseSampleProjDrefImplicitLod,
SpvOpImageSparseSampleProjDrefExplicitLod,
SpvOpImageSparseFetch,
SpvOpImageSparseGather,
SpvOpImageSparseDrefGather,
SpvOpImageSparseTexelsResident,
SpvOpNoLine,
SpvOpAtomicFlagTestAndSet,
SpvOpAtomicFlagClear,
SpvOpImageSparseRead,
SpvOpSizeOf,
SpvOpTypePipeStorage,
SpvOpConstantPipeStorage,
SpvOpCreatePipeFromPipeStorage,
SpvOpGetKernelLocalSizeForSubgroupCount,
SpvOpGetKernelMaxNumSubgroups,
SpvOpTypeNamedBarrier,
SpvOpNamedBarrierInitialize,
SpvOpMemoryNamedBarrier,
SpvOpModuleProcessed,
SpvOpExecutionModeId,
SpvOpDecorateId,
SpvOpSubgroupBallotKHR,
SpvOpSubgroupFirstInvocationKHR,
SpvOpSubgroupAllKHR,
SpvOpSubgroupAnyKHR,
SpvOpSubgroupAllEqualKHR,
SpvOpSubgroupReadInvocationKHR,
});
}
std::string GetVersionString(uint32_t word) {
std::stringstream ss;
ss << "Version " << SPV_SPIRV_VERSION_MAJOR_PART(word)
<< "." << SPV_SPIRV_VERSION_MINOR_PART(word);
return ss.str();
}
std::string GetGeneratorString(uint32_t word) {
return spvGeneratorStr(SPV_GENERATOR_TOOL_PART(word));
}
std::string GetOpcodeString(uint32_t word) {
return spvOpcodeString(static_cast<SpvOp>(word));
}
std::string GetCapabilityString(uint32_t word) {
return libspirv::CapabilityToString(static_cast<SpvCapability>(word));
}
template <class T>
std::string KeyIsLabel(T key) {
std::stringstream ss;
ss << key;
return ss.str();
}
template <class Key>
std::unordered_map<Key, double> GetRecall(
const std::unordered_map<Key, uint32_t>& hist, uint64_t total) {
std::unordered_map<Key, double> freq;
for (const auto& pair : hist) {
const double frequency =
static_cast<double>(pair.second) / static_cast<double>(total);
freq.emplace(pair.first, frequency);
}
return freq;
}
template <class Key>
std::unordered_map<Key, double> GetPrevalence(
const std::unordered_map<Key, uint32_t>& hist) {
uint64_t total = 0;
for (const auto& pair : hist) {
total += pair.second;
}
return GetRecall(hist, total);
}
// Writes |freq| to |out| sorted by frequency in the following format:
// LABEL3 70%
// LABEL1 20%
// LABEL2 10%
// |label_from_key| is used to convert |Key| to label.
template <class Key>
void WriteFreq(std::ostream& out, const std::unordered_map<Key, double>& freq,
std::string (*label_from_key)(Key), double threshold = 0.001) {
std::vector<std::pair<Key, double>> sorted_freq(freq.begin(), freq.end());
std::sort(sorted_freq.begin(), sorted_freq.end(),
[](const std::pair<Key, double>& left,
const std::pair<Key, double>& right) {
return left.second > right.second;
});
for (const auto& pair : sorted_freq) {
if (pair.second < threshold)
break;
out << label_from_key(pair.first) << " " << pair.second * 100.0
<< "%" << std::endl;
}
}
// Writes |hist| to |out| sorted by count in the following format:
// LABEL3 100
// LABEL1 50
// LABEL2 10
// |label_from_key| is used to convert |Key| to label.
template <class Key>
void WriteHist(std::ostream& out, const std::unordered_map<Key, uint32_t>& hist,
std::string (*label_from_key)(Key)) {
std::vector<std::pair<Key, uint32_t>> sorted_hist(hist.begin(), hist.end());
std::sort(sorted_hist.begin(), sorted_hist.end(),
[](const std::pair<Key, uint32_t>& left,
const std::pair<Key, uint32_t>& right) {
return left.second > right.second;
});
for (const auto& pair : sorted_hist) {
out << label_from_key(pair.first) << " " << pair.second << std::endl;
}
}
} // namespace
StatsAnalyzer::StatsAnalyzer(const SpirvStats& stats) : stats_(stats) {
num_modules_ = 0;
for (const auto& pair : stats_.version_hist) {
num_modules_ += pair.second;
}
version_freq_ = GetRecall(stats_.version_hist, num_modules_);
generator_freq_ = GetRecall(stats_.generator_hist, num_modules_);
capability_freq_ = GetRecall(stats_.capability_hist, num_modules_);
extension_freq_ = GetRecall(stats_.extension_hist, num_modules_);
opcode_freq_ = GetPrevalence(stats_.opcode_hist);
}
void StatsAnalyzer::WriteVersion(std::ostream& out) {
WriteFreq(out, version_freq_, GetVersionString);
}
void StatsAnalyzer::WriteGenerator(std::ostream& out) {
WriteFreq(out, generator_freq_, GetGeneratorString);
}
void StatsAnalyzer::WriteCapability(std::ostream& out) {
WriteFreq(out, capability_freq_, GetCapabilityString);
}
void StatsAnalyzer::WriteExtension(std::ostream& out) {
WriteFreq(out, extension_freq_, KeyIsLabel);
}
void StatsAnalyzer::WriteOpcode(std::ostream& out) {
out << "Total unique opcodes used: " << opcode_freq_.size() << std::endl;
WriteFreq(out, opcode_freq_, GetOpcodeString);
}
void StatsAnalyzer::WriteConstantLiterals(std::ostream& out) {
out << "Constant literals" << std::endl;
out << "Float 32" << std::endl;
WriteFreq(out, GetPrevalence(stats_.f32_constant_hist), KeyIsLabel);
out << std::endl << "Float 64" << std::endl;
WriteFreq(out, GetPrevalence(stats_.f64_constant_hist), KeyIsLabel);
out << std::endl << "Unsigned int 16" << std::endl;
WriteFreq(out, GetPrevalence(stats_.u16_constant_hist), KeyIsLabel);
out << std::endl << "Signed int 16" << std::endl;
WriteFreq(out, GetPrevalence(stats_.s16_constant_hist), KeyIsLabel);
out << std::endl << "Unsigned int 32" << std::endl;
WriteFreq(out, GetPrevalence(stats_.u32_constant_hist), KeyIsLabel);
out << std::endl << "Signed int 32" << std::endl;
WriteFreq(out, GetPrevalence(stats_.s32_constant_hist), KeyIsLabel);
out << std::endl << "Unsigned int 64" << std::endl;
WriteFreq(out, GetPrevalence(stats_.u64_constant_hist), KeyIsLabel);
out << std::endl << "Signed int 64" << std::endl;
WriteFreq(out, GetPrevalence(stats_.s64_constant_hist), KeyIsLabel);
}
void StatsAnalyzer::WriteOpcodeMarkov(std::ostream& out) {
if (stats_.opcode_markov_hist.empty())
return;
const std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t>>&
cue_to_hist = stats_.opcode_markov_hist[0];
// Sort by prevalence of the opcodes in opcode_freq_ (descending).
std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>>
sorted_cue_to_hist(cue_to_hist.begin(), cue_to_hist.end());
std::sort(sorted_cue_to_hist.begin(), sorted_cue_to_hist.end(),
[this](
const std::pair<uint32_t,
std::unordered_map<uint32_t, uint32_t>>& left,
const std::pair<uint32_t,
std::unordered_map<uint32_t, uint32_t>>& right) {
const double lf = opcode_freq_[left.first];
const double rf = opcode_freq_[right.first];
if (lf == rf)
return right.first > left.first;
return lf > rf;
});
for (const auto& kv : sorted_cue_to_hist) {
const uint32_t cue = kv.first;
const double kFrequentEnoughToAnalyze = 0.0001;
if (opcode_freq_[cue] < kFrequentEnoughToAnalyze) continue;
const std::unordered_map<uint32_t, uint32_t>& hist = kv.second;
uint32_t total = 0;
for (const auto& pair : hist) {
total += pair.second;
}
std::vector<std::pair<uint32_t, uint32_t>>
sorted_hist(hist.begin(), hist.end());
std::sort(sorted_hist.begin(), sorted_hist.end(),
[](const std::pair<uint32_t, uint32_t>& left,
const std::pair<uint32_t, uint32_t>& right) {
if (left.second == right.second)
return right.first > left.first;
return left.second > right.second;
});
for (const auto& pair : sorted_hist) {
const double prior = opcode_freq_[pair.first];
const double posterior =
static_cast<double>(pair.second) / static_cast<double>(total);
out << GetOpcodeString(cue) << " -> " << GetOpcodeString(pair.first)
<< " " << posterior * 100 << "% (base rate " << prior * 100
<< "%, pair occurrences " << pair.second << ")" << std::endl;
}
}
}
void StatsAnalyzer::WriteCodegenOpcodeHist(std::ostream& out) {
auto all_opcodes = GetAllOpcodes();
// uint64_t is used because kMarkvNoneOfTheAbove is outside of uint32_t range.
out << "std::map<uint64_t, uint32_t> GetOpcodeHist() {\n"
<< " return std::map<uint64_t, uint32_t>({\n";
uint32_t total = 0;
for (const auto& kv : stats_.opcode_hist) {
total += kv.second;
}
for (uint32_t opcode : all_opcodes) {
const auto it = stats_.opcode_hist.find(opcode);
const uint32_t count = it == stats_.opcode_hist.end() ? 0 : it->second;
const double kMaxValue = 1000.0;
uint32_t value = uint32_t(kMaxValue * double(count) / double(total));
if (value == 0)
value = 1;
out << " { SpvOp" << GetOpcodeString(opcode)
<< ", " << value << " },\n";
}
// Add kMarkvNoneOfTheAbove as a signal for unknown opcode.
out << " { kMarkvNoneOfTheAbove, " << 10 << " },\n";
out << " });\n}\n";
}
void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsHist(std::ostream& out) {
out << "std::map<uint64_t, uint32_t> GetOpcodeAndNumOperandsHist() {\n"
<< " return std::map<uint64_t, uint32_t>({\n";
uint32_t total = 0;
for (const auto& kv : stats_.opcode_and_num_operands_hist) {
total += kv.second;
}
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)
continue;
out << " { CombineOpcodeAndNumOperands(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << num_operands << "), " << count << " },\n";
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
out << " });\n}\n";
}
void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsMarkovHuffmanCodecs(
std::ostream& out) {
out << "std::map<uint32_t, std::unique_ptr<HuffmanCodec<uint64_t>>>\n"
<< "GetOpcodeAndNumOperandsMarkovHuffmanCodecs() {\n"
<< " std::map<uint32_t, std::unique_ptr<HuffmanCodec<uint64_t>>> "
<< "codecs;\n";
for (const auto& kv : stats_.opcode_and_num_operands_markov_hist) {
const uint32_t prev_opcode = kv.first;
const double kFrequentEnoughToAnalyze = 0.001;
if (opcode_freq_[prev_opcode] < kFrequentEnoughToAnalyze) continue;
const std::unordered_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";
for (const auto& pair : hist) {
const uint32_t opcode_and_num_operands = pair.first;
const uint32_t opcode = opcode_and_num_operands & 0xFFFF;
if (opcode == SpvOpTypeStruct)
continue;
const uint32_t num_operands = opcode_and_num_operands >> 16;
const uint32_t count = pair.second;
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";
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
out << " }));\n" << std::endl;
out << " codecs.emplace(SpvOp" << GetOpcodeString(prev_opcode)
<< ", std::move(codec));\n";
out << " }\n\n";
}
out << " return codecs;\n}\n";
}
void StatsAnalyzer::WriteCodegenLiteralStringHuffmanCodecs(std::ostream& out) {
out << "std::map<uint32_t, std::unique_ptr<HuffmanCodec<std::string>>>\n"
<< "GetLiteralStringHuffmanCodecs() {\n"
<< " std::map<uint32_t, std::unique_ptr<HuffmanCodec<std::string>>> "
<< "codecs;\n";
for (const auto& kv : stats_.literal_strings_hist) {
const uint32_t opcode = kv.first;
if (opcode == SpvOpName || opcode == SpvOpMemberName)
continue;
const double kOpcodeFrequentEnoughToAnalyze = 0.001;
if (opcode_freq_[opcode] < kOpcodeFrequentEnoughToAnalyze) continue;
const std::unordered_map<std::string, uint32_t>& hist = kv.second;
uint32_t total = 0;
for (const auto& pair : hist) {
total += pair.second;
}
out << " {\n";
out << " std::unique_ptr<HuffmanCodec<std::string>> "
<< "codec(new HuffmanCodec<std::string>({\n";
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";
}
out << " { std::string(\"kMarkvNoneOfTheAbove\"), "
<< 1 + int(total * 0.05) << " },\n";
out << " }));\n" << std::endl;
out << " codecs.emplace(SpvOp" << spvOpcodeString(SpvOp(opcode))
<< ", std::move(codec));\n";
out << " }\n\n";
}
out << " return codecs;\n}\n";
}
void StatsAnalyzer::WriteCodegenNonIdWordHuffmanCodecs(std::ostream& out) {
out << "std::map<std::pair<uint32_t, uint32_t>, "
<< "std::unique_ptr<HuffmanCodec<uint64_t>>>\n"
<< "GetNonIdWordHuffmanCodecs() {\n"
<< " std::map<std::pair<uint32_t, uint32_t>, "
<< "std::unique_ptr<HuffmanCodec<uint64_t>>> codecs;\n";
for (const auto& kv : stats_.operand_slot_non_id_words_hist) {
const auto& opcode_and_index = kv.first;
const uint32_t opcode = opcode_and_index.first;
const uint32_t index = opcode_and_index.second;
const double kOpcodeFrequentEnoughToAnalyze = 0.001;
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";
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";
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
out << " }));\n" << std::endl;
out << " codecs.emplace(std::pair<uint32_t, uint32_t>(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << index << "), std::move(codec));\n";
out << " }\n\n";
}
out << " return codecs;\n}\n";
}
void StatsAnalyzer::WriteCodegenIdDescriptorHuffmanCodecs(
std::ostream& out) {
out << "std::map<std::pair<uint32_t, uint32_t>, "
<< "std::unique_ptr<HuffmanCodec<uint64_t>>>\n"
<< "GetIdDescriptorHuffmanCodecs() {\n"
<< " std::map<std::pair<uint32_t, uint32_t>, "
<< "std::unique_ptr<HuffmanCodec<uint64_t>>> codecs;\n";
for (const auto& kv : stats_.operand_slot_id_descriptor_hist) {
const auto& opcode_and_index = kv.first;
const uint32_t opcode = opcode_and_index.first;
const uint32_t index = opcode_and_index.second;
const double kOpcodeFrequentEnoughToAnalyze = 0.001;
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";
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";
}
out << " { kMarkvNoneOfTheAbove, " << 1 + int(total * 0.05) << " },\n";
out << " }));\n" << std::endl;
out << " codecs.emplace(std::pair<uint32_t, uint32_t>(SpvOp"
<< spvOpcodeString(SpvOp(opcode))
<< ", " << index << "), std::move(codec));\n";
out << " }\n\n";
}
out << " return codecs;\n}\n";
}