| // 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 |