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