Changeset 215533 in webkit


Ignore:
Timestamp:
Apr 19, 2017 3:05:51 PM (7 years ago)
Author:
jfbastien@apple.com
Message:

WebAssembly: fast memory cleanups
https://bugs.webkit.org/show_bug.cgi?id=170909

Reviewed by Saam Barati.

  • b3/B3LowerToAir.cpp: correct comment, and make wasm-independent

(JSC::B3::Air::LowerToAir::lower):

  • b3/B3Procedure.h:
  • b3/B3Validate.cpp:
  • b3/B3Value.cpp:

(JSC::B3::Value::effects):

  • b3/B3WasmBoundsCheckValue.cpp: have the creator pass in a

maximum, so we don't have to know so much about wasm here
(JSC::B3::WasmBoundsCheckValue::WasmBoundsCheckValue):
(JSC::B3::WasmBoundsCheckValue::cloneImpl):
(JSC::B3::WasmBoundsCheckValue::dumpMeta):

  • b3/B3WasmBoundsCheckValue.h:

(JSC::B3::WasmBoundsCheckValue::boundsType):
(JSC::B3::WasmBoundsCheckValue::bounds):

  • b3/air/AirCode.h:
  • b3/air/AirCustom.h:

(JSC::B3::Air::WasmBoundsCheckCustom::generate):

  • b3/testb3.cpp:

(JSC::B3::testWasmBoundsCheck):

  • wasm/WasmB3IRGenerator.cpp:

(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
(JSC::Wasm::createJSToWasmWrapper): remove dead code

  • wasm/WasmMemory.cpp: don't GC if no memory could possibly be free'd

(JSC::Wasm::Memory::initializePreallocations): verbose-only code,
and copy-pasta bug

Location:
trunk/Source/JavaScriptCore
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r215531 r215533  
     12017-04-19  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly: fast memory cleanups
     4        https://bugs.webkit.org/show_bug.cgi?id=170909
     5
     6        Reviewed by Saam Barati.
     7
     8        * b3/B3LowerToAir.cpp: correct comment, and make wasm-independent
     9        (JSC::B3::Air::LowerToAir::lower):
     10        * b3/B3Procedure.h:
     11        * b3/B3Validate.cpp:
     12        * b3/B3Value.cpp:
     13        (JSC::B3::Value::effects):
     14        * b3/B3WasmBoundsCheckValue.cpp: have the creator pass in a
     15        maximum, so we don't have to know so much about wasm here
     16        (JSC::B3::WasmBoundsCheckValue::WasmBoundsCheckValue):
     17        (JSC::B3::WasmBoundsCheckValue::cloneImpl):
     18        (JSC::B3::WasmBoundsCheckValue::dumpMeta):
     19        * b3/B3WasmBoundsCheckValue.h:
     20        (JSC::B3::WasmBoundsCheckValue::boundsType):
     21        (JSC::B3::WasmBoundsCheckValue::bounds):
     22        * b3/air/AirCode.h:
     23        * b3/air/AirCustom.h:
     24        (JSC::B3::Air::WasmBoundsCheckCustom::generate):
     25        * b3/testb3.cpp:
     26        (JSC::B3::testWasmBoundsCheck):
     27        * wasm/WasmB3IRGenerator.cpp:
     28        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
     29        (JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
     30        (JSC::Wasm::createJSToWasmWrapper): remove dead code
     31        * wasm/WasmMemory.cpp: don't GC if no memory could possibly be free'd
     32        (JSC::Wasm::Memory::initializePreallocations): verbose-only code,
     33        and copy-pasta bug
     34
    1352017-04-19  Mark Lam  <mark.lam@apple.com>
    236
  • trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp

    r215407 r215533  
    31473147
    31483148        case B3::WasmBoundsCheck: {
    3149 #if ENABLE(WEBASSEMBLY)
    31503149            WasmBoundsCheckValue* value = m_value->as<WasmBoundsCheckValue>();
    31513150
     
    31653164
    31663165            Arg limit;
    3167             if (value->pinnedGPR() != InvalidGPRReg)
    3168                 limit = Arg(value->pinnedGPR());
    3169             else {
    3170                 // Signaling memories don't pin a register because only the accesses whose reg+imm could ever overflow 4GiB+redzone need to be checked,
    3171                 // and we don't think these will be frequent. All other accesses will trap due to PROT_NONE pages.
    3172                 //
    3173                 // If we got here it's because a memory access had a very large offset. We could check that it doesn't exceed 4GiB+redzone since that's
    3174                 // technically the limit we need to avoid overflowing, but it's better if we use a smaller immediate which codegens more easily.
    3175                 // We know that anything above the declared 'maximum' will trap, so we can compare against that number. If there was no declared
    3176                 // 'maximum' then we still know that any access above 4GiB will trap, no need to add the redzone.
     3166            switch (value->boundsType()) {
     3167            case WasmBoundsCheckValue::Type::Pinned:
     3168                limit = Arg(value->bounds().pinned);
     3169                break;
     3170
     3171            case WasmBoundsCheckValue::Type::Maximum:
    31773172                limit = m_code.newTmp(GP);
    3178                 size_t limitValue = value->maximum() ? value->maximum().bytes() : std::numeric_limits<uint32_t>::max();
    3179                 ASSERT(limitValue <= value->redzoneLimit());
    3180                 if (imm(limitValue))
    3181                     append(Move, imm(limitValue), limit);
     3173                if (imm(value->bounds().maximum))
     3174                    append(Move, imm(value->bounds().maximum), limit);
    31823175                else
    3183                     append(Move, Arg::bigImm(limitValue), limit);
    3184             }
     3176                    append(Move, Arg::bigImm(value->bounds().maximum), limit);
     3177                break;
     3178            }
     3179
    31853180            append(Inst(Air::WasmBoundsCheck, value, ptrPlusImm, limit));
    3186 #else
    3187             append(Air::Oops);
    3188 #endif // ENABLE(WEBASSEMBLY)
    31893181            return;
    31903182        }
  • trunk/Source/JavaScriptCore/b3/B3Procedure.h

    r215292 r215533  
    5959namespace Air { class Code; }
    6060
    61 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg, unsigned);
     61typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg);
    6262typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator;
    6363
  • trunk/Source/JavaScriptCore/b3/B3Validate.cpp

    r215340 r215533  
    471471                VALIDATE(value->numChildren() == 1, ("At ", *value));
    472472                VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
    473                 if (value->as<WasmBoundsCheckValue>()->pinnedGPR() != InvalidGPRReg)
    474                     VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->pinnedGPR()), ("At ", *value));
     473                switch (value->as<WasmBoundsCheckValue>()->boundsType()) {
     474                case WasmBoundsCheckValue::Type::Pinned:
     475                    VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->bounds().pinned), ("At ", *value));
     476                    break;
     477                case WasmBoundsCheckValue::Type::Maximum:
     478                    break;
     479                }
    475480                VALIDATE(m_procedure.code().wasmBoundsCheckGenerator(), ("At ", *value));
    476481                break;
  • trunk/Source/JavaScriptCore/b3/B3Value.cpp

    r215340 r215533  
    666666        break;
    667667    case WasmBoundsCheck:
    668         if (as<WasmBoundsCheckValue>()->pinnedGPR() != InvalidGPRReg)
     668        switch (as<WasmBoundsCheckValue>()->boundsType()) {
     669        case WasmBoundsCheckValue::Type::Pinned:
    669670            result.readsPinned = true;
     671            break;
     672        case WasmBoundsCheckValue::Type::Maximum:
     673            break;
     674        }
    670675        result.exitsSideways = true;
    671676        break;
  • trunk/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.cpp

    r215340 r215533  
    3636}
    3737
    38 WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, GPRReg pinnedGPR, unsigned offset, PageCount maximum)
     38WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, unsigned offset, GPRReg pinnedGPR)
    3939    : Value(CheckedOpcode, WasmBoundsCheck, origin, ptr)
    40     , m_pinnedGPR(pinnedGPR)
    4140    , m_offset(offset)
    42     , m_maximum(maximum)
     41    , m_boundsType(Type::Pinned)
    4342{
     43    m_bounds.pinned = pinnedGPR;
     44}
     45
     46WasmBoundsCheckValue::WasmBoundsCheckValue(Origin origin, Value* ptr, unsigned offset, size_t maximum)
     47    : Value(CheckedOpcode, WasmBoundsCheck, origin, ptr)
     48    , m_offset(offset)
     49    , m_boundsType(Type::Maximum)
     50{
     51#if ENABLE(WEBASSEMBLY)
     52    size_t redzoneLimit = static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + Wasm::Memory::fastMappedRedzoneBytes();
     53    ASSERT_UNUSED(redzoneLimit, maximum <= redzoneLimit);
     54#endif
     55    m_bounds.maximum = maximum;
    4456}
    4557
     
    4961}
    5062
    51 size_t WasmBoundsCheckValue::redzoneLimit() const
    52 {
    53     ASSERT(m_pinnedGPR == InvalidGPRReg);
    54 #if ENABLE(WEBASSEMBLY)
    55     return static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + Wasm::Memory::fastMappedRedzoneBytes();
    56 #else
    57     RELEASE_ASSERT_NOT_REACHED();
    58 #endif
    59 }
    60 
    6163void WasmBoundsCheckValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
    6264{
    63     if (m_pinnedGPR == InvalidGPRReg)
    64         out.print(comma, "redzoneLimit = ", redzoneLimit(), ", offset = ", m_offset);
    65     else
    66         out.print(comma, "sizeRegister = ", m_pinnedGPR, ", offset = ", m_offset);
     65    switch (m_boundsType) {
     66    case Type::Pinned:
     67        out.print(comma, "offset = ", m_offset, ", pinned = ", m_bounds.pinned);
     68        break;
     69    case Type::Maximum:
     70        out.print(comma, "offset = ", m_offset, ", maximum = ", m_bounds.maximum);
     71        break;
     72    }
    6773}
    6874
  • trunk/Source/JavaScriptCore/b3/B3WasmBoundsCheckValue.h

    r215340 r215533  
    3030#include "B3Value.h"
    3131#include "CCallHelpers.h"
    32 #include "WasmPageCount.h"
    3332
    3433namespace JSC { namespace B3 {
     
    4847    ~WasmBoundsCheckValue();
    4948
    50 #if ENABLE(WEBASSEMBLY)
    51     typedef Wasm::PageCount PageCount;
    52 #else
    53     typedef char PageCount;
    54 #endif
     49    enum class Type {
     50        Pinned,
     51        Maximum,
     52    };
    5553
    56     GPRReg pinnedGPR() const { return m_pinnedGPR; }
     54    union Bounds {
     55        GPRReg pinned;
     56        size_t maximum;
     57    };
     58
    5759    unsigned offset() const { return m_offset; }
    58     size_t redzoneLimit() const;
    59     PageCount maximum() const { return m_maximum; }
     60    Type boundsType() const { return m_boundsType; }
     61    Bounds bounds() const { return m_bounds; }
    6062
    6163protected:
     
    6769    friend class Procedure;
    6870
    69     JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, GPRReg pinnedGPR, unsigned offset, PageCount maximum);
     71    JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, unsigned offset, GPRReg pinnedGPR);
     72    JS_EXPORT_PRIVATE WasmBoundsCheckValue(Origin, Value* ptr, unsigned offset, size_t maximum);
    7073
    71     GPRReg m_pinnedGPR;
    7274    unsigned m_offset;
    73     PageCount m_maximum;
     75    Type m_boundsType;
     76    Bounds m_bounds;
    7477};
    7578
  • trunk/Source/JavaScriptCore/b3/air/AirCode.h

    r215292 r215533  
    5656class Disassembler;
    5757
    58 typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg, unsigned);
     58typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&, GPRReg);
    5959typedef SharedTask<WasmBoundsCheckGeneratorFunction> WasmBoundsCheckGenerator;
    6060
  • trunk/Source/JavaScriptCore/b3/air/AirCustom.h

    r214827 r215533  
    315315            [outOfBounds, value] (CCallHelpers& jit, Air::GenerationContext& context) {
    316316                outOfBounds.link(&jit);
    317                 context.code->wasmBoundsCheckGenerator()->run(jit, value->pinnedGPR(), value->offset());
     317                switch (value->boundsType()) {
     318                case WasmBoundsCheckValue::Type::Pinned:
     319                    context.code->wasmBoundsCheckGenerator()->run(jit, value->bounds().pinned);
     320                    break;
     321
     322                case WasmBoundsCheckValue::Type::Maximum:
     323                    context.code->wasmBoundsCheckGenerator()->run(jit, InvalidGPRReg);
     324                    break;
     325                }
    318326            }));
    319327
  • trunk/Source/JavaScriptCore/b3/testb3.cpp

    r215407 r215533  
    1517115171    proc.pinRegister(pinned);
    1517215172
    15173     proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned actualOffset) {
     15173    proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) {
    1517415174        CHECK_EQ(pinnedGPR, pinned);
    15175         CHECK_EQ(actualOffset, offset);
    1517615175
    1517715176        // This should always work because a function this simple should never have callee
     
    1518615185    if (pointerType() != Int32)
    1518715186        left = root->appendNew<Value>(proc, Trunc, Origin(), left);
    15188     Wasm::PageCount maximum;
    15189     root->appendNew<WasmBoundsCheckValue>(proc, Origin(), left, pinned, offset, maximum);
     15187    root->appendNew<WasmBoundsCheckValue>(proc, Origin(), left, pinned, offset);
    1519015188    Value* result = root->appendNew<Const32Value>(proc, Origin(), 0x42);
    1519115189    root->appendNewControlValue(proc, Return, Origin(), result);
  • trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp

    r215407 r215533  
    6060#include "WasmOpcodeOrigin.h"
    6161#include "WasmThunks.h"
     62#include <limits>
    6263#include <wtf/Optional.h>
    6364#include <wtf/StdLibExtras.h>
     
    353354
    354355    if (info.memory) {
    355         m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
     356        m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) {
    356357            AllowMacroScratchRegisterUsage allowScratch(jit);
    357358            switch (m_mode) {
     
    550551{
    551552    ASSERT(m_memoryBaseGPR);
     553
    552554    switch (m_mode) {
    553555    case MemoryMode::BoundsChecking:
     
    555557        ASSERT(m_memorySizeGPR);
    556558        ASSERT(sizeOfOperation + offset > offset);
    557         m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1, m_info.memory.maximum());
     559        m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, sizeOfOperation + offset - 1, m_memorySizeGPR);
    558560        break;
     561
    559562    case MemoryMode::Signaling:
    560         // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current], and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register memory accesses are 32-bit. However WebAssembly register+immediate accesses perform the addition in 64-bit which can push an access above the 32-bit limit. The redzone will catch most small immediates, and we'll explicitly bounds check any register + large immediate access.
    561         if (offset >= Memory::fastMappedRedzoneBytes())
    562             m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, InvalidGPRReg, sizeOfOperation + offset - 1, m_info.memory.maximum());
     563        // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current],
     564        // and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register
     565        // memory accesses are 32-bit. However WebAssembly register + offset accesses perform the addition in 64-bit which can push an access above
     566        // the 32-bit limit (the offset is unsigned 32-bit). The redzone will catch most small offsets, and we'll explicitly bounds check any
     567        // register + large offset access. We don't think this will be generated frequently.
     568        //
     569        // We could check that register + large offset doesn't exceed 4GiB+redzone since that's technically the limit we need to avoid overflowing the
     570        // PROT_NONE region, but it's better if we use a smaller immediate because it can codegens better. We know that anything equal to or greater
     571        // than the declared 'maximum' will trap, so we can compare against that number. If there was no declared 'maximum' then we still know that
     572        // any access equal to or greater than 4GiB will trap, no need to add the redzone.
     573        if (offset >= Memory::fastMappedRedzoneBytes()) {
     574            size_t maximum = m_info.memory.maximum() ? m_info.memory.maximum().bytes() : std::numeric_limits<uint32_t>::max();
     575            m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, sizeOfOperation + offset - 1, maximum);
     576        }
    563577        break;
     578
    564579    case MemoryMode::NumberOfMemoryModes:
    565580        RELEASE_ASSERT_NOT_REACHED();
     
    13011316    compilationContext.jsEntrypointToWasmEntrypointCall = jit.call();
    13021317
    1303     if (!!info.memory) {
    1304         // Resetting the register prevents the GC from mistakenly thinking that the context is still live.
    1305         GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
    1306         jit.move(CCallHelpers::TrustedImm32(0), baseMemory);
    1307     }
    1308 
    13091318    for (const RegisterAtOffset& regAtOffset : registersToSpill) {
    13101319        GPRReg reg = regAtOffset.reg().gpr();
  • trunk/Source/JavaScriptCore/wasm/WasmMemory.cpp

    r215525 r215533  
    4949
    5050NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntGetFastMemory() { CRASH(); }
    51 NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnmapMemoryBytes() { CRASH(); }
     51NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnmapMemory() { CRASH(); }
    5252NEVER_INLINE NO_RETURN_DUE_TO_CRASH void webAssemblyCouldntUnprotectMemory() { CRASH(); }
    5353
     
    6767{
    6868    if (UNLIKELY(munmap(memory, size)))
    69         webAssemblyCouldntUnmapMemoryBytes();
     69        webAssemblyCouldntUnmapMemory();
    7070}
    7171
     
    150150        if (memory)
    151151            dataLogLnIf(verbose, "tryGetFastMemory re-using ", RawPointer(memory));
    152         else {
    153             // No memory was available in the cache. Maybe waiting on GC will find a free one.
     152        else if (currentlyAllocatedFastMemories.load(std::memory_order_acquire) >= 1) {
     153            // No memory was available in the cache, but we know there's at least one currently live. Maybe GC will find a free one.
    154154            // FIXME collectSync(Full) and custom eager destruction of wasm memories could be better. For now use collectAllGarbage. Also, nothing tells us the current VM is holding onto fast memories. https://bugs.webkit.org/show_bug.cgi?id=170748
    155155            dataLogLnIf(verbose, "tryGetFastMemory waiting on GC and retrying");
     
    299299    // Races cannot occur in this function: it is only called at program initialization, before WebAssembly can be invoked.
    300300
    301     const auto startTime = MonotonicTime::now();
     301    MonotonicTime startTime;
     302    if (verbose)
     303        startTime = MonotonicTime::now();
     304
    302305    const size_t desiredFastMemories = std::min<size_t>(Options::webAssemblyFastMemoryPreallocateCount(), fastMemoryCacheHardLimit);
    303306
     
    306309        if (void *memory = mmapBytes(Memory::fastMappedBytes() * numContiguous)) {
    307310            for (size_t subMemory = 0; subMemory < numContiguous; ++subMemory) {
    308                 void* startAddress = reinterpret_cast<char*>(memory) + Memory::fastMappedBytes() * subMemory + subMemory;
     311                void* startAddress = reinterpret_cast<char*>(memory) + Memory::fastMappedBytes() * subMemory;
    309312                bool inserted = false;
    310313                for (size_t cacheEntry = 0; cacheEntry < fastMemoryCacheHardLimit; ++cacheEntry) {
     
    338341    observedMaximumFastMemory.store(fastMemoryPreallocateCount, std::memory_order_relaxed);
    339342
    340     const auto endTime = MonotonicTime::now();
    341 
    342     for (size_t cacheEntry = 0; cacheEntry < fastMemoryPreallocateCount; ++cacheEntry) {
    343         void* startAddress = fastMemoryCache[cacheEntry].load(std::memory_order_relaxed);
    344         ASSERT(startAddress);
    345         dataLogLnIf(verbose, "Pre-allocation of WebAssembly fast memory at ", RawPointer(startAddress));
    346     }
    347     dataLogLnIf(verbose, "Pre-allocated ", fastMemoryPreallocateCount, " WebAssembly fast memories in ", fastMemoryPreallocateCount == 0 ? 0 : fragments, fragments == 1 ? " fragment, took " : " fragments, took ", endTime - startTime);
     343    if (verbose) {
     344        MonotonicTime endTime = MonotonicTime::now();
     345
     346        for (size_t cacheEntry = 0; cacheEntry < fastMemoryPreallocateCount; ++cacheEntry) {
     347            void* startAddress = fastMemoryCache[cacheEntry].load(std::memory_order_relaxed);
     348            ASSERT(startAddress);
     349            dataLogLn("Pre-allocation of WebAssembly fast memory at ", RawPointer(startAddress));
     350        }
     351
     352        dataLogLn("Pre-allocated ", fastMemoryPreallocateCount, " WebAssembly fast memories in ", fastMemoryPreallocateCount == 0 ? 0 : fragments, fragments == 1 ? " fragment, took " : " fragments, took ", endTime - startTime);
     353    }
    348354}
    349355
Note: See TracChangeset for help on using the changeset viewer.