Add a WASM function validator.
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Nov 2016 20:40:40 +0000 (20:40 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Nov 2016 20:40:40 +0000 (20:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161707

Reviewed by Saam Barati.

This is a new template specialization of the Wasm FunctionParser class.  Instead of having
the FunctionParser track what B3 values each stack entry refers to the validator has each
entry refer to the type of the stack entry. Additionally, the control stack tracks what type
of block the object is and what the result type of the block is. The validation functions
for unary, binary, and memory operations are autogenerated by the
generateWasmValidateInlinesHeader.py script.

There are still a couple issue with validating that will be addressed in follow-up patches.
1) We need to handle result types from basic blocks. https://bugs.webkit.org/show_bug.cgi?id=164100
2) We need to handle popping things from stacks when they don't exist. https://bugs.webkit.org/show_bug.cgi?id=164275

* CMakeLists.txt:
* DerivedSources.make:
* JavaScriptCore.xcodeproj/project.pbxproj:
* testWasm.cpp:
(runWasmTests):
* wasm/WasmB3IRGenerator.cpp:
* wasm/WasmFormat.cpp: Added.
(JSC::Wasm::toString):
* wasm/WasmFormat.h:
* wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser<Context>::parseExpression):
(JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::Plan):
* wasm/WasmValidate.cpp: Added.
(JSC::Wasm::Validate::ControlData::ControlData):
(JSC::Wasm::Validate::ControlData::dump):
(JSC::Wasm::Validate::ControlData::type):
(JSC::Wasm::Validate::ControlData::signature):
(JSC::Wasm::Validate::addConstant):
(JSC::Wasm::Validate::isContinuationReachable):
(JSC::Wasm::Validate::errorMessage):
(JSC::Wasm::Validate::Validate):
(JSC::Wasm::Validate::addArguments):
(JSC::Wasm::Validate::addLocal):
(JSC::Wasm::Validate::getLocal):
(JSC::Wasm::Validate::setLocal):
(JSC::Wasm::Validate::addBlock):
(JSC::Wasm::Validate::addLoop):
(JSC::Wasm::Validate::addIf):
(JSC::Wasm::Validate::addElse):
(JSC::Wasm::Validate::addReturn):
(JSC::Wasm::Validate::addBranch):
(JSC::Wasm::Validate::endBlock):
(JSC::Wasm::Validate::addCall):
(JSC::Wasm::Validate::unify):
(JSC::Wasm::Validate::dump):
(JSC::Wasm::validateFunction):
* wasm/WasmValidate.h: Added.
* wasm/generateWasmValidateInlinesHeader.py: Added.
(cppType):
(toCpp):
(unaryMacro):
(binaryMacro):
(loadMacro):
(storeMacro):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208238 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/testWasm.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmFormat.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmValidate.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmValidate.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py [new file with mode: 0755]

index d3f8f07..b452596 100644 (file)
@@ -879,9 +879,11 @@ set(JavaScriptCore_SOURCES
     wasm/JSWebAssembly.cpp
     wasm/WasmB3IRGenerator.cpp
     wasm/WasmCallingConvention.cpp
+    wasm/WasmFormat.cpp
     wasm/WasmMemory.cpp
     wasm/WasmModuleParser.cpp
     wasm/WasmPlan.cpp
+    wasm/WasmValidate.cpp
 
     wasm/js/JSWebAssemblyCompileError.cpp
     wasm/js/JSWebAssemblyInstance.cpp
index 70f94e8..5045a93 100644 (file)
@@ -1,3 +1,68 @@
+2016-11-01  Keith Miller  <keith_miller@apple.com>
+
+        Add a WASM function validator.
+        https://bugs.webkit.org/show_bug.cgi?id=161707
+
+        Reviewed by Saam Barati.
+
+        This is a new template specialization of the Wasm FunctionParser class.  Instead of having
+        the FunctionParser track what B3 values each stack entry refers to the validator has each
+        entry refer to the type of the stack entry. Additionally, the control stack tracks what type
+        of block the object is and what the result type of the block is. The validation functions
+        for unary, binary, and memory operations are autogenerated by the
+        generateWasmValidateInlinesHeader.py script.
+
+        There are still a couple issue with validating that will be addressed in follow-up patches.
+        1) We need to handle result types from basic blocks. https://bugs.webkit.org/show_bug.cgi?id=164100
+        2) We need to handle popping things from stacks when they don't exist. https://bugs.webkit.org/show_bug.cgi?id=164275
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * testWasm.cpp:
+        (runWasmTests):
+        * wasm/WasmB3IRGenerator.cpp:
+        * wasm/WasmFormat.cpp: Added.
+        (JSC::Wasm::toString):
+        * wasm/WasmFormat.h:
+        * wasm/WasmFunctionParser.h:
+        (JSC::Wasm::FunctionParser<Context>::parseExpression):
+        (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::Plan):
+        * wasm/WasmValidate.cpp: Added.
+        (JSC::Wasm::Validate::ControlData::ControlData):
+        (JSC::Wasm::Validate::ControlData::dump):
+        (JSC::Wasm::Validate::ControlData::type):
+        (JSC::Wasm::Validate::ControlData::signature):
+        (JSC::Wasm::Validate::addConstant):
+        (JSC::Wasm::Validate::isContinuationReachable):
+        (JSC::Wasm::Validate::errorMessage):
+        (JSC::Wasm::Validate::Validate):
+        (JSC::Wasm::Validate::addArguments):
+        (JSC::Wasm::Validate::addLocal):
+        (JSC::Wasm::Validate::getLocal):
+        (JSC::Wasm::Validate::setLocal):
+        (JSC::Wasm::Validate::addBlock):
+        (JSC::Wasm::Validate::addLoop):
+        (JSC::Wasm::Validate::addIf):
+        (JSC::Wasm::Validate::addElse):
+        (JSC::Wasm::Validate::addReturn):
+        (JSC::Wasm::Validate::addBranch):
+        (JSC::Wasm::Validate::endBlock):
+        (JSC::Wasm::Validate::addCall):
+        (JSC::Wasm::Validate::unify):
+        (JSC::Wasm::Validate::dump):
+        (JSC::Wasm::validateFunction):
+        * wasm/WasmValidate.h: Added.
+        * wasm/generateWasmValidateInlinesHeader.py: Added.
+        (cppType):
+        (toCpp):
+        (unaryMacro):
+        (binaryMacro):
+        (loadMacro):
+        (storeMacro):
+
 2016-11-01  Saam Barati  <sbarati@apple.com>
 
         We should be able to eliminate rest parameter allocations
 
         Async functions generate bytecode equivalent to the following, which is
         highly dependent on the Generator implementation:
-        
+
         ```
         // Before translation:
         async function asyncfn() {}
         Await Expressions are equivalent to non-delegating Yield expressions, and emit identical bytecode.
 
         There are some caveats to be addressed later:
-        
+
         1) the `op_to_this` is always performed, whether it's used or not, like normal generators. (https://bugs.webkit.org/show_bug.cgi?id=151586)
-        
+
         2) for async arrow functions, the home object is always stored on the "body" function, regardless of whether it's needed or
         not, for the same reason as #1 (and should also be fixed as part of https://bugs.webkit.org/show_bug.cgi?id=151586)
 
            For lazily reified properties, JSFunction::put() implements complex conditional
            behavior that is different than the set of cacheable put operations above.
            Hence, it should not claim that the property put is cacheable.
-    
+
         2. Cacheable puts are cached on the original structure of the object before the
            put operation.
 
         https://bugs.webkit.org/show_bug.cgi?id=163947
 
         Reviewed by Geoffrey Garen.
-        
+
         I want to introduce another HeapTimer. Prior to this change, that would have meant writing
         exact copies of that timer's logic for each platform that has a HeapTimer (CF, GLIB, and
         EFL). That logic would in turn be a duplicate of the logic already present in
         own code for scheduling timers, so a new subclass would have to duplicate that code. Then,
         to add insult to injury, the USE(CF) version of HeapTimer would have to have an extra case
         for that new subclass since it doesn't use virtual methods effectively.
-        
+
         This changes HeapTimer on USE(CF) to know to get its runloop from Heap and to use virtual
         methods effectively so that it doesn't have to know about all of its subclasses.
-        
+
         This also moves IncrementalSweeper's code for scheduling timers into HeapTimer. This means
         that future subclasses of HeapTimer could simply use that logic.
-        
+
         This keeps changes to GCActivityCallback to a minimum. It still has a lot of
         platform-specific code and I'm not sure that this code can be trivially deduplicated since
         that code has more quirks. That's fine for now, since I mainly just need a sane way of
         https://bugs.webkit.org/show_bug.cgi?id=163802
 
         Reviewed by Keith Miller.
-        
+
         JSC often suffers from the inline cargo cult, and Heap is a prime example. This outlines a
         bunch of Heap methods that are either already very big, or call out-of-line methods, or call
         very big methods, or are not called often enough for inlining to matter.
-        
+
         This simplifies concurrent GC work because I'm so tired of recompiling the world when I touch
         one of these methods.
-        
+
         This looks to be perf-neutral.
 
         * heap/Heap.cpp:
         https://bugs.webkit.org/show_bug.cgi?id=163738
 
         Reviewed by Geoffrey Garen.
-        
+
         We need to know if we're currently in an allocation slow path, so that code can assert that
         it's not being used from inside a destructor that runs during a sweep. We need to know if
         we're currently collecting, because some code behaves differently during collection, and
         runs during marking. If we are collecting, we need to know if it's an eden collection or a
         full collection. If we are requesting a collection, we need to know if we're requesting an
         eden collection, a full collection, or any kind of collection.
-        
+
         Prior to this change, you would reason about all of these things using the HeapOperation. It
         had the following states: NoOperation, Allocation, FullCollection, EdenCollection, and
         AnyCollection. NoOperation versus Allocation was primarily for asserting that sweep didn't
         would even use HeapOperation in places where we knew that it could only be either Full or
         Eden, because we just needed a variable to tell us which generation we were talking about.
         It was all very confusing.
-        
+
         Where it completely breaks down is the fact that a concurrent GC has two logical threads, the
         mutator and the collector, which can change state independently. The mutator can be
         allocating. It can also be doing some work to help the GC. That's three states: running,
         states: every combination of mutator running, allocating, or helping GC, crossed with
         collector not running, running eden, or running full. So, this change decouples mutator state
         from collector state and uses two separate fields with two different types.
-        
+
         Mutator state is described using MutatorState, which can be either MutatorState::Running,
         MutatorState::Allocating, or MutatorState::HelpingGC.
-        
+
         Collector state is described using Optional<CollectionScope>. CollectionScope describes how
         big the scope of the collection is, and it can be either CollectionScope::Eden or
         CollectionScope::Full. If the Optional is Nullopt, it means that we are not collecting. This
         want to know about the generation. Also, we can use Nullopt in methods that request
         collection, which those methods take to mean that they can run any kind of collection (the
         old AnyCollection).
-        
+
         Another use of HeapOperation was to answer questions about whether the caller is running as
         part of the GC or as part of the mutator. Optional<CollectionScope> does not answer this,
         since code that runs in the mutator while the mutator is not HelpingGC at the same time as
         checks look for GCThreadType::Helper. The "should I run as mutator" query can now be answered
         by checking with mayBeGCThread, which returns Optional<GCThreadType>; if engaged, then run as
         GC, else run as GC if MutatorState is HelpingGC, else run as mutator.
-        
+
         This doesn't change the way that the GC behaves, but it does change how the GC represents a
         fundamental piece of state. So, it's a big change. It should be perf-neutral (still testing).
 
         https://bugs.webkit.org/show_bug.cgi?id=163686
 
         Reviewed by Geoffrey Garen.
-        
+
         Change the JITWorklist to use AutomaticThread, so that the Baseline JIT's concurrent
         compiler thread shuts down automatically after inactivity.
-        
+
         With this change, all of JSC's threads shut down automatically. If you run splay for a few
         seconds (which fires up all threads - compiler and GC) and then go to sleep for a second,
         you'll see that the only threads left are the main thread and the bmalloc thread.
         https://bugs.webkit.org/show_bug.cgi?id=163615
 
         Reviewed by Mark Lam.
-        
+
         AutomaticThread is a new feature in WTF that allows you to easily create worker threads that
         shut down automatically. This changes DFG::Worklist to use AutomaticThread, so that its
         threads shut down automatically, too. This has the potential to save a lot of memory.
-        
+
         This required some improvements to AutomaticThread: Worklist likes to be able to keep state
         around for the whole lifetime of a thread, and so it likes knowing when threads are born and
         when they die. I added virtual methods for that. Also, Worklist uses notifyOne() so I added
         that, too.
-        
+
         This looks to be perf-neutral.
 
         * dfg/DFGThreadData.cpp:
         https://bugs.webkit.org/show_bug.cgi?id=163576
 
         Reviewed by Andreas Kling.
-        
+
         Added a sleepSeconds() function, which made it easier for me to test this change.
-        
+
         The WTF changes in this patch change how the JSC GC manages threads: the GC threads will now
         shut down automatically after 1 second of inactivity. Maybe this will save some memory.
 
         https://bugs.webkit.org/show_bug.cgi?id=163371
 
         Reviewed by Geoffrey Garen and Saam Barati.
-        
+
         This adds a new kind of call inline cache for when the DFG can prove what the callee
         executable is. In those cases, we can skip some of the things that the traditional call IC
         would do:
-        
+
         - No need to check who the callee is.
         - No need to do arity checks.
-        
+
         This case isn't as simple as just emitting a call instruction since the callee may not be
         compiled at the time that the caller is compiled. So, we need lazy resolution. Also, the
         callee may be jettisoned independently of the caller, so we need to be able to revert the
         call to an unlinked state. This means that we need almost all of the things that
         CallLinkInfo has. CallLinkInfo already knows about different kinds of calls. This patch
         teaches it about new "Direct" call types.
-        
+
         The direct non-tail call IC looks like this:
-        
+
                 set up arguments
             FastPath:
                 call _SlowPath
                 lea -FrameSize(%rbp), %rsp
-            
+
             SlowPath:
                 pop
                 call operationLinkDirectCall
                 check exception
                 jmp FastPath
-        
+
         The job of operationLinkDirectCall is to link the fast path's call entrypoint of the callee.
         This means that in steady state, a call is just that: a call. There are no extra branches or
         checks.
-        
+
         The direct tail call IC is a bit more complicated because the act of setting up arguments
         destroys our frame, which would prevent us from being able to throw an exception if we
         failed to compile the callee. So, direct tail call ICs look like this:
-        
+
                 jmp _SlowPath
             FastPath:
                 set up arguments
                 jmp 0 // patch to jump to callee
-            
+
             SlowPath:
                 silent spill
                 call operationLinkDirectCall
                 silent fill
                 check exception
                 jmp FastPath
-        
+
         The jmp to the slow path is patched to be a fall-through jmp when we link the call.
-        
+
         Direct calls mean less code at call sites, fewer checks on the steady state call fast path,
         and no need for arity fixup. This looks like a slight speed-up (~0.8%) on both Octane and
         AsmBench.
         Specific items which do not contain "prototype" include (most) built-in functions (such as Math.pow),
         MethodDefinitions which are not either class "constructor" methods or GeneratorMethods, AsyncFunctions,
         and ArrowFunctions.
-        
+
         For details, see the following spec text, and the difference between GeneratorMethod evaluation and
         the evaluation of other MethodDefinition forms.
-        
+
         - https://tc39.github.io/ecma262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
         - https://tc39.github.io/ecma262/#sec-arrow-function-definitions-runtime-semantics-evaluation
         - https://tc39.github.io/ecmascript-asyncawait/#async-function-instances
         - https://tc39.github.io/ecma262/#sec-generator-function-definitions-runtime-semantics-propertydefinitionevaluation
-        
+
 
         * runtime/Executable.h:
         * runtime/JSFunction.cpp:
         <rdar://problem/28804381>
 
         Reviewed by Geoffrey Garen.
-        
+
         Before r207408, IRC had a mode where it would silently assign the first assignable register (so
         %rax, %xmm0, etc) to any tmp that was not colorable due to a pathological interference fencepost.
         We reason about interference at instruction boundaries. This means that if you have, for example,
         to only be hit by fuzzing, which may not then stress that code enough to shake out the register
         corruption. Also, this can only happen for floating point registers, so it's hard to get an
         exciting crash. The worst case is that your numbers get all messed up.
-        
+
         This change fixes the issue:
-        
+
         - IRC will now crash if it can't color a tmp.
-        
+
         - IRC doesn't crash on our tests anymore because I added a padInterference() utility that works
           around the interference problem by inserting Nops to pad between those instructions where
           conflating their early and late actions into one interference fencepost could create an
           uncolorable graph.
-        
+
         See https://bugs.webkit.org/show_bug.cgi?id=163548#c2 for a detailed discussion of how the
         problem can arise.
-        
+
         This problem almost made me want to abandon our use of interference at instruction boundaries,
         and introduce something more comprehensive, like interference at various stages of an
         instruction's execution. The reason why I didn't do this is that this problem only arises in well
         padInterference() so the IR stays nice and compact. Those Nops get removed by any phase that does
         DCE, which includes eliminateDeadCode(), allocateStack(), and reportUsedRegisters(). In practice
         allocateStack() kills them.
-        
+
         This also finally refactors our passing of RegisterSet to pass it by value, since it's small
         enough that we're not gaining anything by using references. On x86, RegisterSet ought to be
         smaller than a pointer.
         https://bugs.webkit.org/show_bug.cgi?id=163509
 
         Reviewed by Mark Lam.
-        
+
         The worklist building function in IRC skips temporaries that have no degree. This doesn't appear
         to be necessary. This has been there since the original IRC commit. It hasn't caused bugs because
         ordinarily, the only way to have a tmp with no degree is to not have any mention of that tmp. But
         while working on bug 163371, I hit a crazy corner case where a temporary would have no
         interference edges (i.e. no degree). Here's how it happens:
-        
+
         A spill tmp from a previous iteration of IRC may have no degree: imagine a tmp that is live
         everywhere and interferes with everyone, but has one use like:
 
 
         Then, we might coalesce %someOtherTmp with %newTmp.  Once this happens, if we make the %newTmp be
         the master, we're in deep trouble because %newTmp is not on any worklist.
-        
+
         I don't know how to reproduce this except through the patch in bug 163371. Removing the two lines
         of code that skipped no-degree tmps causes no regressions, and resolves the problem I was having.
 
         https://bugs.webkit.org/show_bug.cgi?id=163343
 
         Reviewed by Mark Lam.
-        
+
         When I first added the concept of NewGrey/OldGrey, I had the SlotVisitor store the old cell
         state in itself, so that it could use it to decide what to do for reportExtraMemoryVisited().
-        
+
         Then I changed it in a recent commit, because I wanted the freedom to have SlotVisitor visit
         multiple objects in tandem. But I never ended up using this capability. Still, I liked the
         new way better: instead of the SlotVisitor rembemering the state-before-blackening, we would
         make the object's state reflect whether it was black for the first time or not. That seemed
         convenient.
-        
+
         Unfortunately it's wrong. After we blacken the object, a concurrent barrier could instantly
         grey it. Then we would forget that we are visiting this object for the first time.
         Subsequent visits will think that they are not the first. So, we will fail to do the right
         thing in reportExtraMemoryVisited().
-        
+
         So, this reverts that change. This is a little more than just a revert, though. I've changed
         the terminology a bit. For example, I got tired of reading Black and having to remind myself
         that it really means that the object has begun being visited, instead of the more strict
         meaning that implies that it has already been visited. We want to say that it's Black or
         currently being scanned. I'm going to adopt Siebert's term for this: Anthracite [1]. So, our
         black CellState is now called AnthraciteOrBlack.
-        
+
         [1] https://pdfs.semanticscholar.org/7ae4/633265aead1f8835cf7966e179d02c2c8a4b.pdf
 
         * heap/CellState.h:
         https://bugs.webkit.org/show_bug.cgi?id=163337
 
         Reviewed by Mark Lam.
-        
+
         It turns out that HeapSnapshot was not down with revisiting. The concurrent GC is going to be
         built around the idea that we can revisit objects many times. This means that any action that
         should only take place once per object must check the object's state. This fixes the snapshot
         code to do this.
-        
+
         While writing this code, I realized that we're actually doing this check incorrectly, so I
         filed bug 163343. That bug requires a race, so we aren't going to see it yet.
 
 
         (InjectedScript.prototype._describe):
         Provide a friendlier name, "Proxy" instead of "ProxyObject".
-        
+
         (InjectedScript.RemoteObject):
         When generating a preview for a Proxy object, generate it from the final target
         and mark it as lossy so that the object can always be expanded to get the internal
         and we asserted that it matched a breakpoint location identified
         by the parser. This could get out of sync, or nodes could forget to
         emit debug hooks expected by the parser.
-        
+
         With this change, we always check and emit a debug hook for any
         node. The default behavior is for BytecodeGenerator::emitNode
         to emit the debug hook when emitting the node itself. This covers
         https://bugs.webkit.org/show_bug.cgi?id=163334
 
         Reviewed by Mark Lam.
-        
+
         I guess that the idea of JITWriteBarrier was to make sure that if you slap some heap pointer
         bits into machine code, then you better execute a barrier on the code block. But it's a
         complicated piece of code, and I can never remember how it quite works. These days it looks
         not really necessary to have something like this, since our convention is that any pointer
         stored in machine code must always be shadowed in the GC heap. I think that convention has
         won by overwhelming majority, so we should finally remove JITWriteBarrier.
-        
+
         A practical outcome of this change is that it makes it easier to implement DirectCall ICs,
         which will have to store the callee in the CallLinkInfo but not in the machine code.
 
         https://bugs.webkit.org/show_bug.cgi?id=162309
 
         Reviewed by Geoffrey Garen.
-        
+
         It used to be that we would forget which objects are live the moment we started collection.
         That's because the flip at the beginning clears all mark bits.
-        
+
         But we already have a facility for tracking objects that are live-but-not-marked. It's called
         newlyAllocated. So, instead of clearing mark bits, we want to just transfer them to
         newlyAllocated. Then we want to clear all newlyAllocated after GC.
-        
+
         This implements such an approach, along with a versioning optimization for newlyAllocated.
         Instead of walking the whole heap to clear newlyAllocated bits at the end of the GC, we bump
         the newlyAllocatedVersion, which causes MarkedBlock to treat newlyAllocated as if it was
         clear.
-        
+
         We could have even avoided allocating newlyAllocated in most cases, since empirically most
         blocks are either completely empty or completely full. An earlier version of this patch did
         this, but it was not better than this patch. In fact, it seemed to actually be worse for PLT
         and membuster.
-        
+
         To validate this change, we now run the conservative scan after the beginMarking flip. And it
         totally works!
-        
+
         This is a huge step towards concurrent GC. It means that we ought to be able to run the
         allocator while marking. Since we already separately made it possible to run the barrier
         while marking, this means that we're pretty much ready for some serious concurrency action.
-        
+
         This appears to be perf-neutral and space-neutral.
 
         * JavaScriptCore.xcodeproj/project.pbxproj:
         https://bugs.webkit.org/show_bug.cgi?id=162749
 
         Reviewed by Yusuke Suzuki.
-        
+
         We have a lot of defenses against emitting code that materializes huge contants. But if we do
         end up with such code in the backend, it's better to convert those materializations into add
         instructions by checking if other registers are known to contain nearby constants. That's
         https://bugs.webkit.org/show_bug.cgi?id=163264
 
         Reviewed by Mark Lam.
-        
+
         When writing the lea patch (r207039), I was very careful about how I convert a Shl into a
         BaseIndex scale. But I forgot to check if the older code for creating BaseIndexes for
         effectiveAddr() got this right. It turns out that the older code missed the <<32 corner
         case.
-        
+
         It's sad that the two paths can't share all of their code, but it's somewhat inevitable due
         to how matching an address and matching a lea have to do very different things. Matching a
         lea means creating an instruction that is distinct from other instructions to do multiple
         has to figure out Add(@value, $const) on its own. This change makes the situation slightly
         more sane by adding a scaleForShl() helper that handles this weird case. It's based on the
         new Shl handling from r207039, and exposes it as an API for effectiveAddr() to use.
-        
+
         The testLoadBaseIndexShift32() used to crash. I don't think that this case affects JS
         content, since <<32 is such a bizarre outlier. I don't think we even have a path along
         which the FTL would emit a 64-bit <<32. It probably won't even affect WebAssembly since
         https://bugs.webkit.org/show_bug.cgi?id=163234
 
         Reviewed by Saam Barati.
-        
+
         This adds comprehensive support for emitting lea on x86.
-        
+
         When adding this, I found that it was useful to also finally add more reassociation. That
         reduces the amount of patterns that the instruction selector has to deal with.
 
         https://bugs.webkit.org/show_bug.cgi?id=163175
 
         Reviewed by Keith Miller.
-        
+
         You can now call Procedure::pinRegister(), or Code::pinRegister(), and it will make this
         register behave as follows:
-        
+
         - B3 and Air will emit no code that modifies the value in this register, except if that
           happens via a Patchpoint or stackmap constraint (i.e. the user explicitly asked for it).
         - B3 and Air will allow others to modify the register. For example, if the register is not
           changing). For example, if we went back to having pinned tag registers, we would tell B3
           to use them by (1) excluding them from any clobber set (easy, since they're callee save)
           and (2) emitting ArgumentReg to grab their value. There's a test that does this.
-        
-        This is accomplished by taking regsInPriorityOrder() and making it a method of Code. Air 
+
+        This is accomplished by taking regsInPriorityOrder() and making it a method of Code. Air
         already used this API when choosing registers in register allocation. Code now also vends a
         mutableRegs() set, which is derived from regsInPriorityOrder(), that can quickly tell you if
         a register can be mutated. Doing it this way means that most of this is a purely mechanical
         change. The calls to mutableRegs() are the places where we had to change logic:
-        
+
         - The register allocators needs to know that coalescing with a precolored pinned tmp is free.
         - The callee-save handler needs to know that we're not supposed to save/restore pinned
           registers.
-        
+
         Note that in this scheme, pinned registers are simply registers that do not appear in
         regsInPriorityOrder(). This means, for example, that we will now say that FP is pinned. So,
         this means that you can also pin registers by calling setRegsInPriorityOrder() and passing a
         https://bugs.webkit.org/show_bug.cgi?id=163172
 
         Reviewed by Keith Miller.
-        
+
         If we have mutable pinned registers then we need to know which operations mutate them. At
         first I considered making this into a heap range thing, but I think that this would be very
         confusing. Also, in the future, we might want to make Effects track register sets of
         the instruction stream with NOPs to provide space for a jump.  This changes
         Air::generate() to use labelIgnoringWatchpoints() to create pcToOriginMap
         entries to eliminate unneccesary NOPs.
-        
+
         * b3/air/AirGenerate.cpp:
         (JSC::B3::Air::generate):
         * b3/testb3.cpp:
         https://bugs.webkit.org/show_bug.cgi?id=162845
 
         Reviewed by Geoffrey Garen.
-        
+
         While writing some documentation, I found some small holes in the code.
 
         * b3/B3Effects.cpp:
         https://bugs.webkit.org/show_bug.cgi?id=162842
 
         Reviewed by Dan Bernstein.
-        
+
         This function has become dead code. This change removes it.
 
         * heap/CellContainer.h:
         https://bugs.webkit.org/show_bug.cgi?id=162689
 
         Reviewed by Geoffrey Garen.
-        
+
         This adds a traps flag to B3::Kind. It also makes B3::Kind work more like Air::Kind, in the
         sense that it's a bag of distinct bits - it doesn't need to be a union unless we get enough
         things that it would make a difference.
-        
+
         The only analysis that needs to know about traps is effects. It now knows that traps implies
         sideExits, which means that this turns off DCE. The only optimization that needs to know
         about traps is eliminateCommonSubexpressions(), which needs to pessimize its store
         elimination if the store traps.
-        
+
         The hard part of this change is teaching the instruction selector to faithfully carry the
         traps flag down to Air. I got this to work by making ArgPromise a non-copyable object that
         knows whether you've used it in an instruction. It knows when you call consume(). If you do
         along with a few other hacks, means that all of the load-op and load-op-store fusions
         correctly carry the trap bit: if any of the B3 loads or stores involved traps then you get
         traps in Air.
-        
+
         This framework also sets us up to do bug 162688, since the ArgPromise::inst() hook is
         powerful enough to allow wrapping the instruction with a Patch.
-        
+
         I added some tests to testb3 that verify that optimizations are appropriately inhibited and
         that the traps flag survives until the bitter end of Air.
 
         https://bugs.webkit.org/show_bug.cgi?id=162764
 
         Reviewed by Saam Barati.
-        
+
         There are some interesting cases where we can reduce the number of constant materializations if
         we teach moveConstants() how to edit code. The two examples that this patch supports are:
-        
+
             - Loads and stores from a constant pointer. Since loads and stores get an offset for free
               and the instruction selector is really good at handling it, and since we can query Air to
               see what kinds of offsets are legal, we can sometimes avoid using a constant pointer that
               is specific to the absolute address of that load and instead pick some other constant
               that is within offset distance of ours.
-            
+
             - Add and Sub by a constant (x + c, x - c). Since x + c = x - -c and x - c = x + -c, we can
               flip Add to Sub or vice versa if the negated constant is available.
-        
+
         This change makes moveConstants() pick the most dominant constant that works for an value. In
         the case of memory accesses, it uses Air::Arg::isValidAddrForm() to work out what other
         constants would work. In the case of Add/Sub, it simply looks for the negated constant. This
         should result in something like a minimal number of constants since these rules always pick the
         most dominant constant that works - so if an Add's constant is already most dominant then
         nothing changes, but if the negated one is more dominant then it becomes a Sub.
-        
+
         This is a 0.5% speed-up on LongSpider and neutral elsewhere. It's a speed-up because the
         absolute address thing reduces the number of address materializations that we have to do, while
         the add/sub thing prevents us from having to materialize 0x1000000000000 to box doubles.
         Ensure that once we have the Script that we always
         resolve the breakpoint location before setting the
         breakpoint. The different paths are:
-        
+
         - setBreakpoint(scriptId, location)
           - Here we know the SourceProvider by its SourceID
             - resolve and set
-        
+
         - setBreakpointByURL(url, location)
           - Search for existing Scripts that match the URL
             - resolve in each and set
           - When new Scripts are parsed that match the URL
             - resolve and set
-            
+
 
 2016-09-30  Joseph Pecoraro  <pecoraro@apple.com>
 
         (JSC::Debugger::stepIntoStatement):
         (JSC::Debugger::exception):
         (JSC::Debugger::didReachBreakpoint):
-        
+
         Use new variable names, and clarify if we should attempt
         to pause or not.
 
         (JSC::Debugger::updateCallFrameInternal):
         (JSC::Debugger::pauseIfNeeded):
         Allow updateCallFrame to either attempt a pause or not.
-        
+
         (JSC::Debugger::atStatement):
         Attempt pause and reset the at first expression flag.
 
         Attempt pause when leaving a function.
         If the user did a step-over and is leaving the
         function, then behave like step-out.
-        
+
         (JSC::Debugger::unwindEvent):
         Behave like return except don't change any
         pausing states. If we needed to pause the
         When the program doesn't have a parent, clear all
         our state so we don't errantly pause on the next
         JavaScript microtask that gets executed.
-        
+
         (JSC::Debugger::clearNextPauseState):
         Helper to clear all of the pause states now that
         it happens in a couple places.
         https://bugs.webkit.org/show_bug.cgi?id=162699
 
         Reviewed by Mark Lam.
-        
+
         This follows a similar change in B3 (r206595) and replaces Air::Opcode with Air::Kind,
         which holds onto the opcode and some additional flags. Because Air is an orthogonal ISA
         (the opcode tells you what the operation does but each operand is allowed to also contain
         effectively instructions for what to do to read or write that operand), the flags are
         meant to be orthogonal to opcode. This allows us to say things like Add32<Trap>, which
         makes sense if any of the operands to the Add32 are addresses.
-        
+
         To demonstrate the flags facility this partly adds a trap flag to Air. B3 doesn't use it
         yet, but I made sure that Air respects it. Basically that means blocking DCE when the flag
         is set, by making it imply hasNonArgNonControlEffects.
         https://bugs.webkit.org/show_bug.cgi?id=162721
 
         Reviewed by Keith Miller.
-        
+
         The put_by_id-in-put_by_val optimization had the write barrier in the wrong place and
         incorrectly filtered on value instead of base.
-        
+
         No reduced test case. You really need to run Dromaeo/jslib to catch it. I love Dromaeo's
         ability to catch GC bugs.
 
         Opcode was how you knew what subclass of Value you had. The opcode told you what the Value
         actually did. This change replaces Opcode with Kind, which is a tuple of opcode and other
         stuff.
-        
+
         Opcodes are great, and that's how most compilers work. But opcodes are one-dimensional. Here
         is how this manifests. Say you have an opcode, like Load. You will be happy if your IR has
         one Load opcode. But then, you might add Load8S/Load8Z/Load16S/Load16Z opcodes, as we have
         done in B3. B3 has one dimension of Load opcodes, which determines something like the C type
         of the load. But in the very near future, we will want to add two more dimensions to Loads:
-        
+
         - A flag to say if the load traps.
         - A flag to say if the load has acquire semantics.
-        
+
         Mapping these three dimensions (type, trap, acquire) onto the one-dimensional Opcode space
         would create mayham: Load8S, Load8STrap, Load8SAcquire, Load8STrapAcquire, Load8Z,
         Load8ZTrap, etc.
-        
+
         This happens in other parts of the IR. For example, we have a dimension of arithmetic
         operations: add, sub, mul, div, mod, etc. Then we have the chill flag. But since opcodes
         are one-dimensional, that means having ChillDiv and ChillMod, and tons of places in the
         compiler that case on both Div and ChillDiv, or case on both Mod and ChillMod, since they
         are only interested in the kind of arithmetic being done and not the chillness.
-        
+
         Though the examples all involve bits (chill or not, trapping or not, etc), I can imagine
         other properties that behave more like small enums, like if we fill out more memory ordering
         modes other than just "acquire? yes/no". There will eventually have to be something like a
         std::memory_order associated with memory accesses.
-        
+
         One approach to this problem is to have a Value subclass that contains fields with the meta
         data. I don't like this for two reasons:
-        
+
         - In bug 162688, I want to make trapping memory accesses have stackmaps. This means that a
           trapping memory access would have a different Value subclass than a non-trapping memory
           access. So, this meta-data needs to channel into ValueType::accepts(). Currently that
           takes Opcode and nothing else.
-        
+
         - Compiler IRs are all about making common tasks easy. If it becomes commonplace for opcodes
           to require a custom Value subclass just for a bit then that's not very easy.
-        
+
         This change addresses this problem by making the compiler pass around Kinds rather than
         Opcodes. A Kind contains an Opcode as well as any number of opcode-specific bits. This
         change demonstrates how Kind should be used by converting chillness to it. Kind has
         chill(Div). IR dumps will print it like this:
 
             Int32 @38 = Div<Chill>(@36, @37, DFG:@24, ControlDependent)
-        
+
         Where "Div<Chill>" is how a Kind that hasExtraBits() dumps itself. If a Kind does not
         hasExtraBits() (the normal case) then it dumps like a normal Opcode (without the "<>").
-        
+
         I replaced many uses of Opcode with Kind. New code has to be mindful that Opcode may not be
         the right way to summarize what a value does, and so in many cases it's better to carry
         around a Kind instead - especially if you will use it to stamp out new Values. Opcode is no
         now wants a Kind instead of an Opcode. All Value constructors now take Kind instead of
         Opcode. But most opcodes don't get any extra Kind bits, and so the code that operates on
         those opcodes is largely unchanged.
-        
+
         * CMakeLists.txt:
         * JavaScriptCore.xcodeproj/project.pbxproj:
         * b3/B3ArgumentRegValue.h:
         https://bugs.webkit.org/show_bug.cgi?id=162316
 
         Reviewed by Geoffrey Garen.
-        
+
         This makes our write barrier behave correctly when it races with the collector. The
         collector wants to do this when visiting:
-        
+
             object->cellState = black
             visit(object)
-        
+
         The mutator wants to do this when storing:
-        
+
             object->property = newValue
             if (object->cellState == black)
                 remember(object)
-        
+
         Prior to this change, this didn't work right because the compiler would sometimes place
         barriers before the store to the property and because the mutator did not have adequate
         fences.
-        
+
         Prior to this change, the DFG and FTL would emit this:
-        
+
             if (object->cellState == black)
                 remember(object)
             object->property = newValue
-        
+
         Which is wrong, because the object could start being scanned just after the cellState
         check, at which point the store would be lost. We need to confirm that the state was not
         black *after* the store! This change was harder than you'd expect: placing the barrier
         (because of the store-load fence) and it writes only cellState (because the B3 heap ranges
         don't have any way to represent any of the GC's other state, which means that B3 does not
         have to worry about aliasing with any of that).
-        
+
         The collector already uses a store-load fence on x86 just after setting the cellState and
         before visiting the object. The mutator needs to do the same. But we cannot put a
         store-load fence of any kind before store barriers, because that causes enormous slow
         reasonable if the slow down only happened while the GC was running. Then, the concurrent GC
         would lift throughput-while-collecting from 0% of peak to 85% of peak. This changes the
         barrier so that it looks like this:
-        
+
             if (object->cellState <= heap.sneakyBlackThreshold)
                 slowPath(object)
-        
+
         Where sneakyBlackThreshold is the normal blackThreshold when we're not collecting, or a
         tautoligical threshold (that makes everything look black) when we are collecting. This
         turns out to not be any more expensive than the barrier in tip of tree when the GC is not
         concurrently yet. I still have more work to do.) The slowPath() does some extra work to
         check if we are concurrently collecting; if so, it does a fence and rechecks if the object
         really did need that barrier.
-        
+
         This also reintroduces elimination of redundant store barriers, which was lost in the last
         store barrier change. We can only do it when there is no possibility of GC, exit, or
         exceptions between the two store barriers. We could remove the exit/exception limitation if
         same object. This same optimization also sometimes strength-reduces the store barrier so
         that it uses a constant black threshold rather than the sneaky one, thereby saving one
         load.
-        
+
         Even with all of those optimizations, I still had problems with barrier cost. I found that one
         of the benchmarks that was being hit particularly hard was JetStream/regexp-2010. Fortunately
         that benchmark does most of its barriers in a tight C++ loop in RegExpMatchesArray.h. When we
         optimization. The most efficient version of such an optimization that I could come up with was
         to have a DeferralContext object that houses a boolean that is false by default, but the GC
         writes true into it if it would have wanted to GC. You thread a pointer to the deferralContext
-        through all of your allocations. This kind of mechanism has the overhead of a zero 
+        through all of your allocations. This kind of mechanism has the overhead of a zero
         initialization on the stack on entry and a zero check on exit. This is probably even efficient
         enough that we could start thinking about having the DFG use it, for example if we found a
         bounded-time section of code with a lot of barriers and entry/exit sites that aren't totally
         wacky. This optimization took this patch from 0.68% JetStream regressed to neutral, according
         to my latest data.
-        
+
         Finally, an earlier version of this change put the store-load fence in B3 IR, so I ended up
         adding FTLOutput support for it and AbstractHeapRepository magic for decorating the heaps.
         I think we might as well keep that, it'll be useful.
 
         Make the subclass of CallSlowPathGenerator that takes arguments variadic
         so it can take any number of arguments. Also updates the slowPathCall helper
-        function to be variadic. I had to move the spill mode and exception check 
+        function to be variadic. I had to move the spill mode and exception check
         requirement parameters to before the arguments since the variadic arguments
         must be at the end. As a convenience, I added an overload of slowPathCall that
         doesn't take spill mode and exception check requirement parameters.
index 738c136..b30c305 100644 (file)
@@ -64,6 +64,7 @@ all : \
     AirOpcode.h \
     YarrCanonicalizeUnicode.cpp \
     WasmOps.h \
+    WasmValidateInlines.h \
 #
 
 # JavaScript builtins.
@@ -301,6 +302,9 @@ YarrCanonicalizeUnicode.cpp: $(JavaScriptCore)/generateYarrCanonicalizeUnicode $
 WasmOps.h: $(JavaScriptCore)/wasm/generateWasmOpsHeader.py $(JavaScriptCore)/wasm/generateWasm.py $(JavaScriptCore)/wasm/wasm.json
        $(PYTHON) $(JavaScriptCore)/wasm/generateWasmOpsHeader.py $(JavaScriptCore)/wasm/wasm.json ./WasmOps.h
 
+WasmValidateInlines.h: $(JavaScriptCore)/wasm/generateWasmValidateInlinesHeader.py $(JavaScriptCore)/wasm/generateWasm.py $(JavaScriptCore)/wasm/wasm.json
+       $(PYTHON) $(JavaScriptCore)/wasm/generateWasmValidateInlinesHeader.py $(JavaScriptCore)/wasm/wasm.json ./WasmValidateInlines.h
+
 # Dynamically-defined targets are listed below. Static targets belong up top.
 
 all : \
index 95ef32b..2fbd2e4 100644 (file)
                52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52C952B819A28A1C0069B386 /* TypeProfiler.cpp */; };
                531374BD1D5CE67600AF7A0B /* WasmPlan.h in Headers */ = {isa = PBXBuildFile; fileRef = 531374BC1D5CE67600AF7A0B /* WasmPlan.h */; };
                531374BF1D5CE95000AF7A0B /* WasmPlan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */; };
+               533B15DF1DC7F463004D500A /* WasmOps.h in Headers */ = {isa = PBXBuildFile; fileRef = 533B15DE1DC7F463004D500A /* WasmOps.h */; };
                5341FC701DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */; };
                5341FC721DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */; };
                53486BB71C1795C300F6F3AF /* JSTypedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 53486BB61C1795C300F6F3AF /* JSTypedArray.h */; settings = {ATTRIBUTES = (Public, ); }; };
                53F40E8B1D5901BB0099A1B6 /* WasmFunctionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */; };
                53F40E8D1D5901F20099A1B6 /* WasmParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8C1D5901F20099A1B6 /* WasmParser.h */; };
                53F40E8F1D5902820099A1B6 /* WasmB3IRGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F40E8E1D5902820099A1B6 /* WasmB3IRGenerator.cpp */; };
-               53F40E911D5903020099A1B6 /* WasmOps.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E901D5903020099A1B6 /* WasmOps.h */; };
                53F40E931D5A4AB30099A1B6 /* WasmB3IRGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E921D5A4AB30099A1B6 /* WasmB3IRGenerator.h */; };
                53F40E951D5A7AEF0099A1B6 /* WasmModuleParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */; };
                53F40E971D5A7BEC0099A1B6 /* WasmModuleParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */; };
                53FA2AE31CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */; };
                53FD04D31D7AB277003287D3 /* WasmCallingConvention.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */; };
                53FD04D41D7AB291003287D3 /* WasmCallingConvention.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FD04D21D7AB187003287D3 /* WasmCallingConvention.h */; };
-               5C4E8E961DBEBE620036F1FC /* JSONParseTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C4E8E941DBEBDA20036F1FC /* JSONParseTest.cpp */; };
+               53FF7F991DBFCD9000A26CCC /* WasmValidate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FF7F981DBFCD9000A26CCC /* WasmValidate.h */; };
+               53FF7F9B1DBFD2B900A26CCC /* WasmValidate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F9A1DBFD2B900A26CCC /* WasmValidate.cpp */; };
+               53FF7F9D1DC00DB100A26CCC /* WasmFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F9C1DC00DB100A26CCC /* WasmFormat.cpp */; };
                5B70CFDE1DB69E6600EC23F9 /* JSAsyncFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B70CFD81DB69E5C00EC23F9 /* JSAsyncFunction.h */; };
                5B70CFDF1DB69E6600EC23F9 /* JSAsyncFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B70CFD91DB69E5C00EC23F9 /* JSAsyncFunction.cpp */; };
                5B70CFE01DB69E6600EC23F9 /* AsyncFunctionPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B70CFDA1DB69E5C00EC23F9 /* AsyncFunctionPrototype.h */; };
                5B70CFE11DB69E6600EC23F9 /* AsyncFunctionPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B70CFDB1DB69E5C00EC23F9 /* AsyncFunctionPrototype.cpp */; };
                5B70CFE21DB69E6600EC23F9 /* AsyncFunctionConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B70CFDC1DB69E5C00EC23F9 /* AsyncFunctionConstructor.h */; };
                5B70CFE31DB69E6600EC23F9 /* AsyncFunctionConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B70CFDD1DB69E5C00EC23F9 /* AsyncFunctionConstructor.cpp */; };
+               5C4E8E961DBEBE620036F1FC /* JSONParseTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C4E8E941DBEBDA20036F1FC /* JSONParseTest.cpp */; };
                5D5D8AD10E0D0EBE00F9C692 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */; };
                5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 14D857740A4696C80032146C /* testapi.js */; };
                5DBB1525131D0BD70056AD36 /* minidom.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 1412110D0A48788700480255 /* minidom.js */; };
                52C952B819A28A1C0069B386 /* TypeProfiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TypeProfiler.cpp; sourceTree = "<group>"; };
                531374BC1D5CE67600AF7A0B /* WasmPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmPlan.h; sourceTree = "<group>"; };
                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmPlan.cpp; sourceTree = "<group>"; };
+               533B15DE1DC7F463004D500A /* WasmOps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmOps.h; sourceTree = "<group>"; };
                5341FC6F1DAC33E500E7E4D7 /* B3WasmBoundsCheckValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3WasmBoundsCheckValue.cpp; path = b3/B3WasmBoundsCheckValue.cpp; sourceTree = "<group>"; };
                5341FC711DAC343C00E7E4D7 /* B3WasmBoundsCheckValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3WasmBoundsCheckValue.h; path = b3/B3WasmBoundsCheckValue.h; sourceTree = "<group>"; };
                53486BB61C1795C300F6F3AF /* JSTypedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArray.h; sourceTree = "<group>"; };
                53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmFunctionParser.h; sourceTree = "<group>"; };
                53F40E8C1D5901F20099A1B6 /* WasmParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmParser.h; sourceTree = "<group>"; };
                53F40E8E1D5902820099A1B6 /* WasmB3IRGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmB3IRGenerator.cpp; sourceTree = "<group>"; };
-               53F40E901D5903020099A1B6 /* WasmOps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmOps.h; sourceTree = "<group>"; };
                53F40E921D5A4AB30099A1B6 /* WasmB3IRGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmB3IRGenerator.h; sourceTree = "<group>"; };
                53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmModuleParser.h; sourceTree = "<group>"; };
                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmModuleParser.cpp; sourceTree = "<group>"; };
                53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp; sourceTree = "<group>"; };
                53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmCallingConvention.cpp; sourceTree = "<group>"; };
                53FD04D21D7AB187003287D3 /* WasmCallingConvention.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmCallingConvention.h; sourceTree = "<group>"; };
-               5C4E8E941DBEBDA20036F1FC /* JSONParseTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSONParseTest.cpp; path = API/tests/JSONParseTest.cpp; sourceTree = "<group>"; };
-               5C4E8E951DBEBDA20036F1FC /* JSONParseTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSONParseTest.h; path = API/tests/JSONParseTest.h; sourceTree = "<group>"; };
+               53FF7F981DBFCD9000A26CCC /* WasmValidate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmValidate.h; sourceTree = "<group>"; };
+               53FF7F9A1DBFD2B900A26CCC /* WasmValidate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmValidate.cpp; sourceTree = "<group>"; };
+               53FF7F9C1DC00DB100A26CCC /* WasmFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmFormat.cpp; sourceTree = "<group>"; };
                5B70CFD81DB69E5C00EC23F9 /* JSAsyncFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAsyncFunction.h; sourceTree = "<group>"; };
                5B70CFD91DB69E5C00EC23F9 /* JSAsyncFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAsyncFunction.cpp; sourceTree = "<group>"; };
                5B70CFDA1DB69E5C00EC23F9 /* AsyncFunctionPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncFunctionPrototype.h; sourceTree = "<group>"; };
                5B70CFDC1DB69E5C00EC23F9 /* AsyncFunctionConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncFunctionConstructor.h; sourceTree = "<group>"; };
                5B70CFDD1DB69E5C00EC23F9 /* AsyncFunctionConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AsyncFunctionConstructor.cpp; sourceTree = "<group>"; };
                5B8243041DB7AA4900EA6384 /* AsyncFunctionPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = AsyncFunctionPrototype.js; sourceTree = "<group>"; };
+               5C4E8E941DBEBDA20036F1FC /* JSONParseTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSONParseTest.cpp; path = API/tests/JSONParseTest.cpp; sourceTree = "<group>"; };
+               5C4E8E951DBEBDA20036F1FC /* JSONParseTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSONParseTest.h; path = API/tests/JSONParseTest.h; sourceTree = "<group>"; };
                5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libedit.dylib; path = /usr/lib/libedit.dylib; sourceTree = "<absolute>"; };
                5DAFD6CB146B686300FBEFB4 /* JSC.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = JSC.xcconfig; sourceTree = "<group>"; };
                5DDDF44614FEE72200B4FB4D /* LLIntDesiredOffsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntDesiredOffsets.h; path = LLIntOffsets/LLIntDesiredOffsets.h; sourceTree = BUILT_PRODUCTS_DIR; };
                                996B73131BD9FA2C00331B84 /* SymbolConstructor.lut.h */,
                                996B73141BD9FA2C00331B84 /* SymbolPrototype.lut.h */,
                                65A946141C8E9F6F00A7209A /* YarrCanonicalizeUnicode.cpp */,
+                               533B15DE1DC7F463004D500A /* WasmOps.h */,
                        );
                        name = "Derived Sources";
                        path = DerivedSources/JavaScriptCore;
                                53F40E921D5A4AB30099A1B6 /* WasmB3IRGenerator.h */,
                                53FD04D11D7AB187003287D3 /* WasmCallingConvention.cpp */,
                                53FD04D21D7AB187003287D3 /* WasmCallingConvention.h */,
-                               53F40E901D5903020099A1B6 /* WasmOps.h */,
+                               53FF7F9C1DC00DB100A26CCC /* WasmFormat.cpp */,
                                7BC547D21B69599B00959B58 /* WasmFormat.h */,
                                53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */,
                                535557151D9DFA32006D583B /* WasmMemory.cpp */,
                                531374BC1D5CE67600AF7A0B /* WasmPlan.h */,
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                53F40E841D58F9770099A1B6 /* WasmSections.h */,
+                               53FF7F9A1DBFD2B900A26CCC /* WasmValidate.cpp */,
+                               53FF7F981DBFCD9000A26CCC /* WasmValidate.h */,
                        );
                        path = wasm;
                        sourceTree = "<group>";
                                0F2BDC491522809600CD8910 /* DFGVariableEvent.h in Headers */,
                                0F2BDC4B1522809D00CD8910 /* DFGVariableEventStream.h in Headers */,
                                0FFFC96014EF90BD00C72532 /* DFGVirtualRegisterAllocationPhase.h in Headers */,
+                               53FF7F991DBFCD9000A26CCC /* WasmValidate.h in Headers */,
                                0FC97F4218202119002C9B26 /* DFGWatchpointCollectionPhase.h in Headers */,
                                0FDB2CE8174830A2007B3C1B /* DFGWorklist.h in Headers */,
                                147341DA1DC0300100AA29BA /* WebAssemblyExecutable.h in Headers */,
                                A7C0C4AC168103020017011D /* JSScriptRefPrivate.h in Headers */,
                                FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */,
                                0F919D11157F332C004A4E7D /* JSSegmentedVariableObject.h in Headers */,
-                               53F40E911D5903020099A1B6 /* WasmOps.h in Headers */,
                                A7299D9E17D12837005F5FF9 /* JSSet.h in Headers */,
                                A790DD70182F499700588807 /* JSSetIterator.h in Headers */,
                                BC18C45E0E16F5CD00B34460 /* CLoopStack.h in Headers */,
                                14F7256614EE265E00B1652B /* WeakHandleOwner.h in Headers */,
                                14E84FA214EE1ACC00D6D5D4 /* WeakImpl.h in Headers */,
                                14BE7D3317135CF400D1807A /* WeakInlines.h in Headers */,
+                               533B15DF1DC7F463004D500A /* WasmOps.h in Headers */,
                                A7CA3AE417DA41AE006538AF /* WeakMapConstructor.h in Headers */,
                                A7CA3AEC17DA5168006538AF /* WeakMapData.h in Headers */,
                                A7CA3AE617DA41AE006538AF /* WeakMapPrototype.h in Headers */,
                                0FF4275715914A20004CB9FF /* LinkBuffer.cpp in Sources */,
                                A7E2EA6C0FB460CF00601F06 /* LiteralParser.cpp in Sources */,
                                A5A1A0951D8CB341004C2EB8 /* DebuggerParseData.cpp in Sources */,
+                               53FF7F9D1DC00DB100A26CCC /* WasmFormat.cpp in Sources */,
                                FE3913541B794F6E00EDAF71 /* LiveObjectList.cpp in Sources */,
                                FE20CE9D15F04A9500DF3430 /* LLIntCLoop.cpp in Sources */,
                                0F4680D214BBD16500BFE272 /* LLIntData.cpp in Sources */,
                                14469DEB107EC7E700650446 /* StringConstructor.cpp in Sources */,
                                70EC0EC61AA0D7DA00B6AAFA /* StringIteratorPrototype.cpp in Sources */,
                                14469DEC107EC7E700650446 /* StringObject.cpp in Sources */,
+                               53FF7F9B1DBFD2B900A26CCC /* WasmValidate.cpp in Sources */,
                                14469DED107EC7E700650446 /* StringPrototype.cpp in Sources */,
                                9335F24D12E6765B002B5553 /* StringRecursionChecker.cpp in Sources */,
                                BCDE3B430E6C832D001453A7 /* Structure.cpp in Sources */,
index 34aea85..d1b88d8 100644 (file)
@@ -269,6 +269,27 @@ inline JSValue boxd(double value)
     return box(bitwise_cast<uint64_t>(value));
 }
 
+static void checkPlan(Plan& plan, unsigned expectedNumberOfFunctions)
+{
+    if (plan.failed()) {
+        dataLogLn("Module failed to compile with error: ", plan.errorMessage());
+        CRASH();
+    }
+
+    if (plan.resultSize() != expectedNumberOfFunctions) {
+        dataLogLn("Incorrect number of functions");
+        CRASH();
+    }
+
+    for (unsigned i = 0; i < expectedNumberOfFunctions; ++i) {
+        if (!plan.result(i)) {
+            dataLogLn("Function at index, " , i, " failed to compile correctly");
+            CRASH();
+        }
+    }
+
+}
+
 // For now we inline the test files.
 static void runWasmTests()
 {
@@ -290,10 +311,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 2 || !plan.result(0) || !plan.result(1)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 2);
 
         // Test this doesn't crash.
         CHECK(isIdentical(invoke<float>(*plan.result(1)->jsEntryPoint, { boxf(0.0), boxf(1.5) }), -1.5f));
@@ -319,10 +337,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 2 || !plan.result(0) || !plan.result(1)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 2);
 
         // Test this doesn't crash.
         CHECK(isIdentical(invoke<float>(*plan.result(1)->jsEntryPoint, { boxf(0.0), boxf(1.5) }), 1.5f));
@@ -353,10 +368,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 2 || !plan.result(0) || !plan.result(1)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 2);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(1)->jsEntryPoint, { box(0) }), 0);
@@ -387,10 +399,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0) }), 1);
@@ -447,10 +456,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 2 || !plan.result(0) || !plan.result(1)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 2);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(1)->jsEntryPoint, { box(0) }), 0);
@@ -463,27 +469,23 @@ static void runWasmTests()
 
     {
         // Generated from:
-        // (module
-        //  (memory 1)
-        //  (func (export "i32_load8_s") (param $i i32) (param $ptr i32) (result i32)
-        //   (i64.store (get_local $ptr) (get_local $i))
-        //   (return (i64.load (get_local $ptr)))
-        //   )
-        //  )
+        //    (module
+        //     (memory 1)
+        //     (func (export "i64") (param $i i64) (param $ptr i32) (result i64)
+        //      (i64.store (get_local $ptr) (get_local $i))
+        //      (return (i64.load (get_local $ptr)))
+        //      )
+        //     )
         Vector<uint8_t> vector = {
             0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
             0x02, 0x02, 0x01, 0x01, 0x02, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80,
-            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x01, 0x0b, 0x69, 0x33,
-            0x32, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x38, 0x5f, 0x73, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80,
-            0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x01, 0x14, 0x00, 0x34, 0x03, 0x00, 0x14,
-            0x01, 0x2b, 0x03, 0x00, 0x09, 0x0f
+            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x03, 0x69, 0x36,
+            0x34, 0x00, 0x00, 0x0a, 0x95, 0x80, 0x80, 0x80, 0x00, 0x01, 0x8f, 0x80, 0x80, 0x80, 0x00, 0x00,
+            0x14, 0x01, 0x14, 0x00, 0x34, 0x03, 0x00, 0x14, 0x01, 0x2b, 0x03, 0x00, 0x09, 0x0f
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(10) }), 0);
@@ -510,10 +512,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(10) }), 0);
@@ -550,10 +549,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -605,10 +601,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -649,10 +642,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -680,10 +670,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0) }), 0);
@@ -710,10 +697,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(10) }), 0);
@@ -741,10 +725,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(10) }), 0);
@@ -781,10 +762,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -836,10 +814,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -880,10 +855,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
         ASSERT(plan.memory()->size());
 
         // Test this doesn't crash.
@@ -911,10 +883,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0) }), 0);
@@ -941,10 +910,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(1) }), 1);
@@ -982,10 +948,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(1) }), 1);
@@ -1009,10 +972,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { }), 5);
@@ -1030,10 +990,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { }), 11);
@@ -1050,10 +1007,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { }), 11);
@@ -1070,10 +1024,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { }), 11);
@@ -1089,10 +1040,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(1) }), 1);
@@ -1118,10 +1066,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0) }), 0);
@@ -1154,10 +1099,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0) }), 0);
@@ -1198,10 +1140,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(1) }), 0);
@@ -1251,10 +1190,7 @@ static void runWasmTests()
         };
 
         Plan plan(*vm, vector);
-        if (plan.failed() || plan.resultSize() != 1 || !plan.result(0)) {
-            dataLogLn("Module failed to compile correctly.");
-            CRASH();
-        }
+        checkPlan(plan, 1);
 
         // Test this doesn't crash.
         CHECK_EQ(invoke<int>(*plan.result(0)->jsEntryPoint, { box(0), box(1) }), 1);
index b5ac7a4..07b329e 100644 (file)
@@ -87,6 +87,10 @@ private:
         {
         }
 
+        LazyBlock()
+        {
+        }
+
         explicit operator bool() const { return !!m_block; }
 
         BasicBlock* get(Procedure& proc)
@@ -118,6 +122,10 @@ public:
                 result.append(proc.addVariable(toB3Type(signature)));
         }
 
+        ControlData()
+        {
+        }
+
         void dump(PrintStream& out) const
         {
             switch (type()) {
@@ -172,8 +180,8 @@ public:
 
     B3IRGenerator(Memory*, Procedure&, Vector<UnlinkedCall>& unlinkedCalls);
 
-    void addArguments(const Vector<Type>&);
-    void addLocal(Type, uint32_t);
+    bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
+    bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
     ExpressionType addConstant(Type, uint64_t);
 
     // Locals
@@ -191,8 +199,8 @@ public:
     // Control flow
     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
-    ControlData WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature);
-    bool WARN_UNUSED_RETURN addElse(ControlData&);
+    bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
+    bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
 
     bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
     bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
@@ -245,16 +253,22 @@ B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure, Vector<Unlink
     }
 }
 
-void B3IRGenerator::addLocal(Type type, uint32_t count)
+bool B3IRGenerator::addLocal(Type type, uint32_t count)
 {
-    m_locals.reserveCapacity(m_locals.size() + count);
+    if (!m_locals.tryReserveCapacity(m_locals.size() + count))
+        return false;
+
     for (uint32_t i = 0; i < count; ++i)
-        m_locals.append(m_proc.addVariable(toB3Type(type)));
+        m_locals.uncheckedAppend(m_proc.addVariable(toB3Type(type)));
+    return true;
 }
 
-void B3IRGenerator::addArguments(const Vector<Type>& types)
+bool B3IRGenerator::addArguments(const Vector<Type>& types)
 {
     ASSERT(!m_locals.size());
+    if (!m_locals.tryReserveCapacity(types.size()))
+        return false;
+
     m_locals.grow(types.size());
     wasmCallingConvention().loadArguments(types, m_proc, m_currentBlock, Origin(),
         [&] (ExpressionType argument, unsigned i) {
@@ -262,6 +276,7 @@ void B3IRGenerator::addArguments(const Vector<Type>& types)
             m_locals[i] = argumentVariable;
             m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), argumentVariable, argument);
         });
+    return true;
 }
 
 bool B3IRGenerator::getLocal(uint32_t index, ExpressionType& result)
@@ -493,7 +508,7 @@ B3IRGenerator::ControlData B3IRGenerator::addLoop(Type signature)
     return ControlData(m_proc, signature, body);
 }
 
-B3IRGenerator::ControlData B3IRGenerator::addIf(ExpressionType condition, Type signature)
+bool B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result)
 {
     // FIXME: This needs to do some kind of stack passing.
 
@@ -507,10 +522,11 @@ B3IRGenerator::ControlData B3IRGenerator::addIf(ExpressionType condition, Type s
     notTaken->addPredecessor(m_currentBlock);
 
     m_currentBlock = taken;
-    return ControlData(m_proc, signature, notTaken, continuation);
+    result = ControlData(m_proc, signature, notTaken, continuation);
+    return true;
 }
 
-bool B3IRGenerator::addElse(ControlData& data)
+bool B3IRGenerator::addElse(ControlData& data, const ExpressionList&)
 {
     ASSERT(data.continuation);
     m_currentBlock = data.special;
diff --git a/Source/JavaScriptCore/wasm/WasmFormat.cpp b/Source/JavaScriptCore/wasm/WasmFormat.cpp
new file mode 100644 (file)
index 0000000..2bdc800
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmFormat.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+namespace JSC { namespace Wasm {
+
+const char* toString(Type type)
+{
+    switch (type) {
+    case Void:
+        return "void";
+    case I32:
+        return "i32";
+    case I64:
+        return "i64";
+    case F32:
+        return "f32";
+    case F64:
+        return "f64";
+    }
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(B3_JIT)
index 7eb5b1a..8286aef 100644 (file)
@@ -97,6 +97,7 @@ inline bool isValueType(Type type)
     return false;
 }
 
+const char* toString(Type);
 
 struct Signature {
     Type returnType;
index 51e2f8b..6e0f2b1 100644 (file)
@@ -73,12 +73,14 @@ FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functio
 {
     if (verbose)
         dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength);
-    m_context.addArguments(m_signature->arguments);
 }
 
 template<typename Context>
 bool FunctionParser<Context>::parse()
 {
+    if (!m_context.addArguments(m_signature->arguments))
+        return false;
+
     uint32_t localCount;
     if (!parseVarUInt32(localCount))
         return false;
@@ -92,7 +94,8 @@ bool FunctionParser<Context>::parse()
         if (!parseValueType(typeOfLocal))
             return false;
 
-        m_context.addLocal(typeOfLocal, numberOfLocals);
+        if (!m_context.addLocal(typeOfLocal, numberOfLocals))
+            return false;
     }
 
     return parseBlock();
@@ -282,12 +285,16 @@ bool FunctionParser<Context>::parseExpression(OpType op)
             return false;
 
         ExpressionType condition = m_expressionStack.takeLast();
-        m_controlStack.append(m_context.addIf(condition, inlineSignature));
+        ControlType control;
+        if (!m_context.addIf(condition, inlineSignature, control))
+            return false;
+
+        m_controlStack.append(control);
         return true;
     }
 
     case OpType::Else: {
-        return m_context.addElse(m_controlStack.last());
+        return m_context.addElse(m_controlStack.last(), m_expressionStack);
     }
 
     case OpType::Br:
@@ -349,7 +356,7 @@ bool FunctionParser<Context>::parseUnreachableExpression(OpType op)
         ControlType& data = m_controlStack.last();
         ASSERT(data.type() == BlockType::If);
         m_unreachableBlocks = 0;
-        return m_context.addElse(data);
+        return m_context.addElse(data, m_expressionStack);
     }
 
     case OpType::End: {
index f9c8a54..739be42 100644 (file)
@@ -32,6 +32,7 @@
 #include "WasmB3IRGenerator.h"
 #include "WasmCallingConvention.h"
 #include "WasmModuleParser.h"
+#include "WasmValidate.h"
 #include <wtf/DataLog.h>
 
 namespace JSC { namespace Wasm {
@@ -63,6 +64,13 @@ Plan::Plan(VM& vm, const uint8_t* source, size_t sourceLength)
         const uint8_t* functionStart = source + info.start;
         size_t functionLength = info.end - info.start;
         ASSERT(functionLength <= sourceLength);
+
+        String error = validateFunction(functionStart, functionLength, info.signature, moduleParser.functionInformation());
+        if (!error.isNull()) {
+            m_errorMessage = error;
+            return;
+        }
+
         m_result.append(parseAndCompile(vm, functionStart, functionLength, moduleParser.memory().get(), info.signature, moduleParser.functionInformation()));
     }
 
diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp
new file mode 100644 (file)
index 0000000..b697e44
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmValidate.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmFunctionParser.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace JSC { namespace Wasm {
+
+class Validate {
+public:
+    class ControlData {
+    public:
+        ControlData(BlockType type, Type signature)
+            : m_blockType(type)
+            , m_signature(signature)
+        {
+        }
+
+        ControlData()
+        {
+        }
+
+        void dump(PrintStream& out) const
+        {
+            switch (type()) {
+            case BlockType::If:
+                out.print("If:    ");
+                break;
+            case BlockType::Block:
+                out.print("Block: ");
+                break;
+            case BlockType::Loop:
+                out.print("Loop:  ");
+                break;
+            }
+        }
+
+        BlockType type() const { return m_blockType; }
+        Type signature() const { return m_signature; }
+    private:
+        BlockType m_blockType;
+        Type m_signature;
+    };
+    typedef Type ExpressionType;
+    typedef ControlData ControlType;
+    typedef Vector<ExpressionType, 1> ExpressionList;
+    static const ExpressionType emptyExpression = Void;
+
+    bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
+    bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
+    ExpressionType addConstant(Type type, uint64_t) { return type; }
+
+    // Locals
+    bool WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
+    bool WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
+
+    // Memory
+    bool WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
+    bool WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
+
+    // Basic operators
+    bool WARN_UNUSED_RETURN binaryOp(BinaryOpType, ExpressionType left, ExpressionType right, ExpressionType& result);
+    bool WARN_UNUSED_RETURN unaryOp(UnaryOpType, ExpressionType arg, ExpressionType& result);
+
+    // Control flow
+    ControlData WARN_UNUSED_RETURN addBlock(Type signature);
+    ControlData WARN_UNUSED_RETURN addLoop(Type signature);
+    bool WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result);
+    bool WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&);
+
+    bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
+    bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
+    bool WARN_UNUSED_RETURN endBlock(ControlData&, ExpressionList& expressionStack);
+
+    bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const FunctionInformation&, const Vector<ExpressionType>& args, ExpressionType& result);
+    bool WARN_UNUSED_RETURN isContinuationReachable(ControlData&) { return true; }
+
+    void dump(const Vector<ControlType>& controlStack, const ExpressionList& expressionStack);
+
+    String errorMessage() const { return m_errorMessage; }
+    Validate(ExpressionType returnType)
+        : m_returnType(returnType)
+    {
+    }
+
+private:
+    bool unify(Type, Type);
+    bool unify(const ExpressionList&, const ControlData&);
+
+    ExpressionType m_returnType;
+    Vector<Type> m_locals;
+    String m_errorMessage;
+};
+
+bool Validate::addArguments(const Vector<Type>& args)
+{
+    for (Type arg : args) {
+        if (!addLocal(arg, 1))
+            return false;
+    }
+    return true;
+}
+
+bool Validate::addLocal(Type type, uint32_t count)
+{
+    if (!m_locals.tryReserveCapacity(m_locals.size() + count))
+        return false;
+
+    for (uint32_t i = 0; i < count; ++i)
+        m_locals.uncheckedAppend(type);
+    return true;
+}
+
+bool Validate::getLocal(uint32_t index, ExpressionType& result)
+{
+    if (index < m_locals.size()) {
+        result = m_locals[index];
+        return true;
+    }
+    m_errorMessage = ASCIILiteral("Attempt to use unknown local.");
+    return false;
+}
+
+bool Validate::setLocal(uint32_t index, ExpressionType value)
+{
+    ExpressionType localType;
+    if (!getLocal(index, localType))
+        return false;
+
+    if (localType == value)
+        return true;
+
+    m_errorMessage = makeString("Attempt to set local with type: ", toString(localType), " with a variable of type: ", toString(value));
+    return false;
+}
+
+Validate::ControlType Validate::addBlock(Type signature)
+{
+    return ControlData(BlockType::Block, signature);
+}
+
+Validate::ControlType Validate::addLoop(Type signature)
+{
+    return ControlData(BlockType::Loop, signature);
+}
+
+bool Validate::addIf(ExpressionType condition, Type signature, ControlType& result)
+{
+    if (condition != I32) {
+        m_errorMessage = makeString("Attempting to use ", toString(condition), " as the condition for an if block");
+        return false;
+    }
+    result = ControlData(BlockType::If, signature);
+    return true;
+}
+
+bool Validate::addElse(ControlType& current, const ExpressionList& values)
+{
+    if (current.type() != BlockType::If) {
+        m_errorMessage = makeString("Attempting to add else block to something other than an if");
+        return false;
+    }
+
+    if (!unify(values, current)) {
+        ASSERT(errorMessage());
+        return false;
+    }
+
+    current = ControlData(BlockType::Block, current.signature());
+    return true;
+}
+
+bool Validate::addReturn(const ExpressionList& returnValues)
+{
+    if (m_returnType == Void)
+        return true;
+    ASSERT(returnValues.size() == 1);
+
+    if (m_returnType == returnValues[0])
+        return true;
+
+    m_errorMessage = makeString("Attempting to add return with type: ", toString(returnValues[0]), " but function expects return with type: ", toString(m_returnType));
+    return false;
+}
+
+bool Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack)
+{
+    // Void means this is not a conditional branch.
+    if (condition != Void && condition != I32) {
+        m_errorMessage = makeString("Attempting to add a conditional branch with condition type: ", toString(condition), " but expected i32.");
+        return false;
+    }
+
+    if (target.type() == BlockType::If)
+        return true;
+
+    if (target.signature() == Void)
+        return true;
+
+    ASSERT(stack.size() == 1);
+    if (target.signature() == stack[0])
+        return true;
+
+    m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but branching with type: ", toString(target.signature()));
+    return false;
+}
+
+bool Validate::endBlock(ControlType& block, ExpressionList& stack)
+{
+    if (block.signature() == Void)
+        return true;
+
+
+    ASSERT(stack.size() == 1);
+    if (block.signature() == stack[0])
+        return true;
+
+    m_errorMessage = makeString("Block fallthrough has expected type: ", toString(block.signature()), " but produced type: ", toString(block.signature()));
+    return false;
+}
+
+bool Validate::addCall(unsigned, const FunctionInformation& info, const Vector<ExpressionType>& args, ExpressionType& result)
+{
+    if (info.signature->arguments.size() != args.size()) {
+        StringBuilder builder;
+        builder.append("Arity mismatch in call, expected: ");
+        builder.appendNumber(info.signature->arguments.size());
+        builder.append(" but got: ");
+        builder.appendNumber(args.size());
+        m_errorMessage = builder.toString();
+        return false;
+    }
+
+    for (unsigned i = 0; i < args.size(); ++i) {
+        if (args[i] != info.signature->arguments[i]) {
+            m_errorMessage = makeString("Expected argument type: ", toString(info.signature->arguments[i]), " does not match passed argument type: ", toString(args[i]));
+            return false;
+        }
+    }
+
+    result = info.signature->returnType;
+    return true;
+}
+
+bool Validate::unify(const ExpressionList& values, const ControlType& block)
+{
+    ASSERT(values.size() <= 1);
+    if (block.signature() == Void)
+        return true;
+
+    if (!values.size()) {
+        m_errorMessage = makeString("Block has non-void signature but has no stack entries on exit");
+        return false;
+    }
+
+    if (values[0] == block.signature())
+        return true;
+
+    m_errorMessage = makeString("Expected control flow to return value with type: ", toString(block.signature()), " but got value with type: ", toString(values[0]));
+    return false;
+}
+
+void Validate::dump(const Vector<ControlType>&, const ExpressionList&)
+{
+    dataLogLn("Validating");
+}
+
+String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const Vector<FunctionInformation>& functions)
+{
+    Validate context(signature->returnType);
+    FunctionParser<Validate> validator(context, source, length, signature, functions);
+    if (!validator.parse()) {
+        // FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
+        return context.errorMessage();
+    }
+
+    return String();
+}
+
+} } // namespace JSC::Wasm
+
+#include "WasmValidateInlines.h"
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/WasmValidate.h b/Source/JavaScriptCore/wasm/WasmValidate.h
new file mode 100644 (file)
index 0000000..76124f0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "WasmFormat.h"
+
+namespace JSC { namespace Wasm {
+
+String validateFunction(const uint8_t*, size_t, const Signature*, const Vector<FunctionInformation>&);
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
diff --git a/Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py b/Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py
new file mode 100755 (executable)
index 0000000..ac5b3f8
--- /dev/null
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2016 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This tool has a couple of helpful macros to process Wasm files from the wasm.json.
+
+from generateWasm import *
+import optparse
+import sys
+
+parser = optparse.OptionParser(usage="usage: %prog <wasm.json> <WasmOps.h>")
+(options, args) = parser.parse_args(sys.argv[0:])
+if len(args) != 3:
+    parser.error(parser.usage)
+
+wasm = Wasm(args[0], args[1])
+opcodes = wasm.opcodes
+wasmValidateInlinesHFile = open(args[2], "w")
+
+
+def cppType(name):
+    result = {
+        "bool": "I32",
+        "addr": "I32",
+        "i32": "I32",
+        "i64": "I64",
+        "f32": "F32",
+        "f64": "F64",
+    }.get(name, None)
+    if result == None:
+        raise ValueError("Unknown type name: " + name)
+    return result
+
+
+def toCpp(name):
+    return wasm.toCpp(name)
+
+
+def unaryMacro(name):
+    op = opcodes[name]
+    return """
+    case UnaryOpType::""" + toCpp(name) + """: {
+        if (value != """ + cppType(op["parameter"][0]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(value));
+            return false;
+        }
+
+        result = """ + cppType(op["return"][0]) + """;
+        return true;
+    }"""
+
+
+def binaryMacro(name):
+    op = opcodes[name]
+    return """
+    case BinaryOpType::""" + toCpp(name) + """: {
+        if (left != """ + cppType(op["parameter"][0]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the left value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(left));
+            return false;
+        }
+
+        if (right != """ + cppType(op["parameter"][1]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the right value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(right));
+            return false;
+        }
+
+        result = """ + cppType(op["return"][0]) + """;
+        return true;
+    }"""
+
+
+def loadMacro(name):
+    op = opcodes[name]
+    return """
+    case LoadOpType::""" + toCpp(name) + """: {
+        if (pointer != """ + cppType(op["parameter"][0]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the pointer to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(pointer));
+            return false;
+        }
+
+        result = """ + cppType(op["return"][0]) + """;
+        return true;
+    }"""
+
+
+def storeMacro(name):
+    op = opcodes[name]
+    return """
+    case StoreOpType::""" + toCpp(name) + """: {
+        if (pointer != """ + cppType(op["parameter"][0]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the pointer to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(pointer));
+            return false;
+        }
+
+        if (value != """ + cppType(op["parameter"][1]) + """) {
+            m_errorMessage = makeString(\"""" + name + """ expects the value to be of type: ", toString(""" + cppType(op["parameter"][0]) + """), " but got a value with type: ", toString(value));
+            return false;
+        }
+
+        return true;
+    }"""
+
+
+unaryCases = "".join([op for op in wasm.opcodeIterator(isUnary, unaryMacro)])
+binaryCases = "".join([op for op in wasm.opcodeIterator(isBinary, binaryMacro)])
+loadCases = "".join([op for op in wasm.opcodeIterator(lambda op: op["category"] == "memory" and len(op["return"]) == 1, loadMacro)])
+storeCases = "".join([op for op in wasm.opcodeIterator(lambda op: op["category"] == "memory" and len(op["return"]) == 0, storeMacro)])
+
+contents = wasm.header + """
+// This file is intended to be inlined by WasmValidate.cpp only! It should not be included elsewhere.
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+namespace JSC { namespace Wasm {
+
+bool Validate::unaryOp(UnaryOpType op, ExpressionType value, ExpressionType& result)
+{
+    switch (op) {
+""" + unaryCases + """
+    }
+}
+
+bool Validate::binaryOp(BinaryOpType op, ExpressionType left, ExpressionType right, ExpressionType& result)
+{
+    switch (op) {
+""" + binaryCases + """
+    }
+}
+
+bool Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t)
+{
+    switch (op) {
+""" + loadCases + """
+    }
+}
+
+bool Validate::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t)
+{
+    switch (op) {
+""" + storeCases + """
+    }
+}
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
+
+"""
+
+wasmValidateInlinesHFile.write(contents)
+wasmValidateInlinesHFile.close()