Home | History | Annotate | Download | only in wasm
      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) {
     34   os << "WASM function with signature ";
     35 
     36   // TODO(titzer): factor out rendering of signatures.
     37   if (function.sig->return_count() == 0) os << "v";
     38   for (size_t i = 0; i < function.sig->return_count(); i++) {
     39     os << WasmOpcodes::ShortNameOf(function.sig->GetReturn(i));
     40   }
     41   os << "_";
     42   if (function.sig->parameter_count() == 0) os << "v";
     43   for (size_t i = 0; i < function.sig->parameter_count(); i++) {
     44     os << WasmOpcodes::ShortNameOf(function.sig->GetParam(i));
     45   }
     46   os << " locals: ";
     47   if (function.local_int32_count)
     48     os << function.local_int32_count << " int32s ";
     49   if (function.local_int64_count)
     50     os << function.local_int64_count << " int64s ";
     51   if (function.local_float32_count)
     52     os << function.local_float32_count << " float32s ";
     53   if (function.local_float64_count)
     54     os << function.local_float64_count << " float64s ";
     55 
     56   os << " code bytes: "
     57      << (function.code_end_offset - function.code_start_offset);
     58   return os;
     59 }
     60 
     61 
     62 // A helper class for compiling multiple wasm functions that offers
     63 // placeholder code objects for calling functions that are not yet compiled.
     64 class WasmLinker {
     65  public:
     66   WasmLinker(Isolate* isolate, size_t size)
     67       : isolate_(isolate), placeholder_code_(size), function_code_(size) {}
     68 
     69   // Get the code object for a function, allocating a placeholder if it has
     70   // not yet been compiled.
     71   Handle<Code> GetFunctionCode(uint32_t index) {
     72     DCHECK(index < function_code_.size());
     73     if (function_code_[index].is_null()) {
     74       // Create a placeholder code object and encode the corresponding index in
     75       // the {constant_pool_offset} field of the code object.
     76       // TODO(titzer): placeholder code objects are somewhat dangerous.
     77       Handle<Code> self(nullptr, isolate_);
     78       byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0};  // fake instructions.
     79       CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr};
     80       Handle<Code> code = isolate_->factory()->NewCode(
     81           desc, Code::KindField::encode(Code::WASM_FUNCTION), self);
     82       code->set_constant_pool_offset(index + kPlaceholderMarker);
     83       placeholder_code_[index] = code;
     84       function_code_[index] = code;
     85     }
     86     return function_code_[index];
     87   }
     88 
     89   void Finish(uint32_t index, Handle<Code> code) {
     90     DCHECK(index < function_code_.size());
     91     function_code_[index] = code;
     92   }
     93 
     94   void Link(Handle<FixedArray> function_table,
     95             std::vector<uint16_t>* functions) {
     96     for (size_t i = 0; i < function_code_.size(); i++) {
     97       LinkFunction(function_code_[i]);
     98     }
     99     if (functions && !function_table.is_null()) {
    100       int table_size = static_cast<int>(functions->size());
    101       DCHECK_EQ(function_table->length(), table_size * 2);
    102       for (int i = 0; i < table_size; i++) {
    103         function_table->set(i + table_size, *function_code_[functions->at(i)]);
    104       }
    105     }
    106   }
    107 
    108  private:
    109   static const int kPlaceholderMarker = 1000000000;
    110 
    111   Isolate* isolate_;
    112   std::vector<Handle<Code>> placeholder_code_;
    113   std::vector<Handle<Code>> function_code_;
    114 
    115   void LinkFunction(Handle<Code> code) {
    116     bool modified = false;
    117     int mode_mask = RelocInfo::kCodeTargetMask;
    118     AllowDeferredHandleDereference embedding_raw_address;
    119     for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
    120       RelocInfo::Mode mode = it.rinfo()->rmode();
    121       if (RelocInfo::IsCodeTarget(mode)) {
    122         Code* target =
    123             Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
    124         if (target->kind() == Code::WASM_FUNCTION &&
    125             target->constant_pool_offset() >= kPlaceholderMarker) {
    126           // Patch direct calls to placeholder code objects.
    127           uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
    128           CHECK(index < function_code_.size());
    129           Handle<Code> new_target = function_code_[index];
    130           if (target != *new_target) {
    131             CHECK_EQ(*placeholder_code_[index], target);
    132             it.rinfo()->set_target_address(new_target->instruction_start(),
    133                                            SKIP_WRITE_BARRIER,
    134                                            SKIP_ICACHE_FLUSH);
    135             modified = true;
    136           }
    137         }
    138       }
    139     }
    140     if (modified) {
    141       Assembler::FlushICache(isolate_, code->instruction_start(),
    142                              code->instruction_size());
    143     }
    144   }
    145 };
    146 
    147 namespace {
    148 // Internal constants for the layout of the module object.
    149 const int kWasmModuleInternalFieldCount = 4;
    150 const int kWasmModuleFunctionTable = 0;
    151 const int kWasmModuleCodeTable = 1;
    152 const int kWasmMemArrayBuffer = 2;
    153 const int kWasmGlobalsArrayBuffer = 3;
    154 
    155 
    156 size_t AllocateGlobalsOffsets(std::vector<WasmGlobal>* globals) {
    157   uint32_t offset = 0;
    158   if (!globals) return 0;
    159   for (WasmGlobal& global : *globals) {
    160     byte size = WasmOpcodes::MemSize(global.type);
    161     offset = (offset + size - 1) & ~(size - 1);  // align
    162     global.offset = offset;
    163     offset += size;
    164   }
    165   return offset;
    166 }
    167 
    168 
    169 void LoadDataSegments(WasmModule* module, byte* mem_addr, size_t mem_size) {
    170   for (const WasmDataSegment& segment : *module->data_segments) {
    171     if (!segment.init) continue;
    172     CHECK_LT(segment.dest_addr, mem_size);
    173     CHECK_LE(segment.source_size, mem_size);
    174     CHECK_LE(segment.dest_addr + segment.source_size, mem_size);
    175     byte* addr = mem_addr + segment.dest_addr;
    176     memcpy(addr, module->module_start + segment.source_offset,
    177            segment.source_size);
    178   }
    179 }
    180 
    181 
    182 Handle<FixedArray> BuildFunctionTable(Isolate* isolate, WasmModule* module) {
    183   if (!module->function_table || module->function_table->size() == 0) {
    184     return Handle<FixedArray>::null();
    185   }
    186   int table_size = static_cast<int>(module->function_table->size());
    187   Handle<FixedArray> fixed = isolate->factory()->NewFixedArray(2 * table_size);
    188   for (int i = 0; i < table_size; i++) {
    189     WasmFunction* function =
    190         &module->functions->at(module->function_table->at(i));
    191     fixed->set(i, Smi::FromInt(function->sig_index));
    192   }
    193   return fixed;
    194 }
    195 
    196 
    197 Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, int size,
    198                                      byte** backing_store) {
    199   void* memory = isolate->array_buffer_allocator()->Allocate(size);
    200   if (!memory) return Handle<JSArrayBuffer>::null();
    201   *backing_store = reinterpret_cast<byte*>(memory);
    202 
    203 #if DEBUG
    204   // Double check the API allocator actually zero-initialized the memory.
    205   for (int i = 0; i < size; i++) {
    206     DCHECK_EQ(0, (*backing_store)[i]);
    207   }
    208 #endif
    209 
    210   Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
    211   JSArrayBuffer::Setup(buffer, isolate, false, memory, size);
    212   buffer->set_is_neuterable(false);
    213   return buffer;
    214 }
    215 }  // namespace
    216 
    217 
    218 WasmModule::WasmModule()
    219     : globals(nullptr),
    220       signatures(nullptr),
    221       functions(nullptr),
    222       data_segments(nullptr),
    223       function_table(nullptr) {}
    224 
    225 
    226 WasmModule::~WasmModule() {
    227   if (globals) delete globals;
    228   if (signatures) delete signatures;
    229   if (functions) delete functions;
    230   if (data_segments) delete data_segments;
    231   if (function_table) delete function_table;
    232 }
    233 
    234 
    235 // Instantiates a wasm module as a JSObject.
    236 //  * allocates a backing store of {mem_size} bytes.
    237 //  * installs a named property "memory" for that buffer if exported
    238 //  * installs named properties on the object for exported functions
    239 //  * compiles wasm code to machine code
    240 MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
    241                                               Handle<JSObject> ffi,
    242                                               Handle<JSArrayBuffer> memory) {
    243   this->shared_isolate = isolate;  // TODO(titzer): have a real shared isolate.
    244   ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
    245 
    246   Factory* factory = isolate->factory();
    247   // Memory is bigger than maximum supported size.
    248   if (memory.is_null() && min_mem_size_log2 > kMaxMemSize) {
    249     thrower.Error("Out of memory: wasm memory too large");
    250     return MaybeHandle<JSObject>();
    251   }
    252 
    253   Handle<Map> map = factory->NewMap(
    254       JS_OBJECT_TYPE,
    255       JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
    256 
    257   //-------------------------------------------------------------------------
    258   // Allocate the module object.
    259   //-------------------------------------------------------------------------
    260   Handle<JSObject> module = factory->NewJSObjectFromMap(map, TENURED);
    261   Handle<FixedArray> code_table =
    262       factory->NewFixedArray(static_cast<int>(functions->size()), TENURED);
    263 
    264   //-------------------------------------------------------------------------
    265   // Allocate the linear memory.
    266   //-------------------------------------------------------------------------
    267   uint32_t mem_size = 1 << min_mem_size_log2;
    268   byte* mem_addr = nullptr;
    269   Handle<JSArrayBuffer> mem_buffer;
    270   if (!memory.is_null()) {
    271     memory->set_is_neuterable(false);
    272     mem_addr = reinterpret_cast<byte*>(memory->backing_store());
    273     mem_size = memory->byte_length()->Number();
    274     mem_buffer = memory;
    275   } else {
    276     mem_buffer = NewArrayBuffer(isolate, mem_size, &mem_addr);
    277     if (!mem_addr) {
    278       // Not enough space for backing store of memory
    279       thrower.Error("Out of memory: wasm memory");
    280       return MaybeHandle<JSObject>();
    281     }
    282   }
    283 
    284   // Load initialized data segments.
    285   LoadDataSegments(this, mem_addr, mem_size);
    286 
    287   module->SetInternalField(kWasmMemArrayBuffer, *mem_buffer);
    288 
    289   if (mem_export) {
    290     // Export the memory as a named property.
    291     Handle<String> name = factory->InternalizeUtf8String("memory");
    292     JSObject::AddProperty(module, name, mem_buffer, READ_ONLY);
    293   }
    294 
    295   //-------------------------------------------------------------------------
    296   // Allocate the globals area if necessary.
    297   //-------------------------------------------------------------------------
    298   size_t globals_size = AllocateGlobalsOffsets(globals);
    299   byte* globals_addr = nullptr;
    300   if (globals_size > 0) {
    301     Handle<JSArrayBuffer> globals_buffer =
    302         NewArrayBuffer(isolate, mem_size, &globals_addr);
    303     if (!globals_addr) {
    304       // Not enough space for backing store of globals.
    305       thrower.Error("Out of memory: wasm globals");
    306       return MaybeHandle<JSObject>();
    307     }
    308 
    309     module->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer);
    310   } else {
    311     module->SetInternalField(kWasmGlobalsArrayBuffer, Smi::FromInt(0));
    312   }
    313 
    314   //-------------------------------------------------------------------------
    315   // Compile all functions in the module.
    316   //-------------------------------------------------------------------------
    317   int index = 0;
    318   WasmLinker linker(isolate, functions->size());
    319   ModuleEnv module_env;
    320   module_env.module = this;
    321   module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr);
    322   module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr) + mem_size;
    323   module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr);
    324   module_env.linker = &linker;
    325   module_env.function_code = nullptr;
    326   module_env.function_table = BuildFunctionTable(isolate, this);
    327   module_env.memory = memory;
    328   module_env.context = isolate->native_context();
    329   module_env.asm_js = false;
    330 
    331   // First pass: compile each function and initialize the code table.
    332   for (const WasmFunction& func : *functions) {
    333     if (thrower.error()) break;
    334 
    335     const char* cstr = GetName(func.name_offset);
    336     Handle<String> name = factory->InternalizeUtf8String(cstr);
    337     Handle<Code> code = Handle<Code>::null();
    338     Handle<JSFunction> function = Handle<JSFunction>::null();
    339     if (func.external) {
    340       // Lookup external function in FFI object.
    341       if (!ffi.is_null()) {
    342         MaybeHandle<Object> result = Object::GetProperty(ffi, name);
    343         if (!result.is_null()) {
    344           Handle<Object> obj = result.ToHandleChecked();
    345           if (obj->IsJSFunction()) {
    346             function = Handle<JSFunction>::cast(obj);
    347             code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
    348                                                     function, index);
    349           } else {
    350             thrower.Error("FFI function #%d:%s is not a JSFunction.", index,
    351                           cstr);
    352             return MaybeHandle<JSObject>();
    353           }
    354         } else {
    355           thrower.Error("FFI function #%d:%s not found.", index, cstr);
    356           return MaybeHandle<JSObject>();
    357         }
    358       } else {
    359         thrower.Error("FFI table is not an object.");
    360         return MaybeHandle<JSObject>();
    361       }
    362     } else {
    363       // Compile the function.
    364       code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func,
    365                                            index);
    366       if (code.is_null()) {
    367         thrower.Error("Compilation of #%d:%s failed.", index, cstr);
    368         return MaybeHandle<JSObject>();
    369       }
    370       if (func.exported) {
    371         function = compiler::CompileJSToWasmWrapper(isolate, &module_env, name,
    372                                                     code, module, index);
    373       }
    374     }
    375     if (!code.is_null()) {
    376       // Install the code into the linker table.
    377       linker.Finish(index, code);
    378       code_table->set(index, *code);
    379     }
    380     if (func.exported) {
    381       // Exported functions are installed as read-only properties on the module.
    382       JSObject::AddProperty(module, name, function, READ_ONLY);
    383     }
    384     index++;
    385   }
    386 
    387   // Second pass: patch all direct call sites.
    388   linker.Link(module_env.function_table, this->function_table);
    389 
    390   module->SetInternalField(kWasmModuleFunctionTable, Smi::FromInt(0));
    391   module->SetInternalField(kWasmModuleCodeTable, *code_table);
    392   return module;
    393 }
    394 
    395 
    396 Handle<Code> ModuleEnv::GetFunctionCode(uint32_t index) {
    397   DCHECK(IsValidFunction(index));
    398   if (linker) return linker->GetFunctionCode(index);
    399   if (function_code) return function_code->at(index);
    400   return Handle<Code>::null();
    401 }
    402 
    403 
    404 compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone,
    405                                                        uint32_t index) {
    406   DCHECK(IsValidFunction(index));
    407   // Always make a direct call to whatever is in the table at that location.
    408   // A wrapper will be generated for FFI calls.
    409   WasmFunction* function = &module->functions->at(index);
    410   return GetWasmCallDescriptor(zone, function->sig);
    411 }
    412 
    413 
    414 int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
    415                                 const byte* module_end, bool asm_js) {
    416   HandleScope scope(isolate);
    417   Zone zone;
    418   // Decode the module, but don't verify function bodies, since we'll
    419   // be compiling them anyway.
    420   ModuleResult result =
    421       DecodeWasmModule(isolate, &zone, module_start, module_end, false, false);
    422   if (result.failed()) {
    423     // Module verification failed. throw.
    424     std::ostringstream str;
    425     str << "WASM.compileRun() failed: " << result;
    426     isolate->Throw(
    427         *isolate->factory()->NewStringFromAsciiChecked(str.str().c_str()));
    428     return -1;
    429   }
    430 
    431   int32_t retval = CompileAndRunWasmModule(isolate, result.val);
    432   delete result.val;
    433   return retval;
    434 }
    435 
    436 
    437 int32_t CompileAndRunWasmModule(Isolate* isolate, WasmModule* module) {
    438   ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
    439 
    440   // Allocate temporary linear memory and globals.
    441   size_t mem_size = 1 << module->min_mem_size_log2;
    442   size_t globals_size = AllocateGlobalsOffsets(module->globals);
    443 
    444   base::SmartArrayPointer<byte> mem_addr(new byte[mem_size]);
    445   base::SmartArrayPointer<byte> globals_addr(new byte[globals_size]);
    446 
    447   memset(mem_addr.get(), 0, mem_size);
    448   memset(globals_addr.get(), 0, globals_size);
    449 
    450   // Create module environment.
    451   WasmLinker linker(isolate, module->functions->size());
    452   ModuleEnv module_env;
    453   module_env.module = module;
    454   module_env.mem_start = reinterpret_cast<uintptr_t>(mem_addr.get());
    455   module_env.mem_end = reinterpret_cast<uintptr_t>(mem_addr.get()) + mem_size;
    456   module_env.globals_area = reinterpret_cast<uintptr_t>(globals_addr.get());
    457   module_env.linker = &linker;
    458   module_env.function_code = nullptr;
    459   module_env.function_table = BuildFunctionTable(isolate, module);
    460   module_env.asm_js = false;
    461 
    462   // Load data segments.
    463   // TODO(titzer): throw instead of crashing if segments don't fit in memory?
    464   LoadDataSegments(module, mem_addr.get(), mem_size);
    465 
    466   // Compile all functions.
    467   Handle<Code> main_code = Handle<Code>::null();  // record last code.
    468   int index = 0;
    469   for (const WasmFunction& func : *module->functions) {
    470     if (!func.external) {
    471       // Compile the function and install it in the code table.
    472       Handle<Code> code = compiler::CompileWasmFunction(
    473           thrower, isolate, &module_env, func, index);
    474       if (!code.is_null()) {
    475         if (func.exported) main_code = code;
    476         linker.Finish(index, code);
    477       }
    478       if (thrower.error()) return -1;
    479     }
    480     index++;
    481   }
    482 
    483   if (!main_code.is_null()) {
    484     linker.Link(module_env.function_table, module->function_table);
    485 #if USE_SIMULATOR && V8_TARGET_ARCH_ARM64
    486     // Run the main code on arm64 simulator.
    487     Simulator* simulator = Simulator::current(isolate);
    488     Simulator::CallArgument args[] = {Simulator::CallArgument(0),
    489                                       Simulator::CallArgument::End()};
    490     return static_cast<int32_t>(simulator->CallInt64(main_code->entry(), args));
    491 #elif USE_SIMULATOR
    492     // Run the main code on simulator.
    493     Simulator* simulator = Simulator::current(isolate);
    494     return static_cast<int32_t>(
    495         simulator->Call(main_code->entry(), 4, 0, 0, 0, 0));
    496 #else
    497     // Run the main code as raw machine code.
    498     int32_t (*raw_func)() = reinterpret_cast<int32_t (*)()>(
    499         reinterpret_cast<uintptr_t>(main_code->entry()));
    500     return raw_func();
    501 #endif
    502   } else {
    503     // No main code was found.
    504     isolate->Throw(*isolate->factory()->NewStringFromStaticChars(
    505         "WASM.compileRun() failed: no valid main code produced."));
    506   }
    507   return -1;
    508 }
    509 }  // namespace wasm
    510 }  // namespace internal
    511 }  // namespace v8
    512