11cb0ef41Sopenharmony_ci// Copyright 2011 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#include "src/codegen/safepoint-table.h" 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci#include <iomanip> 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci#include "src/codegen/assembler-inl.h" 101cb0ef41Sopenharmony_ci#include "src/codegen/macro-assembler.h" 111cb0ef41Sopenharmony_ci#include "src/deoptimizer/deoptimizer.h" 121cb0ef41Sopenharmony_ci#include "src/diagnostics/disasm.h" 131cb0ef41Sopenharmony_ci#include "src/execution/frames-inl.h" 141cb0ef41Sopenharmony_ci#include "src/utils/ostreams.h" 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY 171cb0ef41Sopenharmony_ci#include "src/wasm/wasm-code-manager.h" 181cb0ef41Sopenharmony_ci#endif // V8_ENABLE_WEBASSEMBLY 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_cinamespace v8 { 211cb0ef41Sopenharmony_cinamespace internal { 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciSafepointTable::SafepointTable(Isolate* isolate, Address pc, Code code) 241cb0ef41Sopenharmony_ci : SafepointTable(code.InstructionStart(isolate, pc), 251cb0ef41Sopenharmony_ci code.SafepointTableAddress()) {} 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY 281cb0ef41Sopenharmony_ciSafepointTable::SafepointTable(const wasm::WasmCode* code) 291cb0ef41Sopenharmony_ci : SafepointTable( 301cb0ef41Sopenharmony_ci code->instruction_start(), 311cb0ef41Sopenharmony_ci code->instruction_start() + code->safepoint_table_offset()) {} 321cb0ef41Sopenharmony_ci#endif // V8_ENABLE_WEBASSEMBLY 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ciSafepointTable::SafepointTable(Address instruction_start, 351cb0ef41Sopenharmony_ci Address safepoint_table_address) 361cb0ef41Sopenharmony_ci : instruction_start_(instruction_start), 371cb0ef41Sopenharmony_ci safepoint_table_address_(safepoint_table_address), 381cb0ef41Sopenharmony_ci length_(base::Memory<int>(safepoint_table_address + kLengthOffset)), 391cb0ef41Sopenharmony_ci entry_configuration_(base::Memory<uint32_t>(safepoint_table_address + 401cb0ef41Sopenharmony_ci kEntryConfigurationOffset)) {} 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ciint SafepointTable::find_return_pc(int pc_offset) { 431cb0ef41Sopenharmony_ci for (int i = 0; i < length(); i++) { 441cb0ef41Sopenharmony_ci SafepointEntry entry = GetEntry(i); 451cb0ef41Sopenharmony_ci if (entry.trampoline_pc() == pc_offset || entry.pc() == pc_offset) { 461cb0ef41Sopenharmony_ci return entry.pc(); 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci } 491cb0ef41Sopenharmony_ci UNREACHABLE(); 501cb0ef41Sopenharmony_ci} 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ciSafepointEntry SafepointTable::FindEntry(Address pc) const { 531cb0ef41Sopenharmony_ci int pc_offset = static_cast<int>(pc - instruction_start_); 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci // Check if the PC is pointing at a trampoline. 561cb0ef41Sopenharmony_ci if (has_deopt_data()) { 571cb0ef41Sopenharmony_ci int candidate = -1; 581cb0ef41Sopenharmony_ci for (int i = 0; i < length_; ++i) { 591cb0ef41Sopenharmony_ci int trampoline_pc = GetEntry(i).trampoline_pc(); 601cb0ef41Sopenharmony_ci if (trampoline_pc != -1 && trampoline_pc <= pc_offset) candidate = i; 611cb0ef41Sopenharmony_ci if (trampoline_pc > pc_offset) break; 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci if (candidate != -1) return GetEntry(candidate); 641cb0ef41Sopenharmony_ci } 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci for (int i = 0; i < length_; ++i) { 671cb0ef41Sopenharmony_ci SafepointEntry entry = GetEntry(i); 681cb0ef41Sopenharmony_ci if (i == length_ - 1 || GetEntry(i + 1).pc() > pc_offset) { 691cb0ef41Sopenharmony_ci DCHECK_LE(entry.pc(), pc_offset); 701cb0ef41Sopenharmony_ci return entry; 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci } 731cb0ef41Sopenharmony_ci UNREACHABLE(); 741cb0ef41Sopenharmony_ci} 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_civoid SafepointTable::Print(std::ostream& os) const { 771cb0ef41Sopenharmony_ci os << "Safepoints (entries = " << length_ << ", byte size = " << byte_size() 781cb0ef41Sopenharmony_ci << ")\n"; 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci for (int index = 0; index < length_; index++) { 811cb0ef41Sopenharmony_ci SafepointEntry entry = GetEntry(index); 821cb0ef41Sopenharmony_ci os << reinterpret_cast<const void*>(instruction_start_ + entry.pc()) << " " 831cb0ef41Sopenharmony_ci << std::setw(6) << std::hex << entry.pc() << std::dec; 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci if (!entry.tagged_slots().empty()) { 861cb0ef41Sopenharmony_ci os << " slots (sp->fp): "; 871cb0ef41Sopenharmony_ci for (uint8_t bits : entry.tagged_slots()) { 881cb0ef41Sopenharmony_ci for (int bit = 0; bit < kBitsPerByte; ++bit) { 891cb0ef41Sopenharmony_ci os << ((bits >> bit) & 1); 901cb0ef41Sopenharmony_ci } 911cb0ef41Sopenharmony_ci } 921cb0ef41Sopenharmony_ci } 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci if (entry.tagged_register_indexes() != 0) { 951cb0ef41Sopenharmony_ci os << " registers: "; 961cb0ef41Sopenharmony_ci uint32_t register_bits = entry.tagged_register_indexes(); 971cb0ef41Sopenharmony_ci int bits = 32 - base::bits::CountLeadingZeros32(register_bits); 981cb0ef41Sopenharmony_ci for (int j = bits - 1; j >= 0; --j) { 991cb0ef41Sopenharmony_ci os << ((register_bits >> j) & 1); 1001cb0ef41Sopenharmony_ci } 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci if (entry.has_deoptimization_index()) { 1041cb0ef41Sopenharmony_ci os << " deopt " << std::setw(6) << entry.deoptimization_index() 1051cb0ef41Sopenharmony_ci << " trampoline: " << std::setw(6) << std::hex 1061cb0ef41Sopenharmony_ci << entry.trampoline_pc(); 1071cb0ef41Sopenharmony_ci } 1081cb0ef41Sopenharmony_ci os << "\n"; 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci} 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ciSafepointTableBuilder::Safepoint SafepointTableBuilder::DefineSafepoint( 1131cb0ef41Sopenharmony_ci Assembler* assembler) { 1141cb0ef41Sopenharmony_ci entries_.push_back(EntryBuilder(zone_, assembler->pc_offset_for_safepoint())); 1151cb0ef41Sopenharmony_ci return SafepointTableBuilder::Safepoint(&entries_.back(), this); 1161cb0ef41Sopenharmony_ci} 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ciint SafepointTableBuilder::UpdateDeoptimizationInfo(int pc, int trampoline, 1191cb0ef41Sopenharmony_ci int start, 1201cb0ef41Sopenharmony_ci int deopt_index) { 1211cb0ef41Sopenharmony_ci DCHECK_NE(SafepointEntry::kNoTrampolinePC, trampoline); 1221cb0ef41Sopenharmony_ci DCHECK_NE(SafepointEntry::kNoDeoptIndex, deopt_index); 1231cb0ef41Sopenharmony_ci auto it = entries_.Find(start); 1241cb0ef41Sopenharmony_ci DCHECK(std::any_of(it, entries_.end(), 1251cb0ef41Sopenharmony_ci [pc](auto& entry) { return entry.pc == pc; })); 1261cb0ef41Sopenharmony_ci int index = start; 1271cb0ef41Sopenharmony_ci while (it->pc != pc) ++it, ++index; 1281cb0ef41Sopenharmony_ci it->trampoline = trampoline; 1291cb0ef41Sopenharmony_ci it->deopt_index = deopt_index; 1301cb0ef41Sopenharmony_ci return index; 1311cb0ef41Sopenharmony_ci} 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_civoid SafepointTableBuilder::Emit(Assembler* assembler, int tagged_slots_size) { 1341cb0ef41Sopenharmony_ci DCHECK_LT(max_stack_index_, tagged_slots_size); 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ci#ifdef DEBUG 1371cb0ef41Sopenharmony_ci int last_pc = -1; 1381cb0ef41Sopenharmony_ci int last_trampoline = -1; 1391cb0ef41Sopenharmony_ci for (const EntryBuilder& entry : entries_) { 1401cb0ef41Sopenharmony_ci // Entries are ordered by PC. 1411cb0ef41Sopenharmony_ci DCHECK_LT(last_pc, entry.pc); 1421cb0ef41Sopenharmony_ci last_pc = entry.pc; 1431cb0ef41Sopenharmony_ci // Trampoline PCs are increasing, and larger than regular PCs. 1441cb0ef41Sopenharmony_ci if (entry.trampoline != SafepointEntry::kNoTrampolinePC) { 1451cb0ef41Sopenharmony_ci DCHECK_LT(last_trampoline, entry.trampoline); 1461cb0ef41Sopenharmony_ci DCHECK_LT(entries_.back().pc, entry.trampoline); 1471cb0ef41Sopenharmony_ci last_trampoline = entry.trampoline; 1481cb0ef41Sopenharmony_ci } 1491cb0ef41Sopenharmony_ci // An entry either has trampoline and deopt index, or none of the two. 1501cb0ef41Sopenharmony_ci DCHECK_EQ(entry.trampoline == SafepointEntry::kNoTrampolinePC, 1511cb0ef41Sopenharmony_ci entry.deopt_index == SafepointEntry::kNoDeoptIndex); 1521cb0ef41Sopenharmony_ci } 1531cb0ef41Sopenharmony_ci#endif // DEBUG 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci RemoveDuplicates(); 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci // The encoding is compacted by translating stack slot indices s.t. they 1581cb0ef41Sopenharmony_ci // start at 0. See also below. 1591cb0ef41Sopenharmony_ci tagged_slots_size -= min_stack_index(); 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 1621cb0ef41Sopenharmony_ci // We cannot emit a const pool within the safepoint table. 1631cb0ef41Sopenharmony_ci Assembler::BlockConstPoolScope block_const_pool(assembler); 1641cb0ef41Sopenharmony_ci#endif 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci // Make sure the safepoint table is properly aligned. Pad with nops. 1671cb0ef41Sopenharmony_ci assembler->Align(Code::kMetadataAlignment); 1681cb0ef41Sopenharmony_ci assembler->RecordComment(";;; Safepoint table."); 1691cb0ef41Sopenharmony_ci safepoint_table_offset_ = assembler->pc_offset(); 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci // Compute the required sizes of the fields. 1721cb0ef41Sopenharmony_ci int used_register_indexes = 0; 1731cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1); 1741cb0ef41Sopenharmony_ci int max_pc = SafepointEntry::kNoTrampolinePC; 1751cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1); 1761cb0ef41Sopenharmony_ci int max_deopt_index = SafepointEntry::kNoDeoptIndex; 1771cb0ef41Sopenharmony_ci for (const EntryBuilder& entry : entries_) { 1781cb0ef41Sopenharmony_ci used_register_indexes |= entry.register_indexes; 1791cb0ef41Sopenharmony_ci max_pc = std::max(max_pc, std::max(entry.pc, entry.trampoline)); 1801cb0ef41Sopenharmony_ci max_deopt_index = std::max(max_deopt_index, entry.deopt_index); 1811cb0ef41Sopenharmony_ci } 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci // Derive the bytes and bools for the entry configuration from the values. 1841cb0ef41Sopenharmony_ci auto value_to_bytes = [](int value) { 1851cb0ef41Sopenharmony_ci DCHECK_LE(0, value); 1861cb0ef41Sopenharmony_ci if (value == 0) return 0; 1871cb0ef41Sopenharmony_ci if (value <= 0xff) return 1; 1881cb0ef41Sopenharmony_ci if (value <= 0xffff) return 2; 1891cb0ef41Sopenharmony_ci if (value <= 0xffffff) return 3; 1901cb0ef41Sopenharmony_ci return 4; 1911cb0ef41Sopenharmony_ci }; 1921cb0ef41Sopenharmony_ci bool has_deopt_data = max_deopt_index != -1; 1931cb0ef41Sopenharmony_ci int register_indexes_size = value_to_bytes(used_register_indexes); 1941cb0ef41Sopenharmony_ci // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are 1951cb0ef41Sopenharmony_ci // non-negative. 1961cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1); 1971cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1); 1981cb0ef41Sopenharmony_ci int pc_size = value_to_bytes(max_pc + 1); 1991cb0ef41Sopenharmony_ci int deopt_index_size = value_to_bytes(max_deopt_index + 1); 2001cb0ef41Sopenharmony_ci int tagged_slots_bytes = 2011cb0ef41Sopenharmony_ci (tagged_slots_size + kBitsPerByte - 1) / kBitsPerByte; 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci // Add a CHECK to ensure we never overflow the space in the bitfield, even for 2041cb0ef41Sopenharmony_ci // huge functions which might not be covered by tests. 2051cb0ef41Sopenharmony_ci CHECK(SafepointTable::RegisterIndexesSizeField::is_valid( 2061cb0ef41Sopenharmony_ci register_indexes_size) && 2071cb0ef41Sopenharmony_ci SafepointTable::PcSizeField::is_valid(pc_size) && 2081cb0ef41Sopenharmony_ci SafepointTable::DeoptIndexSizeField::is_valid(deopt_index_size) && 2091cb0ef41Sopenharmony_ci SafepointTable::TaggedSlotsBytesField::is_valid(tagged_slots_bytes)); 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci uint32_t entry_configuration = 2121cb0ef41Sopenharmony_ci SafepointTable::HasDeoptDataField::encode(has_deopt_data) | 2131cb0ef41Sopenharmony_ci SafepointTable::RegisterIndexesSizeField::encode(register_indexes_size) | 2141cb0ef41Sopenharmony_ci SafepointTable::PcSizeField::encode(pc_size) | 2151cb0ef41Sopenharmony_ci SafepointTable::DeoptIndexSizeField::encode(deopt_index_size) | 2161cb0ef41Sopenharmony_ci SafepointTable::TaggedSlotsBytesField::encode(tagged_slots_bytes); 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci // Emit the table header. 2191cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointTable::kLengthOffset == 0 * kIntSize); 2201cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointTable::kEntryConfigurationOffset == 1 * kIntSize); 2211cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointTable::kHeaderSize == 2 * kIntSize); 2221cb0ef41Sopenharmony_ci int length = static_cast<int>(entries_.size()); 2231cb0ef41Sopenharmony_ci assembler->dd(length); 2241cb0ef41Sopenharmony_ci assembler->dd(entry_configuration); 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci auto emit_bytes = [assembler](int value, int bytes) { 2271cb0ef41Sopenharmony_ci DCHECK_LE(0, value); 2281cb0ef41Sopenharmony_ci for (; bytes > 0; --bytes, value >>= 8) assembler->db(value); 2291cb0ef41Sopenharmony_ci DCHECK_EQ(0, value); 2301cb0ef41Sopenharmony_ci }; 2311cb0ef41Sopenharmony_ci // Emit entries, sorted by pc offsets. 2321cb0ef41Sopenharmony_ci for (const EntryBuilder& entry : entries_) { 2331cb0ef41Sopenharmony_ci emit_bytes(entry.pc, pc_size); 2341cb0ef41Sopenharmony_ci if (has_deopt_data) { 2351cb0ef41Sopenharmony_ci // Add 1 so all values (including kNoDeoptIndex and kNoTrampolinePC) are 2361cb0ef41Sopenharmony_ci // non-negative. 2371cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoDeoptIndex == -1); 2381cb0ef41Sopenharmony_ci STATIC_ASSERT(SafepointEntry::kNoTrampolinePC == -1); 2391cb0ef41Sopenharmony_ci emit_bytes(entry.deopt_index + 1, deopt_index_size); 2401cb0ef41Sopenharmony_ci emit_bytes(entry.trampoline + 1, pc_size); 2411cb0ef41Sopenharmony_ci } 2421cb0ef41Sopenharmony_ci emit_bytes(entry.register_indexes, register_indexes_size); 2431cb0ef41Sopenharmony_ci } 2441cb0ef41Sopenharmony_ci 2451cb0ef41Sopenharmony_ci // Emit bitmaps of tagged stack slots. Note the slot list is reversed in the 2461cb0ef41Sopenharmony_ci // encoding. 2471cb0ef41Sopenharmony_ci // TODO(jgruber): Avoid building a reversed copy of the bit vector. 2481cb0ef41Sopenharmony_ci ZoneVector<uint8_t> bits(tagged_slots_bytes, 0, zone_); 2491cb0ef41Sopenharmony_ci for (const EntryBuilder& entry : entries_) { 2501cb0ef41Sopenharmony_ci std::fill(bits.begin(), bits.end(), 0); 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ci // Run through the indexes and build a bitmap. 2531cb0ef41Sopenharmony_ci for (int idx : *entry.stack_indexes) { 2541cb0ef41Sopenharmony_ci // The encoding is compacted by translating stack slot indices s.t. they 2551cb0ef41Sopenharmony_ci // start at 0. See also above. 2561cb0ef41Sopenharmony_ci const int adjusted_idx = idx - min_stack_index(); 2571cb0ef41Sopenharmony_ci DCHECK_GT(tagged_slots_size, adjusted_idx); 2581cb0ef41Sopenharmony_ci int index = tagged_slots_size - 1 - adjusted_idx; 2591cb0ef41Sopenharmony_ci int byte_index = index >> kBitsPerByteLog2; 2601cb0ef41Sopenharmony_ci int bit_index = index & (kBitsPerByte - 1); 2611cb0ef41Sopenharmony_ci bits[byte_index] |= (1u << bit_index); 2621cb0ef41Sopenharmony_ci } 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ci // Emit the bitmap for the current entry. 2651cb0ef41Sopenharmony_ci for (uint8_t byte : bits) assembler->db(byte); 2661cb0ef41Sopenharmony_ci } 2671cb0ef41Sopenharmony_ci} 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_civoid SafepointTableBuilder::RemoveDuplicates() { 2701cb0ef41Sopenharmony_ci // Remove any duplicate entries, i.e. succeeding entries that are identical 2711cb0ef41Sopenharmony_ci // except for the PC. During lookup, we will find the first entry whose PC is 2721cb0ef41Sopenharmony_ci // not larger than the PC at hand, and find the first non-duplicate. 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci if (entries_.size() < 2) return; 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ci auto is_identical_except_for_pc = [](const EntryBuilder& entry1, 2771cb0ef41Sopenharmony_ci const EntryBuilder& entry2) { 2781cb0ef41Sopenharmony_ci if (entry1.deopt_index != entry2.deopt_index) return false; 2791cb0ef41Sopenharmony_ci DCHECK_EQ(entry1.trampoline, entry2.trampoline); 2801cb0ef41Sopenharmony_ci return entry1.register_indexes == entry2.register_indexes && 2811cb0ef41Sopenharmony_ci entry1.stack_indexes->Equals(*entry2.stack_indexes); 2821cb0ef41Sopenharmony_ci }; 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci auto remaining_it = entries_.begin(); 2851cb0ef41Sopenharmony_ci size_t remaining = 0; 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci for (auto it = entries_.begin(), end = entries_.end(); it != end; 2881cb0ef41Sopenharmony_ci ++remaining_it, ++remaining) { 2891cb0ef41Sopenharmony_ci if (remaining_it != it) *remaining_it = *it; 2901cb0ef41Sopenharmony_ci // Merge identical entries. 2911cb0ef41Sopenharmony_ci do { 2921cb0ef41Sopenharmony_ci ++it; 2931cb0ef41Sopenharmony_ci } while (it != end && is_identical_except_for_pc(*it, *remaining_it)); 2941cb0ef41Sopenharmony_ci } 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci entries_.Rewind(remaining); 2971cb0ef41Sopenharmony_ci} 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci} // namespace internal 3001cb0ef41Sopenharmony_ci} // namespace v8 301