11cb0ef41Sopenharmony_ci// Copyright 2018 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#if !V8_ENABLE_WEBASSEMBLY 61cb0ef41Sopenharmony_ci#error This header should only be included if WebAssembly is enabled. 71cb0ef41Sopenharmony_ci#endif // !V8_ENABLE_WEBASSEMBLY 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci#ifndef V8_WASM_JUMP_TABLE_ASSEMBLER_H_ 101cb0ef41Sopenharmony_ci#define V8_WASM_JUMP_TABLE_ASSEMBLER_H_ 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ci#include "src/codegen/macro-assembler.h" 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_cinamespace v8 { 151cb0ef41Sopenharmony_cinamespace internal { 161cb0ef41Sopenharmony_cinamespace wasm { 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci// The jump table is the central dispatch point for all (direct and indirect) 191cb0ef41Sopenharmony_ci// invocations in WebAssembly. It holds one slot per function in a module, with 201cb0ef41Sopenharmony_ci// each slot containing a dispatch to the currently published {WasmCode} that 211cb0ef41Sopenharmony_ci// corresponds to the function. 221cb0ef41Sopenharmony_ci// 231cb0ef41Sopenharmony_ci// Additionally to this main jump table, there exist special jump tables for 241cb0ef41Sopenharmony_ci// other purposes: 251cb0ef41Sopenharmony_ci// - the far stub table contains one entry per wasm runtime stub (see 261cb0ef41Sopenharmony_ci// {WasmCode::RuntimeStubId}, which jumps to the corresponding embedded 271cb0ef41Sopenharmony_ci// builtin, plus (if not the full address space can be reached via the jump 281cb0ef41Sopenharmony_ci// table) one entry per wasm function. 291cb0ef41Sopenharmony_ci// - the lazy compile table contains one entry per wasm function which jumps to 301cb0ef41Sopenharmony_ci// the common {WasmCompileLazy} builtin and passes the function index that was 311cb0ef41Sopenharmony_ci// invoked. 321cb0ef41Sopenharmony_ci// 331cb0ef41Sopenharmony_ci// The main jump table is split into lines of fixed size, with lines laid out 341cb0ef41Sopenharmony_ci// consecutively within the executable memory of the {NativeModule}. The slots 351cb0ef41Sopenharmony_ci// in turn are consecutive within a line, but do not cross line boundaries. 361cb0ef41Sopenharmony_ci// 371cb0ef41Sopenharmony_ci// +- L1 -------------------+ +- L2 -------------------+ +- L3 ... 381cb0ef41Sopenharmony_ci// | S1 | S2 | ... | Sn | x | | S1 | S2 | ... | Sn | x | | S1 ... 391cb0ef41Sopenharmony_ci// +------------------------+ +------------------------+ +---- ... 401cb0ef41Sopenharmony_ci// 411cb0ef41Sopenharmony_ci// The above illustrates jump table lines {Li} containing slots {Si} with each 421cb0ef41Sopenharmony_ci// line containing {n} slots and some padding {x} for alignment purposes. 431cb0ef41Sopenharmony_ci// Other jump tables are just consecutive. 441cb0ef41Sopenharmony_ci// 451cb0ef41Sopenharmony_ci// The main jump table will be patched concurrently while other threads execute 461cb0ef41Sopenharmony_ci// it. The code at the new target might also have been emitted concurrently, so 471cb0ef41Sopenharmony_ci// we need to ensure that there is proper synchronization between code emission, 481cb0ef41Sopenharmony_ci// jump table patching and code execution. 491cb0ef41Sopenharmony_ci// On Intel platforms, this all works out of the box because there is cache 501cb0ef41Sopenharmony_ci// coherency between i-cache and d-cache. 511cb0ef41Sopenharmony_ci// On ARM, it is safe because the i-cache flush after code emission executes an 521cb0ef41Sopenharmony_ci// "ic ivau" (Instruction Cache line Invalidate by Virtual Address to Point of 531cb0ef41Sopenharmony_ci// Unification), which broadcasts to all cores. A core which sees the jump table 541cb0ef41Sopenharmony_ci// update thus also sees the new code. Since the other core does not explicitly 551cb0ef41Sopenharmony_ci// execute an "isb" (Instruction Synchronization Barrier), it might still 561cb0ef41Sopenharmony_ci// execute the old code afterwards, which is no problem, since that code remains 571cb0ef41Sopenharmony_ci// available until it is garbage collected. Garbage collection itself is a 581cb0ef41Sopenharmony_ci// synchronization barrier though. 591cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE JumpTableAssembler : public MacroAssembler { 601cb0ef41Sopenharmony_ci public: 611cb0ef41Sopenharmony_ci // Translate an offset into the continuous jump table to a jump table index. 621cb0ef41Sopenharmony_ci static uint32_t SlotOffsetToIndex(uint32_t slot_offset) { 631cb0ef41Sopenharmony_ci uint32_t line_index = slot_offset / kJumpTableLineSize; 641cb0ef41Sopenharmony_ci uint32_t line_offset = slot_offset % kJumpTableLineSize; 651cb0ef41Sopenharmony_ci DCHECK_EQ(0, line_offset % kJumpTableSlotSize); 661cb0ef41Sopenharmony_ci return line_index * kJumpTableSlotsPerLine + 671cb0ef41Sopenharmony_ci line_offset / kJumpTableSlotSize; 681cb0ef41Sopenharmony_ci } 691cb0ef41Sopenharmony_ci 701cb0ef41Sopenharmony_ci // Translate a jump table index to an offset into the continuous jump table. 711cb0ef41Sopenharmony_ci static uint32_t JumpSlotIndexToOffset(uint32_t slot_index) { 721cb0ef41Sopenharmony_ci uint32_t line_index = slot_index / kJumpTableSlotsPerLine; 731cb0ef41Sopenharmony_ci uint32_t line_offset = 741cb0ef41Sopenharmony_ci (slot_index % kJumpTableSlotsPerLine) * kJumpTableSlotSize; 751cb0ef41Sopenharmony_ci return line_index * kJumpTableLineSize + line_offset; 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci // Determine the size of a jump table containing the given number of slots. 791cb0ef41Sopenharmony_ci static constexpr uint32_t SizeForNumberOfSlots(uint32_t slot_count) { 801cb0ef41Sopenharmony_ci return ((slot_count + kJumpTableSlotsPerLine - 1) / 811cb0ef41Sopenharmony_ci kJumpTableSlotsPerLine) * 821cb0ef41Sopenharmony_ci kJumpTableLineSize; 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci // Translate a far jump table index to an offset into the table. 861cb0ef41Sopenharmony_ci static uint32_t FarJumpSlotIndexToOffset(uint32_t slot_index) { 871cb0ef41Sopenharmony_ci return slot_index * kFarJumpTableSlotSize; 881cb0ef41Sopenharmony_ci } 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci // Translate a far jump table offset to the index into the table. 911cb0ef41Sopenharmony_ci static uint32_t FarJumpSlotOffsetToIndex(uint32_t offset) { 921cb0ef41Sopenharmony_ci DCHECK_EQ(0, offset % kFarJumpTableSlotSize); 931cb0ef41Sopenharmony_ci return offset / kFarJumpTableSlotSize; 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci // Determine the size of a far jump table containing the given number of 971cb0ef41Sopenharmony_ci // slots. 981cb0ef41Sopenharmony_ci static constexpr uint32_t SizeForNumberOfFarJumpSlots( 991cb0ef41Sopenharmony_ci int num_runtime_slots, int num_function_slots) { 1001cb0ef41Sopenharmony_ci int num_entries = num_runtime_slots + num_function_slots; 1011cb0ef41Sopenharmony_ci return num_entries * kFarJumpTableSlotSize; 1021cb0ef41Sopenharmony_ci } 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci // Translate a slot index to an offset into the lazy compile table. 1051cb0ef41Sopenharmony_ci static uint32_t LazyCompileSlotIndexToOffset(uint32_t slot_index) { 1061cb0ef41Sopenharmony_ci return slot_index * kLazyCompileTableSlotSize; 1071cb0ef41Sopenharmony_ci } 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci // Determine the size of a lazy compile table. 1101cb0ef41Sopenharmony_ci static constexpr uint32_t SizeForNumberOfLazyFunctions(uint32_t slot_count) { 1111cb0ef41Sopenharmony_ci return slot_count * kLazyCompileTableSlotSize; 1121cb0ef41Sopenharmony_ci } 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci static void GenerateLazyCompileTable(Address base, uint32_t num_slots, 1151cb0ef41Sopenharmony_ci uint32_t num_imported_functions, 1161cb0ef41Sopenharmony_ci Address wasm_compile_lazy_target) { 1171cb0ef41Sopenharmony_ci uint32_t lazy_compile_table_size = num_slots * kLazyCompileTableSlotSize; 1181cb0ef41Sopenharmony_ci // Assume enough space, so the Assembler does not try to grow the buffer. 1191cb0ef41Sopenharmony_ci JumpTableAssembler jtasm(base, lazy_compile_table_size + 256); 1201cb0ef41Sopenharmony_ci for (uint32_t slot_index = 0; slot_index < num_slots; ++slot_index) { 1211cb0ef41Sopenharmony_ci DCHECK_EQ(slot_index * kLazyCompileTableSlotSize, jtasm.pc_offset()); 1221cb0ef41Sopenharmony_ci jtasm.EmitLazyCompileJumpSlot(slot_index + num_imported_functions, 1231cb0ef41Sopenharmony_ci wasm_compile_lazy_target); 1241cb0ef41Sopenharmony_ci } 1251cb0ef41Sopenharmony_ci DCHECK_EQ(lazy_compile_table_size, jtasm.pc_offset()); 1261cb0ef41Sopenharmony_ci FlushInstructionCache(base, lazy_compile_table_size); 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci static void GenerateFarJumpTable(Address base, Address* stub_targets, 1301cb0ef41Sopenharmony_ci int num_runtime_slots, 1311cb0ef41Sopenharmony_ci int num_function_slots) { 1321cb0ef41Sopenharmony_ci uint32_t table_size = 1331cb0ef41Sopenharmony_ci SizeForNumberOfFarJumpSlots(num_runtime_slots, num_function_slots); 1341cb0ef41Sopenharmony_ci // Assume enough space, so the Assembler does not try to grow the buffer. 1351cb0ef41Sopenharmony_ci JumpTableAssembler jtasm(base, table_size + 256); 1361cb0ef41Sopenharmony_ci int offset = 0; 1371cb0ef41Sopenharmony_ci for (int index = 0; index < num_runtime_slots + num_function_slots; 1381cb0ef41Sopenharmony_ci ++index) { 1391cb0ef41Sopenharmony_ci DCHECK_EQ(offset, FarJumpSlotIndexToOffset(index)); 1401cb0ef41Sopenharmony_ci // Functions slots initially jump to themselves. They are patched before 1411cb0ef41Sopenharmony_ci // being used. 1421cb0ef41Sopenharmony_ci Address target = 1431cb0ef41Sopenharmony_ci index < num_runtime_slots ? stub_targets[index] : base + offset; 1441cb0ef41Sopenharmony_ci jtasm.EmitFarJumpSlot(target); 1451cb0ef41Sopenharmony_ci offset += kFarJumpTableSlotSize; 1461cb0ef41Sopenharmony_ci DCHECK_EQ(offset, jtasm.pc_offset()); 1471cb0ef41Sopenharmony_ci } 1481cb0ef41Sopenharmony_ci FlushInstructionCache(base, table_size); 1491cb0ef41Sopenharmony_ci } 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci static void PatchJumpTableSlot(Address jump_table_slot, 1521cb0ef41Sopenharmony_ci Address far_jump_table_slot, Address target) { 1531cb0ef41Sopenharmony_ci // First, try to patch the jump table slot. 1541cb0ef41Sopenharmony_ci JumpTableAssembler jtasm(jump_table_slot); 1551cb0ef41Sopenharmony_ci if (!jtasm.EmitJumpSlot(target)) { 1561cb0ef41Sopenharmony_ci // If that fails, we need to patch the far jump table slot, and then 1571cb0ef41Sopenharmony_ci // update the jump table slot to jump to this far jump table slot. 1581cb0ef41Sopenharmony_ci DCHECK_NE(kNullAddress, far_jump_table_slot); 1591cb0ef41Sopenharmony_ci JumpTableAssembler::PatchFarJumpSlot(far_jump_table_slot, target); 1601cb0ef41Sopenharmony_ci CHECK(jtasm.EmitJumpSlot(far_jump_table_slot)); 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset()); 1631cb0ef41Sopenharmony_ci FlushInstructionCache(jump_table_slot, kJumpTableSlotSize); 1641cb0ef41Sopenharmony_ci } 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci private: 1671cb0ef41Sopenharmony_ci // Instantiate a {JumpTableAssembler} for patching. 1681cb0ef41Sopenharmony_ci explicit JumpTableAssembler(Address slot_addr, int size = 256) 1691cb0ef41Sopenharmony_ci : MacroAssembler(nullptr, JumpTableAssemblerOptions(), 1701cb0ef41Sopenharmony_ci CodeObjectRequired::kNo, 1711cb0ef41Sopenharmony_ci ExternalAssemblerBuffer( 1721cb0ef41Sopenharmony_ci reinterpret_cast<uint8_t*>(slot_addr), size)) {} 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci// To allow concurrent patching of the jump table entries, we need to ensure 1751cb0ef41Sopenharmony_ci// that the instruction containing the call target does not cross cache-line 1761cb0ef41Sopenharmony_ci// boundaries. The jump table line size has been chosen to satisfy this. 1771cb0ef41Sopenharmony_ci#if V8_TARGET_ARCH_X64 1781cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 64; 1791cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 5; 1801cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 16; 1811cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 10; 1821cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_IA32 1831cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 64; 1841cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 5; 1851cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 5; 1861cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 10; 1871cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM 1881cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 3 * kInstrSize; 1891cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 3 * kInstrSize; 1901cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 2 * kInstrSize; 1911cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 5 * kInstrSize; 1921cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM64 && V8_ENABLE_CONTROL_FLOW_INTEGRITY 1931cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 2 * kInstrSize; 1941cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 2 * kInstrSize; 1951cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize; 1961cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 4 * kInstrSize; 1971cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_ARM64 && !V8_ENABLE_CONTROL_FLOW_INTEGRITY 1981cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 1 * kInstrSize; 1991cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 1 * kInstrSize; 2001cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize; 2011cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 3 * kInstrSize; 2021cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_S390X 2031cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 128; 2041cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 8; 2051cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 16; 2061cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 20; 2071cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_PPC64 2081cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 64; 2091cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 1 * kInstrSize; 2101cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 12 * kInstrSize; 2111cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 12 * kInstrSize; 2121cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_MIPS 2131cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 8 * kInstrSize; 2141cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 8 * kInstrSize; 2151cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize; 2161cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 6 * kInstrSize; 2171cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_MIPS64 2181cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 8 * kInstrSize; 2191cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 8 * kInstrSize; 2201cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize; 2211cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize; 2221cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_RISCV64 2231cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 6 * kInstrSize; 2241cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 6 * kInstrSize; 2251cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 6 * kInstrSize; 2261cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 10 * kInstrSize; 2271cb0ef41Sopenharmony_ci#elif V8_TARGET_ARCH_LOONG64 2281cb0ef41Sopenharmony_ci static constexpr int kJumpTableLineSize = 8 * kInstrSize; 2291cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotSize = 8 * kInstrSize; 2301cb0ef41Sopenharmony_ci static constexpr int kFarJumpTableSlotSize = 4 * kInstrSize; 2311cb0ef41Sopenharmony_ci static constexpr int kLazyCompileTableSlotSize = 8 * kInstrSize; 2321cb0ef41Sopenharmony_ci#else 2331cb0ef41Sopenharmony_ci#error Unknown architecture. 2341cb0ef41Sopenharmony_ci#endif 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci static constexpr int kJumpTableSlotsPerLine = 2371cb0ef41Sopenharmony_ci kJumpTableLineSize / kJumpTableSlotSize; 2381cb0ef41Sopenharmony_ci STATIC_ASSERT(kJumpTableSlotsPerLine >= 1); 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci // {JumpTableAssembler} is never used during snapshot generation, and its code 2411cb0ef41Sopenharmony_ci // must be independent of the code range of any isolate anyway. Just ensure 2421cb0ef41Sopenharmony_ci // that no relocation information is recorded, there is no buffer to store it 2431cb0ef41Sopenharmony_ci // since it is instantiated in patching mode in existing code directly. 2441cb0ef41Sopenharmony_ci static AssemblerOptions JumpTableAssemblerOptions() { 2451cb0ef41Sopenharmony_ci AssemblerOptions options; 2461cb0ef41Sopenharmony_ci options.disable_reloc_info_for_patching = true; 2471cb0ef41Sopenharmony_ci return options; 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci void EmitLazyCompileJumpSlot(uint32_t func_index, 2511cb0ef41Sopenharmony_ci Address lazy_compile_target); 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci // Returns {true} if the jump fits in the jump table slot, {false} otherwise. 2541cb0ef41Sopenharmony_ci bool EmitJumpSlot(Address target); 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci // Initially emit a far jump slot. 2571cb0ef41Sopenharmony_ci void EmitFarJumpSlot(Address target); 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci // Patch an existing far jump slot, and make sure that this updated eventually 2601cb0ef41Sopenharmony_ci // becomes available to all execution units that might execute this code. 2611cb0ef41Sopenharmony_ci static void PatchFarJumpSlot(Address slot, Address target); 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci void NopBytes(int bytes); 2641cb0ef41Sopenharmony_ci}; 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci} // namespace wasm 2671cb0ef41Sopenharmony_ci} // namespace internal 2681cb0ef41Sopenharmony_ci} // namespace v8 2691cb0ef41Sopenharmony_ci 2701cb0ef41Sopenharmony_ci#endif // V8_WASM_JUMP_TABLE_ASSEMBLER_H_ 271