Stats analyzer aggregates OpConstant usage
diff --git a/source/spirv_stats.cpp b/source/spirv_stats.cpp index ef80229..2186e0d 100644 --- a/source/spirv_stats.cpp +++ b/source/spirv_stats.cpp
@@ -73,6 +73,7 @@ ProcessOpcode(); ProcessCapability(); ProcessExtension(); + ProcessConstant(); return SPV_SUCCESS; } @@ -108,6 +109,49 @@ } } + // Collects OpConstant statistics. + void ProcessConstant() { + const Instruction& inst = GetCurrentInstruction(); + if (inst.opcode() != SpvOpConstant) return; + const uint32_t type_id = inst.GetOperandAs<uint32_t>(0); + const auto type_decl_it = vstate_->all_definitions().find(type_id); + assert(type_decl_it != vstate_->all_definitions().end()); + const Instruction& type_decl_inst = *type_decl_it->second; + const SpvOp type_op = type_decl_inst.opcode(); + if (type_op == SpvOpTypeInt) { + const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1); + const uint32_t is_signed = type_decl_inst.GetOperandAs<uint32_t>(2); + assert(is_signed == 0 || is_signed == 1); + if (bit_width == 16) { + if (is_signed) + ++stats_->s16_constant_hist[inst.GetOperandAs<int16_t>(2)]; + else + ++stats_->u16_constant_hist[inst.GetOperandAs<uint16_t>(2)]; + } else if (bit_width == 32) { + if (is_signed) + ++stats_->s32_constant_hist[inst.GetOperandAs<int32_t>(2)]; + else + ++stats_->u32_constant_hist[inst.GetOperandAs<uint32_t>(2)]; + } else if (bit_width == 64) { + if (is_signed) + ++stats_->s64_constant_hist[inst.GetOperandAs<int64_t>(2)]; + else + ++stats_->u64_constant_hist[inst.GetOperandAs<uint64_t>(2)]; + } else { + assert(false && "TypeInt bit width is not 16, 32 or 64"); + } + } else if (type_op == SpvOpTypeFloat) { + const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1); + if (bit_width == 32) { + ++stats_->f32_constant_hist[inst.GetOperandAs<float>(2)]; + } else if (bit_width == 64) { + ++stats_->f64_constant_hist[inst.GetOperandAs<double>(2)]; + } else { + assert(bit_width == 16); + } + } + } + SpirvStats* stats() { return stats_; }
diff --git a/source/spirv_stats.h b/source/spirv_stats.h index d639924..9c7a41a 100644 --- a/source/spirv_stats.h +++ b/source/spirv_stats.h
@@ -39,6 +39,30 @@ // Opcode histogram, SpvOpXXX -> count. std::unordered_map<uint32_t, uint32_t> opcode_hist; + // OpConstant u16 histogram, value -> count. + std::unordered_map<uint16_t, uint32_t> u16_constant_hist; + + // OpConstant u32 histogram, value -> count. + std::unordered_map<uint32_t, uint32_t> u32_constant_hist; + + // OpConstant u64 histogram, value -> count. + std::unordered_map<uint64_t, uint32_t> u64_constant_hist; + + // OpConstant s16 histogram, value -> count. + std::unordered_map<int16_t, uint32_t> s16_constant_hist; + + // OpConstant s32 histogram, value -> count. + std::unordered_map<int32_t, uint32_t> s32_constant_hist; + + // OpConstant s64 histogram, value -> count. + std::unordered_map<int64_t, uint32_t> s64_constant_hist; + + // OpConstant f32 histogram, value -> count. + std::unordered_map<float, uint32_t> f32_constant_hist; + + // OpConstant f64 histogram, value -> count. + std::unordered_map<double, uint32_t> f64_constant_hist; + // Used to collect statistics on opcodes triggering other opcodes. // Container scheme: gap between instructions -> cue opcode -> later opcode // -> count.
diff --git a/source/val/instruction.h b/source/val/instruction.h index 7cef7e3..31b463a 100644 --- a/source/val/instruction.h +++ b/source/val/instruction.h
@@ -15,8 +15,8 @@ #ifndef LIBSPIRV_VAL_INSTRUCTION_H_ #define LIBSPIRV_VAL_INSTRUCTION_H_ +#include <cassert> #include <cstdint> - #include <functional> #include <utility> #include <vector> @@ -76,6 +76,15 @@ return inst_; } + // Casts the words belonging to the operand under |index| to |T| and returns. + template <typename T> + T GetOperandAs(size_t index) const { + const spv_parsed_operand_t& operand = operands_.at(index); + assert(operand.num_words * 4 >= sizeof(T)); + assert(operand.offset + operand.num_words <= inst_.num_words); + return *reinterpret_cast<const T*>(&words_[operand.offset]); + } + private: const std::vector<uint32_t> words_; const std::vector<spv_parsed_operand_t> operands_;
diff --git a/test/stats/stats_aggregate_test.cpp b/test/stats/stats_aggregate_test.cpp index 8b23504..43026e8 100644 --- a/test/stats/stats_aggregate_test.cpp +++ b/test/stats/stats_aggregate_test.cpp
@@ -339,4 +339,98 @@ 1u, stats.opcode_markov_hist[1].at(SpvOpTypeInt).at(SpvOpTypeFloat)); } +TEST(AggregateStats, ConstantLiteralsHistogram) { + const std::string code1 = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +OpMemoryModel Physical32 OpenCL +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%1 = OpConstant %f32 0.1 +%2 = OpConstant %f32 -2 +%3 = OpConstant %f64 -2 +%4 = OpConstant %u16 16 +%5 = OpConstant %u16 2 +%6 = OpConstant %u32 32 +%7 = OpConstant %u64 64 +)"; + + const std::string code2 = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Int16 +OpCapability Int64 +OpMemoryModel Logical GLSL450 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%1 = OpConstant %f32 0.1 +%2 = OpConstant %f32 -2 +%3 = OpConstant %u16 1 +%4 = OpConstant %u16 16 +%5 = OpConstant %u16 2 +%6 = OpConstant %s16 -16 +%7 = OpConstant %u32 32 +%8 = OpConstant %s32 2 +%9 = OpConstant %s32 -32 +%10 = OpConstant %u64 64 +%11 = OpConstant %s64 -64 +)"; + + SpirvStats stats; + + CompileAndAggregateStats(code1, &stats); + EXPECT_EQ(2u, stats.f32_constant_hist.size()); + EXPECT_EQ(1u, stats.f64_constant_hist.size()); + EXPECT_EQ(1u, stats.f32_constant_hist.at(0.1f)); + EXPECT_EQ(1u, stats.f32_constant_hist.at(-2.f)); + EXPECT_EQ(1u, stats.f64_constant_hist.at(-2)); + + EXPECT_EQ(2u, stats.u16_constant_hist.size()); + EXPECT_EQ(0u, stats.s16_constant_hist.size()); + EXPECT_EQ(1u, stats.u32_constant_hist.size()); + EXPECT_EQ(0u, stats.s32_constant_hist.size()); + EXPECT_EQ(1u, stats.u64_constant_hist.size()); + EXPECT_EQ(0u, stats.s64_constant_hist.size()); + EXPECT_EQ(1u, stats.u16_constant_hist.at(16)); + EXPECT_EQ(1u, stats.u16_constant_hist.at(2)); + EXPECT_EQ(1u, stats.u32_constant_hist.at(32)); + EXPECT_EQ(1u, stats.u64_constant_hist.at(64)); + + CompileAndAggregateStats(code2, &stats); + EXPECT_EQ(2u, stats.f32_constant_hist.size()); + EXPECT_EQ(1u, stats.f64_constant_hist.size()); + EXPECT_EQ(2u, stats.f32_constant_hist.at(0.1f)); + EXPECT_EQ(2u, stats.f32_constant_hist.at(-2.f)); + EXPECT_EQ(1u, stats.f64_constant_hist.at(-2)); + + EXPECT_EQ(3u, stats.u16_constant_hist.size()); + EXPECT_EQ(1u, stats.s16_constant_hist.size()); + EXPECT_EQ(1u, stats.u32_constant_hist.size()); + EXPECT_EQ(2u, stats.s32_constant_hist.size()); + EXPECT_EQ(1u, stats.u64_constant_hist.size()); + EXPECT_EQ(1u, stats.s64_constant_hist.size()); + EXPECT_EQ(2u, stats.u16_constant_hist.at(16)); + EXPECT_EQ(2u, stats.u16_constant_hist.at(2)); + EXPECT_EQ(1u, stats.u16_constant_hist.at(1)); + EXPECT_EQ(1u, stats.s16_constant_hist.at(-16)); + EXPECT_EQ(2u, stats.u32_constant_hist.at(32)); + EXPECT_EQ(1u, stats.s32_constant_hist.at(2)); + EXPECT_EQ(1u, stats.s32_constant_hist.at(-32)); + EXPECT_EQ(2u, stats.u64_constant_hist.at(64)); + EXPECT_EQ(1u, stats.s64_constant_hist.at(-64)); +} + } // namespace
diff --git a/tools/stats/stats.cpp b/tools/stats/stats.cpp index 40e8f28..51d6183 100644 --- a/tools/stats/stats.cpp +++ b/tools/stats/stats.cpp
@@ -151,5 +151,8 @@ out << std::endl; analyzer.WriteOpcodeMarkov(out); + out << std::endl; + analyzer.WriteConstantLiterals(out); + return 0; }
diff --git a/tools/stats/stats_analyzer.cpp b/tools/stats/stats_analyzer.cpp index 428265c..9e248a4 100644 --- a/tools/stats/stats_analyzer.cpp +++ b/tools/stats/stats_analyzer.cpp
@@ -47,8 +47,11 @@ return libspirv::CapabilityToString(static_cast<SpvCapability>(word)); } -std::string KeyIsLabel(std::string key) { - return key; +template <class T> +std::string KeyIsLabel(T key) { + std::stringstream ss; + ss << key; + return ss.str(); } template <class Key> @@ -81,7 +84,7 @@ // |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)) { + 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, @@ -90,6 +93,8 @@ }); for (const auto& pair : sorted_freq) { + if (pair.second < threshold) + break; out << label_from_key(pair.first) << " " << pair.second * 100.0 << "%" << std::endl; } @@ -151,6 +156,34 @@ 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;
diff --git a/tools/stats/stats_analyzer.h b/tools/stats/stats_analyzer.h index 809cde2..c1ff187 100644 --- a/tools/stats/stats_analyzer.h +++ b/tools/stats/stats_analyzer.h
@@ -29,6 +29,7 @@ void WriteCapability(std::ostream& out); void WriteExtension(std::ostream& out); void WriteOpcode(std::ostream& out); + void WriteConstantLiterals(std::ostream& out); // Writes first order Markov analysis to |out|. // stats_.opcode_markov_hist needs to contain raw data for at least one