OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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 #include "src/wasm/wasm-code-specialization.h" | |
6 | |
7 #include "src/assembler-inl.h" | |
8 #include "src/objects-inl.h" | |
9 #include "src/source-position-table.h" | |
10 #include "src/wasm/decoder.h" | |
11 #include "src/wasm/wasm-module.h" | |
12 #include "src/wasm/wasm-opcodes.h" | |
13 | |
14 using namespace v8::internal; | |
15 using namespace v8::internal::wasm; | |
16 | |
17 namespace { | |
18 | |
19 int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) { | |
20 DCHECK_EQ(static_cast<int>(kExprCallFunction), static_cast<int>(*pc)); | |
21 decoder.Reset(pc + 1, pc + 6); | |
22 uint32_t call_idx = decoder.consume_u32v("call index"); | |
23 DCHECK(decoder.ok()); | |
24 DCHECK_GE(kMaxInt, call_idx); | |
25 return static_cast<int>(call_idx); | |
26 } | |
27 | |
28 int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator, | |
29 size_t offset_l) { | |
30 DCHECK_GE(kMaxInt, offset_l); | |
31 int offset = static_cast<int>(offset_l); | |
32 DCHECK(!iterator.done()); | |
33 int byte_pos; | |
34 do { | |
35 byte_pos = iterator.source_position().ScriptOffset(); | |
36 iterator.Advance(); | |
37 } while (!iterator.done() && iterator.code_offset() <= offset); | |
38 return byte_pos; | |
39 } | |
40 | |
41 } // namespace | |
42 | |
43 CodeSpecialization::CodeSpecialization(Isolate* isolate, Zone* zone) | |
44 : objects_to_relocate(isolate->heap(), zone) {} | |
45 | |
46 CodeSpecialization::~CodeSpecialization() {} | |
47 | |
48 void CodeSpecialization::RelocateMemoryReferences(Address old_start, | |
49 uint32_t old_size, | |
50 Address new_start, | |
51 uint32_t new_size) { | |
52 DCHECK(old_mem_start == 0 && new_mem_start == 0); | |
53 DCHECK(old_start != 0 || new_start != 0); | |
54 old_mem_start = old_start; | |
55 old_mem_size = old_size; | |
56 new_mem_start = new_start; | |
57 new_mem_size = new_size; | |
58 } | |
59 | |
60 void CodeSpecialization::RelocateGlobals(Address old_start, Address new_start) { | |
61 DCHECK(old_globals_start == 0 && new_globals_start == 0); | |
62 DCHECK(old_start != 0 || new_start != 0); | |
63 old_globals_start = old_start; | |
64 new_globals_start = new_start; | |
65 } | |
66 | |
67 void CodeSpecialization::PatchTableSize(uint32_t old_size, uint32_t new_size) { | |
68 DCHECK(old_function_table_size == 0 && new_function_table_size == 0); | |
69 DCHECK(old_size != 0 || new_size != 0); | |
70 old_function_table_size = old_size; | |
71 new_function_table_size = new_size; | |
72 } | |
73 | |
74 void CodeSpecialization::RelocateDirectCalls( | |
75 Handle<WasmInstanceObject> instance) { | |
76 DCHECK(relocate_direct_calls_instance.is_null()); | |
77 DCHECK(!instance.is_null()); | |
78 relocate_direct_calls_instance = instance; | |
79 } | |
80 | |
81 void CodeSpecialization::RelocateObject(Handle<Object> old_obj, | |
82 Handle<Object> new_obj) { | |
83 DCHECK(!old_obj.is_null() && !new_obj.is_null()); | |
84 has_objects_to_relocate = true; | |
85 objects_to_relocate.Set(*old_obj, new_obj); | |
86 } | |
87 | |
88 bool CodeSpecialization::ApplyToWholeInstance( | |
89 WasmInstanceObject* instance, ICacheFlushMode icache_flush_mode) { | |
90 DisallowHeapAllocation no_gc; | |
91 WasmCompiledModule* compiled_module = instance->compiled_module(); | |
92 FixedArray* code_table = compiled_module->ptr_to_code_table(); | |
93 WasmModule* module = compiled_module->module(); | |
94 std::vector<WasmFunction>* wasm_functions = | |
95 &compiled_module->module()->functions; | |
96 DCHECK_EQ(wasm_functions->size() + | |
97 compiled_module->module()->num_exported_functions, | |
98 code_table->length()); | |
99 | |
100 bool changed = false; | |
101 int func_index = module->num_imported_functions; | |
102 | |
103 // Patch all wasm functions. | |
104 for (int num_wasm_functions = static_cast<int>(wasm_functions->size()); | |
105 func_index < num_wasm_functions; ++func_index) { | |
106 Code* wasm_function = Code::cast(code_table->get(func_index)); | |
107 changed |= ApplyToWasmCode(wasm_function, icache_flush_mode); | |
108 } | |
109 | |
110 // Patch all exported functions. | |
111 for (auto exp : module->export_table) { | |
112 if (exp.kind != kExternalFunction) continue; | |
113 Code* export_wrapper = Code::cast(code_table->get(func_index)); | |
114 DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind()); | |
115 // There must be exactly one call to WASM_FUNCTION or WASM_TO_JS_FUNCTION. | |
116 int num_wasm_calls = 0; | |
117 for (RelocIterator it(export_wrapper, | |
118 RelocInfo::ModeMask(RelocInfo::CODE_TARGET)); | |
119 !it.done(); it.next()) { | |
120 DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode())); | |
121 Code* code = Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); | |
122 // Ignore calls to other builtins like ToNumber. | |
123 if (code->kind() != Code::WASM_FUNCTION && | |
124 code->kind() != Code::WASM_TO_JS_FUNCTION && | |
125 code->builtin_index() != Builtins::kIllegal) | |
126 continue; | |
127 ++num_wasm_calls; | |
128 Code* new_code = Code::cast(code_table->get(exp.index)); | |
129 DCHECK(new_code->kind() == Code::WASM_FUNCTION || | |
130 new_code->kind() == Code::WASM_TO_JS_FUNCTION); | |
131 it.rinfo()->set_target_address(new_code->instruction_start(), | |
132 UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); | |
133 changed = true; | |
134 } | |
135 DCHECK_EQ(1, num_wasm_calls); | |
136 func_index++; | |
137 } | |
138 DCHECK_EQ(code_table->length(), func_index); | |
139 return changed; | |
140 } | |
141 | |
142 bool CodeSpecialization::ApplyToWasmCode(Code* code, | |
143 ICacheFlushMode icache_flush_mode) { | |
144 DisallowHeapAllocation no_gc; | |
145 DCHECK_EQ(Code::WASM_FUNCTION, code->kind()); | |
146 | |
147 bool reloc_mem = old_mem_start || new_mem_start; | |
148 bool reloc_globals = old_globals_start || new_globals_start; | |
149 bool patch_table_size = old_function_table_size || new_function_table_size; | |
150 bool reloc_direct_calls = !relocate_direct_calls_instance.is_null(); | |
151 bool reloc_objects = has_objects_to_relocate; | |
152 | |
153 int reloc_mode = 0; | |
154 auto add_mode = [&reloc_mode](bool cond, RelocInfo::Mode mode) { | |
155 if (cond) reloc_mode |= RelocInfo::ModeMask(mode); | |
156 }; | |
157 add_mode(reloc_mem, RelocInfo::WASM_MEMORY_REFERENCE); | |
158 add_mode(reloc_mem, RelocInfo::WASM_MEMORY_SIZE_REFERENCE); | |
159 add_mode(reloc_globals, RelocInfo::WASM_GLOBAL_REFERENCE); | |
160 add_mode(patch_table_size, RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE); | |
161 add_mode(reloc_direct_calls, RelocInfo::CODE_TARGET); | |
162 add_mode(reloc_objects, RelocInfo::EMBEDDED_OBJECT); | |
163 | |
164 // This is a poor man's replacement for c++17's std::optional. It's lazily | |
165 // initialized when needed. | |
166 union OptionalDirectCallSitePatchingUtils { | |
167 char __; | |
168 struct { | |
169 char initialized; | |
170 SourcePositionTableIterator source_pos_it; | |
titzer
2017/02/20 15:12:01
Can we heap allocate these instead of using this u
Clemens Hammacher
2017/02/20 15:32:57
Sure :)
Done.
| |
171 Decoder decoder; | |
172 const byte* func_bytes; | |
173 } data; | |
174 OptionalDirectCallSitePatchingUtils() : __(0) { data.initialized = false; } | |
175 ~OptionalDirectCallSitePatchingUtils() { | |
176 if (!data.initialized) return; | |
177 data.source_pos_it.~SourcePositionTableIterator(); | |
178 data.decoder.~Decoder(); | |
179 } | |
180 void Init(WasmInstanceObject* instance, Code* code) { | |
181 DCHECK(!data.initialized); | |
182 data.initialized = true; | |
183 new (&data.source_pos_it) | |
184 SourcePositionTableIterator(code->source_position_table()); | |
185 new (&data.decoder) Decoder(nullptr, nullptr); | |
186 FixedArray* deopt_data = code->deoptimization_data(); | |
187 DCHECK_EQ(2, deopt_data->length()); | |
188 WasmCompiledModule* comp_mod = instance->compiled_module(); | |
189 int func_index = Smi::cast(deopt_data->get(1))->value(); | |
190 data.func_bytes = | |
191 comp_mod->module_bytes()->GetChars() + | |
192 comp_mod->module()->functions[func_index].code_start_offset; | |
193 } | |
194 } opt; | |
195 | |
196 bool changed = false; | |
197 | |
198 for (RelocIterator it(code, reloc_mode); !it.done(); it.next()) { | |
199 RelocInfo::Mode mode = it.rinfo()->rmode(); | |
200 switch (mode) { | |
201 case RelocInfo::WASM_MEMORY_REFERENCE: | |
202 case RelocInfo::WASM_MEMORY_SIZE_REFERENCE: | |
203 DCHECK(reloc_mem); | |
204 it.rinfo()->update_wasm_memory_reference(old_mem_start, new_mem_start, | |
205 old_mem_size, new_mem_size, | |
206 icache_flush_mode); | |
207 changed = true; | |
208 break; | |
209 case RelocInfo::WASM_GLOBAL_REFERENCE: | |
210 DCHECK(reloc_globals); | |
211 it.rinfo()->update_wasm_global_reference( | |
212 old_globals_start, new_globals_start, icache_flush_mode); | |
213 changed = true; | |
214 break; | |
215 case RelocInfo::CODE_TARGET: { | |
216 DCHECK(reloc_direct_calls); | |
217 Code* old_code = | |
218 Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); | |
219 // Skip everything which is not a wasm call (stack checks, traps, ...). | |
220 if (old_code->kind() != Code::WASM_FUNCTION && | |
221 old_code->kind() != Code::WASM_TO_JS_FUNCTION && | |
222 old_code->builtin_index() != Builtins::kIllegal) | |
223 continue; | |
224 // Iterate simultaneously over the relocation information and the source | |
225 // position table. For each call in the reloc info, move the source | |
226 // position iterator forward to that position to find the byte offset of | |
227 // the respective call. Then extract the call index from the module wire | |
228 // bytes to find the new compiled function. | |
229 size_t offset = it.rinfo()->pc() - code->instruction_start(); | |
230 if (!opt.data.initialized) | |
231 opt.Init(*relocate_direct_calls_instance, code); | |
232 int byte_pos = | |
233 AdvanceSourcePositionTableIterator(opt.data.source_pos_it, offset); | |
234 int called_func_index = ExtractDirectCallIndex( | |
235 opt.data.decoder, opt.data.func_bytes + byte_pos); | |
236 FixedArray* code_table = | |
237 relocate_direct_calls_instance->compiled_module() | |
238 ->ptr_to_code_table(); | |
239 Code* new_code = Code::cast(code_table->get(called_func_index)); | |
240 it.rinfo()->set_target_address(new_code->instruction_start(), | |
241 UPDATE_WRITE_BARRIER, icache_flush_mode); | |
242 changed = true; | |
243 } break; | |
244 case RelocInfo::EMBEDDED_OBJECT: { | |
245 DCHECK(reloc_objects); | |
246 Object* old = it.rinfo()->target_object(); | |
247 Handle<Object>* new_obj = objects_to_relocate.Find(old); | |
248 if (new_obj) { | |
249 it.rinfo()->set_target_object(**new_obj, UPDATE_WRITE_BARRIER, | |
250 icache_flush_mode); | |
251 changed = true; | |
252 } | |
253 } break; | |
254 case RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE: | |
255 DCHECK(patch_table_size); | |
256 it.rinfo()->update_wasm_function_table_size_reference( | |
257 old_function_table_size, new_function_table_size, | |
258 icache_flush_mode); | |
259 changed = true; | |
260 break; | |
261 default: | |
262 UNREACHABLE(); | |
263 } | |
264 } | |
265 | |
266 return changed; | |
267 } | |
OLD | NEW |