| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // 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 "spirv_stats.h"
- #include <cassert>
- #include <algorithm>
- #include <memory>
- #include <string>
- #include <vector>
- #include "binary.h"
- #include "diagnostic.h"
- #include "enum_string_mapping.h"
- #include "extensions.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 "val/instruction.h"
- #include "val/validation_state.h"
- #include "validate.h"
- using libspirv::IdDescriptorCollection;
- using libspirv::Instruction;
- using libspirv::SpirvStats;
- using libspirv::ValidationState_t;
- namespace {
- // Helper class for stats aggregation. Receives as in/out parameter.
- // Constructs ValidationState and updates it by running validator for each
- // instruction.
- class StatsAggregator {
- public:
- StatsAggregator(SpirvStats* in_out_stats, const spv_const_context context) {
- stats_ = in_out_stats;
- vstate_.reset(new ValidationState_t(context, &validator_options_));
- }
- // 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 */) {
- vstate_->setIdBound(id_bound);
- ++stats_->version_hist[version];
- ++stats_->generator_hist[generator];
- return SPV_SUCCESS;
- }
- // Runs validator to validate the instruction and update vstate_,
- // 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;
- ProcessOpcode();
- ProcessCapability();
- ProcessExtension();
- ProcessConstant();
- ProcessEnums();
- ProcessLiteralStrings();
- ProcessNonIdWords();
- ProcessIdDescriptors();
- return SPV_SUCCESS;
- }
- // Collects statistics of descriptors generated by IdDescriptorCollection.
- void ProcessIdDescriptors() {
- const Instruction& inst = GetCurrentInstruction();
- const uint32_t new_descriptor =
- id_descriptors_.ProcessInstruction(inst.c_inst());
- if (new_descriptor) {
- std::stringstream ss;
- ss << spvOpcodeString(inst.opcode());
- for (size_t i = 1; i < inst.words().size(); ++i) {
- ss << " " << inst.word(i);
- }
- stats_->id_descriptor_labels.emplace(new_descriptor, ss.str());
- }
- uint32_t index = 0;
- for (const auto& operand : inst.operands()) {
- if (spvIsIdType(operand.type)) {
- const uint32_t descriptor =
- 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];
- }
- }
- ++index;
- }
- }
- // Collects statistics of enum words for operands of specific types.
- void ProcessEnums() {
- const Instruction& inst = GetCurrentInstruction();
- for (const auto& operand : inst.operands()) {
- switch (operand.type) {
- case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
- case SPV_OPERAND_TYPE_EXECUTION_MODEL:
- case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
- case SPV_OPERAND_TYPE_MEMORY_MODEL:
- case SPV_OPERAND_TYPE_EXECUTION_MODE:
- case SPV_OPERAND_TYPE_STORAGE_CLASS:
- case SPV_OPERAND_TYPE_DIMENSIONALITY:
- case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
- case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
- case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT:
- case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER:
- case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE:
- case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
- case SPV_OPERAND_TYPE_LINKAGE_TYPE:
- case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
- case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
- case SPV_OPERAND_TYPE_DECORATION:
- case SPV_OPERAND_TYPE_BUILT_IN:
- case SPV_OPERAND_TYPE_GROUP_OPERATION:
- case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
- case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
- case SPV_OPERAND_TYPE_CAPABILITY: {
- ++stats_->enum_hist[operand.type][inst.word(operand.offset)];
- break;
- }
- default:
- break;
- }
- }
- }
- // Collects statistics of literal strings used by opcodes.
- void ProcessLiteralStrings() {
- const Instruction& inst = GetCurrentInstruction();
- for (const auto& operand : inst.operands()) {
- if (operand.type == SPV_OPERAND_TYPE_LITERAL_STRING) {
- const std::string str =
- reinterpret_cast<const char*>(&inst.words()[operand.offset]);
- ++stats_->literal_strings_hist[inst.opcode()][str];
- }
- }
- }
- // Collects statistics of all single word non-id operand slots.
- void ProcessNonIdWords() {
- const Instruction& inst = GetCurrentInstruction();
- 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)];
- }
- ++index;
- }
- }
- // Collects OpCapability statistics.
- void ProcessCapability() {
- const Instruction& inst = GetCurrentInstruction();
- if (inst.opcode() != SpvOpCapability) return;
- const uint32_t capability = inst.word(inst.operands()[0].offset);
- ++stats_->capability_hist[capability];
- }
- // Collects OpExtension statistics.
- void ProcessExtension() {
- const Instruction& inst = GetCurrentInstruction();
- if (inst.opcode() != SpvOpExtension) return;
- const std::string extension = libspirv::GetExtensionString(&inst.c_inst());
- ++stats_->extension_hist[extension];
- }
- // Collects OpCode statistics.
- void ProcessOpcode() {
- auto inst_it = vstate_->ordered_instructions().rbegin();
- const SpvOp opcode = inst_it->opcode();
- ++stats_->opcode_hist[opcode];
- const uint32_t opcode_and_num_operands =
- (uint32_t(inst_it->operands().size()) << 16) | uint32_t(opcode);
- ++stats_->opcode_and_num_operands_hist[opcode_and_num_operands];
- ++inst_it;
- 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];
- }
- 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) {
- auto& hist = (*step_it)[inst_it->opcode()];
- ++hist[opcode];
- }
- }
- // 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_; }
- private:
- // Returns the current instruction (the one last processed by the validator).
- const Instruction& GetCurrentInstruction() const {
- return vstate_->ordered_instructions().back();
- }
- SpirvStats* stats_;
- spv_validator_options_t validator_options_;
- std::unique_ptr<ValidationState_t> vstate_;
- 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) {
- StatsAggregator* stats_aggregator =
- reinterpret_cast<StatsAggregator*>(user_data);
- return stats_aggregator->ProcessHeader(endian, magic, version, generator,
- id_bound, schema);
- }
- 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);
- }
- } // namespace
- 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_const_binary_t binary = {words, num_words};
- spv_endianness_t endian;
- spv_position_t position = {};
- if (spvBinaryEndianness(&binary, &endian)) {
- return libspirv::DiagnosticStream(position, context.consumer,
- SPV_ERROR_INVALID_BINARY)
- << "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.";
- }
- StatsAggregator stats_aggregator(stats, &context);
- return spvBinaryParse(&context, &stats_aggregator, words, num_words,
- ProcessHeader, ProcessInstruction, pDiagnostic);
- }
- } // namespace libspirv
|