Example #1
0
static bool
LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
{
    if (!IsValidAsmJSHeapLength(heap->byteLength())) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
                        "valid length is 0x%x",
                        heap->byteLength(),
                        RoundUpToNextValidAsmJSHeapLength(heap->byteLength())));
        return LinkFail(cx, msg.get());
    }

    // This check is sufficient without considering the size of the loaded datum because heap
    // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
    JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
    if (heap->byteLength() < module.minHeapLength()) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
                        "largest constant heap access offset rounded up to the next valid "
                        "heap size).",
                        heap->byteLength(),
                        module.minHeapLength()));
        return LinkFail(cx, msg.get());
    }

    if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
        return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

    module.initHeap(heap, cx);
    return true;
}
Example #2
0
AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
  : Activation(cx, AsmJS),
    module_(module),
    entrySP_(nullptr),
    profiler_(nullptr),
    resumePC_(nullptr),
    fp_(nullptr),
    exitReason_(AsmJSExit::None)
{
    (void) entrySP_;  // squelch GCC warning

    // NB: this is a hack and can be removed once Ion switches over to
    // JS::ProfilingFrameIterator.
    if (cx->runtime()->spsProfiler.enabled()) {
        profiler_ = &cx->runtime()->spsProfiler;
        profiler_->enterAsmJS("asm.js code :0", this);
    }

    prevAsmJSForModule_ = module.activation();
    module.activation() = this;

    prevAsmJS_ = cx->mainThread().asmJSActivationStack_;
    cx->mainThread().asmJSActivationStack_ = this;

    // Now that the AsmJSActivation is fully initialized, make it visible to
    // asynchronous profiling.
    registerProfiling();
}
Example #3
0
static bool
SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
{
    if (!PerfFuncEnabled())
        return true;

    uintptr_t base = (uintptr_t) module.codeBase();
    const char *filename = module.scriptSource()->filename();

    for (unsigned i = 0; i < module.numPerfFunctions(); i++) {
        const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i);
        uintptr_t start = base + (unsigned long) func.startCodeOffset;
        uintptr_t end   = base + (unsigned long) func.endCodeOffset;
        JS_ASSERT(end >= start);
        size_t size = end - start;

        JSAutoByteString bytes;
        const char *name = AtomToPrintableString(cx, func.name, &bytes);
        if (!name)
            return false;

        writePerfSpewerAsmJSFunctionMap(start, size, filename, func.lineno, func.columnIndex, name);
    }

    return true;
}
Example #4
0
AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
  : Activation(cx, AsmJS),
    module_(module),
    errorRejoinSP_(nullptr),
    profiler_(nullptr),
    resumePC_(nullptr),
    fp_(nullptr),
    exitReason_(AsmJSExit::None)
{
    if (cx->runtime()->spsProfiler.enabled()) {
        // Use a profiler string that matches jsMatch regex in
        // browser/devtools/profiler/cleopatra/js/parserWorker.js.
        // (For now use a single static string to avoid further slowing down
        // calls into asm.js.)
        profiler_ = &cx->runtime()->spsProfiler;
        profiler_->enterAsmJS("asm.js code :0", this);
    }

    prevAsmJSForModule_ = module.activation();
    module.activation() = this;

    prevAsmJS_ = cx->mainThread().asmJSActivationStack_;

    JSRuntime::AutoLockForInterrupt lock(cx->runtime());
    cx->mainThread().asmJSActivationStack_ = this;

    (void) errorRejoinSP_;  // squelch GCC warning
}
Example #5
0
static bool
SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
{
    if (!PerfBlockEnabled())
        return true;

    unsigned long funcBaseAddress = (unsigned long) module.codeBase();
    const char *filename = module.scriptSource()->filename();

    for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
        const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);

        size_t size = func.endCodeOffset - func.startCodeOffset;

        JSAutoByteString bytes;
        const char *name = AtomToPrintableString(cx, func.name, &bytes);
        if (!name)
            return false;

        writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.startCodeOffset,
                                      func.endInlineCodeOffset, size, filename, name, func.blocks);
    }

    return true;
}
static bool
SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
{
    if (!PerfBlockEnabled())
        return true;

    AsmJSPerfSpewer spewer;
    unsigned long funcBaseAddress = (unsigned long) module.functionCode();

    const AsmJSModule::PostLinkFailureInfo &info = module.postLinkFailureInfo();
    const char *filename = const_cast<char *>(info.scriptSource->filename());

    for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
        const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);

        unsigned long size = (unsigned long)func.endCodeOffset - (unsigned long)func.startCodeOffset;
        JSAutoByteString bytes;
        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
        if (!method_name)
            return false;

        spewer.writeBlocksMap(funcBaseAddress, func.startCodeOffset, size, filename, method_name, func.blocks);
    }

    return true;
}
static bool
SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
{
    if (!PerfFuncEnabled())
        return true;

    AsmJSPerfSpewer perfSpewer;

    unsigned long base = (unsigned long) module.functionCode();

    const AsmJSModule::PostLinkFailureInfo &info = module.postLinkFailureInfo();
    const char *filename = const_cast<char *>(info.scriptSource->filename());

    for (unsigned i = 0; i < module.numPerfFunctions(); i++) {
        const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i);

        unsigned long start = base + (unsigned long) func.startCodeOffset;
        unsigned long end   = base + (unsigned long) func.endCodeOffset;
        JS_ASSERT(end >= start);

        unsigned long size = (end - start);

        JSAutoByteString bytes;
        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
        if (!method_name)
            return false;

        unsigned lineno = func.lineno;
        unsigned columnIndex = func.columnIndex;

        perfSpewer.writeFunctionMap(start, size, filename, lineno, columnIndex, method_name);
    }

    return true;
}
Example #8
0
static bool
HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
{
    if (cx->isExceptionPending())
        return false;

    uint32_t begin = module.charsBegin();
    uint32_t end = module.charsEnd();
    Rooted<JSStableString*> src(cx, module.scriptSource()->substring(cx, begin, end));
    if (!src)
        return false;

    RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
                                       cx->global(), name, JSFunction::FinalizeKind,
                                       TenuredObject));
    if (!fun)
        return false;

    AutoNameVector formals(cx);
    formals.reserve(3);
    if (module.globalArgumentName())
        formals.infallibleAppend(module.globalArgumentName());
    if (module.importArgumentName())
        formals.infallibleAppend(module.importArgumentName());
    if (module.bufferArgumentName())
        formals.infallibleAppend(module.bufferArgumentName());

    CompileOptions options(cx);
    options.setPrincipals(cx->compartment()->principals)
           .setOriginPrincipals(module.scriptSource()->originPrincipals())
           .setCompileAndGo(false)
           .setNoScriptRval(false);

    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, src->chars().get(), end - begin))
        return false;

    // Call the function we just recompiled.

    unsigned argc = args.length();

    InvokeArgs args2(cx);
    if (!args2.init(argc))
        return false;

    args2.setCallee(ObjectValue(*fun));
    args2.setThis(args.thisv());
    for (unsigned i = 0; i < argc; i++)
        args2[i].set(args[i]);

    if (!Invoke(cx, args2))
        return false;

    args.rval().set(args2.rval());

    return true;
}
Example #9
0
static bool
SendModuleToAttachedProfiler(JSContext* cx, AsmJSModule& module)
{
#if defined(MOZ_VTUNE)
    if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
        return false;
#endif

#if defined(JS_ION_PERF)
    if (module.numExportedFunctions() > 0) {
        size_t firstEntryCode = size_t(module.codeBase() + module.functionBytes());
        writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, module.codeBytes() - module.functionBytes());
    }
    if (!SendFunctionsToPerf(cx, module))
        return false;
#endif

    return true;
}
Example #10
0
static bool
LinkModuleToHeap(JSContext* cx, AsmJSModule& module, Handle<ArrayBufferObjectMaybeShared*> heap)
{
    uint32_t heapLength = heap->byteLength();

    if (!IsValidAsmJSHeapLength(heapLength)) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
                        "valid length is 0x%x",
                        heapLength,
                        RoundUpToNextValidAsmJSHeapLength(heapLength)));
        return LinkFail(cx, msg.get());
    }

    // This check is sufficient without considering the size of the loaded datum because heap
    // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
    MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
    if (heapLength < module.minHeapLength()) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
                        "by const heap accesses and/or change-heap minimum-length requirements).",
                        heapLength,
                        module.minHeapLength()));
        return LinkFail(cx, msg.get());
    }

    if (heapLength > module.maxHeapLength()) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
                        heapLength,
                        module.maxHeapLength()));
        return LinkFail(cx, msg.get());
    }

    // If we've generated the code with signal handlers in mind (for bounds
    // checks on x64 and for interrupt callback requesting on all platforms),
    // we need to be able to use signals at runtime. In particular, a module
    // can have been created using signals and cached, and executed without
    // signals activated.
    if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
        return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");

    if (heap->is<ArrayBufferObject>()) {
        Rooted<ArrayBufferObject*> abheap(cx, &heap->as<ArrayBufferObject>());
        if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, module.usesSignalHandlersForOOB()))
            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
    }

    module.initHeap(heap, cx);
    return true;
}
Example #11
0
static bool
SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module)
{
#if defined(MOZ_VTUNE)
    if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
        return false;
#endif

#if defined(JS_ION_PERF)
    if (module.numExportedFunctions() > 0) {
        size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0));
        writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode);
    }
    if (!SendBlocksToPerf(cx, module))
        return false;
    if (!SendFunctionsToPerf(cx, module))
        return false;
#endif

    return true;
}
Example #12
0
static bool
HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
{
    if (cx->isExceptionPending())
        return false;

    const AsmJSModule::PostLinkFailureInfo &info = module.postLinkFailureInfo();

    uint32_t length = info.bufEnd_ - info.bufStart_;
    Rooted<JSFlatString*> src(cx, info.scriptSource_->substring(cx, info.bufStart_, info.bufEnd_));
    const jschar *chars = src->chars();

    RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED,
                                       cx->global(), name));
    if (!fun)
        return false;

    AutoNameVector formals(cx);
    formals.reserve(3);
    if (module.globalArgumentName())
        formals.infallibleAppend(module.globalArgumentName());
    if (module.importArgumentName())
        formals.infallibleAppend(module.importArgumentName());
    if (module.bufferArgumentName())
        formals.infallibleAppend(module.bufferArgumentName());

    if (!frontend::CompileFunctionBody(cx, &fun, info.options_, formals, chars, length,
                                       /* isAsmJSRecompile = */ true))
        return false;

    // Call the function we just recompiled.

    unsigned argc = args.length();
    JS_ASSERT(argc <= 3);

    InvokeArgsGuard args2;
    if (!cx->stack.pushInvokeArgs(cx, args.length(), &args2))
        return false;

    args2.setCallee(ObjectValue(*fun));
    args2.setThis(args.thisv());
    if (argc > 0)
        args2[0] = args[0];
    if (argc > 1)
        args2[1] = args[1];
    if (argc > 2)
        args2[2] = args[2];

    if (!Invoke(cx, args2))
        return false;

    args.rval().set(args2.rval());

    return true;
}
Example #13
0
static bool
ChangeHeap(JSContext *cx, AsmJSModule &module, CallArgs args)
{
    HandleValue bufferArg = args.get(0);
    if (!IsArrayBuffer(bufferArg)) {
        ReportIncompatible(cx, args);
        return false;
    }

    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
    uint32_t heapLength = newBuffer->byteLength();
    if (heapLength & module.heapLengthMask() ||
            heapLength < module.minHeapLength() ||
            heapLength > module.maxHeapLength())
    {
        args.rval().set(BooleanValue(false));
        return true;
    }

    if (!module.hasArrayView()) {
        args.rval().set(BooleanValue(true));
        return true;
    }

    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
    MOZ_ASSERT(!IsDeprecatedAsmJSHeapLength(heapLength));

    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, module.usesSignalHandlersForOOB()))
        return false;

    args.rval().set(BooleanValue(module.changeHeap(newBuffer, cx)));
    return true;
}
Example #14
0
static inline void
AssertMatchesCallSite(const AsmJSModule &module, const AsmJSModule::CodeRange *calleeCodeRange,
                      void *callerPC, void *callerFP, void *fp)
{
#ifdef DEBUG
    const AsmJSModule::CodeRange *callerCodeRange = module.lookupCodeRange(callerPC);
    JS_ASSERT(callerCodeRange);
    if (callerCodeRange->isEntry()) {
        JS_ASSERT(callerFP == nullptr);
        return;
    }

    const CallSite *callsite = module.lookupCallSite(callerPC);
    if (calleeCodeRange->isThunk()) {
        JS_ASSERT(!callsite);
        JS_ASSERT(callerCodeRange->isFunction());
    } else {
        JS_ASSERT(callsite);
        JS_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
    }
#endif
}
Example #15
0
static bool
ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
                       HandleValue importVal)
{
    JS_ASSERT(global.which() == AsmJSModule::Global::Variable);

    void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());

    switch (global.varInitKind()) {
      case AsmJSModule::Global::InitConstant: {
        const Value &v = global.varInitConstant();
        switch (global.varInitCoercion()) {
          case AsmJS_ToInt32:
            *(int32_t *)datum = v.toInt32();
            break;
          case AsmJS_ToNumber:
            *(double *)datum = v.toDouble();
            break;
          case AsmJS_FRound:
            *(float *)datum = static_cast<float>(v.toDouble());
            break;
        }
        break;
      }
      case AsmJSModule::Global::InitImport: {
        RootedPropertyName field(cx, global.varImportField());
        RootedValue v(cx);
        if (!GetDataProperty(cx, importVal, field, &v))
            return false;

        switch (global.varInitCoercion()) {
          case AsmJS_ToInt32:
            if (!ToInt32(cx, v, (int32_t *)datum))
                return false;
            break;
          case AsmJS_ToNumber:
            if (!ToNumber(cx, v, (double *)datum))
                return false;
            break;
          case AsmJS_FRound:
            if (!RoundFloat32(cx, v, (float *)datum))
                return false;
            break;
        }
        break;
      }
    }

    return true;
}
Example #16
0
static bool
SendFunctionsToVTune(JSContext *cx, AsmJSModule &module)
{
    uint8_t *base = module.codeBase();

    for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
        const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);

        uint8_t *start = base + func.startCodeOffset;
        uint8_t *end   = base + func.endCodeOffset;
        JS_ASSERT(end >= start);

        unsigned method_id = iJIT_GetNewMethodID();
        if (method_id == 0)
            return false;

        JSAutoByteString bytes;
        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
        if (!method_name)
            return false;

        iJIT_Method_Load method;
        method.method_id = method_id;
        method.method_name = const_cast<char *>(method_name);
        method.method_load_address = (void *)start;
        method.method_size = unsigned(end - start);
        method.line_number_size = 0;
        method.line_number_table = nullptr;
        method.class_id = 0;
        method.class_file_name = nullptr;
        method.source_file_name = nullptr;

        iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void *)&method);
    }

    return true;
}
Example #17
0
static bool
LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
{
    uint32_t heapLength = heap->byteLength();

    if (IsDeprecatedAsmJSHeapLength(heapLength)) {
        LinkFail(cx, "ArrayBuffer byteLengths smaller than 64KB are deprecated and "
                 "will cause a link-time failure in the future");

        // The goal of deprecation is to give apps some time before linking
        // fails. However, if warnings-as-errors is turned on (which happens as
        // part of asm.js testing) an exception may be raised.
        if (cx->isExceptionPending())
            return false;
    }

    if (!IsValidAsmJSHeapLength(heapLength)) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
                        "valid length is 0x%x",
                        heapLength,
                        RoundUpToNextValidAsmJSHeapLength(heapLength)));
        return LinkFail(cx, msg.get());
    }

    // This check is sufficient without considering the size of the loaded datum because heap
    // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
    JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
    if (heapLength < module.minHeapLength()) {
        ScopedJSFreePtr<char> msg(
            JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the "
                        "largest constant heap access offset rounded up to the next valid "
                        "heap size).",
                        heapLength,
                        module.minHeapLength()));
        return LinkFail(cx, msg.get());
    }

    // If we've generated the code with signal handlers in mind (for bounds
    // checks on x64 and for interrupt callback requesting on all platforms),
    // we need to be able to use signals at runtime. In particular, a module
    // can have been created using signals and cached, and executed without
    // signals activated.
    if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
        return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");

    if (!ArrayBufferObject::prepareForAsmJS(cx, heap, module.usesSignalHandlersForOOB()))
        return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

    module.initHeap(heap, cx);
    return true;
}
Example #18
0
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
    if (module.isLinked())
        return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than "
                            "once. This limitation should be removed in a future release. To "
                            "work around this, compile a second module (e.g., using the "
                            "Function constructor).");

    RootedValue globalVal(cx, UndefinedValue());
    if (args.length() > 0)
        globalVal = args[0];

    RootedValue importVal(cx, UndefinedValue());
    if (args.length() > 1)
        importVal = args[1];

    RootedValue bufferVal(cx, UndefinedValue());
    if (args.length() > 2)
        bufferVal = args[2];

    Rooted<ArrayBufferObject*> heap(cx);
    if (module.hasArrayView()) {
        if (!IsTypedArrayBuffer(bufferVal))
            return LinkFail(cx, "bad ArrayBuffer argument");

        heap = &bufferVal.toObject().as<ArrayBufferObject>();

        if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity)
            return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096");

        if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

        module.patchHeapAccesses(heap, cx);
    }

    AutoObjectVector ffis(cx);
    if (!ffis.resize(module.numFFIs()))
        return false;

    for (unsigned i = 0; i < module.numGlobals(); i++) {
        AsmJSModule::Global &global = module.global(i);
        switch (global.which()) {
          case AsmJSModule::Global::Variable:
            if (!ValidateGlobalVariable(cx, module, global, importVal))
                return false;
            break;
          case AsmJSModule::Global::FFI:
            if (!ValidateFFI(cx, global, importVal, &ffis))
                return false;
            break;
          case AsmJSModule::Global::ArrayView:
            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
                return false;
            break;
          case AsmJSModule::Global::MathBuiltin:
            if (!ValidateMathBuiltin(cx, global, globalVal))
                return false;
            break;
          case AsmJSModule::Global::Constant:
            if (!ValidateGlobalConstant(cx, global, globalVal))
                return false;
            break;
        }
    }

    for (unsigned i = 0; i < module.numExits(); i++)
        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();

    module.setIsLinked(heap);
    return true;
}
Example #19
0
static bool
ValidateGlobalVariable(JSContext* cx, const AsmJSModule& module, AsmJSModule::Global& global,
                       HandleValue importVal)
{
    void* datum = module.globalData() + global.varGlobalDataOffset();

    switch (global.varInitKind()) {
      case AsmJSModule::Global::InitConstant: {
        Val v = global.varInitVal();
        switch (v.type()) {
          case ValType::I32:
            *(int32_t*)datum = v.i32();
            break;
          case ValType::I64:
            MOZ_CRASH("int64");
          case ValType::F32:
            *(float*)datum = v.f32();
            break;
          case ValType::F64:
            *(double*)datum = v.f64();
            break;
          case ValType::I32x4:
            memcpy(datum, v.i32x4(), Simd128DataSize);
            break;
          case ValType::F32x4:
            memcpy(datum, v.f32x4(), Simd128DataSize);
            break;
        }
        break;
      }

      case AsmJSModule::Global::InitImport: {
        RootedPropertyName field(cx, global.varImportField());
        RootedValue v(cx);
        if (!GetDataProperty(cx, importVal, field, &v))
            return false;

        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
            return LinkFail(cx, "Imported values must be primitives");

        switch (global.varInitImportType()) {
          case ValType::I32:
            if (!ToInt32(cx, v, (int32_t*)datum))
                return false;
            break;
          case ValType::I64:
            MOZ_CRASH("int64");
          case ValType::F32:
            if (!RoundFloat32(cx, v, (float*)datum))
                return false;
            break;
          case ValType::F64:
            if (!ToNumber(cx, v, (double*)datum))
                return false;
            break;
          case ValType::I32x4: {
            SimdConstant simdConstant;
            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
                return false;
            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
            break;
          }
          case ValType::F32x4: {
            SimdConstant simdConstant;
            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
                return false;
            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
            break;
          }
        }
        break;
      }
    }

    return true;
}
Example #20
0
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
    if (module.isLinked())
        return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than "
                            "once. This limitation should be removed in a future release. To "
                            "work around this, compile a second module (e.g., using the "
                            "Function constructor).");

    RootedValue globalVal(cx, UndefinedValue());
    if (args.length() > 0)
        globalVal = args[0];

    RootedValue importVal(cx, UndefinedValue());
    if (args.length() > 1)
        importVal = args[1];

    RootedValue bufferVal(cx, UndefinedValue());
    if (args.length() > 2)
        bufferVal = args[2];

    Rooted<ArrayBufferObject*> heap(cx);
    if (module.hasArrayView()) {
        if (!IsTypedArrayBuffer(bufferVal))
            return LinkFail(cx, "bad ArrayBuffer argument");

        heap = &bufferVal.toObject().as<ArrayBufferObject>();

        if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity)
            return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096");

        if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

#if defined(JS_CPU_X86)
        void *heapOffset = (void*)heap->dataPointer();
        void *heapLength = (void*)heap->byteLength();
        uint8_t *code = module.functionCode();
        for (unsigned i = 0; i < module.numHeapAccesses(); i++) {
            const AsmJSHeapAccess &access = module.heapAccess(i);
            JSC::X86Assembler::setPointer(access.patchLengthAt(code), heapLength);
            JSC::X86Assembler::setPointer(access.patchOffsetAt(code), heapOffset);
        }
#elif defined(JS_CPU_ARM)
        // Now the length of the array is know, patch all of the bounds check sites
        // with the new length.
        jit::IonContext ic(cx, NULL);
        module.patchBoundsChecks(heap->byteLength());

#endif
    }

    AutoObjectVector ffis(cx);
    if (!ffis.resize(module.numFFIs()))
        return false;

    for (unsigned i = 0; i < module.numGlobals(); i++) {
        AsmJSModule::Global &global = module.global(i);
        switch (global.which()) {
          case AsmJSModule::Global::Variable:
            if (!ValidateGlobalVariable(cx, module, global, importVal))
                return false;
            break;
          case AsmJSModule::Global::FFI:
            if (!ValidateFFI(cx, global, importVal, &ffis))
                return false;
            break;
          case AsmJSModule::Global::ArrayView:
            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
                return false;
            break;
          case AsmJSModule::Global::MathBuiltin:
            if (!ValidateMathBuiltin(cx, global, globalVal))
                return false;
            break;
          case AsmJSModule::Global::Constant:
            if (!ValidateGlobalConstant(cx, global, globalVal))
                return false;
            break;
        }
    }

    for (unsigned i = 0; i < module.numExits(); i++)
        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();

    module.setIsLinked(heap);
    return true;
}
Example #21
0
static bool
HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
                         HandlePropertyName name)
{
    if (cx->isExceptionPending())
        return false;

    // Source discarding is allowed to affect JS semantics because it is never
    // enabled for normal JS content.
    bool haveSource = module.scriptSource()->hasSourceData();
    if (!haveSource && !JSScript::loadSource(cx, module.scriptSource(), &haveSource))
        return false;
    if (!haveSource) {
        JS_ReportError(cx, "asm.js link failure with source discarding enabled");
        return false;
    }

    uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
    uint32_t end = module.srcEndBeforeCurly();
    Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end));
    if (!src)
        return false;

    RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
                                               name, gc::AllocKind::FUNCTION,
                                               TenuredObject));
    if (!fun)
        return false;

    Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
    if (!formals.reserve(3))
        return false;

    if (module.globalArgumentName())
        formals.infallibleAppend(module.globalArgumentName());
    if (module.importArgumentName())
        formals.infallibleAppend(module.importArgumentName());
    if (module.bufferArgumentName())
        formals.infallibleAppend(module.bufferArgumentName());

    CompileOptions options(cx);
    options.setMutedErrors(module.scriptSource()->mutedErrors())
           .setFile(module.scriptSource()->filename())
           .setNoScriptRval(false);

    // The exported function inherits an implicit strict context if the module
    // also inherited it somehow.
    if (module.strict())
        options.strictOption = true;

    AutoStableStringChars stableChars(cx);
    if (!stableChars.initTwoByte(cx, src))
        return false;

    const char16_t* chars = stableChars.twoByteRange().start().get();
    SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                              ? SourceBufferHolder::GiveOwnership
                                              : SourceBufferHolder::NoOwnership;
    SourceBufferHolder srcBuf(chars, end - begin, ownership);
    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf,
                                       /* enclosingScope = */ nullptr))
        return false;

    // Call the function we just recompiled.
    args.setCallee(ObjectValue(*fun));
    return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
}
Example #22
0
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
    module.setIsDynamicallyLinked();

    RootedValue globalVal(cx);
    if (args.length() > 0)
        globalVal = args[0];

    RootedValue importVal(cx);
    if (args.length() > 1)
        importVal = args[1];

    RootedValue bufferVal(cx);
    if (args.length() > 2)
        bufferVal = args[2];

    Rooted<ArrayBufferObject*> heap(cx);
    if (module.hasArrayView()) {
        if (!IsTypedArrayBuffer(bufferVal))
            return LinkFail(cx, "bad ArrayBuffer argument");

        heap = &AsTypedArrayBuffer(bufferVal);
        if (!LinkModuleToHeap(cx, module, heap))
            return false;
    }

    AutoObjectVector ffis(cx);
    if (!ffis.resize(module.numFFIs()))
        return false;

    for (unsigned i = 0; i < module.numGlobals(); i++) {
        AsmJSModule::Global &global = module.global(i);
        switch (global.which()) {
          case AsmJSModule::Global::Variable:
            if (!ValidateGlobalVariable(cx, module, global, importVal))
                return false;
            break;
          case AsmJSModule::Global::FFI:
            if (!ValidateFFI(cx, global, importVal, &ffis))
                return false;
            break;
          case AsmJSModule::Global::ArrayView:
            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
                return false;
            break;
          case AsmJSModule::Global::MathBuiltinFunction:
            if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                return false;
            break;
          case AsmJSModule::Global::Constant:
            if (!ValidateConstant(cx, global, globalVal))
                return false;
            break;
        }
    }

    for (unsigned i = 0; i < module.numExits(); i++)
        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();

    return true;
}
Example #23
0
static bool
ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
                       HandleValue importVal)
{
    MOZ_ASSERT(global.which() == AsmJSModule::Global::Variable);

    void *datum = module.globalVarToGlobalDatum(global);

    switch (global.varInitKind()) {
    case AsmJSModule::Global::InitConstant: {
        const AsmJSNumLit &lit = global.varInitNumLit();
        switch (lit.which()) {
        case AsmJSNumLit::Fixnum:
        case AsmJSNumLit::NegativeInt:
        case AsmJSNumLit::BigUnsigned:
            *(int32_t *)datum = lit.scalarValue().toInt32();
            break;
        case AsmJSNumLit::Double:
            *(double *)datum = lit.scalarValue().toDouble();
            break;
        case AsmJSNumLit::Float:
            *(float *)datum = static_cast<float>(lit.scalarValue().toDouble());
            break;
        case AsmJSNumLit::Int32x4:
            memcpy(datum, lit.simdValue().asInt32x4(), Simd128DataSize);
            break;
        case AsmJSNumLit::Float32x4:
            memcpy(datum, lit.simdValue().asFloat32x4(), Simd128DataSize);
            break;
        case AsmJSNumLit::OutOfRangeInt:
            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("OutOfRangeInt isn't valid in the first place");
        }
        break;
    }

    case AsmJSModule::Global::InitImport: {
        RootedPropertyName field(cx, global.varImportField());
        RootedValue v(cx);
        if (!GetDataProperty(cx, importVal, field, &v))
            return false;

        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
            return LinkFail(cx, "Imported values must be primitives");

        SimdConstant simdConstant;
        switch (global.varInitCoercion()) {
        case AsmJS_ToInt32:
            if (!ToInt32(cx, v, (int32_t *)datum))
                return false;
            break;
        case AsmJS_ToNumber:
            if (!ToNumber(cx, v, (double *)datum))
                return false;
            break;
        case AsmJS_FRound:
            if (!RoundFloat32(cx, v, (float *)datum))
                return false;
            break;
        case AsmJS_ToInt32x4:
            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
                return false;
            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
            break;
        case AsmJS_ToFloat32x4:
            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
                return false;
            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
            break;
        }
        break;
    }
    }

    return true;
}
Example #24
0
static bool
ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
                       HandleValue importVal)
{
    JS_ASSERT(global.which() == AsmJSModule::Global::Variable);

    void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());

    switch (global.varInitKind()) {
    case AsmJSModule::Global::InitConstant: {
        const AsmJSNumLit &lit = global.varInitNumLit();
        const Value &v = lit.value();
        switch (lit.which()) {
        case AsmJSNumLit::Fixnum:
        case AsmJSNumLit::NegativeInt:
        case AsmJSNumLit::BigUnsigned:
            *(int32_t *)datum = v.toInt32();
            break;
        case AsmJSNumLit::Double:
            *(double *)datum = v.toDouble();
            break;
        case AsmJSNumLit::Float:
            *(float *)datum = static_cast<float>(v.toDouble());
            break;
        case AsmJSNumLit::OutOfRangeInt:
            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("OutOfRangeInt isn't valid in the first place");
        }
        break;
    }
    case AsmJSModule::Global::InitImport: {
        RootedPropertyName field(cx, global.varImportField());
        RootedValue v(cx);
        if (!GetDataProperty(cx, importVal, field, &v))
            return false;

        if (!v.isPrimitive()) {
            // Ideally, we'd reject all non-primitives, but Emscripten has a bug
            // that generates code that passes functions for some imports. To
            // avoid breaking all the code that contains this bug, we make an
            // exception for functions that don't have user-defined valueOf or
            // toString, for their coercions are not observable and coercion via
            // ToNumber/ToInt32 definitely produces NaN/0. We should remove this
            // special case later once most apps have been built with newer
            // Emscripten.
            jsid toString = NameToId(cx->names().toString);
            if (!v.toObject().is<JSFunction>() ||
                    !HasObjectValueOf(&v.toObject(), cx) ||
                    !ClassMethodIsNative(cx, &v.toObject(), &JSFunction::class_, toString, fun_toString))
            {
                return LinkFail(cx, "Imported values must be primitives");
            }
        }

        switch (global.varInitCoercion()) {
        case AsmJS_ToInt32:
            if (!ToInt32(cx, v, (int32_t *)datum))
                return false;
            break;
        case AsmJS_ToNumber:
            if (!ToNumber(cx, v, (double *)datum))
                return false;
            break;
        case AsmJS_FRound:
            if (!RoundFloat32(cx, v, (float *)datum))
                return false;
            break;
        }
        break;
    }
    }

    return true;
}
Example #25
0
static bool
HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name)
{
    if (cx->isExceptionPending())
        return false;

    uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
    uint32_t end = module.srcEndBeforeCurly();
    Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end));
    if (!src)
        return false;

    RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
                                       cx->global(), name, JSFunction::FinalizeKind,
                                       TenuredObject));
    if (!fun)
        return false;

    AutoNameVector formals(cx);
    formals.reserve(3);
    if (module.globalArgumentName())
        formals.infallibleAppend(module.globalArgumentName());
    if (module.importArgumentName())
        formals.infallibleAppend(module.importArgumentName());
    if (module.bufferArgumentName())
        formals.infallibleAppend(module.bufferArgumentName());

    CompileOptions options(cx);
    options.setOriginPrincipals(module.scriptSource()->originPrincipals())
    .setFile(module.scriptSource()->filename())
    .setCompileAndGo(false)
    .setNoScriptRval(false);

    // The exported function inherits an implicit strict context if the module
    // also inherited it somehow.
    if (module.strict())
        options.strictOption = true;

    AutoStableStringChars stableChars(cx);
    if (!stableChars.initTwoByte(cx, src))
        return false;

    const jschar *chars = stableChars.twoByteRange().start().get();
    SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
            ? SourceBufferHolder::GiveOwnership
            : SourceBufferHolder::NoOwnership;
    SourceBufferHolder srcBuf(chars, end - begin, ownership);
    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
        return false;

    // Call the function we just recompiled.
    args.setCallee(ObjectValue(*fun));
    return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
}
Example #26
0
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
    module.setIsDynamicallyLinked(cx->runtime());

    HandleValue globalVal = args.get(0);
    HandleValue importVal = args.get(1);
    HandleValue bufferVal = args.get(2);

    Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
    if (module.hasArrayView()) {
        if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
            return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
        if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
            return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
        heap = &AsAnyArrayBuffer(bufferVal);
        if (!LinkModuleToHeap(cx, module, heap))
            return false;
    }

    AutoObjectVector ffis(cx);
    if (!ffis.resize(module.numFFIs()))
        return false;

    for (unsigned i = 0; i < module.numGlobals(); i++) {
        AsmJSModule::Global &global = module.global(i);
        switch (global.which()) {
        case AsmJSModule::Global::Variable:
            if (!ValidateGlobalVariable(cx, module, global, importVal))
                return false;
            break;
        case AsmJSModule::Global::FFI:
            if (!ValidateFFI(cx, global, importVal, &ffis))
                return false;
            break;
        case AsmJSModule::Global::ArrayView:
        case AsmJSModule::Global::SharedArrayView:
        case AsmJSModule::Global::ArrayViewCtor:
            if (!ValidateArrayView(cx, global, globalVal, module.hasArrayView() && module.isSharedView()))
                return false;
            break;
        case AsmJSModule::Global::ByteLength:
            if (!ValidateByteLength(cx, globalVal))
                return false;
            break;
        case AsmJSModule::Global::MathBuiltinFunction:
            if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                return false;
            break;
        case AsmJSModule::Global::AtomicsBuiltinFunction:
            if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
                return false;
            break;
        case AsmJSModule::Global::Constant:
            if (!ValidateConstant(cx, global, globalVal))
                return false;
            break;
        case AsmJSModule::Global::SimdCtor:
            if (!ValidateSimdType(cx, global, globalVal))
                return false;
            break;
        case AsmJSModule::Global::SimdOperation:
            if (!ValidateSimdOperation(cx, global, globalVal))
                return false;
            break;
        }
    }

    for (unsigned i = 0; i < module.numExits(); i++)
        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();

    module.initGlobalNaN();

    return true;
}
Example #27
0
static bool
DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
    if (module.isLinked())
        return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than "
                            "once. This limitation should be removed in a future release. To "
                            "work around this, compile a second module (e.g., using the "
                            "Function constructor).");
    module.setIsLinked();

    RootedValue globalVal(cx, UndefinedValue());
    if (args.length() > 0)
        globalVal = args[0];

    RootedValue importVal(cx, UndefinedValue());
    if (args.length() > 1)
        importVal = args[1];

    RootedValue bufferVal(cx, UndefinedValue());
    if (args.length() > 2)
        bufferVal = args[2];

    Rooted<ArrayBufferObject*> heap(cx);
    if (module.hasArrayView()) {
        if (!IsTypedArrayBuffer(bufferVal))
            return LinkFail(cx, "bad ArrayBuffer argument");

        heap = &bufferVal.toObject().as<ArrayBufferObject>();

        if (!IsValidAsmJSHeapLength(heap->byteLength())) {
            return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next valid length is 0x%x",
                                            heap->byteLength(),
                                            RoundUpToNextValidAsmJSHeapLength(heap->byteLength())));
        }

        // This check is sufficient without considering the size of the loaded datum because heap
        // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
        JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
        if (heap->byteLength() < module.minHeapLength()) {
            return LinkFail(cx, JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the largest constant heap access offset rounded up to the next valid heap size).",
                                            heap->byteLength(), module.minHeapLength()));
        }

        if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
            return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

        module.initHeap(heap, cx);
    }

    AutoObjectVector ffis(cx);
    if (!ffis.resize(module.numFFIs()))
        return false;

    for (unsigned i = 0; i < module.numGlobals(); i++) {
        AsmJSModule::Global &global = module.global(i);
        switch (global.which()) {
          case AsmJSModule::Global::Variable:
            if (!ValidateGlobalVariable(cx, module, global, importVal))
                return false;
            break;
          case AsmJSModule::Global::FFI:
            if (!ValidateFFI(cx, global, importVal, &ffis))
                return false;
            break;
          case AsmJSModule::Global::ArrayView:
            if (!ValidateArrayView(cx, global, globalVal, bufferVal))
                return false;
            break;
          case AsmJSModule::Global::MathBuiltin:
            if (!ValidateMathBuiltin(cx, global, globalVal))
                return false;
            break;
          case AsmJSModule::Global::Constant:
            if (!ValidateGlobalConstant(cx, global, globalVal))
                return false;
            break;
        }
    }

    for (unsigned i = 0; i < module.numExits(); i++)
        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();

    return true;
}