Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2016 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmInstance.h"
20 :
21 : #include "jit/BaselineJIT.h"
22 : #include "jit/InlinableNatives.h"
23 : #include "jit/JitCommon.h"
24 : #include "wasm/WasmBuiltins.h"
25 : #include "wasm/WasmModule.h"
26 :
27 : #include "jsobjinlines.h"
28 :
29 : #include "vm/ArrayBufferObject-inl.h"
30 :
31 : using namespace js;
32 : using namespace js::jit;
33 : using namespace js::wasm;
34 : using mozilla::BinarySearch;
35 : using mozilla::BitwiseCast;
36 : using mozilla::IsNaN;
37 : using mozilla::Swap;
38 :
39 3 : class SigIdSet
40 : {
41 : typedef HashMap<const Sig*, uint32_t, SigHashPolicy, SystemAllocPolicy> Map;
42 : Map map_;
43 :
44 : public:
45 0 : ~SigIdSet() {
46 0 : MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !map_.initialized() || map_.empty());
47 0 : }
48 :
49 0 : bool ensureInitialized(JSContext* cx) {
50 0 : if (!map_.initialized() && !map_.init()) {
51 0 : ReportOutOfMemory(cx);
52 0 : return false;
53 : }
54 :
55 0 : return true;
56 : }
57 :
58 0 : bool allocateSigId(JSContext* cx, const Sig& sig, const void** sigId) {
59 0 : Map::AddPtr p = map_.lookupForAdd(sig);
60 0 : if (p) {
61 0 : MOZ_ASSERT(p->value() > 0);
62 0 : p->value()++;
63 0 : *sigId = p->key();
64 0 : return true;
65 : }
66 :
67 0 : UniquePtr<Sig> clone = MakeUnique<Sig>();
68 0 : if (!clone || !clone->clone(sig) || !map_.add(p, clone.get(), 1)) {
69 0 : ReportOutOfMemory(cx);
70 0 : return false;
71 : }
72 :
73 0 : *sigId = clone.release();
74 0 : MOZ_ASSERT(!(uintptr_t(*sigId) & SigIdDesc::ImmediateBit));
75 0 : return true;
76 : }
77 :
78 0 : void deallocateSigId(const Sig& sig, const void* sigId) {
79 0 : Map::Ptr p = map_.lookup(sig);
80 0 : MOZ_RELEASE_ASSERT(p && p->key() == sigId && p->value() > 0);
81 :
82 0 : p->value()--;
83 0 : if (!p->value()) {
84 0 : js_delete(p->key());
85 0 : map_.remove(p);
86 : }
87 0 : }
88 : };
89 :
90 : ExclusiveData<SigIdSet>* sigIdSet = nullptr;
91 :
92 : bool
93 3 : js::wasm::InitInstanceStaticData()
94 : {
95 3 : MOZ_ASSERT(!sigIdSet);
96 3 : sigIdSet = js_new<ExclusiveData<SigIdSet>>(mutexid::WasmSigIdSet);
97 3 : return sigIdSet != nullptr;
98 : }
99 :
100 : void
101 0 : js::wasm::ShutDownInstanceStaticData()
102 : {
103 0 : MOZ_ASSERT(sigIdSet);
104 0 : js_delete(sigIdSet);
105 0 : sigIdSet = nullptr;
106 0 : }
107 :
108 : const void**
109 0 : Instance::addressOfSigId(const SigIdDesc& sigId) const
110 : {
111 0 : return (const void**)(globalSegment().globalData() + sigId.globalDataOffset());
112 : }
113 :
114 : FuncImportTls&
115 0 : Instance::funcImportTls(const FuncImport& fi)
116 : {
117 0 : return *(FuncImportTls*)(globalSegment().globalData() + fi.tlsDataOffset());
118 : }
119 :
120 : TableTls&
121 0 : Instance::tableTls(const TableDesc& td) const
122 : {
123 0 : return *(TableTls*)(globalSegment().globalData() + td.globalDataOffset);
124 : }
125 :
126 : bool
127 0 : Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
128 : MutableHandleValue rval)
129 : {
130 0 : Tier tier = Tier::TBD;
131 :
132 0 : const FuncImport& fi = metadata(tier).funcImports[funcImportIndex];
133 :
134 0 : InvokeArgs args(cx);
135 0 : if (!args.init(cx, argc))
136 0 : return false;
137 :
138 0 : bool hasI64Arg = false;
139 0 : MOZ_ASSERT(fi.sig().args().length() == argc);
140 0 : for (size_t i = 0; i < argc; i++) {
141 0 : switch (fi.sig().args()[i]) {
142 : case ValType::I32:
143 0 : args[i].set(Int32Value(*(int32_t*)&argv[i]));
144 0 : break;
145 : case ValType::F32:
146 0 : args[i].set(JS::CanonicalizedDoubleValue(*(float*)&argv[i]));
147 0 : break;
148 : case ValType::F64:
149 0 : args[i].set(JS::CanonicalizedDoubleValue(*(double*)&argv[i]));
150 0 : break;
151 : case ValType::I64: {
152 0 : if (!JitOptions.wasmTestMode) {
153 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64);
154 0 : return false;
155 : }
156 0 : RootedObject obj(cx, CreateI64Object(cx, *(int64_t*)&argv[i]));
157 0 : if (!obj)
158 0 : return false;
159 0 : args[i].set(ObjectValue(*obj));
160 0 : hasI64Arg = true;
161 0 : break;
162 : }
163 : case ValType::I8x16:
164 : case ValType::I16x8:
165 : case ValType::I32x4:
166 : case ValType::F32x4:
167 : case ValType::B8x16:
168 : case ValType::B16x8:
169 : case ValType::B32x4:
170 0 : MOZ_CRASH("unhandled type in callImport");
171 : }
172 : }
173 :
174 0 : FuncImportTls& import = funcImportTls(fi);
175 0 : RootedFunction importFun(cx, &import.obj->as<JSFunction>());
176 0 : RootedValue fval(cx, ObjectValue(*import.obj));
177 0 : RootedValue thisv(cx, UndefinedValue());
178 0 : if (!Call(cx, fval, thisv, args, rval))
179 0 : return false;
180 :
181 : // Throw an error if returning i64 and not in test mode.
182 0 : if (!JitOptions.wasmTestMode && fi.sig().ret() == ExprType::I64) {
183 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64);
184 0 : return false;
185 : }
186 :
187 : // Don't try to optimize if the function has at least one i64 arg or if
188 : // it returns an int64. GenerateJitExit relies on this, as does the
189 : // type inference code below in this function.
190 0 : if (hasI64Arg || fi.sig().ret() == ExprType::I64)
191 0 : return true;
192 :
193 : // The import may already have become optimized.
194 0 : void* jitExitCode = codeBase(tier) + fi.jitExitCodeOffset();
195 0 : if (import.code == jitExitCode)
196 0 : return true;
197 :
198 : // Test if the function is JIT compiled.
199 0 : if (!importFun->hasScript())
200 0 : return true;
201 :
202 0 : JSScript* script = importFun->nonLazyScript();
203 0 : if (!script->hasBaselineScript()) {
204 0 : MOZ_ASSERT(!script->hasIonScript());
205 0 : return true;
206 : }
207 :
208 : // Don't enable jit entry when we have a pending ion builder.
209 : // Take the interpreter path which will link it and enable
210 : // the fast path on the next call.
211 0 : if (script->baselineScript()->hasPendingIonBuilder())
212 0 : return true;
213 :
214 : // Currently we can't rectify arguments. Therefore disable if argc is too low.
215 0 : if (importFun->nargs() > fi.sig().args().length())
216 0 : return true;
217 :
218 : // Ensure the argument types are included in the argument TypeSets stored in
219 : // the TypeScript. This is necessary for Ion, because the import will use
220 : // the skip-arg-checks entry point.
221 : //
222 : // Note that the TypeScript is never discarded while the script has a
223 : // BaselineScript, so if those checks hold now they must hold at least until
224 : // the BaselineScript is discarded and when that happens the import is
225 : // patched back.
226 0 : if (!TypeScript::ThisTypes(script)->hasType(TypeSet::UndefinedType()))
227 0 : return true;
228 0 : for (uint32_t i = 0; i < importFun->nargs(); i++) {
229 0 : TypeSet::Type type = TypeSet::UnknownType();
230 0 : switch (fi.sig().args()[i]) {
231 0 : case ValType::I32: type = TypeSet::Int32Type(); break;
232 0 : case ValType::I64: MOZ_CRASH("can't happen because of above guard");
233 0 : case ValType::F32: type = TypeSet::DoubleType(); break;
234 0 : case ValType::F64: type = TypeSet::DoubleType(); break;
235 0 : case ValType::I8x16: MOZ_CRASH("NYI");
236 0 : case ValType::I16x8: MOZ_CRASH("NYI");
237 0 : case ValType::I32x4: MOZ_CRASH("NYI");
238 0 : case ValType::F32x4: MOZ_CRASH("NYI");
239 0 : case ValType::B8x16: MOZ_CRASH("NYI");
240 0 : case ValType::B16x8: MOZ_CRASH("NYI");
241 0 : case ValType::B32x4: MOZ_CRASH("NYI");
242 : }
243 0 : if (!TypeScript::ArgTypes(script, i)->hasType(type))
244 0 : return true;
245 : }
246 :
247 : // Let's optimize it!
248 0 : if (!script->baselineScript()->addDependentWasmImport(cx, *this, funcImportIndex))
249 0 : return false;
250 :
251 0 : import.code = jitExitCode;
252 0 : import.baselineScript = script->baselineScript();
253 0 : return true;
254 : }
255 :
256 : /* static */ int32_t
257 0 : Instance::callImport_void(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
258 : {
259 0 : JSContext* cx = TlsContext.get();
260 0 : RootedValue rval(cx);
261 0 : return instance->callImport(cx, funcImportIndex, argc, argv, &rval);
262 : }
263 :
264 : /* static */ int32_t
265 0 : Instance::callImport_i32(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
266 : {
267 0 : JSContext* cx = TlsContext.get();
268 0 : RootedValue rval(cx);
269 0 : if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
270 0 : return false;
271 :
272 0 : return ToInt32(cx, rval, (int32_t*)argv);
273 : }
274 :
275 : /* static */ int32_t
276 0 : Instance::callImport_i64(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
277 : {
278 0 : JSContext* cx = TlsContext.get();
279 0 : RootedValue rval(cx);
280 0 : if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
281 0 : return false;
282 :
283 0 : return ReadI64Object(cx, rval, (int64_t*)argv);
284 : }
285 :
286 : /* static */ int32_t
287 0 : Instance::callImport_f64(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
288 : {
289 0 : JSContext* cx = TlsContext.get();
290 0 : RootedValue rval(cx);
291 0 : if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
292 0 : return false;
293 :
294 0 : return ToNumber(cx, rval, (double*)argv);
295 : }
296 :
297 : /* static */ uint32_t
298 0 : Instance::growMemory_i32(Instance* instance, uint32_t delta)
299 : {
300 0 : MOZ_ASSERT(!instance->isAsmJS());
301 :
302 0 : JSContext* cx = TlsContext.get();
303 0 : RootedWasmMemoryObject memory(cx, instance->memory_);
304 :
305 0 : uint32_t ret = WasmMemoryObject::grow(memory, delta, cx);
306 :
307 : // If there has been a moving grow, this Instance should have been notified.
308 0 : MOZ_RELEASE_ASSERT(instance->tlsData()->memoryBase ==
309 : instance->memory_->buffer().dataPointerEither());
310 :
311 0 : return ret;
312 : }
313 :
314 : /* static */ uint32_t
315 0 : Instance::currentMemory_i32(Instance* instance)
316 : {
317 0 : uint32_t byteLength = instance->memoryLength();
318 0 : MOZ_ASSERT(byteLength % wasm::PageSize == 0);
319 0 : return byteLength / wasm::PageSize;
320 : }
321 :
322 0 : Instance::Instance(JSContext* cx,
323 : Handle<WasmInstanceObject*> object,
324 : SharedCode code,
325 : UniqueDebugState debug,
326 : UniqueGlobalSegment globals,
327 : HandleWasmMemoryObject memory,
328 : SharedTableVector&& tables,
329 : Handle<FunctionVector> funcImports,
330 0 : const ValVector& globalImports)
331 0 : : compartment_(cx->compartment()),
332 : object_(object),
333 : code_(code),
334 0 : debug_(Move(debug)),
335 0 : globals_(Move(globals)),
336 : memory_(memory),
337 0 : tables_(Move(tables)),
338 0 : enterFrameTrapsEnabled_(false)
339 : {
340 : #ifdef DEBUG
341 0 : for (auto t : metadata().tiers())
342 0 : MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
343 : #endif
344 0 : MOZ_ASSERT(tables_.length() == metadata().tables.length());
345 :
346 0 : tlsData()->memoryBase = memory ? memory->buffer().dataPointerEither().unwrap() : nullptr;
347 : #ifndef WASM_HUGE_MEMORY
348 : tlsData()->boundsCheckLimit = memory ? memory->buffer().wasmBoundsCheckLimit() : 0;
349 : #endif
350 0 : tlsData()->instance = this;
351 0 : tlsData()->addressOfContext = (JSContext**)object->zone()->group()->addressOfOwnerContext();
352 :
353 0 : Tier callerTier = Tier::TBD;
354 0 : Tier calleeTier = Tier::TBD;
355 :
356 0 : for (size_t i = 0; i < metadata(callerTier).funcImports.length(); i++) {
357 0 : HandleFunction f = funcImports[i];
358 0 : const FuncImport& fi = metadata(callerTier).funcImports[i];
359 0 : FuncImportTls& import = funcImportTls(fi);
360 0 : if (!isAsmJS() && IsExportedWasmFunction(f)) {
361 0 : WasmInstanceObject* calleeInstanceObj = ExportedFunctionToInstanceObject(f);
362 0 : const CodeRange& codeRange = calleeInstanceObj->getExportedFunctionCodeRange(f, calleeTier);
363 0 : Instance& calleeInstance = calleeInstanceObj->instance();
364 0 : import.tls = calleeInstance.tlsData();
365 0 : import.code = calleeInstance.codeBase(calleeTier) + codeRange.funcNormalEntry();
366 0 : import.baselineScript = nullptr;
367 0 : import.obj = calleeInstanceObj;
368 0 : } else if (void* thunk = MaybeGetBuiltinThunk(f, fi.sig(), cx)) {
369 0 : import.tls = tlsData();
370 0 : import.code = thunk;
371 0 : import.baselineScript = nullptr;
372 0 : import.obj = f;
373 : } else {
374 0 : import.tls = tlsData();
375 0 : import.code = codeBase(callerTier) + fi.interpExitCodeOffset();
376 0 : import.baselineScript = nullptr;
377 0 : import.obj = f;
378 : }
379 : }
380 :
381 0 : for (size_t i = 0; i < tables_.length(); i++) {
382 0 : const TableDesc& td = metadata().tables[i];
383 0 : TableTls& table = tableTls(td);
384 0 : table.length = tables_[i]->length();
385 0 : table.base = tables_[i]->base();
386 : }
387 :
388 0 : uint8_t* globalData = globals_->globalData();
389 :
390 0 : for (size_t i = 0; i < metadata().globals.length(); i++) {
391 0 : const GlobalDesc& global = metadata().globals[i];
392 0 : if (global.isConstant())
393 0 : continue;
394 :
395 0 : uint8_t* globalAddr = globalData + global.offset();
396 0 : switch (global.kind()) {
397 : case GlobalKind::Import: {
398 0 : globalImports[global.importIndex()].writePayload(globalAddr);
399 0 : break;
400 : }
401 : case GlobalKind::Variable: {
402 0 : const InitExpr& init = global.initExpr();
403 0 : switch (init.kind()) {
404 : case InitExpr::Kind::Constant: {
405 0 : init.val().writePayload(globalAddr);
406 0 : break;
407 : }
408 : case InitExpr::Kind::GetGlobal: {
409 0 : const GlobalDesc& imported = metadata().globals[init.globalIndex()];
410 0 : globalImports[imported.importIndex()].writePayload(globalAddr);
411 0 : break;
412 : }
413 : }
414 0 : break;
415 : }
416 : case GlobalKind::Constant: {
417 0 : MOZ_CRASH("skipped at the top");
418 : }
419 : }
420 : }
421 0 : }
422 :
423 : bool
424 0 : Instance::init(JSContext* cx)
425 : {
426 0 : if (memory_ && memory_->movingGrowable() && !memory_->addMovingGrowObserver(cx, object_))
427 0 : return false;
428 :
429 0 : for (const SharedTable& table : tables_) {
430 0 : if (table->movingGrowable() && !table->addMovingGrowObserver(cx, object_))
431 0 : return false;
432 : }
433 :
434 0 : if (!metadata().sigIds.empty()) {
435 0 : ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet->lock();
436 :
437 0 : if (!lockedSigIdSet->ensureInitialized(cx))
438 0 : return false;
439 :
440 0 : for (const SigWithId& sig : metadata().sigIds) {
441 : const void* sigId;
442 0 : if (!lockedSigIdSet->allocateSigId(cx, sig, &sigId))
443 0 : return false;
444 :
445 0 : *addressOfSigId(sig.id) = sigId;
446 : }
447 : }
448 :
449 0 : return true;
450 : }
451 :
452 0 : Instance::~Instance()
453 : {
454 0 : compartment_->wasm.unregisterInstance(*this);
455 :
456 0 : const FuncImportVector& funcImports = metadata(code().anyTier()).funcImports;
457 :
458 0 : for (unsigned i = 0; i < funcImports.length(); i++) {
459 0 : FuncImportTls& import = funcImportTls(funcImports[i]);
460 0 : if (import.baselineScript)
461 0 : import.baselineScript->removeDependentWasmImport(*this, i);
462 : }
463 :
464 0 : if (!metadata().sigIds.empty()) {
465 0 : ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet->lock();
466 :
467 0 : for (const SigWithId& sig : metadata().sigIds) {
468 0 : if (const void* sigId = *addressOfSigId(sig.id))
469 0 : lockedSigIdSet->deallocateSigId(sig, sigId);
470 : }
471 : }
472 0 : }
473 :
474 : size_t
475 0 : Instance::memoryMappedSize() const
476 : {
477 0 : return memory_->buffer().wasmMappedSize();
478 : }
479 :
480 : bool
481 0 : Instance::memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const
482 : {
483 0 : MOZ_ASSERT(numBytes > 0);
484 :
485 0 : if (!metadata().usesMemory())
486 0 : return false;
487 :
488 0 : uint8_t* base = memoryBase().unwrap(/* comparison */);
489 0 : if (addr < base)
490 0 : return false;
491 :
492 0 : size_t lastByteOffset = addr - base + (numBytes - 1);
493 0 : return lastByteOffset >= memoryLength() && lastByteOffset < memoryMappedSize();
494 : }
495 :
496 : void
497 0 : Instance::tracePrivate(JSTracer* trc)
498 : {
499 : // This method is only called from WasmInstanceObject so the only reason why
500 : // TraceEdge is called is so that the pointer can be updated during a moving
501 : // GC. TraceWeakEdge may sound better, but it is less efficient given that
502 : // we know object_ is already marked.
503 0 : MOZ_ASSERT(!gc::IsAboutToBeFinalized(&object_));
504 0 : TraceEdge(trc, &object_, "wasm instance object");
505 :
506 0 : for (const FuncImport& fi : metadata(code().anyTier()).funcImports)
507 0 : TraceNullableEdge(trc, &funcImportTls(fi).obj, "wasm import");
508 :
509 0 : for (const SharedTable& table : tables_)
510 0 : table->trace(trc);
511 :
512 0 : TraceNullableEdge(trc, &memory_, "wasm buffer");
513 0 : }
514 :
515 : void
516 0 : Instance::trace(JSTracer* trc)
517 : {
518 : // Technically, instead of having this method, the caller could use
519 : // Instance::object() to get the owning WasmInstanceObject to mark,
520 : // but this method is simpler and more efficient. The trace hook of
521 : // WasmInstanceObject will call Instance::tracePrivate at which point we
522 : // can mark the rest of the children.
523 0 : TraceEdge(trc, &object_, "wasm instance object");
524 0 : }
525 :
526 : SharedMem<uint8_t*>
527 0 : Instance::memoryBase() const
528 : {
529 0 : MOZ_ASSERT(metadata().usesMemory());
530 0 : MOZ_ASSERT(tlsData()->memoryBase == memory_->buffer().dataPointerEither());
531 0 : return memory_->buffer().dataPointerEither();
532 : }
533 :
534 : size_t
535 0 : Instance::memoryLength() const
536 : {
537 0 : return memory_->buffer().byteLength();
538 : }
539 :
540 : WasmInstanceObject*
541 0 : Instance::objectUnbarriered() const
542 : {
543 0 : return object_.unbarrieredGet();
544 : }
545 :
546 : WasmInstanceObject*
547 0 : Instance::object() const
548 : {
549 0 : return object_;
550 : }
551 :
552 : bool
553 0 : Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
554 : {
555 : // If there has been a moving grow, this Instance should have been notified.
556 0 : MOZ_RELEASE_ASSERT(!memory_ || tlsData()->memoryBase == memory_->buffer().dataPointerEither());
557 :
558 0 : Tier tier = Tier::TBD;
559 :
560 0 : const FuncExport& func = metadata(tier).lookupFuncExport(funcIndex);
561 :
562 : // The calling convention for an external call into wasm is to pass an
563 : // array of 16-byte values where each value contains either a coerced int32
564 : // (in the low word), a double value (in the low dword) or a SIMD vector
565 : // value, with the coercions specified by the wasm signature. The external
566 : // entry point unpacks this array into the system-ABI-specified registers
567 : // and stack memory and then calls into the internal entry point. The return
568 : // value is stored in the first element of the array (which, therefore, must
569 : // have length >= 1).
570 0 : Vector<ExportArg, 8> exportArgs(cx);
571 0 : if (!exportArgs.resize(Max<size_t>(1, func.sig().args().length())))
572 0 : return false;
573 :
574 0 : RootedValue v(cx);
575 0 : for (unsigned i = 0; i < func.sig().args().length(); ++i) {
576 0 : v = i < args.length() ? args[i] : UndefinedValue();
577 0 : switch (func.sig().arg(i)) {
578 : case ValType::I32:
579 0 : if (!ToInt32(cx, v, (int32_t*)&exportArgs[i]))
580 0 : return false;
581 0 : break;
582 : case ValType::I64:
583 0 : if (!JitOptions.wasmTestMode) {
584 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64);
585 0 : return false;
586 : }
587 0 : if (!ReadI64Object(cx, v, (int64_t*)&exportArgs[i]))
588 0 : return false;
589 0 : break;
590 : case ValType::F32:
591 0 : if (JitOptions.wasmTestMode && v.isObject()) {
592 0 : if (!ReadCustomFloat32NaNObject(cx, v, (uint32_t*)&exportArgs[i]))
593 0 : return false;
594 0 : break;
595 : }
596 0 : if (!RoundFloat32(cx, v, (float*)&exportArgs[i]))
597 0 : return false;
598 0 : break;
599 : case ValType::F64:
600 0 : if (JitOptions.wasmTestMode && v.isObject()) {
601 0 : if (!ReadCustomDoubleNaNObject(cx, v, (uint64_t*)&exportArgs[i]))
602 0 : return false;
603 0 : break;
604 : }
605 0 : if (!ToNumber(cx, v, (double*)&exportArgs[i]))
606 0 : return false;
607 0 : break;
608 : case ValType::I8x16: {
609 : SimdConstant simd;
610 0 : if (!ToSimdConstant<Int8x16>(cx, v, &simd))
611 0 : return false;
612 0 : memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize);
613 0 : break;
614 : }
615 : case ValType::I16x8: {
616 : SimdConstant simd;
617 0 : if (!ToSimdConstant<Int16x8>(cx, v, &simd))
618 0 : return false;
619 0 : memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize);
620 0 : break;
621 : }
622 : case ValType::I32x4: {
623 : SimdConstant simd;
624 0 : if (!ToSimdConstant<Int32x4>(cx, v, &simd))
625 0 : return false;
626 0 : memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize);
627 0 : break;
628 : }
629 : case ValType::F32x4: {
630 : SimdConstant simd;
631 0 : if (!ToSimdConstant<Float32x4>(cx, v, &simd))
632 0 : return false;
633 0 : memcpy(&exportArgs[i], simd.asFloat32x4(), Simd128DataSize);
634 0 : break;
635 : }
636 : case ValType::B8x16: {
637 : SimdConstant simd;
638 0 : if (!ToSimdConstant<Bool8x16>(cx, v, &simd))
639 0 : return false;
640 : // Bool8x16 uses the same representation as Int8x16.
641 0 : memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize);
642 0 : break;
643 : }
644 : case ValType::B16x8: {
645 : SimdConstant simd;
646 0 : if (!ToSimdConstant<Bool16x8>(cx, v, &simd))
647 0 : return false;
648 : // Bool16x8 uses the same representation as Int16x8.
649 0 : memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize);
650 0 : break;
651 : }
652 : case ValType::B32x4: {
653 : SimdConstant simd;
654 0 : if (!ToSimdConstant<Bool32x4>(cx, v, &simd))
655 0 : return false;
656 : // Bool32x4 uses the same representation as Int32x4.
657 0 : memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize);
658 0 : break;
659 : }
660 : }
661 : }
662 :
663 : {
664 : // Push a WasmActivation to describe the wasm frames we're about to push
665 : // when running this module. Additionally, push a JitActivation so that
666 : // the optimized wasm-to-Ion FFI call path (which we want to be very
667 : // fast) can avoid doing so. The JitActivation is marked as inactive so
668 : // stack iteration will skip over it.
669 0 : WasmActivation activation(cx);
670 0 : JitActivation jitActivation(cx, /* active */ false);
671 :
672 : // Call the per-exported-function trampoline created by GenerateEntry.
673 0 : auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase(tier) + func.entryOffset());
674 0 : if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), tlsData()))
675 0 : return false;
676 : }
677 :
678 0 : if (isAsmJS() && args.isConstructing()) {
679 : // By spec, when a JS function is called as a constructor and this
680 : // function returns a primary type, which is the case for all asm.js
681 : // exported functions, the returned value is discarded and an empty
682 : // object is returned instead.
683 0 : PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx);
684 0 : if (!obj)
685 0 : return false;
686 0 : args.rval().set(ObjectValue(*obj));
687 0 : return true;
688 : }
689 :
690 0 : void* retAddr = &exportArgs[0];
691 0 : JSObject* retObj = nullptr;
692 0 : switch (func.sig().ret()) {
693 : case ExprType::Void:
694 0 : args.rval().set(UndefinedValue());
695 0 : break;
696 : case ExprType::I32:
697 0 : args.rval().set(Int32Value(*(int32_t*)retAddr));
698 0 : break;
699 : case ExprType::I64:
700 0 : if (!JitOptions.wasmTestMode) {
701 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64);
702 0 : return false;
703 : }
704 0 : retObj = CreateI64Object(cx, *(int64_t*)retAddr);
705 0 : if (!retObj)
706 0 : return false;
707 0 : break;
708 : case ExprType::F32:
709 0 : if (JitOptions.wasmTestMode && IsNaN(*(float*)retAddr)) {
710 0 : retObj = CreateCustomNaNObject(cx, (float*)retAddr);
711 0 : if (!retObj)
712 0 : return false;
713 0 : break;
714 : }
715 0 : args.rval().set(NumberValue(*(float*)retAddr));
716 0 : break;
717 : case ExprType::F64:
718 0 : if (JitOptions.wasmTestMode && IsNaN(*(double*)retAddr)) {
719 0 : retObj = CreateCustomNaNObject(cx, (double*)retAddr);
720 0 : if (!retObj)
721 0 : return false;
722 0 : break;
723 : }
724 0 : args.rval().set(NumberValue(*(double*)retAddr));
725 0 : break;
726 : case ExprType::I8x16:
727 0 : retObj = CreateSimd<Int8x16>(cx, (int8_t*)retAddr);
728 0 : if (!retObj)
729 0 : return false;
730 0 : break;
731 : case ExprType::I16x8:
732 0 : retObj = CreateSimd<Int16x8>(cx, (int16_t*)retAddr);
733 0 : if (!retObj)
734 0 : return false;
735 0 : break;
736 : case ExprType::I32x4:
737 0 : retObj = CreateSimd<Int32x4>(cx, (int32_t*)retAddr);
738 0 : if (!retObj)
739 0 : return false;
740 0 : break;
741 : case ExprType::F32x4:
742 0 : retObj = CreateSimd<Float32x4>(cx, (float*)retAddr);
743 0 : if (!retObj)
744 0 : return false;
745 0 : break;
746 : case ExprType::B8x16:
747 0 : retObj = CreateSimd<Bool8x16>(cx, (int8_t*)retAddr);
748 0 : if (!retObj)
749 0 : return false;
750 0 : break;
751 : case ExprType::B16x8:
752 0 : retObj = CreateSimd<Bool16x8>(cx, (int16_t*)retAddr);
753 0 : if (!retObj)
754 0 : return false;
755 0 : break;
756 : case ExprType::B32x4:
757 0 : retObj = CreateSimd<Bool32x4>(cx, (int32_t*)retAddr);
758 0 : if (!retObj)
759 0 : return false;
760 0 : break;
761 : case ExprType::Limit:
762 0 : MOZ_CRASH("Limit");
763 : }
764 :
765 0 : if (retObj)
766 0 : args.rval().set(ObjectValue(*retObj));
767 :
768 0 : return true;
769 : }
770 :
771 : bool
772 0 : Instance::getFuncName(uint32_t funcIndex, UTF8Bytes* name) const
773 : {
774 0 : return metadata().getFuncName(debug_->maybeBytecode(), funcIndex, name);
775 : }
776 :
777 : JSAtom*
778 0 : Instance::getFuncAtom(JSContext* cx, uint32_t funcIndex) const
779 : {
780 0 : UTF8Bytes name;
781 0 : if (!getFuncName(funcIndex, &name))
782 0 : return nullptr;
783 :
784 0 : return AtomizeUTF8Chars(cx, name.begin(), name.length());
785 : }
786 :
787 : void
788 0 : Instance::ensureProfilingLabels(bool profilingEnabled) const
789 : {
790 0 : return code_->ensureProfilingLabels(debug_->maybeBytecode(), profilingEnabled);
791 : }
792 :
793 : void
794 0 : Instance::onMovingGrowMemory(uint8_t* prevMemoryBase)
795 : {
796 0 : MOZ_ASSERT(!isAsmJS());
797 0 : ArrayBufferObject& buffer = memory_->buffer().as<ArrayBufferObject>();
798 0 : tlsData()->memoryBase = buffer.dataPointer();
799 : #ifndef WASM_HUGE_MEMORY
800 : tlsData()->boundsCheckLimit = buffer.wasmBoundsCheckLimit();
801 : #endif
802 0 : }
803 :
804 : void
805 0 : Instance::onMovingGrowTable()
806 : {
807 0 : MOZ_ASSERT(!isAsmJS());
808 0 : MOZ_ASSERT(tables_.length() == 1);
809 0 : TableTls& table = tableTls(metadata().tables[0]);
810 0 : table.length = tables_[0]->length();
811 0 : table.base = tables_[0]->base();
812 0 : }
813 :
814 : void
815 0 : Instance::deoptimizeImportExit(uint32_t funcImportIndex)
816 : {
817 0 : const FuncImport& fi = metadata(code().anyTier()).funcImports[funcImportIndex];
818 0 : FuncImportTls& import = funcImportTls(fi);
819 0 : import.code = codeBase(Tier::TBD) + fi.interpExitCodeOffset();
820 0 : import.baselineScript = nullptr;
821 0 : }
822 :
823 : void
824 0 : Instance::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
825 : {
826 0 : if (enterFrameTrapsEnabled_ == enabled)
827 0 : return;
828 :
829 0 : debug_->adjustEnterAndLeaveFrameTrapsState(cx, enabled);
830 0 : enterFrameTrapsEnabled_ = enabled;
831 : }
832 :
833 : void
834 0 : Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
835 : Metadata::SeenSet* seenMetadata,
836 : ShareableBytes::SeenSet* seenBytes,
837 : Code::SeenSet* seenCode,
838 : Table::SeenSet* seenTables,
839 : size_t* code,
840 : size_t* data) const
841 : {
842 0 : *data += mallocSizeOf(this) + globals_->sizeOfMisc(mallocSizeOf);
843 :
844 0 : for (const SharedTable& table : tables_)
845 0 : *data += table->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenTables);
846 :
847 0 : debug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data);
848 0 : code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
849 0 : }
850 :
851 : // We will emit SIMD memory accesses that require 16-byte alignment.
852 : static const size_t TlsAlign = Simd128DataSize;
853 :
854 : /* static */ UniqueGlobalSegment
855 0 : GlobalSegment::create(uint32_t globalDataLength)
856 : {
857 0 : MOZ_ASSERT(globalDataLength % gc::SystemPageSize() == 0);
858 :
859 0 : auto gs = MakeUnique<GlobalSegment>();
860 0 : if (!gs)
861 0 : return nullptr;
862 :
863 0 : void* allocatedBase = js_calloc(TlsAlign + offsetof(TlsData, globalArea) + globalDataLength);
864 0 : if (!allocatedBase)
865 0 : return nullptr;
866 :
867 0 : TlsData* tlsData = reinterpret_cast<TlsData*>(AlignBytes(size_t(allocatedBase), TlsAlign));
868 0 : tlsData->allocatedBase = allocatedBase;
869 :
870 0 : gs->tlsData_ = tlsData;
871 0 : gs->globalDataLength_ = globalDataLength;
872 :
873 0 : return gs;
874 : }
875 :
876 0 : GlobalSegment::~GlobalSegment()
877 : {
878 0 : if (tlsData_)
879 0 : js_free(tlsData_->allocatedBase);
880 0 : }
881 :
882 : size_t
883 0 : GlobalSegment::sizeOfMisc(MallocSizeOf mallocSizeOf) const
884 : {
885 : // Note, once the GlobalSegment is shared among instances, we will have to
886 : // take that sharing into account.
887 0 : return mallocSizeOf(this) + mallocSizeOf(tlsData_);
888 : }
|