示例#1
0
Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
    : m_size(initial.bytes())
    , m_initial(initial)
    , m_maximum(maximum)
    , m_mode(Mode::BoundsChecking)
    // FIXME: If we add signal based bounds checking then we need extra space for overflow on load.
    // see: https://bugs.webkit.org/show_bug.cgi?id=162693
{
    RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.

    m_mappedCapacity = maximum ? maximum.bytes() : PageCount::max().bytes();
    if (!m_mappedCapacity) {
        // This means we specified a zero as maximum (which means we also have zero as initial size).
        RELEASE_ASSERT(m_size == 0);
        m_memory = nullptr;
        m_mappedCapacity = 0;
        failed = false;
        if (verbose)
            dataLogLn("Memory::Memory allocating nothing ", *this);
        return;
    }

    // FIXME: It would be nice if we had a VM tag for wasm memory. https://bugs.webkit.org/show_bug.cgi?id=163600
    void* result = Options::simulateWebAssemblyLowMemory() ? MAP_FAILED : mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
    if (result == MAP_FAILED) {
        // Try again with a different number.
        if (verbose)
            dataLogLn("Memory::Memory mmap failed once for capacity, trying again", *this);
        m_mappedCapacity = m_size;
        if (!m_mappedCapacity) {
            m_memory = nullptr;
            failed = false;
            if (verbose)
                dataLogLn("Memory::Memory mmap not trying again because size is zero ", *this);
            return;
        }

        result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
        if (result == MAP_FAILED) {
            if (verbose)
                dataLogLn("Memory::Memory mmap failed twice ", *this);
            failed = true;
            return;
        }
    }

    ASSERT(m_size <= m_mappedCapacity);
    {
        bool success = !mprotect(result, static_cast<size_t>(m_size), PROT_READ | PROT_WRITE);
        RELEASE_ASSERT(success);
    }

    m_memory = result;
    failed = false;
    if (verbose)
        dataLogLn("Memory::Memory mmap succeeded ", *this);
}
示例#2
0
void Plan::run()
{
    if (verbose)
        dataLogLn("Starting plan.");
    {
        ModuleParser moduleParser(m_source, m_sourceLength);
        if (!moduleParser.parse()) {
            if (verbose)
                dataLogLn("Parsing module failed: ", moduleParser.errorMessage());
            m_errorMessage = moduleParser.errorMessage();
            return;
        }
        m_moduleInformation = WTFMove(moduleParser.moduleInformation());
    }
    if (verbose)
        dataLogLn("Parsed module.");

    if (!m_compiledFunctions.tryReserveCapacity(m_moduleInformation->functions.size())) {
        StringBuilder builder;
        builder.appendLiteral("Failed allocating enough space for ");
        builder.appendNumber(m_moduleInformation->functions.size());
        builder.appendLiteral(" compiled functions");
        m_errorMessage = builder.toString();
        return;
    }

    for (const FunctionInformation& info : m_moduleInformation->functions) {
        if (verbose)
            dataLogLn("Processing function starting at: ", info.start, " and ending at: ", info.end);
        const uint8_t* functionStart = m_source + info.start;
        size_t functionLength = info.end - info.start;
        ASSERT(functionLength <= m_sourceLength);

        String error = validateFunction(functionStart, functionLength, info.signature, m_moduleInformation->functions);
        if (!error.isNull()) {
            if (verbose) {
                for (unsigned i = 0; i < functionLength; ++i)
                    dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
                dataLogLn();
            }
            m_errorMessage = error;
            return;
        }

        m_compiledFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, m_moduleInformation->memory.get(), info.signature, m_moduleInformation->functions));
    }

    // Patch the call sites for each function.
    for (std::unique_ptr<FunctionCompilation>& functionPtr : m_compiledFunctions) {
        FunctionCompilation* function = functionPtr.get();
        for (auto& call : function->unlinkedCalls)
            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_compiledFunctions[call.functionIndex]->code->code()));
    }

    m_failed = false;
}
示例#3
0
bool ModuleParser::parseData()
{
    uint32_t segmentCount;
    if (!parseVarUInt32(segmentCount)
        || segmentCount == std::numeric_limits<uint32_t>::max()
        || !m_module->data.tryReserveCapacity(segmentCount))
        return false;
    if (verbose)
        dataLogLn("  segments: ", segmentCount);

    for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
        if (verbose)
            dataLogLn("  segment #", segmentNumber);
        uint32_t index;
        uint64_t offset;
        uint8_t initOpcode;
        uint32_t dataByteLength;
        if (!parseVarUInt32(index)
            || index)
            return false;

        if (!parseInitExpr(initOpcode, offset))
            return false;

        if (initOpcode != OpType::I32Const)
            return false;

        if (verbose)
            dataLogLn("    offset: ", offset);

        if (!parseVarUInt32(dataByteLength)
            || dataByteLength == std::numeric_limits<uint32_t>::max())
            return false;
        if (verbose)
            dataLogLn("    data bytes: ", dataByteLength);

        Segment* segment = Segment::make(offset, dataByteLength);
        if (!segment)
            return false;
        m_module->data.uncheckedAppend(Segment::makePtr(segment));
        for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
            uint8_t byte;
            if (!parseUInt8(byte))
                return false;
            segment->byte(dataByte) = byte;
            if (verbose)
                dataLogLn("    [", dataByte, "] = ", segment->byte(dataByte));
        }
    }
    return true;
}
示例#4
0
Memory::~Memory()
{
    if (verbose)
        dataLogLn("Memory::~Memory ", *this);
    if (m_memory) {
        if (munmap(m_memory, m_mappedCapacity))
            CRASH();
    }
}
void ObjectInitializationScope::verifyPropertiesAreInitialized(JSObject* object)
{
    Butterfly* butterfly = object->butterfly();
    Structure* structure = object->structure(m_vm);
    IndexingType indexingType = structure->indexingType();
    unsigned vectorLength = butterfly->vectorLength();
    if (UNLIKELY(hasUndecided(indexingType)) || !hasIndexedProperties(indexingType)) {
        // Nothing to verify.
    } else if (LIKELY(!hasAnyArrayStorage(indexingType))) {
        auto data = butterfly->contiguous().data();
        for (unsigned i = 0; i < vectorLength; ++i) {
            if (isScribbledValue(data[i].get())) {
                dataLogLn("Found scribbled value at i = ", i);
                ASSERT_NOT_REACHED();
            }
        }
    } else {
        ArrayStorage* storage = butterfly->arrayStorage();
        for (unsigned i = 0; i < vectorLength; ++i) {
            if (isScribbledValue(storage->m_vector[i].get())) {
                dataLogLn("Found scribbled value at i = ", i);
                ASSERT_NOT_REACHED();
            }
        }
    }

    auto isSafeEmptyValueForGCScanning = [] (JSValue value) {
#if USE(JSVALUE64)
        return !value;
#else
        return !value || !JSValue::encode(value);
#endif
    };

    for (int64_t i = 0; i < static_cast<int64_t>(structure->outOfLineCapacity()); i++) {
        // We rely on properties past the last offset be zero for concurrent GC.
        if (i + firstOutOfLineOffset > structure->lastOffset())
            ASSERT(isSafeEmptyValueForGCScanning(butterfly->propertyStorage()[-i - 1].get()));
        else if (isScribbledValue(butterfly->propertyStorage()[-i - 1].get())) {
            dataLogLn("Found scribbled property at i = ", -i - 1);
            ASSERT_NOT_REACHED();
        }
    }
}
示例#6
0
bool Plan::parseAndValidateModule()
{
    MonotonicTime startTime;
    if (verbose || Options::reportCompileTimes())
        startTime = MonotonicTime::now();

    {
        ModuleParser moduleParser(m_vm, m_source, m_sourceLength);
        auto parseResult = moduleParser.parse();
        if (!parseResult) {
            m_errorMessage = parseResult.error();
            return false;
        }
        m_moduleInformation = WTFMove(parseResult->module);
        m_functionLocationInBinary = WTFMove(parseResult->functionLocationInBinary);
        m_moduleSignatureIndicesToUniquedSignatureIndices = WTFMove(parseResult->moduleSignatureIndicesToUniquedSignatureIndices);
    }

    for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
        if (verbose)
            dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end);
        const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
        size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
        ASSERT(functionLength <= m_sourceLength);
        SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
        const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);

        auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);
        if (!validationResult) {
            if (verbose) {
                for (unsigned i = 0; i < functionLength; ++i)
                    dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
                dataLogLn();
            }
            m_errorMessage = makeString(validationResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected.
            return false;
        }
    }

    if (verbose || Options::reportCompileTimes())
        dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to validate module");
    return true;
}
示例#7
0
const Signature* SignatureInformation::get(VM* vm, SignatureIndex index)
{
    ASSERT(index != Signature::invalidIndex);
    SignatureInformation* info = get(vm);
    LockHolder lock(info->m_lock);

    if (verbose)
        dataLogLn("Got signature ", *info->m_signatures.at(index), " at index ", index);
    return info->m_signatures.at(index);
}
示例#8
0
SignatureIndex SignatureInformation::adopt(VM* vm, Signature* signature)
{
    SignatureInformation* info = get(vm);
    LockHolder lock(info->m_lock);

    SignatureIndex nextValue = info->m_signatures.size();
    auto addResult = info->m_signatureMap.add(SignatureHash { signature }, nextValue);
    if (addResult.isNewEntry) {
        ASSERT(nextValue == addResult.iterator->value);
        if (verbose)
            dataLogLn("Adopt new signature ", *signature, " with index ", addResult.iterator->value, " hash: ", signature->hash());
        info->m_signatures.append(signature);
        return nextValue;
    }
    if (verbose)
        dataLogLn("Existing signature ", *signature, " with index ", addResult.iterator->value, " hash: ", signature->hash());
    Signature::destroy(signature);
    ASSERT(addResult.iterator->value != Signature::invalidIndex);
    return addResult.iterator->value;
}
示例#9
0
bool Memory::grow(PageCount newSize)
{
    RELEASE_ASSERT(newSize > PageCount::fromBytes(m_size));

    if (verbose)
        dataLogLn("Memory::grow to ", newSize, " from ", *this);

    if (maximum() && newSize > maximum())
        return false;

    uint64_t desiredSize = newSize.bytes();

    if (m_memory && desiredSize <= m_mappedCapacity) {
        bool success = !mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE);
        RELEASE_ASSERT(success);
        m_size = desiredSize;
        if (verbose)
            dataLogLn("Memory::grow in-place ", *this);
        return true;
    }

    // Otherwise, let's try to make some new memory.
    void* newMemory = mmap(nullptr, desiredSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    if (newMemory == MAP_FAILED)
        return false;

    if (m_memory) {
        memcpy(newMemory, m_memory, m_size);
        bool success = !munmap(m_memory, m_mappedCapacity);
        RELEASE_ASSERT(success);
    }
    m_memory = newMemory;
    m_mappedCapacity = desiredSize;
    m_size = desiredSize;

    if (verbose)
        dataLogLn("Memory::grow ", *this);
    return true;
}
    bool run()
    {
        ASSERT(m_graph.m_form == SSA);

        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;
            block->ssa->availabilityAtHead.clear();
            block->ssa->availabilityAtTail.clear();
        }

        BasicBlock* root = m_graph.block(0);
        root->ssa->availabilityAtHead.m_locals.fill(Availability::unavailable());
        for (unsigned argument = m_graph.m_argumentFormats.size(); argument--;) {
            FlushedAt flushedAt = FlushedAt(
                m_graph.m_argumentFormats[argument],
                virtualRegisterForArgument(argument));
            root->ssa->availabilityAtHead.m_locals.argument(argument) = Availability(flushedAt);
        }

        // This could be made more efficient by processing blocks in reverse postorder.

        LocalOSRAvailabilityCalculator calculator(m_graph);
        bool changed;
        do {
            changed = false;

            for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
                BasicBlock* block = m_graph.block(blockIndex);
                if (!block)
                    continue;

                calculator.beginBlock(block);

                for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex)
                    calculator.executeNode(block->at(nodeIndex));

                if (calculator.m_availability == block->ssa->availabilityAtTail)
                    continue;

                block->ssa->availabilityAtTail = calculator.m_availability;
                changed = true;

                for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
                    BasicBlock* successor = block->successor(successorIndex);
                    successor->ssa->availabilityAtHead.merge(calculator.m_availability);
                    successor->ssa->availabilityAtHead.pruneByLiveness(
                        m_graph, successor->at(0)->origin.forExit);
                }
            }
        } while (changed);

        if (validationEnabled()) {

            for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
                BasicBlock* block = m_graph.block(blockIndex);
                if (!block)
                    continue;

                calculator.beginBlock(block);

                for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
                    if (block->at(nodeIndex)->origin.exitOK) {
                        // If we're allowed to exit here, the heap must be in a state
                        // where exiting wouldn't crash. These particular fields are
                        // required for correctness because we use them during OSR exit
                        // to do meaningful things. It would be wrong for any of them
                        // to be dead.

                        AvailabilityMap availabilityMap = calculator.m_availability;
                        availabilityMap.pruneByLiveness(m_graph, block->at(nodeIndex)->origin.forExit);

                        for (auto heapPair : availabilityMap.m_heap) {
                            switch (heapPair.key.kind()) {
                            case ActivationScopePLoc:
                            case ActivationSymbolTablePLoc:
                            case FunctionActivationPLoc:
                            case FunctionExecutablePLoc:
                            case StructurePLoc:
                                if (heapPair.value.isDead()) {
                                    dataLogLn("PromotedHeapLocation is dead, but should not be: ", heapPair.key);
                                    availabilityMap.dump(WTF::dataFile());
                                    CRASH();
                                }
                                break;

                            default:
                                break;
                            }
                        }
                    }

                    calculator.executeNode(block->at(nodeIndex));
                }
            }
        }

        return true;
    }
示例#11
0
// We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy.
// The reason this is OK is that we guarantee that the main thread doesn't continue until all threads
// that could touch its stack are done executing.
SUPPRESS_ASAN 
void Plan::run()
{
    if (!parseAndValidateModule())
        return;

    auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
        if (UNLIKELY(!vector.tryReserveCapacity(size))) {
            StringBuilder builder;
            builder.appendLiteral("Failed allocating enough space for ");
            builder.appendNumber(size);
            builder.append(what);
            m_errorMessage = builder.toString();
            return false;
        }
        return true;
    };

    if (!tryReserveCapacity(m_wasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs")
        || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls")
        || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions")
        || !tryReserveCapacity(m_compilationContexts, m_functionLocationInBinary.size(), " compilation contexts"))
        return;

    m_unlinkedWasmToWasmCalls.resize(m_functionLocationInBinary.size());
    m_wasmInternalFunctions.resize(m_functionLocationInBinary.size());
    m_compilationContexts.resize(m_functionLocationInBinary.size());

    for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
        Import* import = &m_moduleInformation->imports[importIndex];
        if (import->kind != ExternalKind::Function)
            continue;
        unsigned importFunctionIndex = m_wasmExitStubs.size();
        if (verbose)
            dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
        SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex);
        m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex));
    }

    m_currentIndex = 0;

    auto doWork = [this] {
        while (true) {
            uint32_t functionIndex;
            {
                auto locker = holdLock(m_lock);
                if (m_currentIndex >= m_functionLocationInBinary.size())
                    return;
                functionIndex = m_currentIndex;
                ++m_currentIndex;
            }

            const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
            size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
            ASSERT(functionLength <= m_sourceLength);
            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
            const Signature* signature = SignatureInformation::get(m_vm, signatureIndex);
            unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex;
            ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex);
            ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices));

            m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
            auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices);

            if (UNLIKELY(!parseAndCompileResult)) {
                auto locker = holdLock(m_lock);
                if (!m_errorMessage) {
                    // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
                    m_errorMessage = makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected.
                }
                m_currentIndex = m_functionLocationInBinary.size();

                // We will terminate on the next execution.
                continue; 
            }

            m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult);
        }
    };

    MonotonicTime startTime;
    if (verbose || Options::reportCompileTimes())
        startTime = MonotonicTime::now();

    uint32_t threadCount = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1;
    uint32_t numWorkerThreads = threadCount - 1;
    Vector<ThreadIdentifier> threads;
    threads.reserveCapacity(numWorkerThreads);
    for (uint32_t i = 0; i < numWorkerThreads; i++)
        threads.uncheckedAppend(createThread("jsc.wasm-b3-compilation.thread", doWork));

    doWork(); // Let the main thread do some work too.

    for (uint32_t i = 0; i < numWorkerThreads; i++)
        waitForThreadCompletion(threads[i]);

    for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) {
        {
            CompilationContext& context = m_compilationContexts[functionIndex];
            SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex];
            String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString();
            {
                LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr);
                m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation =
                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts));
            }

            {
                LinkBuffer linkBuffer(*m_vm, *context.jsEntrypointJIT, nullptr);
                linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress()));

                m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation =
                    std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts));
            }
        }
    }

    if (verbose || Options::reportCompileTimes()) {
        dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(),
            " us to compile and link the module");
    }

    // Patch the call sites for each WebAssembly function.
    for (auto& unlinked : m_unlinkedWasmToWasmCalls) {
        for (auto& call : unlinked) {
            void* executableAddress;
            if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) {
                // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462
                executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs
                    ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress()
                    : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress();
            } else {
                ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs);
                executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress();
            }
            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress));
        }
    }

    m_failed = false;
}