Chromium Code Reviews| 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 |