1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/AsmJSLink.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1009 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/AsmJSLink.h" 1.11 + 1.12 +#include "mozilla/BinarySearch.h" 1.13 +#include "mozilla/PodOperations.h" 1.14 + 1.15 +#ifdef MOZ_VTUNE 1.16 +# include "vtune/VTuneWrapper.h" 1.17 +#endif 1.18 + 1.19 +#include "jscntxt.h" 1.20 +#include "jsmath.h" 1.21 +#include "jsprf.h" 1.22 +#include "jswrapper.h" 1.23 + 1.24 +#include "frontend/BytecodeCompiler.h" 1.25 +#include "jit/AsmJSModule.h" 1.26 +#include "jit/Ion.h" 1.27 +#include "jit/JitCommon.h" 1.28 +#ifdef JS_ION_PERF 1.29 +# include "jit/PerfSpewer.h" 1.30 +#endif 1.31 +#include "vm/StringBuffer.h" 1.32 + 1.33 +#include "jsobjinlines.h" 1.34 + 1.35 +using namespace js; 1.36 +using namespace js::jit; 1.37 + 1.38 +using mozilla::BinarySearch; 1.39 +using mozilla::IsNaN; 1.40 +using mozilla::PodZero; 1.41 + 1.42 +AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation) 1.43 +{ 1.44 + if (!activation || activation->isInterruptedSP()) { 1.45 + PodZero(this); 1.46 + JS_ASSERT(done()); 1.47 + return; 1.48 + } 1.49 + 1.50 + module_ = &activation->module(); 1.51 + sp_ = activation->exitSP(); 1.52 + 1.53 +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 1.54 + // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call 1.55 + // to C++. Since the call instruction pushes the return address, we know 1.56 + // that the return address is 1 word below exitSP. 1.57 + returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*)); 1.58 +#else 1.59 + // For calls to Ion/C++ on ARM, the *caller* pushes the return address on 1.60 + // the stack. For Ion, this is just part of the ABI. For C++, the return 1.61 + // address is explicitly pushed before the call since we cannot expect the 1.62 + // callee to immediately push lr. This means that exitSP points to the 1.63 + // return address. 1.64 + returnAddress_ = *(uint8_t**)sp_; 1.65 +#endif 1.66 + 1.67 + settle(); 1.68 +} 1.69 + 1.70 +struct GetCallSite 1.71 +{ 1.72 + const AsmJSModule &module; 1.73 + GetCallSite(const AsmJSModule &module) : module(module) {} 1.74 + uint32_t operator[](size_t index) const { 1.75 + return module.callSite(index).returnAddressOffset(); 1.76 + } 1.77 +}; 1.78 + 1.79 +void 1.80 +AsmJSFrameIterator::popFrame() 1.81 +{ 1.82 + // After adding stackDepth, sp points to the word before the return address, 1.83 + // on both ARM and x86/x64. 1.84 + sp_ += callsite_->stackDepth(); 1.85 + returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*)); 1.86 +} 1.87 + 1.88 +void 1.89 +AsmJSFrameIterator::settle() 1.90 +{ 1.91 + while (true) { 1.92 + uint32_t target = returnAddress_ - module_->codeBase(); 1.93 + size_t lowerBound = 0; 1.94 + size_t upperBound = module_->numCallSites(); 1.95 + 1.96 + size_t match; 1.97 + if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) { 1.98 + callsite_ = nullptr; 1.99 + return; 1.100 + } 1.101 + 1.102 + callsite_ = &module_->callSite(match); 1.103 + 1.104 + if (callsite_->isExit()) { 1.105 + popFrame(); 1.106 + continue; 1.107 + } 1.108 + 1.109 + if (callsite_->isEntry()) { 1.110 + callsite_ = nullptr; 1.111 + return; 1.112 + } 1.113 + 1.114 + JS_ASSERT(callsite_->isNormal()); 1.115 + return; 1.116 + } 1.117 +} 1.118 + 1.119 +JSAtom * 1.120 +AsmJSFrameIterator::functionDisplayAtom() const 1.121 +{ 1.122 + JS_ASSERT(!done()); 1.123 + return module_->functionName(callsite_->functionNameIndex()); 1.124 +} 1.125 + 1.126 +unsigned 1.127 +AsmJSFrameIterator::computeLine(uint32_t *column) const 1.128 +{ 1.129 + JS_ASSERT(!done()); 1.130 + if (column) 1.131 + *column = callsite_->column(); 1.132 + return callsite_->line(); 1.133 +} 1.134 + 1.135 +static bool 1.136 +CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj) 1.137 +{ 1.138 + ScopedJSDeletePtr<AsmJSModule> module; 1.139 + if (!moduleObj->module().clone(cx, &module)) 1.140 + return false; 1.141 + 1.142 + module->staticallyLink(cx); 1.143 + 1.144 + AsmJSModuleObject *newModuleObj = AsmJSModuleObject::create(cx, &module); 1.145 + if (!newModuleObj) 1.146 + return false; 1.147 + 1.148 + moduleObj.set(newModuleObj); 1.149 + return true; 1.150 +} 1.151 + 1.152 +static bool 1.153 +LinkFail(JSContext *cx, const char *str) 1.154 +{ 1.155 + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, 1.156 + nullptr, JSMSG_USE_ASM_LINK_FAIL, str); 1.157 + return false; 1.158 +} 1.159 + 1.160 +static bool 1.161 +GetDataProperty(JSContext *cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v) 1.162 +{ 1.163 + if (!objVal.isObject()) 1.164 + return LinkFail(cx, "accessing property of non-object"); 1.165 + 1.166 + Rooted<JSPropertyDescriptor> desc(cx); 1.167 + RootedObject obj(cx, &objVal.toObject()); 1.168 + RootedId id(cx, NameToId(field)); 1.169 + if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) 1.170 + return false; 1.171 + 1.172 + if (!desc.object()) 1.173 + return LinkFail(cx, "property not present on object"); 1.174 + 1.175 + if (desc.hasGetterOrSetterObject()) 1.176 + return LinkFail(cx, "property is not a data property"); 1.177 + 1.178 + v.set(desc.value()); 1.179 + return true; 1.180 +} 1.181 + 1.182 +static bool 1.183 +ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global, 1.184 + HandleValue importVal) 1.185 +{ 1.186 + JS_ASSERT(global.which() == AsmJSModule::Global::Variable); 1.187 + 1.188 + void *datum = module.globalVarIndexToGlobalDatum(global.varIndex()); 1.189 + 1.190 + switch (global.varInitKind()) { 1.191 + case AsmJSModule::Global::InitConstant: { 1.192 + const Value &v = global.varInitConstant(); 1.193 + switch (global.varInitCoercion()) { 1.194 + case AsmJS_ToInt32: 1.195 + *(int32_t *)datum = v.toInt32(); 1.196 + break; 1.197 + case AsmJS_ToNumber: 1.198 + *(double *)datum = v.toDouble(); 1.199 + break; 1.200 + case AsmJS_FRound: 1.201 + *(float *)datum = static_cast<float>(v.toDouble()); 1.202 + break; 1.203 + } 1.204 + break; 1.205 + } 1.206 + case AsmJSModule::Global::InitImport: { 1.207 + RootedPropertyName field(cx, global.varImportField()); 1.208 + RootedValue v(cx); 1.209 + if (!GetDataProperty(cx, importVal, field, &v)) 1.210 + return false; 1.211 + 1.212 + switch (global.varInitCoercion()) { 1.213 + case AsmJS_ToInt32: 1.214 + if (!ToInt32(cx, v, (int32_t *)datum)) 1.215 + return false; 1.216 + break; 1.217 + case AsmJS_ToNumber: 1.218 + if (!ToNumber(cx, v, (double *)datum)) 1.219 + return false; 1.220 + break; 1.221 + case AsmJS_FRound: 1.222 + if (!RoundFloat32(cx, v, (float *)datum)) 1.223 + return false; 1.224 + break; 1.225 + } 1.226 + break; 1.227 + } 1.228 + } 1.229 + 1.230 + return true; 1.231 +} 1.232 + 1.233 +static bool 1.234 +ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal, 1.235 + AutoObjectVector *ffis) 1.236 +{ 1.237 + RootedPropertyName field(cx, global.ffiField()); 1.238 + RootedValue v(cx); 1.239 + if (!GetDataProperty(cx, importVal, field, &v)) 1.240 + return false; 1.241 + 1.242 + if (!v.isObject() || !v.toObject().is<JSFunction>()) 1.243 + return LinkFail(cx, "FFI imports must be functions"); 1.244 + 1.245 + (*ffis)[global.ffiIndex()] = &v.toObject().as<JSFunction>(); 1.246 + return true; 1.247 +} 1.248 + 1.249 +static bool 1.250 +ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal, 1.251 + HandleValue bufferVal) 1.252 +{ 1.253 + RootedPropertyName field(cx, global.viewName()); 1.254 + RootedValue v(cx); 1.255 + if (!GetDataProperty(cx, globalVal, field, &v)) 1.256 + return false; 1.257 + 1.258 + if (!IsTypedArrayConstructor(v, global.viewType())) 1.259 + return LinkFail(cx, "bad typed array constructor"); 1.260 + 1.261 + return true; 1.262 +} 1.263 + 1.264 +static bool 1.265 +ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal) 1.266 +{ 1.267 + RootedValue v(cx); 1.268 + if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) 1.269 + return false; 1.270 + RootedPropertyName field(cx, global.mathName()); 1.271 + if (!GetDataProperty(cx, v, field, &v)) 1.272 + return false; 1.273 + 1.274 + Native native = nullptr; 1.275 + switch (global.mathBuiltinFunction()) { 1.276 + case AsmJSMathBuiltin_sin: native = math_sin; break; 1.277 + case AsmJSMathBuiltin_cos: native = math_cos; break; 1.278 + case AsmJSMathBuiltin_tan: native = math_tan; break; 1.279 + case AsmJSMathBuiltin_asin: native = math_asin; break; 1.280 + case AsmJSMathBuiltin_acos: native = math_acos; break; 1.281 + case AsmJSMathBuiltin_atan: native = math_atan; break; 1.282 + case AsmJSMathBuiltin_ceil: native = math_ceil; break; 1.283 + case AsmJSMathBuiltin_floor: native = math_floor; break; 1.284 + case AsmJSMathBuiltin_exp: native = math_exp; break; 1.285 + case AsmJSMathBuiltin_log: native = math_log; break; 1.286 + case AsmJSMathBuiltin_pow: native = js_math_pow; break; 1.287 + case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break; 1.288 + case AsmJSMathBuiltin_min: native = js_math_min; break; 1.289 + case AsmJSMathBuiltin_max: native = js_math_max; break; 1.290 + case AsmJSMathBuiltin_abs: native = js_math_abs; break; 1.291 + case AsmJSMathBuiltin_atan2: native = math_atan2; break; 1.292 + case AsmJSMathBuiltin_imul: native = math_imul; break; 1.293 + case AsmJSMathBuiltin_fround: native = math_fround; break; 1.294 + } 1.295 + 1.296 + if (!IsNativeFunction(v, native)) 1.297 + return LinkFail(cx, "bad Math.* builtin function"); 1.298 + 1.299 + return true; 1.300 +} 1.301 + 1.302 +static bool 1.303 +ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal) 1.304 +{ 1.305 + RootedPropertyName field(cx, global.constantName()); 1.306 + RootedValue v(cx, globalVal); 1.307 + 1.308 + if (global.constantKind() == AsmJSModule::Global::MathConstant) { 1.309 + if (!GetDataProperty(cx, v, cx->names().Math, &v)) 1.310 + return false; 1.311 + } 1.312 + 1.313 + if (!GetDataProperty(cx, v, field, &v)) 1.314 + return false; 1.315 + if (!v.isNumber()) 1.316 + return LinkFail(cx, "math / global constant value needs to be a number"); 1.317 + 1.318 + // NaN != NaN 1.319 + if (IsNaN(global.constantValue())) { 1.320 + if (!IsNaN(v.toNumber())) 1.321 + return LinkFail(cx, "global constant value needs to be NaN"); 1.322 + } else { 1.323 + if (v.toNumber() != global.constantValue()) 1.324 + return LinkFail(cx, "global constant value mismatch"); 1.325 + } 1.326 + 1.327 + return true; 1.328 +} 1.329 + 1.330 +static bool 1.331 +LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap) 1.332 +{ 1.333 + uint32_t heapLength = heap->byteLength(); 1.334 + if (!IsValidAsmJSHeapLength(heapLength)) { 1.335 + ScopedJSFreePtr<char> msg( 1.336 + JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next " 1.337 + "valid length is 0x%x", 1.338 + heapLength, 1.339 + RoundUpToNextValidAsmJSHeapLength(heapLength))); 1.340 + return LinkFail(cx, msg.get()); 1.341 + } 1.342 + 1.343 + // This check is sufficient without considering the size of the loaded datum because heap 1.344 + // loads and stores start on an aligned boundary and the heap byteLength has larger alignment. 1.345 + JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX); 1.346 + if (heapLength < module.minHeapLength()) { 1.347 + ScopedJSFreePtr<char> msg( 1.348 + JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the" 1.349 + "largest constant heap access offset rounded up to the next valid " 1.350 + "heap size).", 1.351 + heapLength, 1.352 + module.minHeapLength())); 1.353 + return LinkFail(cx, msg.get()); 1.354 + } 1.355 + 1.356 + if (!ArrayBufferObject::prepareForAsmJS(cx, heap)) 1.357 + return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use"); 1.358 + 1.359 + module.initHeap(heap, cx); 1.360 + return true; 1.361 +} 1.362 + 1.363 +static bool 1.364 +DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module) 1.365 +{ 1.366 + module.setIsDynamicallyLinked(); 1.367 + 1.368 + RootedValue globalVal(cx); 1.369 + if (args.length() > 0) 1.370 + globalVal = args[0]; 1.371 + 1.372 + RootedValue importVal(cx); 1.373 + if (args.length() > 1) 1.374 + importVal = args[1]; 1.375 + 1.376 + RootedValue bufferVal(cx); 1.377 + if (args.length() > 2) 1.378 + bufferVal = args[2]; 1.379 + 1.380 + Rooted<ArrayBufferObject*> heap(cx); 1.381 + if (module.hasArrayView()) { 1.382 + if (!IsTypedArrayBuffer(bufferVal)) 1.383 + return LinkFail(cx, "bad ArrayBuffer argument"); 1.384 + 1.385 + heap = &AsTypedArrayBuffer(bufferVal); 1.386 + if (!LinkModuleToHeap(cx, module, heap)) 1.387 + return false; 1.388 + } 1.389 + 1.390 + AutoObjectVector ffis(cx); 1.391 + if (!ffis.resize(module.numFFIs())) 1.392 + return false; 1.393 + 1.394 + for (unsigned i = 0; i < module.numGlobals(); i++) { 1.395 + AsmJSModule::Global &global = module.global(i); 1.396 + switch (global.which()) { 1.397 + case AsmJSModule::Global::Variable: 1.398 + if (!ValidateGlobalVariable(cx, module, global, importVal)) 1.399 + return false; 1.400 + break; 1.401 + case AsmJSModule::Global::FFI: 1.402 + if (!ValidateFFI(cx, global, importVal, &ffis)) 1.403 + return false; 1.404 + break; 1.405 + case AsmJSModule::Global::ArrayView: 1.406 + if (!ValidateArrayView(cx, global, globalVal, bufferVal)) 1.407 + return false; 1.408 + break; 1.409 + case AsmJSModule::Global::MathBuiltinFunction: 1.410 + if (!ValidateMathBuiltinFunction(cx, global, globalVal)) 1.411 + return false; 1.412 + break; 1.413 + case AsmJSModule::Global::Constant: 1.414 + if (!ValidateConstant(cx, global, globalVal)) 1.415 + return false; 1.416 + break; 1.417 + } 1.418 + } 1.419 + 1.420 + for (unsigned i = 0; i < module.numExits(); i++) 1.421 + module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>(); 1.422 + 1.423 + return true; 1.424 +} 1.425 + 1.426 +static const unsigned ASM_MODULE_SLOT = 0; 1.427 +static const unsigned ASM_EXPORT_INDEX_SLOT = 1; 1.428 + 1.429 +static unsigned 1.430 +FunctionToExportedFunctionIndex(HandleFunction fun) 1.431 +{ 1.432 + Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT); 1.433 + return v.toInt32(); 1.434 +} 1.435 + 1.436 +static const AsmJSModule::ExportedFunction & 1.437 +FunctionToExportedFunction(HandleFunction fun, AsmJSModule &module) 1.438 +{ 1.439 + unsigned funIndex = FunctionToExportedFunctionIndex(fun); 1.440 + return module.exportedFunction(funIndex); 1.441 +} 1.442 + 1.443 +static AsmJSModule & 1.444 +FunctionToEnclosingModule(HandleFunction fun) 1.445 +{ 1.446 + return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module(); 1.447 +} 1.448 + 1.449 +// The JSNative for the functions nested in an asm.js module. Calling this 1.450 +// native will trampoline into generated code. 1.451 +static bool 1.452 +CallAsmJS(JSContext *cx, unsigned argc, Value *vp) 1.453 +{ 1.454 + CallArgs callArgs = CallArgsFromVp(argc, vp); 1.455 + RootedFunction callee(cx, &callArgs.callee().as<JSFunction>()); 1.456 + 1.457 + // An asm.js function stores, in its extended slots: 1.458 + // - a pointer to the module from which it was returned 1.459 + // - its index in the ordered list of exported functions 1.460 + AsmJSModule &module = FunctionToEnclosingModule(callee); 1.461 + 1.462 + // An exported function points to the code as well as the exported 1.463 + // function's signature, which implies the dynamic coercions performed on 1.464 + // the arguments. 1.465 + const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module); 1.466 + 1.467 + // The calling convention for an external call into asm.js is to pass an 1.468 + // array of 8-byte values where each value contains either a coerced int32 1.469 + // (in the low word) or double value, with the coercions specified by the 1.470 + // asm.js signature. The external entry point unpacks this array into the 1.471 + // system-ABI-specified registers and stack memory and then calls into the 1.472 + // internal entry point. The return value is stored in the first element of 1.473 + // the array (which, therefore, must have length >= 1). 1.474 + 1.475 + js::Vector<uint64_t, 8> coercedArgs(cx); 1.476 + if (!coercedArgs.resize(Max<size_t>(1, func.numArgs()))) 1.477 + return false; 1.478 + 1.479 + RootedValue v(cx); 1.480 + for (unsigned i = 0; i < func.numArgs(); ++i) { 1.481 + v = i < callArgs.length() ? callArgs[i] : UndefinedValue(); 1.482 + switch (func.argCoercion(i)) { 1.483 + case AsmJS_ToInt32: 1.484 + if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i])) 1.485 + return false; 1.486 + break; 1.487 + case AsmJS_ToNumber: 1.488 + if (!ToNumber(cx, v, (double*)&coercedArgs[i])) 1.489 + return false; 1.490 + break; 1.491 + case AsmJS_FRound: 1.492 + if (!RoundFloat32(cx, v, (float *)&coercedArgs[i])) 1.493 + return false; 1.494 + break; 1.495 + } 1.496 + } 1.497 + 1.498 + // An asm.js module is specialized to its heap's base address and length 1.499 + // which is normally immutable except for the neuter operation that occurs 1.500 + // when an ArrayBuffer is transfered. Throw an internal error if we're 1.501 + // about to run with a neutered heap. 1.502 + if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) { 1.503 + js_ReportOverRecursed(cx); 1.504 + return false; 1.505 + } 1.506 + 1.507 + { 1.508 + // Push an AsmJSActivation to describe the asm.js frames we're about to 1.509 + // push when running this module. Additionally, push a JitActivation so 1.510 + // that the optimized asm.js-to-Ion FFI call path (which we want to be 1.511 + // very fast) can avoid doing so. The JitActivation is marked as 1.512 + // inactive so stack iteration will skip over it. 1.513 + AsmJSActivation activation(cx, module); 1.514 + JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false); 1.515 + 1.516 + // Call the per-exported-function trampoline created by GenerateEntry. 1.517 + AsmJSModule::CodePtr enter = module.entryTrampoline(func); 1.518 + if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData())) 1.519 + return false; 1.520 + } 1.521 + 1.522 + switch (func.returnType()) { 1.523 + case AsmJSModule::Return_Void: 1.524 + callArgs.rval().set(UndefinedValue()); 1.525 + break; 1.526 + case AsmJSModule::Return_Int32: 1.527 + callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); 1.528 + break; 1.529 + case AsmJSModule::Return_Double: 1.530 + callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0])); 1.531 + break; 1.532 + } 1.533 + 1.534 + return true; 1.535 +} 1.536 + 1.537 +static JSFunction * 1.538 +NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func, 1.539 + HandleObject moduleObj, unsigned exportIndex) 1.540 +{ 1.541 + RootedPropertyName name(cx, func.name()); 1.542 + JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(), 1.543 + JSFunction::NATIVE_FUN, cx->global(), name, 1.544 + JSFunction::ExtendedFinalizeKind); 1.545 + if (!fun) 1.546 + return nullptr; 1.547 + 1.548 + fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj)); 1.549 + fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex)); 1.550 + return fun; 1.551 +} 1.552 + 1.553 +static bool 1.554 +HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name) 1.555 +{ 1.556 + if (cx->isExceptionPending()) 1.557 + return false; 1.558 + 1.559 + uint32_t begin = module.offsetToEndOfUseAsm(); 1.560 + uint32_t end = module.funcEndBeforeCurly(); 1.561 + Rooted<JSFlatString*> src(cx, module.scriptSource()->substring(cx, begin, end)); 1.562 + if (!src) 1.563 + return false; 1.564 + 1.565 + RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, 1.566 + cx->global(), name, JSFunction::FinalizeKind, 1.567 + TenuredObject)); 1.568 + if (!fun) 1.569 + return false; 1.570 + 1.571 + AutoNameVector formals(cx); 1.572 + formals.reserve(3); 1.573 + if (module.globalArgumentName()) 1.574 + formals.infallibleAppend(module.globalArgumentName()); 1.575 + if (module.importArgumentName()) 1.576 + formals.infallibleAppend(module.importArgumentName()); 1.577 + if (module.bufferArgumentName()) 1.578 + formals.infallibleAppend(module.bufferArgumentName()); 1.579 + 1.580 + CompileOptions options(cx); 1.581 + options.setOriginPrincipals(module.scriptSource()->originPrincipals()) 1.582 + .setCompileAndGo(false) 1.583 + .setNoScriptRval(false); 1.584 + 1.585 + SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership); 1.586 + if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf)) 1.587 + return false; 1.588 + 1.589 + // Call the function we just recompiled. 1.590 + args.setCallee(ObjectValue(*fun)); 1.591 + return Invoke(cx, args); 1.592 +} 1.593 + 1.594 +#ifdef MOZ_VTUNE 1.595 +static bool 1.596 +SendFunctionsToVTune(JSContext *cx, AsmJSModule &module) 1.597 +{ 1.598 + uint8_t *base = module.codeBase(); 1.599 + 1.600 + for (unsigned i = 0; i < module.numProfiledFunctions(); i++) { 1.601 + const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i); 1.602 + 1.603 + uint8_t *start = base + func.pod.startCodeOffset; 1.604 + uint8_t *end = base + func.pod.endCodeOffset; 1.605 + JS_ASSERT(end >= start); 1.606 + 1.607 + unsigned method_id = iJIT_GetNewMethodID(); 1.608 + if (method_id == 0) 1.609 + return false; 1.610 + 1.611 + JSAutoByteString bytes; 1.612 + const char *method_name = AtomToPrintableString(cx, func.name, &bytes); 1.613 + if (!method_name) 1.614 + return false; 1.615 + 1.616 + iJIT_Method_Load method; 1.617 + method.method_id = method_id; 1.618 + method.method_name = const_cast<char *>(method_name); 1.619 + method.method_load_address = (void *)start; 1.620 + method.method_size = unsigned(end - start); 1.621 + method.line_number_size = 0; 1.622 + method.line_number_table = nullptr; 1.623 + method.class_id = 0; 1.624 + method.class_file_name = nullptr; 1.625 + method.source_file_name = nullptr; 1.626 + 1.627 + iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void *)&method); 1.628 + } 1.629 + 1.630 + return true; 1.631 +} 1.632 +#endif 1.633 + 1.634 +#ifdef JS_ION_PERF 1.635 +static bool 1.636 +SendFunctionsToPerf(JSContext *cx, AsmJSModule &module) 1.637 +{ 1.638 + if (!PerfFuncEnabled()) 1.639 + return true; 1.640 + 1.641 + uintptr_t base = (uintptr_t) module.codeBase(); 1.642 + const char *filename = module.scriptSource()->filename(); 1.643 + 1.644 + for (unsigned i = 0; i < module.numProfiledFunctions(); i++) { 1.645 + const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i); 1.646 + uintptr_t start = base + (unsigned long) func.pod.startCodeOffset; 1.647 + uintptr_t end = base + (unsigned long) func.pod.endCodeOffset; 1.648 + JS_ASSERT(end >= start); 1.649 + size_t size = end - start; 1.650 + 1.651 + JSAutoByteString bytes; 1.652 + const char *name = AtomToPrintableString(cx, func.name, &bytes); 1.653 + if (!name) 1.654 + return false; 1.655 + 1.656 + writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno, 1.657 + func.pod.columnIndex, name); 1.658 + } 1.659 + 1.660 + return true; 1.661 +} 1.662 + 1.663 +static bool 1.664 +SendBlocksToPerf(JSContext *cx, AsmJSModule &module) 1.665 +{ 1.666 + if (!PerfBlockEnabled()) 1.667 + return true; 1.668 + 1.669 + unsigned long funcBaseAddress = (unsigned long) module.codeBase(); 1.670 + const char *filename = module.scriptSource()->filename(); 1.671 + 1.672 + for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) { 1.673 + const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i); 1.674 + 1.675 + size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset; 1.676 + 1.677 + JSAutoByteString bytes; 1.678 + const char *name = AtomToPrintableString(cx, func.name, &bytes); 1.679 + if (!name) 1.680 + return false; 1.681 + 1.682 + writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset, 1.683 + func.endInlineCodeOffset, size, filename, name, func.blocks); 1.684 + } 1.685 + 1.686 + return true; 1.687 +} 1.688 +#endif 1.689 + 1.690 +static bool 1.691 +SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module) 1.692 +{ 1.693 +#if defined(MOZ_VTUNE) 1.694 + if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module)) 1.695 + return false; 1.696 +#endif 1.697 + 1.698 +#if defined(JS_ION_PERF) 1.699 + if (module.numExportedFunctions() > 0) { 1.700 + size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0)); 1.701 + writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode); 1.702 + } 1.703 + if (!SendBlocksToPerf(cx, module)) 1.704 + return false; 1.705 + if (!SendFunctionsToPerf(cx, module)) 1.706 + return false; 1.707 +#endif 1.708 + 1.709 + return true; 1.710 +} 1.711 + 1.712 + 1.713 +static JSObject * 1.714 +CreateExportObject(JSContext *cx, Handle<AsmJSModuleObject*> moduleObj) 1.715 +{ 1.716 + AsmJSModule &module = moduleObj->module(); 1.717 + 1.718 + if (module.numExportedFunctions() == 1) { 1.719 + const AsmJSModule::ExportedFunction &func = module.exportedFunction(0); 1.720 + if (!func.maybeFieldName()) 1.721 + return NewExportedFunction(cx, func, moduleObj, 0); 1.722 + } 1.723 + 1.724 + gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions()); 1.725 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind)); 1.726 + if (!obj) 1.727 + return nullptr; 1.728 + 1.729 + for (unsigned i = 0; i < module.numExportedFunctions(); i++) { 1.730 + const AsmJSModule::ExportedFunction &func = module.exportedFunction(i); 1.731 + 1.732 + RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i)); 1.733 + if (!fun) 1.734 + return nullptr; 1.735 + 1.736 + JS_ASSERT(func.maybeFieldName() != nullptr); 1.737 + RootedId id(cx, NameToId(func.maybeFieldName())); 1.738 + RootedValue val(cx, ObjectValue(*fun)); 1.739 + if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE)) 1.740 + return nullptr; 1.741 + } 1.742 + 1.743 + return obj; 1.744 +} 1.745 + 1.746 +static const unsigned MODULE_FUN_SLOT = 0; 1.747 + 1.748 +static AsmJSModuleObject & 1.749 +ModuleFunctionToModuleObject(JSFunction *fun) 1.750 +{ 1.751 + return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>(); 1.752 +} 1.753 + 1.754 +// Implements the semantics of an asm.js module function that has been successfully validated. 1.755 +static bool 1.756 +LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp) 1.757 +{ 1.758 + CallArgs args = CallArgsFromVp(argc, vp); 1.759 + 1.760 + // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended 1.761 + // function and stores its module in an extended slot. 1.762 + RootedFunction fun(cx, &args.callee().as<JSFunction>()); 1.763 + Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun)); 1.764 + 1.765 + // All ICache flushing of the module being linked has been inhibited under the 1.766 + // assumption that the module is flushed after dynamic linking (when the last code 1.767 + // mutation occurs). Thus, enter an AutoFlushICache context for the entire module 1.768 + // now. The module range is set below. 1.769 + AutoFlushICache afc("LinkAsmJS"); 1.770 + 1.771 + // When a module is linked, it is dynamically specialized to the given 1.772 + // arguments (buffer, ffis). Thus, if the module is linked again (it is just 1.773 + // a function so it can be called multiple times), we need to clone a new 1.774 + // module. 1.775 + if (moduleObj->module().isDynamicallyLinked()) { 1.776 + if (!CloneModule(cx, &moduleObj)) 1.777 + return false; 1.778 + } else { 1.779 + // CloneModule already calls setAutoFlushICacheRange internally before patching 1.780 + // the cloned module, so avoid calling twice. 1.781 + moduleObj->module().setAutoFlushICacheRange(); 1.782 + } 1.783 + 1.784 + AsmJSModule &module = moduleObj->module(); 1.785 + 1.786 + // Link the module by performing the link-time validation checks in the 1.787 + // asm.js spec and then patching the generated module to associate it with 1.788 + // the given heap (ArrayBuffer) and a new global data segment (the closure 1.789 + // state shared by the inner asm.js functions). 1.790 + if (!DynamicallyLinkModule(cx, args, module)) { 1.791 + // Linking failed, so reparse the entire asm.js module from scratch to 1.792 + // get normal interpreted bytecode which we can simply Invoke. Very slow. 1.793 + RootedPropertyName name(cx, fun->name()); 1.794 + return HandleDynamicLinkFailure(cx, args, module, name); 1.795 + } 1.796 + 1.797 + // Notify profilers so that asm.js generated code shows up with JS function 1.798 + // names and lines in native (i.e., not SPS) profilers. 1.799 + if (!SendModuleToAttachedProfiler(cx, module)) 1.800 + return false; 1.801 + 1.802 + // Link-time validation succeeded, so wrap all the exported functions with 1.803 + // CallAsmJS builtins that trampoline into the generated code. 1.804 + JSObject *obj = CreateExportObject(cx, moduleObj); 1.805 + if (!obj) 1.806 + return false; 1.807 + 1.808 + args.rval().set(ObjectValue(*obj)); 1.809 + return true; 1.810 +} 1.811 + 1.812 +JSFunction * 1.813 +js::NewAsmJSModuleFunction(ExclusiveContext *cx, JSFunction *origFun, HandleObject moduleObj) 1.814 +{ 1.815 + RootedPropertyName name(cx, origFun->name()); 1.816 + 1.817 + JSFunction::Flags flags = origFun->isLambda() ? JSFunction::NATIVE_LAMBDA_FUN 1.818 + : JSFunction::NATIVE_FUN; 1.819 + JSFunction *moduleFun = NewFunction(cx, NullPtr(), LinkAsmJS, origFun->nargs(), 1.820 + flags, NullPtr(), name, 1.821 + JSFunction::ExtendedFinalizeKind, TenuredObject); 1.822 + if (!moduleFun) 1.823 + return nullptr; 1.824 + 1.825 + moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj)); 1.826 + return moduleFun; 1.827 +} 1.828 + 1.829 +bool 1.830 +js::IsAsmJSModuleNative(js::Native native) 1.831 +{ 1.832 + return native == LinkAsmJS; 1.833 +} 1.834 + 1.835 +static bool 1.836 +IsMaybeWrappedNativeFunction(const Value &v, Native native, JSFunction **fun = nullptr) 1.837 +{ 1.838 + if (!v.isObject()) 1.839 + return false; 1.840 + 1.841 + JSObject *obj = CheckedUnwrap(&v.toObject()); 1.842 + if (!obj) 1.843 + return false; 1.844 + 1.845 + if (!obj->is<JSFunction>()) 1.846 + return false; 1.847 + 1.848 + if (fun) 1.849 + *fun = &obj->as<JSFunction>(); 1.850 + 1.851 + return obj->as<JSFunction>().maybeNative() == native; 1.852 +} 1.853 + 1.854 +bool 1.855 +js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp) 1.856 +{ 1.857 + CallArgs args = CallArgsFromVp(argc, vp); 1.858 + bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS); 1.859 + args.rval().set(BooleanValue(rval)); 1.860 + return true; 1.861 +} 1.862 + 1.863 +bool 1.864 +js::IsAsmJSModule(HandleFunction fun) 1.865 +{ 1.866 + return fun->isNative() && fun->maybeNative() == LinkAsmJS; 1.867 +} 1.868 + 1.869 +JSString * 1.870 +js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda) 1.871 +{ 1.872 + AsmJSModule &module = ModuleFunctionToModuleObject(fun).module(); 1.873 + 1.874 + uint32_t begin = module.funcStart(); 1.875 + uint32_t end = module.funcEndAfterCurly(); 1.876 + ScriptSource *source = module.scriptSource(); 1.877 + StringBuffer out(cx); 1.878 + 1.879 + // Whether the function has been created with a Function ctor 1.880 + bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded(); 1.881 + 1.882 + if (addParenToLambda && fun->isLambda() && !out.append("(")) 1.883 + return nullptr; 1.884 + 1.885 + if (!out.append("function ")) 1.886 + return nullptr; 1.887 + 1.888 + if (fun->atom() && !out.append(fun->atom())) 1.889 + return nullptr; 1.890 + 1.891 + if (funCtor) { 1.892 + // Functions created with the function constructor don't have arguments in their source. 1.893 + if (!out.append("(")) 1.894 + return nullptr; 1.895 + 1.896 + if (PropertyName *argName = module.globalArgumentName()) { 1.897 + if (!out.append(argName->chars(), argName->length())) 1.898 + return nullptr; 1.899 + } 1.900 + if (PropertyName *argName = module.importArgumentName()) { 1.901 + if (!out.append(", ") || !out.append(argName->chars(), argName->length())) 1.902 + return nullptr; 1.903 + } 1.904 + if (PropertyName *argName = module.bufferArgumentName()) { 1.905 + if (!out.append(", ") || !out.append(argName->chars(), argName->length())) 1.906 + return nullptr; 1.907 + } 1.908 + 1.909 + if (!out.append(") {\n")) 1.910 + return nullptr; 1.911 + } 1.912 + 1.913 + Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); 1.914 + if (!src) 1.915 + return nullptr; 1.916 + 1.917 + if (module.strict()) { 1.918 + // We need to add "use strict" in the body right after the opening 1.919 + // brace. 1.920 + size_t bodyStart = 0, bodyEnd; 1.921 + 1.922 + // No need to test for functions created with the Function ctor as 1.923 + // these doesn't implicitly inherit the "use strict" context. Strict mode is 1.924 + // enabled for functions created with the Function ctor only if they begin with 1.925 + // the "use strict" directive, but these functions won't validate as asm.js 1.926 + // modules. 1.927 + 1.928 + ConstTwoByteChars chars(src->chars(), src->length()); 1.929 + if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd)) 1.930 + return nullptr; 1.931 + 1.932 + if (!out.append(chars, bodyStart) || 1.933 + !out.append("\n\"use strict\";\n") || 1.934 + !out.append(chars + bodyStart, src->length() - bodyStart)) 1.935 + { 1.936 + return nullptr; 1.937 + } 1.938 + } else { 1.939 + if (!out.append(src->chars(), src->length())) 1.940 + return nullptr; 1.941 + } 1.942 + 1.943 + if (funCtor && !out.append("\n}")) 1.944 + return nullptr; 1.945 + 1.946 + if (addParenToLambda && fun->isLambda() && !out.append(")")) 1.947 + return nullptr; 1.948 + 1.949 + return out.finishString(); 1.950 +} 1.951 + 1.952 +bool 1.953 +js::IsAsmJSModuleLoadedFromCache(JSContext *cx, unsigned argc, Value *vp) 1.954 +{ 1.955 + CallArgs args = CallArgsFromVp(argc, vp); 1.956 + 1.957 + JSFunction *fun; 1.958 + if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) { 1.959 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL, 1.960 + "argument passed to isAsmJSModuleLoadedFromCache is not a " 1.961 + "validated asm.js module"); 1.962 + return false; 1.963 + } 1.964 + 1.965 + bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().loadedFromCache(); 1.966 + 1.967 + args.rval().set(BooleanValue(loadedFromCache)); 1.968 + return true; 1.969 +} 1.970 + 1.971 +bool 1.972 +js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp) 1.973 +{ 1.974 + CallArgs args = CallArgsFromVp(argc, vp); 1.975 + bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS); 1.976 + args.rval().set(BooleanValue(rval)); 1.977 + return true; 1.978 +} 1.979 + 1.980 +bool 1.981 +js::IsAsmJSFunction(HandleFunction fun) 1.982 +{ 1.983 + return fun->isNative() && fun->maybeNative() == CallAsmJS; 1.984 +} 1.985 + 1.986 +JSString * 1.987 +js::AsmJSFunctionToString(JSContext *cx, HandleFunction fun) 1.988 +{ 1.989 + AsmJSModule &module = FunctionToEnclosingModule(fun); 1.990 + const AsmJSModule::ExportedFunction &f = FunctionToExportedFunction(fun, module); 1.991 + uint32_t begin = module.funcStart() + f.startOffsetInModule(); 1.992 + uint32_t end = module.funcStart() + f.endOffsetInModule(); 1.993 + 1.994 + ScriptSource *source = module.scriptSource(); 1.995 + StringBuffer out(cx); 1.996 + 1.997 + // asm.js functions cannot have been created with a Function constructor 1.998 + // as they belong within a module. 1.999 + JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded())); 1.1000 + 1.1001 + if (!out.append("function ")) 1.1002 + return nullptr; 1.1003 + 1.1004 + Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end)); 1.1005 + if (!src) 1.1006 + return nullptr; 1.1007 + 1.1008 + if (!out.append(src->chars(), src->length())) 1.1009 + return nullptr; 1.1010 + 1.1011 + return out.finishString(); 1.1012 +}