Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/assembler-inl.h" | |
| 5 #include "src/assert-scope.h" | 6 #include "src/assert-scope.h" |
| 7 #include "src/compiler/wasm-compiler.h" | |
| 6 #include "src/debug/debug.h" | 8 #include "src/debug/debug.h" |
| 7 #include "src/factory.h" | 9 #include "src/factory.h" |
| 10 #include "src/frames-inl.h" | |
| 8 #include "src/isolate.h" | 11 #include "src/isolate.h" |
| 9 #include "src/wasm/module-decoder.h" | 12 #include "src/wasm/module-decoder.h" |
| 13 #include "src/wasm/wasm-interpreter.h" | |
| 10 #include "src/wasm/wasm-module.h" | 14 #include "src/wasm/wasm-module.h" |
| 11 #include "src/wasm/wasm-objects.h" | 15 #include "src/wasm/wasm-objects.h" |
| 16 #include "src/zone/accounting-allocator.h" | |
| 12 | 17 |
| 13 using namespace v8::internal; | 18 using namespace v8::internal; |
| 14 using namespace v8::internal::wasm; | 19 using namespace v8::internal::wasm; |
| 15 | 20 |
| 21 namespace { | |
| 22 | |
| 23 class InterpreterHandle { | |
| 24 AccountingAllocator allocator_; | |
| 25 WasmInstance instance_; | |
| 26 WasmInterpreter interpreter_; | |
| 27 | |
| 28 public: | |
| 29 // Initialize in the right order, using helper methods to make this possible. | |
| 30 // WasmInterpreter has to be allocated in place, since it is not movable. | |
| 31 InterpreterHandle(Isolate *isolate, WasmDebugInfo *debug_info) | |
| 32 : instance_(debug_info->wasm_instance()->compiled_module()->module()), | |
| 33 interpreter_(GetBytesEnv(&instance_, debug_info), &allocator_) { | |
| 34 Handle<JSArrayBuffer> mem_buffer = | |
| 35 handle(debug_info->wasm_instance()->memory_buffer(), isolate); | |
| 36 if (mem_buffer->IsUndefined(isolate)) { | |
| 37 DCHECK_EQ(0, instance_.module->min_mem_pages); | |
| 38 instance_.mem_start = nullptr; | |
| 39 instance_.mem_size = 0; | |
| 40 } else { | |
| 41 instance_.mem_start = | |
| 42 reinterpret_cast<byte *>(mem_buffer->backing_store()); | |
| 43 CHECK(mem_buffer->byte_length()->ToUint32(&instance_.mem_size)); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 static ModuleBytesEnv GetBytesEnv(WasmInstance *instance, | |
| 48 WasmDebugInfo *debug_info) { | |
| 49 // Return raw pointer into heap. The WasmInterpreter will make its own copy | |
| 50 // of this data anyway, and there is no heap allocation in-between. | |
| 51 SeqOneByteString *bytes_str = | |
| 52 debug_info->wasm_instance()->compiled_module()->module_bytes(); | |
| 53 Vector<const byte> bytes(bytes_str->GetChars(), bytes_str->length()); | |
| 54 return ModuleBytesEnv(instance->module, instance, bytes); | |
| 55 } | |
| 56 | |
| 57 WasmInterpreter *interpreter() { return &interpreter_; } | |
| 58 const WasmModule *module() { return instance_.module; } | |
| 59 | |
| 60 void Execute(uint32_t func_index, uint8_t *arg_buffer) { | |
| 61 DCHECK_GE(module()->functions.size(), func_index); | |
| 62 FunctionSig *sig = module()->functions[func_index].sig; | |
| 63 DCHECK_GE(kMaxInt, sig->parameter_count()); | |
| 64 int num_params = static_cast<int>(sig->parameter_count()); | |
| 65 ScopedVector<WasmVal> wasm_args(num_params); | |
| 66 uint8_t *arg_buf_ptr = arg_buffer; | |
| 67 for (int i = 0; i < num_params; ++i) { | |
| 68 uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i)); | |
| 69 #define CASE_ARG_TYPE(type, ctype) \ | |
| 70 case type: \ | |
| 71 DCHECK_EQ(param_size, sizeof(ctype)); \ | |
| 72 wasm_args[i] = WasmVal(*reinterpret_cast<ctype *>(arg_buf_ptr)); \ | |
| 73 break; | |
| 74 switch (sig->GetParam(i)) { | |
| 75 CASE_ARG_TYPE(kWasmI32, uint32_t) | |
| 76 CASE_ARG_TYPE(kWasmI64, uint64_t) | |
| 77 CASE_ARG_TYPE(kWasmF32, float) | |
| 78 CASE_ARG_TYPE(kWasmF64, double) | |
| 79 #undef CASE_ARG_TYPE | |
| 80 default: | |
| 81 UNREACHABLE(); | |
| 82 } | |
| 83 arg_buf_ptr += param_size; | |
| 84 } | |
| 85 | |
| 86 WasmInterpreter::Thread *thread = interpreter_.GetThread(0); | |
| 87 // We do not support reentering an already running interpreter at the moment | |
| 88 // (like INTERPRETER -> JS -> WASM -> INTERPRETER). | |
| 89 DCHECK(thread->state() == WasmInterpreter::STOPPED || | |
| 90 thread->state() == WasmInterpreter::FINISHED); | |
| 91 thread->Reset(); | |
| 92 thread->PushFrame(&module()->functions[func_index], wasm_args.start()); | |
| 93 WasmInterpreter::State state; | |
| 94 do { | |
| 95 state = thread->Run(); | |
| 96 switch (state) { | |
| 97 case WasmInterpreter::State::PAUSED: { | |
| 98 // We hit a breakpoint. | |
| 99 // TODO(clemensh): Handle this. | |
| 100 } break; | |
| 101 case WasmInterpreter::State::FINISHED: | |
| 102 // Perfect, just break the switch and exit the loop. | |
| 103 break; | |
| 104 case WasmInterpreter::State::TRAPPED: | |
| 105 // TODO(clemensh): Generate appropriate JS exception. | |
| 106 UNIMPLEMENTED(); | |
| 107 break; | |
| 108 // STOPPED and RUNNING should never occur here. | |
| 109 case WasmInterpreter::State::STOPPED: | |
| 110 case WasmInterpreter::State::RUNNING: | |
| 111 default: | |
| 112 UNREACHABLE(); | |
| 113 } | |
| 114 } while (state != WasmInterpreter::State::FINISHED); | |
| 115 | |
| 116 // Copy back the return value | |
| 117 DCHECK_GE(1, sig->return_count()); | |
|
titzer
2017/01/12 15:32:05
I think you should check against kV8MaxWasmFunctio
Clemens Hammacher
2017/01/12 19:19:54
Done.
| |
| 118 if (sig->return_count()) { | |
| 119 WasmVal ret_val = thread->GetReturnValue(0); | |
| 120 #define CASE_RET_TYPE(type, ctype) \ | |
| 121 case type: \ | |
| 122 DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \ | |
| 123 *reinterpret_cast<ctype *>(arg_buffer) = ret_val.to<ctype>(); \ | |
| 124 break; | |
| 125 switch (sig->GetReturn(0)) { | |
| 126 CASE_RET_TYPE(kWasmI32, uint32_t) | |
| 127 CASE_RET_TYPE(kWasmI64, uint64_t) | |
| 128 CASE_RET_TYPE(kWasmF32, float) | |
| 129 CASE_RET_TYPE(kWasmF64, double) | |
| 130 #undef CASE_RET_TYPE | |
| 131 default: | |
| 132 UNREACHABLE(); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 }; | |
| 137 | |
| 138 InterpreterHandle *GetOrCreateInterpreterHandle( | |
| 139 Isolate *isolate, Handle<WasmDebugInfo> debug_info) { | |
| 140 Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle), | |
| 141 isolate); | |
| 142 if (handle->IsUndefined(isolate)) { | |
| 143 InterpreterHandle *cpp_handle = new InterpreterHandle(isolate, *debug_info); | |
| 144 handle = Managed<InterpreterHandle>::New(isolate, cpp_handle); | |
| 145 debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle); | |
| 146 } | |
| 147 | |
| 148 return Handle<Managed<InterpreterHandle>>::cast(handle)->get(); | |
| 149 } | |
| 150 | |
| 151 int GetNumFunctions(WasmInstanceObject *instance) { | |
| 152 size_t num_functions = | |
| 153 instance->compiled_module()->module()->functions.size(); | |
| 154 DCHECK_GE(kMaxInt, num_functions); | |
| 155 return static_cast<int>(num_functions); | |
| 156 } | |
| 157 | |
| 158 Handle<FixedArray> GetOrCreateInterpretedFunctions( | |
| 159 Isolate *isolate, Handle<WasmDebugInfo> debug_info) { | |
| 160 Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions), | |
| 161 isolate); | |
| 162 if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj); | |
| 163 | |
| 164 Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray( | |
| 165 GetNumFunctions(debug_info->wasm_instance())); | |
| 166 debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr); | |
| 167 return new_arr; | |
| 168 } | |
| 169 | |
| 170 void RedirectCallsitesInCode(Code *code, Code *old_target, Code *new_target) { | |
| 171 DisallowHeapAllocation no_gc; | |
| 172 for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done(); | |
| 173 it.next()) { | |
| 174 DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode())); | |
| 175 Code *target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); | |
| 176 if (target != old_target) continue; | |
| 177 it.rinfo()->set_target_address(new_target->instruction_start()); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 void RedirectCallsitesInInstance(Isolate *isolate, WasmInstanceObject *instance, | |
| 182 Code *old_target, Code *new_target) { | |
| 183 DisallowHeapAllocation no_gc; | |
| 184 // Redirect in all wasm functions. | |
| 185 FixedArray *code_table = instance->compiled_module()->ptr_to_code_table(); | |
| 186 for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) | |
|
titzer
2017/01/12 15:32:05
Please add brackets.
Clemens Hammacher
2017/01/12 19:19:54
Done.
| |
| 187 RedirectCallsitesInCode(Code::cast(code_table->get(i)), old_target, | |
| 188 new_target); | |
| 189 | |
| 190 // Redirect in all exported functions. | |
|
titzer
2017/01/12 15:32:05
"Redirect all calls in exported functions"? and mo
Clemens Hammacher
2017/01/12 19:19:54
Done, and removed the DCHECK which does not really
| |
| 191 DCHECK(!instance->compiled_module()->is_asm_js()); | |
| 192 | |
| 193 FixedArray *weak_exported_functions = | |
| 194 instance->compiled_module()->ptr_to_weak_exported_functions(); | |
| 195 for (int i = 0, e = weak_exported_functions->length(); i != e; ++i) { | |
| 196 WeakCell *weak_function = WeakCell::cast(weak_exported_functions->get(i)); | |
| 197 if (weak_function->cleared()) continue; | |
| 198 Code *code = JSFunction::cast(weak_function->value())->code(); | |
| 199 RedirectCallsitesInCode(code, old_target, new_target); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void EnsureRedirectToInterpreter(Isolate *isolate, | |
| 204 Handle<WasmDebugInfo> debug_info, | |
| 205 int func_index) { | |
| 206 DCHECK_LE(0, func_index); | |
| 207 Handle<FixedArray> interpreted_functions = | |
| 208 GetOrCreateInterpretedFunctions(isolate, debug_info); | |
| 209 DCHECK_GT(interpreted_functions->length(), func_index); | |
|
titzer
2017/01/12 15:32:05
The FixedArray should do this bounds check for you
Clemens Hammacher
2017/01/12 19:19:54
Yeah, but it's only SLOW_DCHECK.
I removed it for
| |
| 210 if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return; | |
| 211 | |
| 212 Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); | |
| 213 Handle<Code> new_code = compiler::CompileWasmInterpreterEntry( | |
| 214 isolate, func_index, | |
| 215 instance->compiled_module()->module()->functions[func_index].sig, | |
| 216 instance); | |
| 217 | |
| 218 Handle<FixedArray> code_table = instance->compiled_module()->code_table(); | |
| 219 DCHECK_GT(code_table->length(), func_index); | |
|
titzer
2017/01/12 15:32:05
Same here. The FixedArray will bounds check for yo
Clemens Hammacher
2017/01/12 19:19:54
Done.
| |
| 220 Handle<Code> old_code = | |
| 221 code_table->GetValueChecked<Code>(isolate, func_index); | |
| 222 interpreted_functions->set(func_index, *new_code); | |
| 223 | |
| 224 RedirectCallsitesInInstance(isolate, *instance, *old_code, *new_code); | |
| 225 } | |
| 226 | |
| 227 } // namespace | |
| 228 | |
| 16 Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) { | 229 Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) { |
| 17 Isolate *isolate = instance->GetIsolate(); | 230 Isolate *isolate = instance->GetIsolate(); |
| 18 Factory *factory = isolate->factory(); | 231 Factory *factory = isolate->factory(); |
| 19 Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED); | 232 Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED); |
| 20 arr->set(kInstance, *instance); | 233 arr->set(kInstance, *instance); |
| 21 | |
| 22 return Handle<WasmDebugInfo>::cast(arr); | 234 return Handle<WasmDebugInfo>::cast(arr); |
| 23 } | 235 } |
| 24 | 236 |
| 25 bool WasmDebugInfo::IsDebugInfo(Object *object) { | 237 bool WasmDebugInfo::IsDebugInfo(Object *object) { |
| 26 if (!object->IsFixedArray()) return false; | 238 if (!object->IsFixedArray()) return false; |
| 27 FixedArray *arr = FixedArray::cast(object); | 239 FixedArray *arr = FixedArray::cast(object); |
| 28 return arr->length() == kFieldCount && IsWasmInstance(arr->get(kInstance)); | 240 if (arr->length() != kFieldCount) return false; |
| 241 if (!IsWasmInstance(arr->get(kInstance))) return false; | |
| 242 Isolate *isolate = arr->GetIsolate(); | |
| 243 if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) && | |
| 244 !arr->get(kInterpreterHandle)->IsForeign()) | |
| 245 return false; | |
| 246 return true; | |
| 29 } | 247 } |
| 30 | 248 |
| 31 WasmDebugInfo *WasmDebugInfo::cast(Object *object) { | 249 WasmDebugInfo *WasmDebugInfo::cast(Object *object) { |
| 32 DCHECK(IsDebugInfo(object)); | 250 DCHECK(IsDebugInfo(object)); |
| 33 return reinterpret_cast<WasmDebugInfo *>(object); | 251 return reinterpret_cast<WasmDebugInfo *>(object); |
| 34 } | 252 } |
| 35 | 253 |
| 36 WasmInstanceObject *WasmDebugInfo::wasm_instance() { | 254 WasmInstanceObject *WasmDebugInfo::wasm_instance() { |
| 37 return WasmInstanceObject::cast(get(kInstance)); | 255 return WasmInstanceObject::cast(get(kInstance)); |
| 38 } | 256 } |
| 39 | 257 |
| 258 void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info, | |
| 259 int func_index, int offset) { | |
| 260 Isolate *isolate = debug_info->GetIsolate(); | |
| 261 InterpreterHandle *handle = GetOrCreateInterpreterHandle(isolate, debug_info); | |
| 262 WasmInterpreter *interpreter = handle->interpreter(); | |
| 263 DCHECK_LE(0, func_index); | |
| 264 DCHECK_GT(handle->module()->functions.size(), func_index); | |
| 265 const WasmFunction *func = &handle->module()->functions[func_index]; | |
| 266 interpreter->SetBreakpoint(func, offset, true); | |
| 267 EnsureRedirectToInterpreter(isolate, debug_info, func_index); | |
| 268 } | |
| 269 | |
| 40 void WasmDebugInfo::RunInterpreter(Handle<WasmDebugInfo> debug_info, | 270 void WasmDebugInfo::RunInterpreter(Handle<WasmDebugInfo> debug_info, |
| 41 int func_index, uint8_t *arg_buffer) { | 271 int func_index, uint8_t *arg_buffer) { |
| 42 // TODO(clemensh): Implement this. | 272 DCHECK_LE(0, func_index); |
| 43 UNIMPLEMENTED(); | 273 InterpreterHandle *interp_handle = |
| 274 GetOrCreateInterpreterHandle(debug_info->GetIsolate(), debug_info); | |
| 275 interp_handle->Execute(static_cast<uint32_t>(func_index), arg_buffer); | |
| 44 } | 276 } |
| OLD | NEW |