// 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 "tools/stats/spirv_stats.h" #include <cassert> #include <algorithm> #include <memory> #include <string> #include "source/diagnostic.h" #include "source/enum_string_mapping.h" #include "source/extensions.h" #include "source/id_descriptor.h" #include "source/instruction.h" #include "source/opcode.h" #include "source/operand.h" #include "source/val/instruction.h" #include "source/val/validate.h" #include "source/val/validation_state.h" #include "spirv-tools/libspirv.h" namespace spvtools { namespace stats { 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 val::ValidationState_t* state) : stats_(in_out_stats), vstate_(state) {} // Processes the instructions to collect stats. void aggregate() { const auto& instructions = vstate_->ordered_instructions(); ++stats_->version_hist[vstate_->version()]; ++stats_->generator_hist[vstate_->generator()]; for (size_t i = 0; i < instructions.size(); ++i) { const auto& inst = instructions[i]; ProcessOpcode(&inst, i); ProcessCapability(&inst); ProcessExtension(&inst); ProcessConstant(&inst); } } // Collects OpCapability statistics. void ProcessCapability(const val::Instruction* inst) { 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 val::Instruction* inst) { if (inst->opcode() != SpvOpExtension) return; const std::string extension = GetExtensionString(&inst->c_inst()); ++stats_->extension_hist[extension]; } // Collects OpCode statistics. void ProcessOpcode(const val::Instruction* inst, size_t idx) { const SpvOp opcode = inst->opcode(); ++stats_->opcode_hist[opcode]; if (idx == 0) return; --idx; const auto& instructions = vstate_->ordered_instructions(); auto step_it = stats_->opcode_markov_hist.begin(); for (; step_it != stats_->opcode_markov_hist.end(); --idx, ++step_it) { auto& hist = (*step_it)[instructions[idx].opcode()]; ++hist[opcode]; if (idx == 0) break; } } // Collects OpConstant statistics. void ProcessConstant(const val::Instruction* inst) { 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 val::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); } } } private: SpirvStats* stats_; const val::ValidationState_t* vstate_; IdDescriptorCollection id_descriptors_; }; } // namespace spv_result_t AggregateStats(const spv_context context, const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, SpirvStats* stats) { std::unique_ptr<val::ValidationState_t> vstate; spv_validator_options_t options; spv_result_t result = ValidateBinaryAndKeepValidationState( context, &options, words, num_words, pDiagnostic, &vstate); if (result != SPV_SUCCESS) return result; StatsAggregator stats_aggregator(stats, vstate.get()); stats_aggregator.aggregate(); return SPV_SUCCESS; } } // namespace stats } // namespace spvtools