• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_WASM_JUMP_TABLE_ASSEMBLER_H_
6 #define V8_WASM_JUMP_TABLE_ASSEMBLER_H_
7 
8 #include "src/macro-assembler.h"
9 #include "src/wasm/wasm-code-manager.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14 
15 // The jump table is the central dispatch point for all (direct and indirect)
16 // invocations in WebAssembly. It holds one slot per function in a module, with
17 // each slot containing a dispatch to the currently published {WasmCode} that
18 // corresponds to the function.
19 //
20 // Note that the table is split into lines of fixed size, with lines laid out
21 // consecutively within the executable memory of the {NativeModule}. The slots
22 // in turn are consecutive within a line, but do not cross line boundaries.
23 //
24 //   +- L1 -------------------+ +- L2 -------------------+ +- L3 ...
25 //   | S1 | S2 | ... | Sn | x | | S1 | S2 | ... | Sn | x | | S1  ...
26 //   +------------------------+ +------------------------+ +---- ...
27 //
28 // The above illustrates jump table lines {Li} containing slots {Si} with each
29 // line containing {n} slots and some padding {x} for alignment purposes.
30 class JumpTableAssembler : public TurboAssembler {
31  public:
32   // Translate an offset into the continuous jump table to a jump table index.
SlotOffsetToIndex(uint32_t slot_offset)33   static uint32_t SlotOffsetToIndex(uint32_t slot_offset) {
34     uint32_t line_index = slot_offset / kJumpTableLineSize;
35     uint32_t line_offset = slot_offset % kJumpTableLineSize;
36     DCHECK_EQ(0, line_offset % kJumpTableSlotSize);
37     return line_index * kJumpTableSlotsPerLine +
38            line_offset / kJumpTableSlotSize;
39   }
40 
41   // Translate a jump table index to an offset into the continuous jump table.
SlotIndexToOffset(uint32_t slot_index)42   static uint32_t SlotIndexToOffset(uint32_t slot_index) {
43     uint32_t line_index = slot_index / kJumpTableSlotsPerLine;
44     uint32_t line_offset =
45         (slot_index % kJumpTableSlotsPerLine) * kJumpTableSlotSize;
46     return line_index * kJumpTableLineSize + line_offset;
47   }
48 
49   // Determine the size of a jump table containing the given number of slots.
SizeForNumberOfSlots(uint32_t slot_count)50   static constexpr uint32_t SizeForNumberOfSlots(uint32_t slot_count) {
51     // TODO(wasm): Once the {RoundUp} utility handles non-powers of two values,
52     // use: {RoundUp<kJumpTableSlotsPerLine>(slot_count) * kJumpTableLineSize}
53     return ((slot_count + kJumpTableSlotsPerLine - 1) /
54             kJumpTableSlotsPerLine) *
55            kJumpTableLineSize;
56   }
57 
EmitLazyCompileJumpSlot(Address base,uint32_t slot_index,uint32_t func_index,Address lazy_compile_target,WasmCode::FlushICache flush_i_cache)58   static void EmitLazyCompileJumpSlot(Address base, uint32_t slot_index,
59                                       uint32_t func_index,
60                                       Address lazy_compile_target,
61                                       WasmCode::FlushICache flush_i_cache) {
62     Address slot = base + SlotIndexToOffset(slot_index);
63     JumpTableAssembler jtasm(slot);
64     jtasm.EmitLazyCompileJumpSlot(func_index, lazy_compile_target);
65     jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset());
66     if (flush_i_cache) {
67       Assembler::FlushICache(slot, kJumpTableSlotSize);
68     }
69   }
70 
PatchJumpTableSlot(Address base,uint32_t slot_index,Address new_target,WasmCode::FlushICache flush_i_cache)71   static void PatchJumpTableSlot(Address base, uint32_t slot_index,
72                                  Address new_target,
73                                  WasmCode::FlushICache flush_i_cache) {
74     Address slot = base + SlotIndexToOffset(slot_index);
75     JumpTableAssembler jtasm(slot);
76     jtasm.EmitJumpSlot(new_target);
77     jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset());
78     if (flush_i_cache) {
79       Assembler::FlushICache(slot, kJumpTableSlotSize);
80     }
81   }
82 
83  private:
84   // Instantiate a {JumpTableAssembler} for patching.
85   explicit JumpTableAssembler(Address slot_addr, int size = 256)
TurboAssembler(nullptr,JumpTableAssemblerOptions (),reinterpret_cast<void * > (slot_addr),size,CodeObjectRequired::kNo)86       : TurboAssembler(nullptr, JumpTableAssemblerOptions(),
87                        reinterpret_cast<void*>(slot_addr), size,
88                        CodeObjectRequired::kNo) {}
89 
90 // To allow concurrent patching of the jump table entries, we need to ensure
91 // that the instruction containing the call target does not cross cache-line
92 // boundaries. The jump table line size has been chosen to satisfy this.
93 #if V8_TARGET_ARCH_X64
94   static constexpr int kJumpTableLineSize = 64;
95   static constexpr int kJumpTableSlotSize = 18;
96 #elif V8_TARGET_ARCH_IA32
97   static constexpr int kJumpTableLineSize = 64;
98   static constexpr int kJumpTableSlotSize = 10;
99 #elif V8_TARGET_ARCH_ARM
100   static constexpr int kJumpTableLineSize = 5 * kInstrSize;
101   static constexpr int kJumpTableSlotSize = 5 * kInstrSize;
102 #elif V8_TARGET_ARCH_ARM64
103   static constexpr int kJumpTableLineSize = 3 * kInstrSize;
104   static constexpr int kJumpTableSlotSize = 3 * kInstrSize;
105 #elif V8_TARGET_ARCH_S390X
106   static constexpr int kJumpTableLineSize = 20;
107   static constexpr int kJumpTableSlotSize = 20;
108 #elif V8_TARGET_ARCH_S390
109   static constexpr int kJumpTableLineSize = 14;
110   static constexpr int kJumpTableSlotSize = 14;
111 #elif V8_TARGET_ARCH_PPC64
112   static constexpr int kJumpTableLineSize = 48;
113   static constexpr int kJumpTableSlotSize = 48;
114 #elif V8_TARGET_ARCH_PPC
115   static constexpr int kJumpTableLineSize = 24;
116   static constexpr int kJumpTableSlotSize = 24;
117 #elif V8_TARGET_ARCH_MIPS
118   static constexpr int kJumpTableLineSize = 6 * kInstrSize;
119   static constexpr int kJumpTableSlotSize = 6 * kInstrSize;
120 #elif V8_TARGET_ARCH_MIPS64
121   static constexpr int kJumpTableLineSize = 8 * kInstrSize;
122   static constexpr int kJumpTableSlotSize = 8 * kInstrSize;
123 #else
124   static constexpr int kJumpTableLineSize = 1;
125   static constexpr int kJumpTableSlotSize = 1;
126 #endif
127 
128   static constexpr int kJumpTableSlotsPerLine =
129       kJumpTableLineSize / kJumpTableSlotSize;
130 
131   // {JumpTableAssembler} is never used during snapshot generation, and its code
132   // must be independent of the code range of any isolate anyway. Just ensure
133   // that no relocation information is recorded, there is no buffer to store it
134   // since it is instantiated in patching mode in existing code directly.
JumpTableAssemblerOptions()135   static AssemblerOptions JumpTableAssemblerOptions() {
136     AssemblerOptions options;
137     options.disable_reloc_info_for_patching = true;
138     return options;
139   }
140 
141   void EmitLazyCompileJumpSlot(uint32_t func_index,
142                                Address lazy_compile_target);
143 
144   void EmitJumpSlot(Address target);
145 
146   void NopBytes(int bytes);
147 };
148 
149 }  // namespace wasm
150 }  // namespace internal
151 }  // namespace v8
152 
153 #endif  // V8_WASM_JUMP_TABLE_ASSEMBLER_H_
154