Bug 162693 - WASM should support faster loads.
Summary: WASM should support faster loads.
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Keith Miller
URL:
Keywords:
Depends on: 162689
Blocks: 159775 164032
  Show dependency treegraph
 
Reported: 2016-09-28 11:21 PDT by Keith Miller
Modified: 2017-03-07 10:45 PST (History)
11 users (show)

See Also:


Attachments
WIP (38.60 KB, patch)
2017-02-27 14:24 PST, Keith Miller
no flags Details | Formatted Diff | Diff
WIP (40.34 KB, patch)
2017-02-27 16:33 PST, Keith Miller
no flags Details | Formatted Diff | Diff
WIP (68.82 KB, patch)
2017-02-28 12:56 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Compiles (83.13 KB, patch)
2017-02-28 18:50 PST, Keith Miller
no flags Details | Formatted Diff | Diff
WIP (93.40 KB, patch)
2017-03-01 13:15 PST, Keith Miller
no flags Details | Formatted Diff | Diff
passes tests (115.47 KB, patch)
2017-03-01 21:08 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Patch (112.21 KB, patch)
2017-03-02 09:24 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Patch (112.68 KB, patch)
2017-03-02 09:51 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Patch (112.71 KB, patch)
2017-03-02 10:01 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Patch (112.95 KB, patch)
2017-03-02 17:24 PST, Keith Miller
no flags Details | Formatted Diff | Diff
Patch for landing (113.02 KB, patch)
2017-03-03 13:43 PST, Keith Miller
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Keith Miller 2016-09-28 11:21:11 PDT
...
Comment 1 Keith Miller 2017-02-27 14:24:43 PST
Created attachment 302873 [details]
WIP
Comment 2 Keith Miller 2017-02-27 16:33:12 PST
Created attachment 302888 [details]
WIP
Comment 3 Keith Miller 2017-02-27 16:34:22 PST
Seems to not crash when doing an invalid load. Now to fix some new failing tests and cleanup.
Comment 4 Keith Miller 2017-02-28 12:56:15 PST
Created attachment 302971 [details]
WIP
Comment 5 Keith Miller 2017-02-28 12:59:01 PST
I realized that I need to have a code block for Wasm Modules since we might need to have different blocks for different memory types. I've already added a JSWebAssemblyCodeBlock class (no relation no the JS one, maybe I should pick a different name?) but I need to make it play nice with Wasm::Plan.
Comment 6 Keith Miller 2017-02-28 18:50:17 PST
Created attachment 303026 [details]
Compiles
Comment 7 Keith Miller 2017-03-01 13:15:16 PST
Created attachment 303107 [details]
WIP
Comment 8 Keith Miller 2017-03-01 21:08:19 PST
Created attachment 303172 [details]
passes tests
Comment 9 Keith Miller 2017-03-02 09:24:54 PST
Created attachment 303204 [details]
Patch
Comment 10 Keith Miller 2017-03-02 09:25:17 PST
I'm going to test on iOS now.
Comment 11 WebKit Commit Bot 2017-03-02 09:27:05 PST
Attachment 303204 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:123:  Missing space after ,  [whitespace/comma] [3]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h:70:  The parameter name "mode" adds no information, so it should be removed.  [readability/parameter_name] [5]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp:47:  Missing space after ,  [whitespace/comma] [3]
Total errors found: 3 in 37 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 12 Keith Miller 2017-03-02 09:51:30 PST
Created attachment 303211 [details]
Patch
Comment 13 WebKit Commit Bot 2017-03-02 09:54:31 PST
Attachment 303211 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:123:  Missing space after ,  [whitespace/comma] [3]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h:70:  The parameter name "mode" adds no information, so it should be removed.  [readability/parameter_name] [5]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp:47:  Missing space after ,  [whitespace/comma] [3]
Total errors found: 3 in 37 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 14 Keith Miller 2017-03-02 10:01:43 PST
Created attachment 303214 [details]
Patch
Comment 15 WebKit Commit Bot 2017-03-02 10:04:11 PST
Attachment 303214 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:123:  Missing space after ,  [whitespace/comma] [3]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h:70:  The parameter name "mode" adds no information, so it should be removed.  [readability/parameter_name] [5]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp:47:  Missing space after ,  [whitespace/comma] [3]
Total errors found: 3 in 37 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 16 Saam Barati 2017-03-02 14:21:53 PST
Comment on attachment 303214 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=303214&action=review

Initial pass of comments. Gonna keep reviewing.

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:42
> +static const bool verbose = false;

Style: Why not just constexpr? Then I suspect this wouldn't need to be in an anonymous namespace.

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:85
> +                FirstArgumentGPR = static_cast<uint64_t>(ExceptionType::OutOfBoundsMemoryAccess);
> +                InstructionPointerGPR = reinterpret_cast<uint64_t>(exceptionStub.code().executableAddress());

Style: These feel like they should be functions, and the function can handle the various platforms as needed.

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:91
> +    sigaction(signal, &oldSigBusHandler, nullptr);

It might be worth a comment here, saying, we only use fastMemory in processes we control, and we expect the old handler to crash.
Otherwise, we'd need a way to re-install the Wasm handler if we think there is any way old handler would recover from the SIGBUS.

> Source/JavaScriptCore/wasm/WasmMemory.cpp:59
> +// We use this as a heuristic to guess mode a memory import will be. Most of the time we expect users to

"guess mode" => "guess what mode"
Also, maybe you can open a bug to consider alternatives to this? And to test out various programs to see how they behave?

> Source/JavaScriptCore/wasm/WasmMemory.cpp:66
> +Memory::Mode Memory::lastAllocatedMode()
>  {
> -    switch (mode) {
> -    case Mode::BoundsChecking: return "BoundsChecking";
> +    return lastAllocatedMemoryMode;
> +}

I don't think this will work because it's racy inside the process.

> Source/JavaScriptCore/wasm/WasmMemory.cpp:68
> +static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^33 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold.");

Nit: It's not quite 2^33, it's 2^33 - 2

> Source/JavaScriptCore/wasm/WasmMemory.cpp:70
> +static const size_t fastMemoryMappedBytes = static_cast<size_t>(std::numeric_limits<uint32_t>::max()) * 2; // pointer max + offset max. This is all we need since a load straddling readable memory will trap.

Nit: I think this could use some explaining:
The largest possible pointer is UINT32_MAX, the largest possible offset is also UINT32_MAX, therefore, the largest possible base of a load is UINT32_MAX + UINT32_MAX

Stated this way, I wonder if your size needs +1 to it?

> Source/JavaScriptCore/wasm/WasmMemory.cpp:77
> +inline Deque<void*, maxFastMemories>& availableFastMemories()
> +{
> +    static NeverDestroyed<Deque<void*, maxFastMemories>> availableFastMemories;
> +    return availableFastMemories;
> +}

You need a lock protecting accesses to this, given that it's per process.

> Source/JavaScriptCore/wasm/WasmMemory.cpp:134
> +    // We need to mprotect PROT_NONE all the existing memory. If we can't, for
> +    // whatever reason, we should bail out and get rid of this memory.
> +    if (mprotect(memory, writableSize, PROT_NONE))
> +        return;

This should probably crash IMO

> Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp:91
> +        m_mode = Memory::lastAllocatedMode();

See earlier comment about this being racy and wrong. Why not just get this off the created memory if there is one? Or take a racy guess if you didn't create the memory

> Source/JavaScriptCore/wasm/WasmMemoryInformation.h:70
> +    // This is a unique_ptr so we can pass ModuleInformation as const and still be
> +    // able to take ownership of this.

It's not a unique_ptr. Please remove comment.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp:67
> +    // We can't use a ref here since it doesn't have a copy constructor...

See below, I think you can delete this.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp:68
> +    RefPtr<Wasm::Memory> memoryRefPtr = &m_memory.get();

I think webkit style is to call this protectedMemory or something similar.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp:69
> +    auto destructor = [memoryRefPtr] (void*) { };

I think you can make the above a Ref, and then declare this lambda like:

auto destructor = [memoryProtector = WTFMove(memoryProtector)] (void*) { };
Comment 17 Keith Miller 2017-03-02 17:24:04 PST
Created attachment 303273 [details]
Patch
Comment 18 WebKit Commit Bot 2017-03-02 17:26:59 PST
Attachment 303273 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:123:  Missing space after ,  [whitespace/comma] [3]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h:70:  The parameter name "mode" adds no information, so it should be removed.  [readability/parameter_name] [5]
ERROR: Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp:47:  Missing space after ,  [whitespace/comma] [3]
Total errors found: 3 in 37 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 19 Saam Barati 2017-03-03 11:09:59 PST
Comment on attachment 303273 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=303273&action=review

r=me
I think I found one actual bug. I have some misc suggestions, and some requests for tests. Comments below.

> Source/WTF/ChangeLog:17
> +        Also, add a operator! to Ref has the same semantics as C++ refs.

"Ref has" => "Ref that has"

> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:498
> +    auto makeKind = [&] (B3::Opcode opcode) -> B3::Kind {
> +        if (m_info.memory.mode() == Memory::Signaling)
> +            return trapping(opcode);
> +        return opcode;
> +    };

Nit: This is identical to the lambda inside emitStoreOp. You could make it a member function on B3IRGenerator to remove the duplicate code.

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:77
> +            if (start <= faultingInstruction && faultingInstruction <= end) {

Should not be <= end. I think it should be <, right?

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:82
> +                MacroAssemblerCodeRef exceptionStub = vm->jitStubs->existingCTIStub(throwExceptionFromWasmThunkGenerator);
> +                // If for whatever reason we don't have a stub then we should just treat this like a regular crash.
> +                if (!exceptionStub.size())
> +                    break;

This seems like it should go outside the loop.

> Source/JavaScriptCore/wasm/WasmMemory.cpp:70
> +static const size_t fastMemoryMappedBytes = (static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 1) * 2; // pointer max + offset max. This is all we need since a load straddling readable memory will trap.

Nit: I think this should be:
UINT32_MAX*2 + 1, not (UINT32_MAX + 1)*2

Also, please add some tests for this. It'd be nice to test loads at the largest possible (pointer+offset) combination.
Can you also test loads that straddle the mapped PROT_READ/WRITE region?

> Source/JavaScriptCore/wasm/WasmMemory.cpp:89
> +    if (!vm.getCTIStub(throwExceptionFromWasmThunkGenerator).size())

Style nit: size seems like a weird condition here. Why not just use ! on the actual MacroAssemblerCodeRef itself?

> Source/JavaScriptCore/wasm/WasmMemory.cpp:260
> +        if (mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE)) {
> +            dataLogLnIf(verbose, "Memory::grow in-place failed ", *this);
> +            return false;
> +        }

I think this should be a crash.

> Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp:94
> +        if (!isImport) {
> +            m_reservedMemory = Memory::create(vm, initial, maximum, Memory::Signaling);
> +            if (m_reservedMemory) {
> +                ASSERT(!!*m_reservedMemory);
> +                m_mode = m_reservedMemory->mode();
> +            }
> +        }
> +        m_mode = Memory::lastAllocatedMode();

This is still a bit wrong. The if (m_reservedMemory) branch should not drop into the ::lastAllocatedMode() assignment.

> Source/JavaScriptCore/wasm/WasmMemoryInformation.h:62
> +    Memory& takeReservedMemory() { ASSERT(hasReservedMemory()); return *m_reservedMemory.leakRef(); }

Style suggestion:
Currently, this is used in only one place, and it correctly handles the leakRef adoptRef semantics, but I think it'd be more clear if this function just returned a Ref<Memory>&&, and then the caller could not get it wrong.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp:67
> +    // We can't use a ref here since it doesn't have a copy constructor...

Remove comment.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:97
> +    // We don't have a code block for this mode, we need to recompile...

Do you have a test for this?
If not, please add.

> Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:101
> +    auto* codeBlock = buildCodeBlock(vm, exec, plan, mode);
> +    RETURN_IF_EXCEPTION(scope, nullptr);

It would be good to have a test for this. Is it possible to have that happen deterministically?

> Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp:272
> +

Style: Remove newline.

> Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp:83
> +    return JSWebAssemblyModule::create(vm, state, structure, base + byteOffset, byteSize);

You need a scope.release() before this call.
Comment 20 Keith Miller 2017-03-03 13:42:54 PST
Comment on attachment 303273 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=303273&action=review

>> Source/WTF/ChangeLog:17
>> +        Also, add a operator! to Ref has the same semantics as C++ refs.
> 
> "Ref has" => "Ref that has"

Fixed.

>> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:498
>> +    };
> 
> Nit: This is identical to the lambda inside emitStoreOp. You could make it a member function on B3IRGenerator to remove the duplicate code.

Fixed.

>> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:77
>> +            if (start <= faultingInstruction && faultingInstruction <= end) {
> 
> Should not be <= end. I think it should be <, right?

fixed.

>> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:82
>> +                    break;
> 
> This seems like it should go outside the loop.

We need to have the vm, which we get from the set.

>> Source/JavaScriptCore/wasm/WasmMemory.cpp:70
>> +static const size_t fastMemoryMappedBytes = (static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 1) * 2; // pointer max + offset max. This is all we need since a load straddling readable memory will trap.
> 
> Nit: I think this should be:
> UINT32_MAX*2 + 1, not (UINT32_MAX + 1)*2
> 
> Also, please add some tests for this. It'd be nice to test loads at the largest possible (pointer+offset) combination.
> Can you also test loads that straddle the mapped PROT_READ/WRITE region?

Changed for simplicity.

Those tests already existed from before, when we did the bounds checking the first time. Although, I'll add a test for the UINT32_MAX*2 + 1 case.

>> Source/JavaScriptCore/wasm/WasmMemory.cpp:89
>> +    if (!vm.getCTIStub(throwExceptionFromWasmThunkGenerator).size())
> 
> Style nit: size seems like a weird condition here. Why not just use ! on the actual MacroAssemblerCodeRef itself?

Oh, I missed there was a operator! on MacroAssemblerCodeRef. Fixed.

>> Source/JavaScriptCore/wasm/WasmMemory.cpp:260
>> +        }
> 
> I think this should be a crash.

I don't think so since we might have just run out of physical memory on the device, even though we were able to reserve the virtual address space before.

>> Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp:94
>> +        m_mode = Memory::lastAllocatedMode();
> 
> This is still a bit wrong. The if (m_reservedMemory) branch should not drop into the ::lastAllocatedMode() assignment.

whoops, fixed.

>> Source/JavaScriptCore/wasm/WasmMemoryInformation.h:62
>> +    Memory& takeReservedMemory() { ASSERT(hasReservedMemory()); return *m_reservedMemory.leakRef(); }
> 
> Style suggestion:
> Currently, this is used in only one place, and it correctly handles the leakRef adoptRef semantics, but I think it'd be more clear if this function just returned a Ref<Memory>&&, and then the caller could not get it wrong.

changed.

>> Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:97
>> +    // We don't have a code block for this mode, we need to recompile...
> 
> Do you have a test for this?
> If not, please add.

This just happens in some of the tests.

>> Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp:101
>> +    RETURN_IF_EXCEPTION(scope, nullptr);
> 
> It would be good to have a test for this. Is it possible to have that happen deterministically?

I don't know if there is a way to do that now.

>> Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp:272
>> +
> 
> Style: Remove newline.

fixed.
Comment 21 Keith Miller 2017-03-03 13:43:15 PST
Created attachment 303337 [details]
Patch for landing
Comment 22 WebKit Commit Bot 2017-03-03 14:23:16 PST
Comment on attachment 303337 [details]
Patch for landing

Clearing flags on attachment: 303337

Committed r213386: <http://trac.webkit.org/changeset/213386>
Comment 23 WebKit Commit Bot 2017-03-03 14:23:23 PST
All reviewed patches have been landed.  Closing bug.
Comment 24 Saam Barati 2017-03-05 22:21:55 PST
Comment on attachment 303337 [details]
Patch for landing

View in context: https://bugs.webkit.org/attachment.cgi?id=303337&action=review

> Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:77
> +            if (start <= faultingInstruction && faultingInstruction < end) {

Keith, would it also make sense to make sure that the faulting address is in range of one of the fast memories we allocated? Seems like it'd be easy enough to keep a list of them.
Comment 25 Keith Miller 2017-03-05 22:24:03 PST
(In reply to comment #24)
> Comment on attachment 303337 [details]
> Patch for landing
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=303337&action=review
> 
> > Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:77
> > +            if (start <= faultingInstruction && faultingInstruction < end) {
> 
> Keith, would it also make sense to make sure that the faulting address is in
> range of one of the fast memories we allocated? Seems like it'd be easy
> enough to keep a list of them.

Yeah, we can do that too.
Comment 26 Saam Barati 2017-03-07 10:45:05 PST
(In reply to comment #25)
> (In reply to comment #24)
> > Comment on attachment 303337 [details]
> > Patch for landing
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=303337&action=review
> > 
> > > Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp:77
> > > +            if (start <= faultingInstruction && faultingInstruction < end) {
> > 
> > Keith, would it also make sense to make sure that the faulting address is in
> > range of one of the fast memories we allocated? Seems like it'd be easy
> > enough to keep a list of them.
> 
> Yeah, we can do that too.

I filed:
https://bugs.webkit.org/show_bug.cgi?id=169290