js/src/jit/AsmJSLink.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/AsmJSLink.h"
     9 #include "mozilla/BinarySearch.h"
    10 #include "mozilla/PodOperations.h"
    12 #ifdef MOZ_VTUNE
    13 # include "vtune/VTuneWrapper.h"
    14 #endif
    16 #include "jscntxt.h"
    17 #include "jsmath.h"
    18 #include "jsprf.h"
    19 #include "jswrapper.h"
    21 #include "frontend/BytecodeCompiler.h"
    22 #include "jit/AsmJSModule.h"
    23 #include "jit/Ion.h"
    24 #include "jit/JitCommon.h"
    25 #ifdef JS_ION_PERF
    26 # include "jit/PerfSpewer.h"
    27 #endif
    28 #include "vm/StringBuffer.h"
    30 #include "jsobjinlines.h"
    32 using namespace js;
    33 using namespace js::jit;
    35 using mozilla::BinarySearch;
    36 using mozilla::IsNaN;
    37 using mozilla::PodZero;
    39 AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
    40 {
    41     if (!activation || activation->isInterruptedSP()) {
    42         PodZero(this);
    43         JS_ASSERT(done());
    44         return;
    45     }
    47     module_ = &activation->module();
    48     sp_ = activation->exitSP();
    50 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
    51     // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
    52     // to C++. Since the call instruction pushes the return address, we know
    53     // that the return address is 1 word below exitSP.
    54     returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
    55 #else
    56     // For calls to Ion/C++ on ARM, the *caller* pushes the return address on
    57     // the stack. For Ion, this is just part of the ABI. For C++, the return
    58     // address is explicitly pushed before the call since we cannot expect the
    59     // callee to immediately push lr. This means that exitSP points to the
    60     // return address.
    61     returnAddress_ = *(uint8_t**)sp_;
    62 #endif
    64     settle();
    65 }
    67 struct GetCallSite
    68 {
    69     const AsmJSModule &module;
    70     GetCallSite(const AsmJSModule &module) : module(module) {}
    71     uint32_t operator[](size_t index) const {
    72         return module.callSite(index).returnAddressOffset();
    73     }
    74 };
    76 void
    77 AsmJSFrameIterator::popFrame()
    78 {
    79     // After adding stackDepth, sp points to the word before the return address,
    80     // on both ARM and x86/x64.
    81     sp_ += callsite_->stackDepth();
    82     returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
    83 }
    85 void
    86 AsmJSFrameIterator::settle()
    87 {
    88     while (true) {
    89         uint32_t target = returnAddress_ - module_->codeBase();
    90         size_t lowerBound = 0;
    91         size_t upperBound = module_->numCallSites();
    93         size_t match;
    94         if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
    95             callsite_ = nullptr;
    96             return;
    97         }
    99         callsite_ = &module_->callSite(match);
   101         if (callsite_->isExit()) {
   102             popFrame();
   103             continue;
   104         }
   106         if (callsite_->isEntry()) {
   107             callsite_ = nullptr;
   108             return;
   109         }
   111         JS_ASSERT(callsite_->isNormal());
   112         return;
   113     }
   114 }
   116 JSAtom *
   117 AsmJSFrameIterator::functionDisplayAtom() const
   118 {
   119     JS_ASSERT(!done());
   120     return module_->functionName(callsite_->functionNameIndex());
   121 }
   123 unsigned
   124 AsmJSFrameIterator::computeLine(uint32_t *column) const
   125 {
   126     JS_ASSERT(!done());
   127     if (column)
   128         *column = callsite_->column();
   129     return callsite_->line();
   130 }
   132 static bool
   133 CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
   134 {
   135     ScopedJSDeletePtr<AsmJSModule> module;
   136     if (!moduleObj->module().clone(cx, &module))
   137         return false;
   139     module->staticallyLink(cx);
   141     AsmJSModuleObject *newModuleObj = AsmJSModuleObject::create(cx, &module);
   142     if (!newModuleObj)
   143         return false;
   145     moduleObj.set(newModuleObj);
   146     return true;
   147 }
   149 static bool
   150 LinkFail(JSContext *cx, const char *str)
   151 {
   152     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
   153                                  nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
   154     return false;
   155 }
   157 static bool
   158 GetDataProperty(JSContext *cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
   159 {
   160     if (!objVal.isObject())
   161         return LinkFail(cx, "accessing property of non-object");
   163     Rooted<JSPropertyDescriptor> desc(cx);
   164     RootedObject obj(cx, &objVal.toObject());
   165     RootedId id(cx, NameToId(field));
   166     if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
   167         return false;
   169     if (!desc.object())
   170         return LinkFail(cx, "property not present on object");
   172     if (desc.hasGetterOrSetterObject())
   173         return LinkFail(cx, "property is not a data property");
   175     v.set(desc.value());
   176     return true;
   177 }
   179 static bool
   180 ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
   181                        HandleValue importVal)
   182 {
   183     JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
   185     void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());
   187     switch (global.varInitKind()) {
   188       case AsmJSModule::Global::InitConstant: {
   189         const Value &v = global.varInitConstant();
   190         switch (global.varInitCoercion()) {
   191           case AsmJS_ToInt32:
   192             *(int32_t *)datum = v.toInt32();
   193             break;
   194           case AsmJS_ToNumber:
   195             *(double *)datum = v.toDouble();
   196             break;
   197           case AsmJS_FRound:
   198             *(float *)datum = static_cast<float>(v.toDouble());
   199             break;
   200         }
   201         break;
   202       }
   203       case AsmJSModule::Global::InitImport: {
   204         RootedPropertyName field(cx, global.varImportField());
   205         RootedValue v(cx);
   206         if (!GetDataProperty(cx, importVal, field, &v))
   207             return false;
   209         switch (global.varInitCoercion()) {
   210           case AsmJS_ToInt32:
   211             if (!ToInt32(cx, v, (int32_t *)datum))
   212                 return false;
   213             break;
   214           case AsmJS_ToNumber:
   215             if (!ToNumber(cx, v, (double *)datum))
   216                 return false;
   217             break;
   218           case AsmJS_FRound:
   219             if (!RoundFloat32(cx, v, (float *)datum))
   220                 return false;
   221             break;
   222         }
   223         break;
   224       }
   225     }
   227     return true;
   228 }
   230 static bool
   231 ValidateFFI(JSContext *cx, AsmJSModule::Global &global, HandleValue importVal,
   232             AutoObjectVector *ffis)
   233 {
   234     RootedPropertyName field(cx, global.ffiField());
   235     RootedValue v(cx);
   236     if (!GetDataProperty(cx, importVal, field, &v))
   237         return false;
   239     if (!v.isObject() || !v.toObject().is<JSFunction>())
   240         return LinkFail(cx, "FFI imports must be functions");
   242     (*ffis)[global.ffiIndex()] = &v.toObject().as<JSFunction>();
   243     return true;
   244 }
   246 static bool
   247 ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
   248                   HandleValue bufferVal)
   249 {
   250     RootedPropertyName field(cx, global.viewName());
   251     RootedValue v(cx);
   252     if (!GetDataProperty(cx, globalVal, field, &v))
   253         return false;
   255     if (!IsTypedArrayConstructor(v, global.viewType()))
   256         return LinkFail(cx, "bad typed array constructor");
   258     return true;
   259 }
   261 static bool
   262 ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
   263 {
   264     RootedValue v(cx);
   265     if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
   266         return false;
   267     RootedPropertyName field(cx, global.mathName());
   268     if (!GetDataProperty(cx, v, field, &v))
   269         return false;
   271     Native native = nullptr;
   272     switch (global.mathBuiltinFunction()) {
   273       case AsmJSMathBuiltin_sin: native = math_sin; break;
   274       case AsmJSMathBuiltin_cos: native = math_cos; break;
   275       case AsmJSMathBuiltin_tan: native = math_tan; break;
   276       case AsmJSMathBuiltin_asin: native = math_asin; break;
   277       case AsmJSMathBuiltin_acos: native = math_acos; break;
   278       case AsmJSMathBuiltin_atan: native = math_atan; break;
   279       case AsmJSMathBuiltin_ceil: native = math_ceil; break;
   280       case AsmJSMathBuiltin_floor: native = math_floor; break;
   281       case AsmJSMathBuiltin_exp: native = math_exp; break;
   282       case AsmJSMathBuiltin_log: native = math_log; break;
   283       case AsmJSMathBuiltin_pow: native = js_math_pow; break;
   284       case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break;
   285       case AsmJSMathBuiltin_min: native = js_math_min; break;
   286       case AsmJSMathBuiltin_max: native = js_math_max; break;
   287       case AsmJSMathBuiltin_abs: native = js_math_abs; break;
   288       case AsmJSMathBuiltin_atan2: native = math_atan2; break;
   289       case AsmJSMathBuiltin_imul: native = math_imul; break;
   290       case AsmJSMathBuiltin_fround: native = math_fround; break;
   291     }
   293     if (!IsNativeFunction(v, native))
   294         return LinkFail(cx, "bad Math.* builtin function");
   296     return true;
   297 }
   299 static bool
   300 ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
   301 {
   302     RootedPropertyName field(cx, global.constantName());
   303     RootedValue v(cx, globalVal);
   305     if (global.constantKind() == AsmJSModule::Global::MathConstant) {
   306         if (!GetDataProperty(cx, v, cx->names().Math, &v))
   307             return false;
   308     }
   310     if (!GetDataProperty(cx, v, field, &v))
   311         return false;
   312     if (!v.isNumber())
   313         return LinkFail(cx, "math / global constant value needs to be a number");
   315     // NaN != NaN
   316     if (IsNaN(global.constantValue())) {
   317         if (!IsNaN(v.toNumber()))
   318             return LinkFail(cx, "global constant value needs to be NaN");
   319     } else {
   320         if (v.toNumber() != global.constantValue())
   321             return LinkFail(cx, "global constant value mismatch");
   322     }
   324     return true;
   325 }
   327 static bool
   328 LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
   329 {
   330     uint32_t heapLength = heap->byteLength();
   331     if (!IsValidAsmJSHeapLength(heapLength)) {
   332         ScopedJSFreePtr<char> msg(
   333             JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
   334                         "valid length is 0x%x",
   335                         heapLength,
   336                         RoundUpToNextValidAsmJSHeapLength(heapLength)));
   337         return LinkFail(cx, msg.get());
   338     }
   340     // This check is sufficient without considering the size of the loaded datum because heap
   341     // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
   342     JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
   343     if (heapLength < module.minHeapLength()) {
   344         ScopedJSFreePtr<char> msg(
   345             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
   346                         "largest constant heap access offset rounded up to the next valid "
   347                         "heap size).",
   348                         heapLength,
   349                         module.minHeapLength()));
   350         return LinkFail(cx, msg.get());
   351     }
   353     if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
   354         return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
   356     module.initHeap(heap, cx);
   357     return true;
   358 }
   360 static bool
   361 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
   362 {
   363     module.setIsDynamicallyLinked();
   365     RootedValue globalVal(cx);
   366     if (args.length() > 0)
   367         globalVal = args[0];
   369     RootedValue importVal(cx);
   370     if (args.length() > 1)
   371         importVal = args[1];
   373     RootedValue bufferVal(cx);
   374     if (args.length() > 2)
   375         bufferVal = args[2];
   377     Rooted<ArrayBufferObject*> heap(cx);
   378     if (module.hasArrayView()) {
   379         if (!IsTypedArrayBuffer(bufferVal))
   380             return LinkFail(cx, "bad ArrayBuffer argument");
   382         heap = &AsTypedArrayBuffer(bufferVal);
   383         if (!LinkModuleToHeap(cx, module, heap))
   384             return false;
   385     }
   387     AutoObjectVector ffis(cx);
   388     if (!ffis.resize(module.numFFIs()))
   389         return false;
   391     for (unsigned i = 0; i < module.numGlobals(); i++) {
   392         AsmJSModule::Global &global = module.global(i);
   393         switch (global.which()) {
   394           case AsmJSModule::Global::Variable:
   395             if (!ValidateGlobalVariable(cx, module, global, importVal))
   396                 return false;
   397             break;
   398           case AsmJSModule::Global::FFI:
   399             if (!ValidateFFI(cx, global, importVal, &ffis))
   400                 return false;
   401             break;
   402           case AsmJSModule::Global::ArrayView:
   403             if (!ValidateArrayView(cx, global, globalVal, bufferVal))
   404                 return false;
   405             break;
   406           case AsmJSModule::Global::MathBuiltinFunction:
   407             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
   408                 return false;
   409             break;
   410           case AsmJSModule::Global::Constant:
   411             if (!ValidateConstant(cx, global, globalVal))
   412                 return false;
   413             break;
   414         }
   415     }
   417     for (unsigned i = 0; i < module.numExits(); i++)
   418         module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
   420     return true;
   421 }
   423 static const unsigned ASM_MODULE_SLOT = 0;
   424 static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
   426 static unsigned
   427 FunctionToExportedFunctionIndex(HandleFunction fun)
   428 {
   429     Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
   430     return v.toInt32();
   431 }
   433 static const AsmJSModule::ExportedFunction &
   434 FunctionToExportedFunction(HandleFunction fun, AsmJSModule &module)
   435 {
   436     unsigned funIndex = FunctionToExportedFunctionIndex(fun);
   437     return module.exportedFunction(funIndex);
   438 }
   440 static AsmJSModule &
   441 FunctionToEnclosingModule(HandleFunction fun)
   442 {
   443     return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
   444 }
   446 // The JSNative for the functions nested in an asm.js module. Calling this
   447 // native will trampoline into generated code.
   448 static bool
   449 CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
   450 {
   451     CallArgs callArgs = CallArgsFromVp(argc, vp);
   452     RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());
   454     // An asm.js function stores, in its extended slots:
   455     //  - a pointer to the module from which it was returned
   456     //  - its index in the ordered list of exported functions
   457     AsmJSModule &module = FunctionToEnclosingModule(callee);
   459     // An exported function points to the code as well as the exported
   460     // function's signature, which implies the dynamic coercions performed on
   461     // the arguments.
   462     const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);
   464     // The calling convention for an external call into asm.js is to pass an
   465     // array of 8-byte values where each value contains either a coerced int32
   466     // (in the low word) or double value, with the coercions specified by the
   467     // asm.js signature. The external entry point unpacks this array into the
   468     // system-ABI-specified registers and stack memory and then calls into the
   469     // internal entry point. The return value is stored in the first element of
   470     // the array (which, therefore, must have length >= 1).
   472     js::Vector<uint64_t, 8> coercedArgs(cx);
   473     if (!coercedArgs.resize(Max<size_t>(1, func.numArgs())))
   474         return false;
   476     RootedValue v(cx);
   477     for (unsigned i = 0; i < func.numArgs(); ++i) {
   478         v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
   479         switch (func.argCoercion(i)) {
   480           case AsmJS_ToInt32:
   481             if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
   482                 return false;
   483             break;
   484           case AsmJS_ToNumber:
   485             if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
   486                 return false;
   487             break;
   488           case AsmJS_FRound:
   489             if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
   490                 return false;
   491             break;
   492         }
   493     }
   495     // An asm.js module is specialized to its heap's base address and length
   496     // which is normally immutable except for the neuter operation that occurs
   497     // when an ArrayBuffer is transfered. Throw an internal error if we're
   498     // about to run with a neutered heap.
   499     if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
   500         js_ReportOverRecursed(cx);
   501         return false;
   502     }
   504     {
   505         // Push an AsmJSActivation to describe the asm.js frames we're about to
   506         // push when running this module. Additionally, push a JitActivation so
   507         // that the optimized asm.js-to-Ion FFI call path (which we want to be
   508         // very fast) can avoid doing so. The JitActivation is marked as
   509         // inactive so stack iteration will skip over it.
   510         AsmJSActivation activation(cx, module);
   511         JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false);
   513         // Call the per-exported-function trampoline created by GenerateEntry.
   514         AsmJSModule::CodePtr enter = module.entryTrampoline(func);
   515         if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
   516             return false;
   517     }
   519     switch (func.returnType()) {
   520       case AsmJSModule::Return_Void:
   521         callArgs.rval().set(UndefinedValue());
   522         break;
   523       case AsmJSModule::Return_Int32:
   524         callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
   525         break;
   526       case AsmJSModule::Return_Double:
   527         callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
   528         break;
   529     }
   531     return true;
   532 }
   534 static JSFunction *
   535 NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func,
   536                     HandleObject moduleObj, unsigned exportIndex)
   537 {
   538     RootedPropertyName name(cx, func.name());
   539     JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(),
   540                                   JSFunction::NATIVE_FUN, cx->global(), name,
   541                                   JSFunction::ExtendedFinalizeKind);
   542     if (!fun)
   543         return nullptr;
   545     fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj));
   546     fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
   547     return fun;
   548 }
   550 static bool
   551 HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
   552 {
   553     if (cx->isExceptionPending())
   554         return false;
   556     uint32_t begin = module.offsetToEndOfUseAsm();
   557     uint32_t end = module.funcEndBeforeCurly();
   558     Rooted<JSFlatString*> src(cx, module.scriptSource()->substring(cx, begin, end));
   559     if (!src)
   560         return false;
   562     RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
   563                                        cx->global(), name, JSFunction::FinalizeKind,
   564                                        TenuredObject));
   565     if (!fun)
   566         return false;
   568     AutoNameVector formals(cx);
   569     formals.reserve(3);
   570     if (module.globalArgumentName())
   571         formals.infallibleAppend(module.globalArgumentName());
   572     if (module.importArgumentName())
   573         formals.infallibleAppend(module.importArgumentName());
   574     if (module.bufferArgumentName())
   575         formals.infallibleAppend(module.bufferArgumentName());
   577     CompileOptions options(cx);
   578     options.setOriginPrincipals(module.scriptSource()->originPrincipals())
   579            .setCompileAndGo(false)
   580            .setNoScriptRval(false);
   582     SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership);
   583     if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
   584         return false;
   586     // Call the function we just recompiled.
   587     args.setCallee(ObjectValue(*fun));
   588     return Invoke(cx, args);
   589 }
   591 #ifdef MOZ_VTUNE
   592 static bool
   593 SendFunctionsToVTune(JSContext *cx, AsmJSModule &module)
   594 {
   595     uint8_t *base = module.codeBase();
   597     for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
   598         const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
   600         uint8_t *start = base + func.pod.startCodeOffset;
   601         uint8_t *end   = base + func.pod.endCodeOffset;
   602         JS_ASSERT(end >= start);
   604         unsigned method_id = iJIT_GetNewMethodID();
   605         if (method_id == 0)
   606             return false;
   608         JSAutoByteString bytes;
   609         const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
   610         if (!method_name)
   611             return false;
   613         iJIT_Method_Load method;
   614         method.method_id = method_id;
   615         method.method_name = const_cast<char *>(method_name);
   616         method.method_load_address = (void *)start;
   617         method.method_size = unsigned(end - start);
   618         method.line_number_size = 0;
   619         method.line_number_table = nullptr;
   620         method.class_id = 0;
   621         method.class_file_name = nullptr;
   622         method.source_file_name = nullptr;
   624         iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void *)&method);
   625     }
   627     return true;
   628 }
   629 #endif
   631 #ifdef JS_ION_PERF
   632 static bool
   633 SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
   634 {
   635     if (!PerfFuncEnabled())
   636         return true;
   638     uintptr_t base = (uintptr_t) module.codeBase();
   639     const char *filename = module.scriptSource()->filename();
   641     for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
   642         const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
   643         uintptr_t start = base + (unsigned long) func.pod.startCodeOffset;
   644         uintptr_t end   = base + (unsigned long) func.pod.endCodeOffset;
   645         JS_ASSERT(end >= start);
   646         size_t size = end - start;
   648         JSAutoByteString bytes;
   649         const char *name = AtomToPrintableString(cx, func.name, &bytes);
   650         if (!name)
   651             return false;
   653         writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno,
   654                                         func.pod.columnIndex, name);
   655     }
   657     return true;
   658 }
   660 static bool
   661 SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
   662 {
   663     if (!PerfBlockEnabled())
   664         return true;
   666     unsigned long funcBaseAddress = (unsigned long) module.codeBase();
   667     const char *filename = module.scriptSource()->filename();
   669     for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
   670         const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
   672         size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset;
   674         JSAutoByteString bytes;
   675         const char *name = AtomToPrintableString(cx, func.name, &bytes);
   676         if (!name)
   677             return false;
   679         writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset,
   680                                       func.endInlineCodeOffset, size, filename, name, func.blocks);
   681     }
   683     return true;
   684 }
   685 #endif
   687 static bool
   688 SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module)
   689 {
   690 #if defined(MOZ_VTUNE)
   691     if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
   692         return false;
   693 #endif
   695 #if defined(JS_ION_PERF)
   696     if (module.numExportedFunctions() > 0) {
   697         size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0));
   698         writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode);
   699     }
   700     if (!SendBlocksToPerf(cx, module))
   701         return false;
   702     if (!SendFunctionsToPerf(cx, module))
   703         return false;
   704 #endif
   706     return true;
   707 }
   710 static JSObject *
   711 CreateExportObject(JSContext *cx, Handle<AsmJSModuleObject*> moduleObj)
   712 {
   713     AsmJSModule &module = moduleObj->module();
   715     if (module.numExportedFunctions() == 1) {
   716         const AsmJSModule::ExportedFunction &func = module.exportedFunction(0);
   717         if (!func.maybeFieldName())
   718             return NewExportedFunction(cx, func, moduleObj, 0);
   719     }
   721     gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions());
   722     RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
   723     if (!obj)
   724         return nullptr;
   726     for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
   727         const AsmJSModule::ExportedFunction &func = module.exportedFunction(i);
   729         RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
   730         if (!fun)
   731             return nullptr;
   733         JS_ASSERT(func.maybeFieldName() != nullptr);
   734         RootedId id(cx, NameToId(func.maybeFieldName()));
   735         RootedValue val(cx, ObjectValue(*fun));
   736         if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
   737             return nullptr;
   738     }
   740     return obj;
   741 }
   743 static const unsigned MODULE_FUN_SLOT = 0;
   745 static AsmJSModuleObject &
   746 ModuleFunctionToModuleObject(JSFunction *fun)
   747 {
   748     return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>();
   749 }
   751 // Implements the semantics of an asm.js module function that has been successfully validated.
   752 static bool
   753 LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp)
   754 {
   755     CallArgs args = CallArgsFromVp(argc, vp);
   757     // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
   758     // function and stores its module in an extended slot.
   759     RootedFunction fun(cx, &args.callee().as<JSFunction>());
   760     Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun));
   762     // All ICache flushing of the module being linked has been inhibited under the
   763     // assumption that the module is flushed after dynamic linking (when the last code
   764     // mutation occurs).  Thus, enter an AutoFlushICache context for the entire module
   765     // now.  The module range is set below.
   766     AutoFlushICache afc("LinkAsmJS");
   768     // When a module is linked, it is dynamically specialized to the given
   769     // arguments (buffer, ffis). Thus, if the module is linked again (it is just
   770     // a function so it can be called multiple times), we need to clone a new
   771     // module.
   772     if (moduleObj->module().isDynamicallyLinked()) {
   773         if (!CloneModule(cx, &moduleObj))
   774             return false;
   775     } else {
   776         // CloneModule already calls setAutoFlushICacheRange internally before patching
   777         // the cloned module, so avoid calling twice.
   778         moduleObj->module().setAutoFlushICacheRange();
   779     }
   781     AsmJSModule &module = moduleObj->module();
   783     // Link the module by performing the link-time validation checks in the
   784     // asm.js spec and then patching the generated module to associate it with
   785     // the given heap (ArrayBuffer) and a new global data segment (the closure
   786     // state shared by the inner asm.js functions).
   787     if (!DynamicallyLinkModule(cx, args, module)) {
   788         // Linking failed, so reparse the entire asm.js module from scratch to
   789         // get normal interpreted bytecode which we can simply Invoke. Very slow.
   790         RootedPropertyName name(cx, fun->name());
   791         return HandleDynamicLinkFailure(cx, args, module, name);
   792     }
   794     // Notify profilers so that asm.js generated code shows up with JS function
   795     // names and lines in native (i.e., not SPS) profilers.
   796     if (!SendModuleToAttachedProfiler(cx, module))
   797         return false;
   799     // Link-time validation succeeded, so wrap all the exported functions with
   800     // CallAsmJS builtins that trampoline into the generated code.
   801     JSObject *obj = CreateExportObject(cx, moduleObj);
   802     if (!obj)
   803         return false;
   805     args.rval().set(ObjectValue(*obj));
   806     return true;
   807 }
   809 JSFunction *
   810 js::NewAsmJSModuleFunction(ExclusiveContext *cx, JSFunction *origFun, HandleObject moduleObj)
   811 {
   812     RootedPropertyName name(cx, origFun->name());
   814     JSFunction::Flags flags = origFun->isLambda() ? JSFunction::NATIVE_LAMBDA_FUN
   815                                                   : JSFunction::NATIVE_FUN;
   816     JSFunction *moduleFun = NewFunction(cx, NullPtr(), LinkAsmJS, origFun->nargs(),
   817                                         flags, NullPtr(), name,
   818                                         JSFunction::ExtendedFinalizeKind, TenuredObject);
   819     if (!moduleFun)
   820         return nullptr;
   822     moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj));
   823     return moduleFun;
   824 }
   826 bool
   827 js::IsAsmJSModuleNative(js::Native native)
   828 {
   829     return native == LinkAsmJS;
   830 }
   832 static bool
   833 IsMaybeWrappedNativeFunction(const Value &v, Native native, JSFunction **fun = nullptr)
   834 {
   835     if (!v.isObject())
   836         return false;
   838     JSObject *obj = CheckedUnwrap(&v.toObject());
   839     if (!obj)
   840         return false;
   842     if (!obj->is<JSFunction>())
   843         return false;
   845     if (fun)
   846         *fun = &obj->as<JSFunction>();
   848     return obj->as<JSFunction>().maybeNative() == native;
   849 }
   851 bool
   852 js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
   853 {
   854     CallArgs args = CallArgsFromVp(argc, vp);
   855     bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
   856     args.rval().set(BooleanValue(rval));
   857     return true;
   858 }
   860 bool
   861 js::IsAsmJSModule(HandleFunction fun)
   862 {
   863     return fun->isNative() && fun->maybeNative() == LinkAsmJS;
   864 }
   866 JSString *
   867 js::AsmJSModuleToString(JSContext *cx, HandleFunction fun, bool addParenToLambda)
   868 {
   869     AsmJSModule &module = ModuleFunctionToModuleObject(fun).module();
   871     uint32_t begin = module.funcStart();
   872     uint32_t end = module.funcEndAfterCurly();
   873     ScriptSource *source = module.scriptSource();
   874     StringBuffer out(cx);
   876     // Whether the function has been created with a Function ctor
   877     bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
   879     if (addParenToLambda && fun->isLambda() && !out.append("("))
   880         return nullptr;
   882     if (!out.append("function "))
   883         return nullptr;
   885     if (fun->atom() && !out.append(fun->atom()))
   886         return nullptr;
   888     if (funCtor) {
   889         // Functions created with the function constructor don't have arguments in their source.
   890         if (!out.append("("))
   891             return nullptr;
   893         if (PropertyName *argName = module.globalArgumentName()) {
   894             if (!out.append(argName->chars(), argName->length()))
   895                 return nullptr;
   896         }
   897         if (PropertyName *argName = module.importArgumentName()) {
   898             if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
   899                 return nullptr;
   900         }
   901         if (PropertyName *argName = module.bufferArgumentName()) {
   902             if (!out.append(", ") || !out.append(argName->chars(), argName->length()))
   903                 return nullptr;
   904         }
   906         if (!out.append(") {\n"))
   907             return nullptr;
   908     }
   910     Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
   911     if (!src)
   912         return nullptr;
   914     if (module.strict()) {
   915         // We need to add "use strict" in the body right after the opening
   916         // brace.
   917         size_t bodyStart = 0, bodyEnd;
   919         // No need to test for functions created with the Function ctor as
   920         // these doesn't implicitly inherit the "use strict" context. Strict mode is
   921         // enabled for functions created with the Function ctor only if they begin with
   922         // the "use strict" directive, but these functions won't validate as asm.js
   923         // modules.
   925         ConstTwoByteChars chars(src->chars(), src->length());
   926         if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd))
   927             return nullptr;
   929         if (!out.append(chars, bodyStart) ||
   930             !out.append("\n\"use strict\";\n") ||
   931             !out.append(chars + bodyStart, src->length() - bodyStart))
   932         {
   933             return nullptr;
   934         }
   935     } else {
   936         if (!out.append(src->chars(), src->length()))
   937             return nullptr;
   938     }
   940     if (funCtor && !out.append("\n}"))
   941         return nullptr;
   943     if (addParenToLambda && fun->isLambda() && !out.append(")"))
   944         return nullptr;
   946     return out.finishString();
   947 }
   949 bool
   950 js::IsAsmJSModuleLoadedFromCache(JSContext *cx, unsigned argc, Value *vp)
   951 {
   952     CallArgs args = CallArgsFromVp(argc, vp);
   954     JSFunction *fun;
   955     if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
   956         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
   957                              "argument passed to isAsmJSModuleLoadedFromCache is not a "
   958                              "validated asm.js module");
   959         return false;
   960     }
   962     bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().loadedFromCache();
   964     args.rval().set(BooleanValue(loadedFromCache));
   965     return true;
   966 }
   968 bool
   969 js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
   970 {
   971     CallArgs args = CallArgsFromVp(argc, vp);
   972     bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
   973     args.rval().set(BooleanValue(rval));
   974     return true;
   975 }
   977 bool
   978 js::IsAsmJSFunction(HandleFunction fun)
   979 {
   980     return fun->isNative() && fun->maybeNative() == CallAsmJS;
   981 }
   983 JSString *
   984 js::AsmJSFunctionToString(JSContext *cx, HandleFunction fun)
   985 {
   986     AsmJSModule &module = FunctionToEnclosingModule(fun);
   987     const AsmJSModule::ExportedFunction &f = FunctionToExportedFunction(fun, module);
   988     uint32_t begin = module.funcStart() + f.startOffsetInModule();
   989     uint32_t end = module.funcStart() + f.endOffsetInModule();
   991     ScriptSource *source = module.scriptSource();
   992     StringBuffer out(cx);
   994     // asm.js functions cannot have been created with a Function constructor
   995     // as they belong within a module.
   996     JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
   998     if (!out.append("function "))
   999         return nullptr;
  1001     Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
  1002     if (!src)
  1003         return nullptr;
  1005     if (!out.append(src->chars(), src->length()))
  1006         return nullptr;
  1008     return out.finishString();

mercurial