%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/pgo.cc |
// Copyright 2022 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/pgo.h" #include "src/wasm/decoder.h" #include "src/wasm/wasm-module-builder.h" // For {ZoneBuffer}. namespace v8::internal::wasm { constexpr uint8_t kFunctionExecutedBit = 1 << 0; constexpr uint8_t kFunctionTieredUpBit = 1 << 1; class ProfileGenerator { public: ProfileGenerator(const WasmModule* module, const uint32_t* tiering_budget_array) : module_(module), type_feedback_mutex_guard_(&module->type_feedback.mutex), tiering_budget_array_(tiering_budget_array) {} base::OwnedVector<uint8_t> GetProfileData() { ZoneBuffer buffer{&zone_}; SerializeTypeFeedback(buffer); SerializeTieringInfo(buffer); return base::OwnedVector<uint8_t>::Of(buffer); } private: void SerializeTypeFeedback(ZoneBuffer& buffer) { const std::unordered_map<uint32_t, FunctionTypeFeedback>& feedback_for_function = module_->type_feedback.feedback_for_function; // Get an ordered list of function indexes, so we generate deterministic // data. std::vector<uint32_t> ordered_function_indexes; ordered_function_indexes.reserve(feedback_for_function.size()); for (const auto& entry : feedback_for_function) { // Skip functions for which we have no feedback. if (entry.second.feedback_vector.empty()) continue; ordered_function_indexes.push_back(entry.first); } std::sort(ordered_function_indexes.begin(), ordered_function_indexes.end()); buffer.write_u32v(static_cast<uint32_t>(ordered_function_indexes.size())); for (const uint32_t func_index : ordered_function_indexes) { buffer.write_u32v(func_index); // Serialize {feedback_vector}. const FunctionTypeFeedback& feedback = feedback_for_function.at(func_index); buffer.write_u32v(static_cast<uint32_t>(feedback.feedback_vector.size())); for (const CallSiteFeedback& call_site_feedback : feedback.feedback_vector) { int cases = call_site_feedback.num_cases(); buffer.write_i32v(cases); for (int i = 0; i < cases; ++i) { buffer.write_i32v(call_site_feedback.function_index(i)); buffer.write_i32v(call_site_feedback.call_count(i)); } } // Serialize {call_targets}. buffer.write_u32v(static_cast<uint32_t>(feedback.call_targets.size())); for (uint32_t call_target : feedback.call_targets) { buffer.write_u32v(call_target); } } } void SerializeTieringInfo(ZoneBuffer& buffer) { const std::unordered_map<uint32_t, FunctionTypeFeedback>& feedback_for_function = module_->type_feedback.feedback_for_function; const uint32_t initial_budget = v8_flags.wasm_tiering_budget; for (uint32_t declared_index = 0; declared_index < module_->num_declared_functions; ++declared_index) { uint32_t func_index = declared_index + module_->num_imported_functions; auto feedback_it = feedback_for_function.find(func_index); int prio = feedback_it == feedback_for_function.end() ? 0 : feedback_it->second.tierup_priority; DCHECK_LE(0, prio); uint32_t remaining_budget = tiering_budget_array_[declared_index]; DCHECK_GE(initial_budget, remaining_budget); bool was_tiered_up = prio > 0; bool was_executed = was_tiered_up || remaining_budget != initial_budget; // TODO(13209): Make this less V8-specific for productionization. buffer.write_u8((was_executed ? kFunctionExecutedBit : 0) | (was_tiered_up ? kFunctionTieredUpBit : 0)); } } private: const WasmModule* module_; AccountingAllocator allocator_; Zone zone_{&allocator_, "wasm::ProfileGenerator"}; base::SharedMutexGuard<base::kShared> type_feedback_mutex_guard_; const uint32_t* const tiering_budget_array_; }; void DeserializeTypeFeedback(Decoder& decoder, const WasmModule* module) { base::SharedMutexGuard<base::kShared> type_feedback_guard{ &module->type_feedback.mutex}; std::unordered_map<uint32_t, FunctionTypeFeedback>& feedback_for_function = module->type_feedback.feedback_for_function; uint32_t num_entries = decoder.consume_u32v("num function entries"); CHECK_LE(num_entries, module->num_declared_functions); for (uint32_t missing_entries = num_entries; missing_entries > 0; --missing_entries) { FunctionTypeFeedback feedback; uint32_t function_index = decoder.consume_u32v("function index"); // Deserialize {feedback_vector}. uint32_t feedback_vector_size = decoder.consume_u32v("feedback vector size"); feedback.feedback_vector.resize(feedback_vector_size); for (CallSiteFeedback& feedback : feedback.feedback_vector) { int num_cases = decoder.consume_i32v("num cases"); if (num_cases == 0) continue; // no feedback if (num_cases == 1) { // monomorphic int called_function_index = decoder.consume_i32v("function index"); int call_count = decoder.consume_i32v("call count"); feedback = CallSiteFeedback{called_function_index, call_count}; } else { // polymorphic auto* polymorphic = new CallSiteFeedback::PolymorphicCase[num_cases]; for (int i = 0; i < num_cases; ++i) { polymorphic[i].function_index = decoder.consume_i32v("function index"); polymorphic[i].absolute_call_frequency = decoder.consume_i32v("call count"); } feedback = CallSiteFeedback{polymorphic, num_cases}; } } // Deserialize {call_targets}. uint32_t num_call_targets = decoder.consume_u32v("num call targets"); feedback.call_targets = base::OwnedVector<uint32_t>::NewForOverwrite(num_call_targets); for (uint32_t& call_target : feedback.call_targets) { call_target = decoder.consume_u32v("call target"); } // Finally, insert the new feedback into the map. Overwrite existing // feedback, but check for consistency. auto [feedback_it, is_new] = feedback_for_function.emplace(function_index, std::move(feedback)); if (!is_new) { FunctionTypeFeedback& old_feedback = feedback_it->second; CHECK(old_feedback.feedback_vector.empty() || old_feedback.feedback_vector.size() == feedback_vector_size); CHECK_EQ(old_feedback.call_targets.as_vector(), feedback.call_targets.as_vector()); std::swap(old_feedback.feedback_vector, feedback.feedback_vector); } } } std::unique_ptr<ProfileInformation> DeserializeTieringInformation( Decoder& decoder, const WasmModule* module) { std::vector<uint32_t> executed_functions; std::vector<uint32_t> tiered_up_functions; uint32_t start = module->num_imported_functions; uint32_t end = start + module->num_declared_functions; for (uint32_t func_index = start; func_index < end; ++func_index) { uint8_t tiering_info = decoder.consume_u8("tiering info"); CHECK_EQ(0, tiering_info & ~3); bool was_executed = tiering_info & kFunctionExecutedBit; bool was_tiered_up = tiering_info & kFunctionTieredUpBit; if (was_tiered_up) tiered_up_functions.push_back(func_index); if (was_executed) executed_functions.push_back(func_index); } return std::make_unique<ProfileInformation>(std::move(executed_functions), std::move(tiered_up_functions)); } std::unique_ptr<ProfileInformation> RestoreProfileData( const WasmModule* module, base::Vector<uint8_t> profile_data) { Decoder decoder{profile_data.begin(), profile_data.end()}; DeserializeTypeFeedback(decoder, module); std::unique_ptr<ProfileInformation> pgo_info = DeserializeTieringInformation(decoder, module); CHECK(decoder.ok()); CHECK_EQ(decoder.pc(), decoder.end()); return pgo_info; } void DumpProfileToFile(const WasmModule* module, base::Vector<const uint8_t> wire_bytes, uint32_t* tiering_budget_array) { CHECK(!wire_bytes.empty()); // File are named `profile-wasm-<hash>`. // We use the same hash as for reported scripts, to make it easier to // correlate files to wasm modules (see {CreateWasmScript}). uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes)); base::EmbeddedVector<char, 32> filename; SNPrintF(filename, "profile-wasm-%08x", hash); ProfileGenerator profile_generator{module, tiering_budget_array}; base::OwnedVector<uint8_t> profile_data = profile_generator.GetProfileData(); PrintF( "Dumping Wasm PGO data to file '%s' (module size %zu, %u declared " "functions, %zu bytes PGO data)\n", filename.begin(), wire_bytes.size(), module->num_declared_functions, profile_data.size()); if (FILE* file = base::OS::FOpen(filename.begin(), "wb")) { size_t written = fwrite(profile_data.begin(), 1, profile_data.size(), file); CHECK_EQ(profile_data.size(), written); base::Fclose(file); } } std::unique_ptr<ProfileInformation> LoadProfileFromFile( const WasmModule* module, base::Vector<const uint8_t> wire_bytes) { CHECK(!wire_bytes.empty()); // File are named `profile-wasm-<hash>`. // We use the same hash as for reported scripts, to make it easier to // correlate files to wasm modules (see {CreateWasmScript}). uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes)); base::EmbeddedVector<char, 32> filename; SNPrintF(filename, "profile-wasm-%08x", hash); FILE* file = base::OS::FOpen(filename.begin(), "rb"); if (!file) { PrintF("No Wasm PGO data found: Cannot open file '%s'\n", filename.begin()); return {}; } fseek(file, 0, SEEK_END); size_t size = ftell(file); rewind(file); PrintF("Loading Wasm PGO data from file '%s' (%zu bytes)\n", filename.begin(), size); base::OwnedVector<uint8_t> profile_data = base::OwnedVector<uint8_t>::NewForOverwrite(size); for (size_t read = 0; read < size;) { read += fread(profile_data.begin() + read, 1, size - read, file); CHECK(!ferror(file)); } base::Fclose(file); return RestoreProfileData(module, profile_data.as_vector()); } } // namespace v8::internal::wasm