Implement some arithmetic instructions in WebAssembly
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Sep 2015 22:45:03 +0000 (22:45 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Sep 2015 22:45:03 +0000 (22:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148737

Patch by Sukolsak Sakshuwong <sukolsak@gmail.com> on 2015-09-03
Reviewed by Geoffrey Garen.

This patch implements the addition and subtraction instructions in
WebAssembly using a stack-based approach: each instruction reads its
operands from the top of the 'temporary' stack, pops them, and
optionally pushes a return value to the stack. Since operands are passed
on the stack, we don't use the arguments that are passed to the methods
of WASMFunctionCompiler, and we don't use the return values from these
methods. (We will use them when we implement LLVM IR generation for
WebAssembly, where each expression is an LLVMValueRef.)

* tests/stress/wasm-arithmetic.js: Added.
* tests/stress/wasm-arithmetic.wasm: Added.
* wasm/WASMFunctionCompiler.h:
(JSC::WASMFunctionCompiler::endFunction):
(JSC::WASMFunctionCompiler::buildReturn):
(JSC::WASMFunctionCompiler::buildImmediateI32):
(JSC::WASMFunctionCompiler::buildBinaryI32):
(JSC::WASMFunctionCompiler::temporaryAddress):
* wasm/WASMFunctionParser.cpp:
(JSC::WASMFunctionParser::parseReturnStatement):
(JSC::WASMFunctionParser::parseExpressionI32):
(JSC::WASMFunctionParser::parseImmediateExpressionI32):
(JSC::WASMFunctionParser::parseBinaryExpressionI32):
* wasm/WASMFunctionParser.h:
* wasm/WASMFunctionSyntaxChecker.h:
(JSC::WASMFunctionSyntaxChecker::startFunction):
(JSC::WASMFunctionSyntaxChecker::endFunction):
(JSC::WASMFunctionSyntaxChecker::buildReturn):
(JSC::WASMFunctionSyntaxChecker::buildImmediateI32):
(JSC::WASMFunctionSyntaxChecker::buildBinaryI32):
(JSC::WASMFunctionSyntaxChecker::stackHeight):
(JSC::WASMFunctionSyntaxChecker::updateTempStackHeight):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/tests/stress/wasm-arithmetic.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/wasm-arithmetic.wasm [new file with mode: 0644]
Source/JavaScriptCore/wasm/WASMFunctionCompiler.h
Source/JavaScriptCore/wasm/WASMFunctionParser.cpp
Source/JavaScriptCore/wasm/WASMFunctionParser.h
Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h

index 757c8f8..0b38dbe 100644 (file)
@@ -1,3 +1,42 @@
+2015-09-03  Sukolsak Sakshuwong  <sukolsak@gmail.com>
+
+        Implement some arithmetic instructions in WebAssembly
+        https://bugs.webkit.org/show_bug.cgi?id=148737
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements the addition and subtraction instructions in
+        WebAssembly using a stack-based approach: each instruction reads its
+        operands from the top of the 'temporary' stack, pops them, and
+        optionally pushes a return value to the stack. Since operands are passed
+        on the stack, we don't use the arguments that are passed to the methods
+        of WASMFunctionCompiler, and we don't use the return values from these
+        methods. (We will use them when we implement LLVM IR generation for
+        WebAssembly, where each expression is an LLVMValueRef.)
+
+        * tests/stress/wasm-arithmetic.js: Added.
+        * tests/stress/wasm-arithmetic.wasm: Added.
+        * wasm/WASMFunctionCompiler.h:
+        (JSC::WASMFunctionCompiler::endFunction):
+        (JSC::WASMFunctionCompiler::buildReturn):
+        (JSC::WASMFunctionCompiler::buildImmediateI32):
+        (JSC::WASMFunctionCompiler::buildBinaryI32):
+        (JSC::WASMFunctionCompiler::temporaryAddress):
+        * wasm/WASMFunctionParser.cpp:
+        (JSC::WASMFunctionParser::parseReturnStatement):
+        (JSC::WASMFunctionParser::parseExpressionI32):
+        (JSC::WASMFunctionParser::parseImmediateExpressionI32):
+        (JSC::WASMFunctionParser::parseBinaryExpressionI32):
+        * wasm/WASMFunctionParser.h:
+        * wasm/WASMFunctionSyntaxChecker.h:
+        (JSC::WASMFunctionSyntaxChecker::startFunction):
+        (JSC::WASMFunctionSyntaxChecker::endFunction):
+        (JSC::WASMFunctionSyntaxChecker::buildReturn):
+        (JSC::WASMFunctionSyntaxChecker::buildImmediateI32):
+        (JSC::WASMFunctionSyntaxChecker::buildBinaryI32):
+        (JSC::WASMFunctionSyntaxChecker::stackHeight):
+        (JSC::WASMFunctionSyntaxChecker::updateTempStackHeight):
+
 2015-09-03  Brian Burg  <bburg@apple.com>
 
         Web Inspector: should crash on purpose if InjectedScriptSource.js is unparseable
diff --git a/Source/JavaScriptCore/tests/stress/wasm-arithmetic.js b/Source/JavaScriptCore/tests/stress/wasm-arithmetic.js
new file mode 100644 (file)
index 0000000..b37a3cc
--- /dev/null
@@ -0,0 +1,22 @@
+//@ skip
+
+/*
+wasm-arithmetic.wasm is generated by pack-asmjs <https://github.com/WebAssembly/polyfill-prototype-1> from the following script:
+
+function asmModule(global, env, buffer) {
+    "use asm";
+
+    function main() {
+        return (10 + 40) - 8;
+    }
+
+    return {
+        main: main
+    };
+}
+*/
+
+var module = loadWebAssembly("wasm-arithmetic.wasm");
+var result = module.main();
+if (result != 42)
+    throw "Error: bad result: " + result;
diff --git a/Source/JavaScriptCore/tests/stress/wasm-arithmetic.wasm b/Source/JavaScriptCore/tests/stress/wasm-arithmetic.wasm
new file mode 100644 (file)
index 0000000..34ed85d
Binary files /dev/null and b/Source/JavaScriptCore/tests/stress/wasm-arithmetic.wasm differ
index a2565a4..7376fd9 100644 (file)
@@ -33,6 +33,8 @@
 #include "LinkBuffer.h"
 #include "MaxFrameExtentForSlowPathCall.h"
 
+#define UNUSED 0
+
 namespace JSC {
 
 class WASMFunctionCompiler : private CCallHelpers {
@@ -40,12 +42,6 @@ public:
     typedef int Expression;
     typedef int Statement;
 
-    union StackSlot {
-        int32_t intValue;
-        float floatValue;
-        double doubleValue;
-    };
-
     WASMFunctionCompiler(VM& vm, CodeBlock* codeBlock, unsigned stackHeight)
         : CCallHelpers(&vm, codeBlock)
         , m_stackHeight(stackHeight)
@@ -102,6 +98,8 @@ public:
 
     void endFunction()
     {
+        ASSERT(!m_tempStackTop);
+
         // FIXME: Remove these if the last statement is a return statement.
         move(TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
         emitFunctionEpilogue();
@@ -129,13 +127,68 @@ public:
         m_codeBlock->capabilityLevel();
     }
 
+    void buildReturn(int, WASMExpressionType returnType)
+    {
+        switch (returnType) {
+        case WASMExpressionType::I32:
+            load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::returnValueGPR);
+            or64(GPRInfo::tagTypeNumberRegister, GPRInfo::returnValueGPR);
+            m_tempStackTop--;
+            break;
+        case WASMExpressionType::Void:
+            move(TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+        emitFunctionEpilogue();
+        ret();
+    }
+
+    int buildImmediateI32(uint32_t immediate)
+    {
+        store32(TrustedImm32(immediate), temporaryAddress(m_tempStackTop++));
+        return UNUSED;
+    }
+
+    int buildBinaryI32(int, int, WASMOpExpressionI32 op)
+    {
+        load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0);
+        load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
+        switch (op) {
+        case WASMOpExpressionI32::Add:
+            add32(GPRInfo::regT1, GPRInfo::regT0);
+            break;
+        case WASMOpExpressionI32::Sub:
+            sub32(GPRInfo::regT1, GPRInfo::regT0);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+        m_tempStackTop--;
+        store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
+        return UNUSED;
+    }
+
 private:
+    union StackSlot {
+        int32_t intValue;
+        float floatValue;
+        double doubleValue;
+    };
+
     Address localAddress(unsigned localIndex) const
     {
         ASSERT(localIndex < m_numberOfLocals);
         return Address(GPRInfo::callFrameRegister, -(localIndex + 1) * sizeof(StackSlot));
     }
 
+    Address temporaryAddress(unsigned temporaryIndex) const
+    {
+        ASSERT(m_numberOfLocals + temporaryIndex < m_stackHeight);
+        return Address(GPRInfo::callFrameRegister, -(m_numberOfLocals + temporaryIndex + 1) * sizeof(StackSlot));
+    }
+
     void throwStackOverflowError()
     {
         setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock));
@@ -156,6 +209,7 @@ private:
 
     unsigned m_stackHeight;
     unsigned m_numberOfLocals;
+    unsigned m_tempStackTop { 0 };
 
     Label m_beginLabel;
     Jump m_stackOverflow;
index 5d404c7..c8b6484 100644 (file)
@@ -226,9 +226,12 @@ ContextStatement WASMFunctionParser::parseSetLocalStatement(Context& context)
 template <class Context>
 ContextStatement WASMFunctionParser::parseReturnStatement(Context& context)
 {
-    if (m_returnType != WASMExpressionType::Void)
-        parseExpression(context, m_returnType);
-    // FIXME: Implement this instruction.
+    ContextExpression expression = 0;
+    if (m_returnType != WASMExpressionType::Void) {
+        expression = parseExpression(context, m_returnType);
+        PROPAGATE_ERROR();
+    }
+    context.buildReturn(expression, m_returnType);
     return UNUSED;
 }
 
@@ -422,6 +425,9 @@ ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
         switch (op) {
         case WASMOpExpressionI32::Immediate:
             return parseImmediateExpressionI32(context);
+        case WASMOpExpressionI32::Add:
+        case WASMOpExpressionI32::Sub:
+            return parseBinaryExpressionI32(context, op);
         case WASMOpExpressionI32::ConstantPoolIndex:
         case WASMOpExpressionI32::GetLocal:
         case WASMOpExpressionI32::GetGlobal:
@@ -451,8 +457,6 @@ ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
         case WASMOpExpressionI32::FromF32:
         case WASMOpExpressionI32::FromF64:
         case WASMOpExpressionI32::Negate:
-        case WASMOpExpressionI32::Add:
-        case WASMOpExpressionI32::Sub:
         case WASMOpExpressionI32::Mul:
         case WASMOpExpressionI32::SDiv:
         case WASMOpExpressionI32::UDiv:
@@ -515,10 +519,9 @@ ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
 }
 
 template <class Context>
-ContextExpression WASMFunctionParser::parseImmediateExpressionI32(Context&, uint32_t)
+ContextExpression WASMFunctionParser::parseImmediateExpressionI32(Context& context, uint32_t immediate)
 {
-    // FIXME: Implement this instruction.
-    return UNUSED;
+    return context.buildImmediateI32(immediate);
 }
 
 template <class Context>
@@ -529,6 +532,16 @@ ContextExpression WASMFunctionParser::parseImmediateExpressionI32(Context& conte
     return parseImmediateExpressionI32(context, immediate);
 }
 
+template <class Context>
+ContextExpression WASMFunctionParser::parseBinaryExpressionI32(Context& context, WASMOpExpressionI32 op)
+{
+    ContextExpression left = parseExpressionI32(context);
+    PROPAGATE_ERROR();
+    ContextExpression right = parseExpressionI32(context);
+    PROPAGATE_ERROR();
+    return context.buildBinaryI32(left, right, op);
+}
+
 } // namespace JSC
 
 #endif // ENABLE(WEBASSEMBLY)
index 3463062..9ecc026 100644 (file)
@@ -80,6 +80,7 @@ private:
     template <class Context> ContextExpression parseExpressionI32(Context&);
     template <class Context> ContextExpression parseImmediateExpressionI32(Context&, uint32_t immediate);
     template <class Context> ContextExpression parseImmediateExpressionI32(Context&);
+    template <class Context> ContextExpression parseBinaryExpressionI32(Context&, WASMOpExpressionI32);
 
     JSWASMModule* m_module;
     WASMReader m_reader;
index 5d329c3..1851f8d 100644 (file)
@@ -28,6 +28,8 @@
 
 #if ENABLE(WEBASSEMBLY)
 
+#define UNUSED 0
+
 namespace JSC {
 
 class WASMFunctionSyntaxChecker {
@@ -37,16 +39,48 @@ public:
 
     void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables)
     {
-        // FIXME: Need to include the number of temporaries used.
-        m_stackHeight = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables;
+        m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables;
     }
 
-    void endFunction() { }
+    void endFunction()
+    {
+        ASSERT(!m_tempStackTop);
+    }
 
-    unsigned stackHeight() { return m_stackHeight; }
+    void buildReturn(int, WASMExpressionType returnType)
+    {
+        if (returnType != WASMExpressionType::Void)
+            m_tempStackTop--;
+    }
+
+    int buildImmediateI32(uint32_t)
+    {
+        m_tempStackTop++;
+        updateTempStackHeight();
+        return UNUSED;
+    }
+
+    int buildBinaryI32(int, int, WASMOpExpressionI32)
+    {
+        m_tempStackTop--;
+        return UNUSED;
+    }
+
+    unsigned stackHeight()
+    {
+        return m_numberOfLocals + m_tempStackHeight;
+    }
 
 private:
-    unsigned m_stackHeight;
+    void updateTempStackHeight()
+    {
+        if (m_tempStackTop > m_tempStackHeight)
+            m_tempStackHeight = m_tempStackTop;
+    }
+
+    unsigned m_numberOfLocals;
+    unsigned m_tempStackTop { 0 };
+    unsigned m_tempStackHeight { 0 };
 };
 
 } // namespace JSC