js/src/jit/AsmJSLink.cpp

changeset 0
6474c204b198
     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 +}

mercurial