Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 1 | // Copyright 2015 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/macro-assembler.h" |
| 6 | #include "src/objects.h" |
| 7 | #include "src/v8.h" |
| 8 | |
| 9 | #include "src/simulator.h" |
| 10 | |
| 11 | #include "src/wasm/ast-decoder.h" |
| 12 | #include "src/wasm/module-decoder.h" |
| 13 | #include "src/wasm/wasm-module.h" |
| 14 | #include "src/wasm/wasm-result.h" |
| 15 | |
| 16 | #include "src/compiler/wasm-compiler.h" |
| 17 | |
| 18 | namespace v8 { |
| 19 | namespace internal { |
| 20 | namespace wasm { |
| 21 | |
| 22 | std::ostream& operator<<(std::ostream& os, const WasmModule& module) { |
| 23 | os << "WASM module with "; |
| 24 | os << (1 << module.min_mem_size_log2) << " min mem"; |
| 25 | os << (1 << module.max_mem_size_log2) << " max mem"; |
| 26 | if (module.functions) os << module.functions->size() << " functions"; |
| 27 | if (module.globals) os << module.functions->size() << " globals"; |
| 28 | if (module.data_segments) os << module.functions->size() << " data segments"; |
| 29 | return os; |
| 30 | } |
| 31 | |
| 32 | |
| 33 | std::ostream& operator<<(std::ostream& os, const WasmFunction& function) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 34 | os << "WASM function with signature " << *function.sig; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 35 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 36 | os << " locals: "; |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 37 | if (function.local_i32_count) os << function.local_i32_count << " i32s "; |
| 38 | if (function.local_i64_count) os << function.local_i64_count << " i64s "; |
| 39 | if (function.local_f32_count) os << function.local_f32_count << " f32s "; |
| 40 | if (function.local_f64_count) os << function.local_f64_count << " f64s "; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 41 | |
| 42 | os << " code bytes: " |
| 43 | << (function.code_end_offset - function.code_start_offset); |
| 44 | return os; |
| 45 | } |
| 46 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 47 | std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) { |
| 48 | os << "#" << pair.function_->func_index << ":"; |
| 49 | if (pair.function_->name_offset > 0) { |
| 50 | if (pair.module_) { |
| 51 | os << pair.module_->GetName(pair.function_->name_offset); |
| 52 | } else { |
| 53 | os << "+" << pair.function_->func_index; |
| 54 | } |
| 55 | } else { |
| 56 | os << "?"; |
| 57 | } |
| 58 | return os; |
| 59 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 60 | |
| 61 | // A helper class for compiling multiple wasm functions that offers |
| 62 | // placeholder code objects for calling functions that are not yet compiled. |
| 63 | class WasmLinker { |
| 64 | public: |
| 65 | WasmLinker(Isolate* isolate, size_t size) |
| 66 | : isolate_(isolate), placeholder_code_(size), function_code_(size) {} |
| 67 | |
| 68 | // Get the code object for a function, allocating a placeholder if it has |
| 69 | // not yet been compiled. |
| 70 | Handle<Code> GetFunctionCode(uint32_t index) { |
| 71 | DCHECK(index < function_code_.size()); |
| 72 | if (function_code_[index].is_null()) { |
| 73 | // Create a placeholder code object and encode the corresponding index in |
| 74 | // the {constant_pool_offset} field of the code object. |
| 75 | // TODO(titzer): placeholder code objects are somewhat dangerous. |
| 76 | Handle<Code> self(nullptr, isolate_); |
| 77 | byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions. |
| 78 | CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr}; |
| 79 | Handle<Code> code = isolate_->factory()->NewCode( |
| 80 | desc, Code::KindField::encode(Code::WASM_FUNCTION), self); |
| 81 | code->set_constant_pool_offset(index + kPlaceholderMarker); |
| 82 | placeholder_code_[index] = code; |
| 83 | function_code_[index] = code; |
| 84 | } |
| 85 | return function_code_[index]; |
| 86 | } |
| 87 | |
| 88 | void Finish(uint32_t index, Handle<Code> code) { |
| 89 | DCHECK(index < function_code_.size()); |
| 90 | function_code_[index] = code; |
| 91 | } |
| 92 | |
| 93 | void Link(Handle<FixedArray> function_table, |
| 94 | std::vector<uint16_t>* functions) { |
| 95 | for (size_t i = 0; i < function_code_.size(); i++) { |
| 96 | LinkFunction(function_code_[i]); |
| 97 | } |
| 98 | if (functions && !function_table.is_null()) { |
| 99 | int table_size = static_cast<int>(functions->size()); |
| 100 | DCHECK_EQ(function_table->length(), table_size * 2); |
| 101 | for (int i = 0; i < table_size; i++) { |
| 102 | function_table->set(i + table_size, *function_code_[functions->at(i)]); |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | private: |
| 108 | static const int kPlaceholderMarker = 1000000000; |
| 109 | |
| 110 | Isolate* isolate_; |
| 111 | std::vector<Handle<Code>> placeholder_code_; |
| 112 | std::vector<Handle<Code>> function_code_; |
| 113 | |
| 114 | void LinkFunction(Handle<Code> code) { |
| 115 | bool modified = false; |
| 116 | int mode_mask = RelocInfo::kCodeTargetMask; |
| 117 | AllowDeferredHandleDereference embedding_raw_address; |
| 118 | for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) { |
| 119 | RelocInfo::Mode mode = it.rinfo()->rmode(); |
| 120 | if (RelocInfo::IsCodeTarget(mode)) { |
| 121 | Code* target = |
| 122 | Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
| 123 | if (target->kind() == Code::WASM_FUNCTION && |
| 124 | target->constant_pool_offset() >= kPlaceholderMarker) { |
| 125 | // Patch direct calls to placeholder code objects. |
| 126 | uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; |
| 127 | CHECK(index < function_code_.size()); |
| 128 | Handle<Code> new_target = function_code_[index]; |
| 129 | if (target != *new_target) { |
| 130 | CHECK_EQ(*placeholder_code_[index], target); |
| 131 | it.rinfo()->set_target_address(new_target->instruction_start(), |
| 132 | SKIP_WRITE_BARRIER, |
| 133 | SKIP_ICACHE_FLUSH); |
| 134 | modified = true; |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | if (modified) { |
| 140 | Assembler::FlushICache(isolate_, code->instruction_start(), |
| 141 | code->instruction_size()); |
| 142 | } |
| 143 | } |
| 144 | }; |
| 145 | |
| 146 | namespace { |
| 147 | // Internal constants for the layout of the module object. |
| 148 | const int kWasmModuleInternalFieldCount = 4; |
| 149 | const int kWasmModuleFunctionTable = 0; |
| 150 | const int kWasmModuleCodeTable = 1; |
| 151 | const int kWasmMemArrayBuffer = 2; |
| 152 | const int kWasmGlobalsArrayBuffer = 3; |
| 153 | |
| 154 | |
| 155 | size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>* globals) { |
| 156 | uint32_t offset = 0; |
| 157 | if (!globals) return 0; |
| 158 | for (WasmGlobal& global : *globals) { |
| 159 | byte size = WasmOpcodes::MemSize(global.type); |
| 160 | offset = (offset + size - 1) & ~(size - 1); // align |
| 161 | global.offset = offset; |
| 162 | offset += size; |
| 163 | } |
| 164 | return offset; |
| 165 | } |
| 166 | |
| 167 | |
| 168 | void LoadDataSegments(WasmModule* module, byte* mem_addr, size_t mem_size) { |
| 169 | for (const WasmDataSegment& segment : *module->data_segments) { |
| 170 | if (!segment.init) continue; |
| 171 | CHECK_LT(segment.dest_addr, mem_size); |
| 172 | CHECK_LE(segment.source_size, mem_size); |
| 173 | CHECK_LE(segment.dest_addr + segment.source_size, mem_size); |
| 174 | byte* addr = mem_addr + segment.dest_addr; |
| 175 | memcpy(addr, module->module_start + segment.source_offset, |
| 176 | segment.source_size); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | |
| 181 | Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) { |
| 182 | if (!module->function_table || module->function_table->size() == 0) { |
| 183 | return Handle<FixedArray>::null(); |
| 184 | } |
| 185 | int table_size = static_cast<int>(module->function_table->size()); |
| 186 | Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size); |
| 187 | for (int i = 0; i < table_size; i++) { |
| 188 | WasmFunction* function = |
| 189 | &module->functions->at(module->function_table->at(i)); |
| 190 | fixed->set(i, Smi::FromInt(function->sig_index)); |
| 191 | } |
| 192 | return fixed; |
| 193 | } |
| 194 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 195 | Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 196 | byte** backing_store) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 197 | if (size > (1 << WasmModule::kMaxMemSize)) { |
| 198 | // TODO(titzer): lift restriction on maximum memory allocated here. |
| 199 | *backing_store = nullptr; |
| 200 | return Handle<JSArrayBuffer>::null(); |
| 201 | } |
| 202 | void* memory = |
| 203 | isolate->array_buffer_allocator()->Allocate(static_cast<int>(size)); |
| 204 | if (!memory) { |
| 205 | *backing_store = nullptr; |
| 206 | return Handle<JSArrayBuffer>::null(); |
| 207 | } |
| 208 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 209 | *backing_store = reinterpret_cast<byte*>(memory); |
| 210 | |
| 211 | #if DEBUG |
| 212 | // Double check the API allocator actually zero-initialized the memory. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 213 | byte* bytes = reinterpret_cast<byte*>(*backing_store); |
| 214 | for (size_t i = 0; i < size; i++) { |
| 215 | DCHECK_EQ(0, bytes[i]); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 216 | } |
| 217 | #endif |
| 218 | |
| 219 | Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer(); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 220 | JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size)); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 221 | buffer->set_is_neuterable(false); |
| 222 | return buffer; |
| 223 | } |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 224 | |
| 225 | // Set the memory for a module instance to be the {memory} array buffer. |
| 226 | void SetMemory(WasmModuleInstance* instance, Handle<JSArrayBuffer> memory) { |
| 227 | memory->set_is_neuterable(false); |
| 228 | instance->mem_start = reinterpret_cast<byte*>(memory->backing_store()); |
| 229 | instance->mem_size = memory->byte_length()->Number(); |
| 230 | instance->mem_buffer = memory; |
| 231 | } |
| 232 | |
| 233 | // Allocate memory for a module instance as a new JSArrayBuffer. |
| 234 | bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate, |
| 235 | WasmModuleInstance* instance) { |
| 236 | DCHECK(instance->module); |
| 237 | DCHECK(instance->mem_buffer.is_null()); |
| 238 | |
| 239 | if (instance->module->min_mem_size_log2 > WasmModule::kMaxMemSize) { |
| 240 | thrower->Error("Out of memory: wasm memory too large"); |
| 241 | return false; |
| 242 | } |
| 243 | instance->mem_size = static_cast<size_t>(1) |
| 244 | << instance->module->min_mem_size_log2; |
| 245 | instance->mem_buffer = |
| 246 | NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start); |
| 247 | if (!instance->mem_start) { |
| 248 | thrower->Error("Out of memory: wasm memory"); |
| 249 | instance->mem_size = 0; |
| 250 | return false; |
| 251 | } |
| 252 | return true; |
| 253 | } |
| 254 | |
| 255 | bool AllocateGlobals(ErrorThrower* thrower, Isolate* isolate, |
| 256 | WasmModuleInstance* instance) { |
| 257 | instance->globals_size = AllocateGlobalsOffsets(instance->module->globals); |
| 258 | |
| 259 | if (instance->globals_size > 0) { |
| 260 | instance->globals_buffer = NewArrayBuffer(isolate, instance->globals_size, |
| 261 | &instance->globals_start); |
| 262 | if (!instance->globals_start) { |
| 263 | // Not enough space for backing store of globals. |
| 264 | thrower->Error("Out of memory: wasm globals"); |
| 265 | return false; |
| 266 | } |
| 267 | } |
| 268 | return true; |
| 269 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 270 | } // namespace |
| 271 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 272 | WasmModule::WasmModule() |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 273 | : shared_isolate(nullptr), |
| 274 | module_start(nullptr), |
| 275 | module_end(nullptr), |
| 276 | min_mem_size_log2(0), |
| 277 | max_mem_size_log2(0), |
| 278 | mem_export(false), |
| 279 | mem_external(false), |
| 280 | start_function_index(-1), |
| 281 | globals(nullptr), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 282 | signatures(nullptr), |
| 283 | functions(nullptr), |
| 284 | data_segments(nullptr), |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 285 | function_table(nullptr), |
| 286 | import_table(nullptr) {} |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 287 | |
| 288 | WasmModule::~WasmModule() { |
| 289 | if (globals) delete globals; |
| 290 | if (signatures) delete signatures; |
| 291 | if (functions) delete functions; |
| 292 | if (data_segments) delete data_segments; |
| 293 | if (function_table) delete function_table; |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 294 | if (import_table) delete import_table; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 295 | } |
| 296 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 297 | static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower, |
| 298 | Handle<JSObject> ffi, |
| 299 | uint32_t index, |
| 300 | Handle<String> name, |
| 301 | const char* cstr) { |
| 302 | if (!ffi.is_null()) { |
| 303 | MaybeHandle<Object> result = Object::GetProperty(ffi, name); |
| 304 | if (!result.is_null()) { |
| 305 | Handle<Object> obj = result.ToHandleChecked(); |
| 306 | if (obj->IsJSFunction()) { |
| 307 | return Handle<JSFunction>::cast(obj); |
| 308 | } else { |
| 309 | thrower.Error("FFI function #%d:%s is not a JSFunction.", index, cstr); |
| 310 | return MaybeHandle<JSFunction>(); |
| 311 | } |
| 312 | } else { |
| 313 | thrower.Error("FFI function #%d:%s not found.", index, cstr); |
| 314 | return MaybeHandle<JSFunction>(); |
| 315 | } |
| 316 | } else { |
| 317 | thrower.Error("FFI table is not an object."); |
| 318 | return MaybeHandle<JSFunction>(); |
| 319 | } |
| 320 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 321 | |
| 322 | // Instantiates a wasm module as a JSObject. |
| 323 | // * allocates a backing store of {mem_size} bytes. |
| 324 | // * installs a named property "memory" for that buffer if exported |
| 325 | // * installs named properties on the object for exported functions |
| 326 | // * compiles wasm code to machine code |
| 327 | MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, |
| 328 | Handle<JSObject> ffi, |
| 329 | Handle<JSArrayBuffer> memory) { |
| 330 | this->shared_isolate = isolate; // TODO(titzer): have a real shared isolate. |
| 331 | ErrorThrower thrower(isolate, "WasmModule::Instantiate()"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 332 | Factory* factory = isolate->factory(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 333 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 334 | //------------------------------------------------------------------------- |
| 335 | // Allocate the instance and its JS counterpart. |
| 336 | //------------------------------------------------------------------------- |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 337 | Handle<Map> map = factory->NewMap( |
| 338 | JS_OBJECT_TYPE, |
| 339 | JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 340 | WasmModuleInstance instance(this); |
| 341 | std::vector<Handle<Code>> import_code; |
| 342 | instance.context = isolate->native_context(); |
| 343 | instance.js_object = factory->NewJSObjectFromMap(map, TENURED); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 344 | Handle<FixedArray> code_table = |
| 345 | factory->NewFixedArray(static_cast<int>(functions->size()), TENURED); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 346 | instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 347 | |
| 348 | //------------------------------------------------------------------------- |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 349 | // Allocate and initialize the linear memory. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 350 | //------------------------------------------------------------------------- |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 351 | if (memory.is_null()) { |
| 352 | if (!AllocateMemory(&thrower, isolate, &instance)) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 353 | return MaybeHandle<JSObject>(); |
| 354 | } |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 355 | } else { |
| 356 | SetMemory(&instance, memory); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 357 | } |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 358 | instance.js_object->SetInternalField(kWasmMemArrayBuffer, |
| 359 | *instance.mem_buffer); |
| 360 | LoadDataSegments(this, instance.mem_start, instance.mem_size); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 361 | |
| 362 | if (mem_export) { |
| 363 | // Export the memory as a named property. |
| 364 | Handle<String> name = factory->InternalizeUtf8String("memory"); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 365 | JSObject::AddProperty(instance.js_object, name, instance.mem_buffer, |
| 366 | READ_ONLY); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | //------------------------------------------------------------------------- |
| 370 | // Allocate the globals area if necessary. |
| 371 | //------------------------------------------------------------------------- |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 372 | if (!AllocateGlobals(&thrower, isolate, &instance)) { |
| 373 | return MaybeHandle<JSObject>(); |
| 374 | } |
| 375 | if (!instance.globals_buffer.is_null()) { |
| 376 | instance.js_object->SetInternalField(kWasmGlobalsArrayBuffer, |
| 377 | *instance.globals_buffer); |
| 378 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 379 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 380 | //------------------------------------------------------------------------- |
| 381 | // Compile wrappers to imported functions. |
| 382 | //------------------------------------------------------------------------- |
| 383 | uint32_t index = 0; |
| 384 | instance.function_table = BuildFunctionTable(isolate, this); |
| 385 | WasmLinker linker(isolate, functions->size()); |
| 386 | ModuleEnv module_env; |
| 387 | module_env.module = this; |
| 388 | module_env.instance = &instance; |
| 389 | module_env.linker = &linker; |
| 390 | module_env.asm_js = false; |
| 391 | |
| 392 | if (import_table->size() > 0) { |
| 393 | instance.import_code = &import_code; |
| 394 | instance.import_code->reserve(import_table->size()); |
| 395 | for (const WasmImport& import : *import_table) { |
| 396 | const char* cstr = GetName(import.function_name_offset); |
| 397 | Handle<String> name = factory->InternalizeUtf8String(cstr); |
| 398 | MaybeHandle<JSFunction> function = |
| 399 | LookupFunction(thrower, ffi, index, name, cstr); |
| 400 | if (function.is_null()) return MaybeHandle<JSObject>(); |
| 401 | Handle<Code> code = compiler::CompileWasmToJSWrapper( |
| 402 | isolate, &module_env, function.ToHandleChecked(), import.sig, cstr); |
| 403 | instance.import_code->push_back(code); |
| 404 | index++; |
| 405 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 406 | } |
| 407 | |
| 408 | //------------------------------------------------------------------------- |
| 409 | // Compile all functions in the module. |
| 410 | //------------------------------------------------------------------------- |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 411 | |
| 412 | // First pass: compile each function and initialize the code table. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 413 | index = 0; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 414 | for (const WasmFunction& func : *functions) { |
| 415 | if (thrower.error()) break; |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 416 | DCHECK_EQ(index, func.func_index); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 417 | |
| 418 | const char* cstr = GetName(func.name_offset); |
| 419 | Handle<String> name = factory->InternalizeUtf8String(cstr); |
| 420 | Handle<Code> code = Handle<Code>::null(); |
| 421 | Handle<JSFunction> function = Handle<JSFunction>::null(); |
| 422 | if (func.external) { |
| 423 | // Lookup external function in FFI object. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 424 | MaybeHandle<JSFunction> function = |
| 425 | LookupFunction(thrower, ffi, index, name, cstr); |
| 426 | if (function.is_null()) return MaybeHandle<JSObject>(); |
| 427 | code = compiler::CompileWasmToJSWrapper( |
| 428 | isolate, &module_env, function.ToHandleChecked(), func.sig, cstr); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 429 | } else { |
| 430 | // Compile the function. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 431 | code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 432 | if (code.is_null()) { |
| 433 | thrower.Error("Compilation of #%d:%s failed.", index, cstr); |
| 434 | return MaybeHandle<JSObject>(); |
| 435 | } |
| 436 | if (func.exported) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 437 | function = compiler::CompileJSToWasmWrapper( |
| 438 | isolate, &module_env, name, code, instance.js_object, index); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 439 | } |
| 440 | } |
| 441 | if (!code.is_null()) { |
| 442 | // Install the code into the linker table. |
| 443 | linker.Finish(index, code); |
| 444 | code_table->set(index, *code); |
| 445 | } |
| 446 | if (func.exported) { |
| 447 | // Exported functions are installed as read-only properties on the module. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 448 | JSObject::AddProperty(instance.js_object, name, function, READ_ONLY); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 449 | } |
| 450 | index++; |
| 451 | } |
| 452 | |
| 453 | // Second pass: patch all direct call sites. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 454 | linker.Link(instance.function_table, this->function_table); |
| 455 | instance.js_object->SetInternalField(kWasmModuleFunctionTable, |
| 456 | Smi::FromInt(0)); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 457 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 458 | // Run the start function if one was specified. |
| 459 | if (this->start_function_index >= 0) { |
| 460 | HandleScope scope(isolate); |
| 461 | uint32_t index = static_cast<uint32_t>(this->start_function_index); |
| 462 | Handle<String> name = isolate->factory()->NewStringFromStaticChars("start"); |
| 463 | Handle<Code> code = linker.GetFunctionCode(index); |
| 464 | Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper( |
| 465 | isolate, &module_env, name, code, instance.js_object, index); |
| 466 | |
| 467 | // Call the JS function. |
| 468 | Handle<Object> undefined(isolate->heap()->undefined_value(), isolate); |
| 469 | MaybeHandle<Object> retval = |
| 470 | Execution::Call(isolate, jsfunc, undefined, 0, nullptr); |
| 471 | |
| 472 | if (retval.is_null()) { |
| 473 | thrower.Error("WASM.instantiateModule(): start function failed"); |
| 474 | } |
| 475 | } |
| 476 | return instance.js_object; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 477 | } |
| 478 | |
| 479 | |
| 480 | Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) { |
| 481 | DCHECK(IsValidFunction(index)); |
| 482 | if (linker) return linker->GetFunctionCode(index); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 483 | if (instance && instance->function_code) { |
| 484 | return instance->function_code->at(index); |
| 485 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 486 | return Handle<Code>::null(); |
| 487 | } |
| 488 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 489 | Handle<Code> ModuleEnv::GetImportCode(uint32_t index) { |
| 490 | DCHECK(IsValidImport(index)); |
| 491 | if (instance && instance->import_code) { |
| 492 | return instance->import_code->at(index); |
| 493 | } |
| 494 | return Handle<Code>::null(); |
| 495 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 496 | |
| 497 | compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone, |
| 498 | uint32_t index) { |
| 499 | DCHECK(IsValidFunction(index)); |
| 500 | // Always make a direct call to whatever is in the table at that location. |
| 501 | // A wrapper will be generated for FFI calls. |
| 502 | WasmFunction* function = &module->functions->at(index); |
| 503 | return GetWasmCallDescriptor(zone, function->sig); |
| 504 | } |
| 505 | |
| 506 | |
| 507 | int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, |
| 508 | const byte* module_end, bool asm_js) { |
| 509 | HandleScope scope(isolate); |
| 510 | Zone zone; |
| 511 | // Decode the module, but don't verify function bodies, since we'll |
| 512 | // be compiling them anyway. |
| 513 | ModuleResult result = |
| 514 | DecodeWasmModule(isolate, &zone, module_start, module_end, false, false); |
| 515 | if (result.failed()) { |
| 516 | // Module verification failed. throw. |
| 517 | std::ostringstream str; |
| 518 | str << "WASM.compileRun() failed: " << result; |
| 519 | isolate->Throw( |
| 520 | *isolate->factory()->NewStringFromAsciiChecked(str.str().c_str())); |
| 521 | return -1; |
| 522 | } |
| 523 | |
| 524 | int32_t retval = CompileAndRunWasmModule(isolate, result.val); |
| 525 | delete result.val; |
| 526 | return retval; |
| 527 | } |
| 528 | |
| 529 | |
| 530 | int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) { |
| 531 | ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 532 | WasmModuleInstance instance(module); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 533 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 534 | // Allocate and initialize the linear memory. |
| 535 | if (!AllocateMemory(&thrower, isolate, &instance)) { |
| 536 | return -1; |
| 537 | } |
| 538 | LoadDataSegments(module, instance.mem_start, instance.mem_size); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 539 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 540 | // Allocate the globals area if necessary. |
| 541 | if (!AllocateGlobals(&thrower, isolate, &instance)) { |
| 542 | return -1; |
| 543 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 544 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 545 | // Build the function table. |
| 546 | instance.function_table = BuildFunctionTable(isolate, module); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 547 | |
| 548 | // Create module environment. |
| 549 | WasmLinker linker(isolate, module->functions->size()); |
| 550 | ModuleEnv module_env; |
| 551 | module_env.module = module; |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 552 | module_env.instance = &instance; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 553 | module_env.linker = &linker; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 554 | module_env.asm_js = false; |
| 555 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 556 | // Compile all functions. |
| 557 | Handle<Code> main_code = Handle<Code>::null(); // record last code. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 558 | uint32_t index = 0; |
| 559 | int main_index = 0; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 560 | for (const WasmFunction& func : *module->functions) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 561 | DCHECK_EQ(index, func.func_index); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 562 | if (!func.external) { |
| 563 | // Compile the function and install it in the code table. |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 564 | Handle<Code> code = |
| 565 | compiler::CompileWasmFunction(thrower, isolate, &module_env, func); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 566 | if (!code.is_null()) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 567 | if (func.exported) { |
| 568 | main_code = code; |
| 569 | main_index = index; |
| 570 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 571 | linker.Finish(index, code); |
| 572 | } |
| 573 | if (thrower.error()) return -1; |
| 574 | } |
| 575 | index++; |
| 576 | } |
| 577 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 578 | if (main_code.is_null()) { |
| 579 | thrower.Error("WASM.compileRun() failed: no main code found"); |
| 580 | return -1; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 581 | } |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame^] | 582 | |
| 583 | linker.Link(instance.function_table, instance.module->function_table); |
| 584 | |
| 585 | // Wrap the main code so it can be called as a JS function. |
| 586 | Handle<String> name = isolate->factory()->NewStringFromStaticChars("main"); |
| 587 | Handle<JSObject> module_object = Handle<JSObject>(0, isolate); |
| 588 | Handle<JSFunction> jsfunc = compiler::CompileJSToWasmWrapper( |
| 589 | isolate, &module_env, name, main_code, module_object, main_index); |
| 590 | |
| 591 | // Call the JS function. |
| 592 | Handle<Object> undefined(isolate->heap()->undefined_value(), isolate); |
| 593 | MaybeHandle<Object> retval = |
| 594 | Execution::Call(isolate, jsfunc, undefined, 0, nullptr); |
| 595 | |
| 596 | // The result should be a number. |
| 597 | if (retval.is_null()) { |
| 598 | thrower.Error("WASM.compileRun() failed: Invocation was null"); |
| 599 | return -1; |
| 600 | } |
| 601 | Handle<Object> result = retval.ToHandleChecked(); |
| 602 | if (result->IsSmi()) { |
| 603 | return Smi::cast(*result)->value(); |
| 604 | } |
| 605 | if (result->IsHeapNumber()) { |
| 606 | return static_cast<int32_t>(HeapNumber::cast(*result)->value()); |
| 607 | } |
| 608 | thrower.Error("WASM.compileRun() failed: Return value should be number"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 609 | return -1; |
| 610 | } |
| 611 | } // namespace wasm |
| 612 | } // namespace internal |
| 613 | } // namespace v8 |