Line data Source code
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 74969324 : class V8_EXPORT_PRIVATE JumpTableAssembler : public MacroAssembler {
31 : public:
32 : // Translate an offset into the continuous jump table to a jump table index.
33 : static uint32_t SlotOffsetToIndex(uint32_t slot_offset) {
34 40656 : uint32_t line_index = slot_offset / kJumpTableLineSize;
35 40656 : uint32_t line_offset = slot_offset % kJumpTableLineSize;
36 : DCHECK_EQ(0, line_offset % kJumpTableSlotSize);
37 40656 : return line_index * kJumpTableSlotsPerLine +
38 40656 : line_offset / kJumpTableSlotSize;
39 : }
40 :
41 : // Translate a jump table index to an offset into the continuous jump table.
42 : static uint32_t SlotIndexToOffset(uint32_t slot_index) {
43 1856673 : uint32_t line_index = slot_index / kJumpTableSlotsPerLine;
44 : uint32_t line_offset =
45 1856673 : (slot_index % kJumpTableSlotsPerLine) * kJumpTableSlotSize;
46 1856673 : return line_index * kJumpTableLineSize + line_offset;
47 : }
48 :
49 : // Determine the size of a jump table containing the given number of slots.
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 2623919 : return ((slot_count + kJumpTableSlotsPerLine - 1) /
54 : kJumpTableSlotsPerLine) *
55 2623919 : kJumpTableLineSize;
56 : }
57 :
58 : // Translate a stub slot index to an offset into the continuous jump table.
59 : static uint32_t StubSlotIndexToOffset(uint32_t slot_index) {
60 72048289 : return slot_index * kJumpTableStubSlotSize;
61 : }
62 :
63 : // Determine the size of a jump table containing only runtime stub slots.
64 : static constexpr uint32_t SizeForNumberOfStubSlots(uint32_t slot_count) {
65 : return slot_count * kJumpTableStubSlotSize;
66 : }
67 :
68 15093 : static void EmitLazyCompileJumpSlot(Address base, uint32_t slot_index,
69 : uint32_t func_index,
70 : Address lazy_compile_target,
71 : WasmCode::FlushICache flush_i_cache) {
72 15093 : Address slot = base + SlotIndexToOffset(slot_index);
73 15093 : JumpTableAssembler jtasm(slot);
74 15093 : jtasm.EmitLazyCompileJumpSlot(func_index, lazy_compile_target);
75 15093 : jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset());
76 15093 : if (flush_i_cache) {
77 : FlushInstructionCache(slot, kJumpTableSlotSize);
78 : }
79 15093 : }
80 :
81 36024144 : static void EmitRuntimeStubSlot(Address base, uint32_t slot_index,
82 : Address builtin_target,
83 : WasmCode::FlushICache flush_i_cache) {
84 36024144 : Address slot = base + StubSlotIndexToOffset(slot_index);
85 36024144 : JumpTableAssembler jtasm(slot);
86 36024139 : jtasm.EmitRuntimeStubSlot(builtin_target);
87 36024131 : jtasm.NopBytes(kJumpTableStubSlotSize - jtasm.pc_offset());
88 36024130 : if (flush_i_cache) {
89 : FlushInstructionCache(slot, kJumpTableStubSlotSize);
90 : }
91 36024145 : }
92 :
93 1445392 : static void PatchJumpTableSlot(Address base, uint32_t slot_index,
94 : Address new_target,
95 : WasmCode::FlushICache flush_i_cache) {
96 1445392 : Address slot = base + SlotIndexToOffset(slot_index);
97 1445392 : JumpTableAssembler jtasm(slot);
98 1445430 : jtasm.EmitJumpSlot(new_target);
99 1445347 : jtasm.NopBytes(kJumpTableSlotSize - jtasm.pc_offset());
100 1445418 : if (flush_i_cache) {
101 : FlushInstructionCache(slot, kJumpTableSlotSize);
102 : }
103 1445433 : }
104 :
105 : private:
106 : // Instantiate a {JumpTableAssembler} for patching.
107 37484579 : explicit JumpTableAssembler(Address slot_addr, int size = 256)
108 37484572 : : MacroAssembler(nullptr, JumpTableAssemblerOptions(),
109 : CodeObjectRequired::kNo,
110 74969151 : ExternalAssemblerBuffer(
111 74969198 : reinterpret_cast<uint8_t*>(slot_addr), size)) {}
112 :
113 : // To allow concurrent patching of the jump table entries, we need to ensure
114 : // that the instruction containing the call target does not cross cache-line
115 : // boundaries. The jump table line size has been chosen to satisfy this.
116 : #if V8_TARGET_ARCH_X64
117 : static constexpr int kJumpTableLineSize = 64;
118 : static constexpr int kJumpTableSlotSize = 18;
119 : static constexpr int kJumpTableStubSlotSize = 18;
120 : #elif V8_TARGET_ARCH_IA32
121 : static constexpr int kJumpTableLineSize = 64;
122 : static constexpr int kJumpTableSlotSize = 10;
123 : static constexpr int kJumpTableStubSlotSize = 10;
124 : #elif V8_TARGET_ARCH_ARM
125 : static constexpr int kJumpTableLineSize = 5 * kInstrSize;
126 : static constexpr int kJumpTableSlotSize = 5 * kInstrSize;
127 : static constexpr int kJumpTableStubSlotSize = 5 * kInstrSize;
128 : #elif V8_TARGET_ARCH_ARM64
129 : static constexpr int kJumpTableLineSize = 3 * kInstrSize;
130 : static constexpr int kJumpTableSlotSize = 3 * kInstrSize;
131 : static constexpr int kJumpTableStubSlotSize = 6 * kInstrSize;
132 : #elif V8_TARGET_ARCH_S390X
133 : static constexpr int kJumpTableLineSize = 128;
134 : static constexpr int kJumpTableSlotSize = 20;
135 : static constexpr int kJumpTableStubSlotSize = 14;
136 : #elif V8_TARGET_ARCH_PPC64
137 : static constexpr int kJumpTableLineSize = 64;
138 : static constexpr int kJumpTableSlotSize = 48;
139 : static constexpr int kJumpTableStubSlotSize = 7 * kInstrSize;
140 : #elif V8_TARGET_ARCH_MIPS
141 : static constexpr int kJumpTableLineSize = 6 * kInstrSize;
142 : static constexpr int kJumpTableSlotSize = 6 * kInstrSize;
143 : static constexpr int kJumpTableStubSlotSize = 4 * kInstrSize;
144 : #elif V8_TARGET_ARCH_MIPS64
145 : static constexpr int kJumpTableLineSize = 8 * kInstrSize;
146 : static constexpr int kJumpTableSlotSize = 8 * kInstrSize;
147 : static constexpr int kJumpTableStubSlotSize = 6 * kInstrSize;
148 : #else
149 : static constexpr int kJumpTableLineSize = 1;
150 : static constexpr int kJumpTableSlotSize = 1;
151 : static constexpr int kJumpTableStubSlotSize = 1;
152 : #endif
153 :
154 : static constexpr int kJumpTableSlotsPerLine =
155 : kJumpTableLineSize / kJumpTableSlotSize;
156 :
157 : // {JumpTableAssembler} is never used during snapshot generation, and its code
158 : // must be independent of the code range of any isolate anyway. Just ensure
159 : // that no relocation information is recorded, there is no buffer to store it
160 : // since it is instantiated in patching mode in existing code directly.
161 : static AssemblerOptions JumpTableAssemblerOptions() {
162 37484626 : AssemblerOptions options;
163 37484626 : options.disable_reloc_info_for_patching = true;
164 : return options;
165 : }
166 :
167 : void EmitLazyCompileJumpSlot(uint32_t func_index,
168 : Address lazy_compile_target);
169 :
170 : void EmitRuntimeStubSlot(Address builtin_target);
171 :
172 : void EmitJumpSlot(Address target);
173 :
174 : void NopBytes(int bytes);
175 : };
176 :
177 : } // namespace wasm
178 : } // namespace internal
179 : } // namespace v8
180 :
181 : #endif // V8_WASM_JUMP_TABLE_ASSEMBLER_H_
|