Changeset 189396 in webkit


Ignore:
Timestamp:
Sep 4, 2015 3:47:23 PM (9 years ago)
Author:
commit-queue@webkit.org
Message:

Implement the signed division instruction in WebAssembly
https://bugs.webkit.org/show_bug.cgi?id=148772

Patch by Sukolsak Sakshuwong <Sukolsak Sakshuwong> on 2015-09-04
Reviewed by Geoffrey Garen.

This patch implements the signed division instruction in WebAssembly
for 32-bit integers. We use the IDIV instruction on x86 and x86-64 and
use a C function on all other platforms. We throw an exception if

  • the denominator is zero, or
  • the numerator is -231 and the denominator is -1.
  • jit/JITOperations.cpp:
  • jit/JITOperations.h:
  • tests/stress/wasm-arithmetic.js:

(shouldBe):
(shouldThrow):

  • tests/stress/wasm-arithmetic.wasm:
  • wasm/WASMFunctionCompiler.h:

(JSC::operationDiv):
(JSC::WASMFunctionCompiler::endFunction):
(JSC::WASMFunctionCompiler::buildBinaryI32):
(JSC::WASMFunctionCompiler::appendCall):
(JSC::WASMFunctionCompiler::appendCallWithExceptionCheck):
(JSC::WASMFunctionCompiler::callOperation):
(JSC::WASMFunctionCompiler::throwStackOverflowError): Deleted.

  • wasm/WASMFunctionParser.cpp:

(JSC::WASMFunctionParser::parseExpressionI32):

Location:
trunk/Source/JavaScriptCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r189382 r189396  
     12015-09-04  Sukolsak Sakshuwong  <sukolsak@gmail.com>
     2
     3        Implement the signed division instruction in WebAssembly
     4        https://bugs.webkit.org/show_bug.cgi?id=148772
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        This patch implements the signed division instruction in WebAssembly
     9        for 32-bit integers. We use the IDIV instruction on x86 and x86-64 and
     10        use a C function on all other platforms. We throw an exception if
     11        - the denominator is zero, or
     12        - the numerator is -2^31 and the denominator is -1.
     13
     14        * jit/JITOperations.cpp:
     15        * jit/JITOperations.h:
     16        * tests/stress/wasm-arithmetic.js:
     17        (shouldBe):
     18        (shouldThrow):
     19        * tests/stress/wasm-arithmetic.wasm:
     20        * wasm/WASMFunctionCompiler.h:
     21        (JSC::operationDiv):
     22        (JSC::WASMFunctionCompiler::endFunction):
     23        (JSC::WASMFunctionCompiler::buildBinaryI32):
     24        (JSC::WASMFunctionCompiler::appendCall):
     25        (JSC::WASMFunctionCompiler::appendCallWithExceptionCheck):
     26        (JSC::WASMFunctionCompiler::callOperation):
     27        (JSC::WASMFunctionCompiler::throwStackOverflowError): Deleted.
     28        * wasm/WASMFunctionParser.cpp:
     29        (JSC::WASMFunctionParser::parseExpressionI32):
     30
    1312015-09-04  Sukolsak Sakshuwong  <sukolsak@gmail.com>
    232
  • trunk/Source/JavaScriptCore/jit/JITOperations.cpp

    r189339 r189396  
    9696}
    9797
     98#if ENABLE(WEBASSEMBLY)
     99void JIT_OPERATION operationThrowDivideError(ExecState* exec)
     100{
     101    VM* vm = &exec->vm();
     102    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
     103    CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
     104
     105    NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
     106    ErrorHandlingScope errorScope(*vm);
     107    vm->throwException(callerFrame, createError(callerFrame, ASCIILiteral("Division by zero or division overflow.")));
     108}
     109#endif
     110
    98111int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
    99112{
  • trunk/Source/JavaScriptCore/jit/JITOperations.h

    r189279 r189396  
    246246
    247247void JIT_OPERATION operationThrowStackOverflowError(ExecState*, CodeBlock*) WTF_INTERNAL;
     248#if ENABLE(WEBASSEMBLY)
     249void JIT_OPERATION operationThrowDivideError(ExecState*) WTF_INTERNAL;
     250#endif
    248251int32_t JIT_OPERATION operationCallArityCheck(ExecState*) WTF_INTERNAL;
    249252int32_t JIT_OPERATION operationConstructArityCheck(ExecState*) WTF_INTERNAL;
  • trunk/Source/JavaScriptCore/tests/stress/wasm-arithmetic.js

    r189303 r189396  
    11//@ skip
     2
     3function shouldBe(actual, expected) {
     4    if (actual !== expected)
     5        throw new Error('bad value: ' + actual);
     6}
     7
     8function shouldThrow(func, message) {
     9    var error = null;
     10    try {
     11        func();
     12    } catch (e) {
     13        error = e;
     14    }
     15    if (!error)
     16        throw new Error("not thrown.");
     17    if (String(error) !== message)
     18        throw new Error("bad error: " + String(error));
     19}
    220
    321/*
     
    725    "use asm";
    826
    9     function main() {
    10         return (10 + 40) - 8;
     27    function addSubtract() {
     28        return ((10 + 40) - 8) | 0;
     29    }
     30
     31    function addOverflow() {
     32        return (2147483647 + 1) | 0;
     33    }
     34
     35    function divide() {
     36        return (42 / 5) | 0;
     37    }
     38
     39    function divideByZero() {
     40        return (1 / 0) | 0;
     41    }
     42
     43    function divideOverflow() {
     44        return (-2147483648 / -1) | 0;
    1145    }
    1246
    1347    return {
    14         main: main
     48        addSubtract: addSubtract,
     49        addOverflow: addOverflow,
     50        divide: divide,
     51        divideByZero: divideByZero,
     52        divideOverflow: divideOverflow,
    1553    };
    1654}
     
    1856
    1957var module = loadWebAssembly("wasm-arithmetic.wasm");
    20 var result = module.main();
    21 if (result != 42)
    22     throw "Error: bad result: " + result;
     58
     59shouldBe(module.addSubtract(), 42);
     60
     61shouldBe(module.addOverflow(), -2147483648);
     62
     63shouldBe(module.divide(), 8);
     64
     65shouldThrow(() => {
     66    module.divideByZero();
     67}, "Error: Division by zero or division overflow.");
     68
     69shouldThrow(() => {
     70    module.divideOverflow();
     71}, "Error: Division by zero or division overflow.");
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h

    r189382 r189396  
    3838namespace JSC {
    3939
     40#if !CPU(X86) && !CPU(X86_64)
     41static int32_t JIT_OPERATION operationDiv(int32_t left, int32_t right)
     42{
     43    return left / right;
     44}
     45#endif
     46
    4047class WASMFunctionCompiler : private CCallHelpers {
    4148public:
     
    109116        if (maxFrameExtentForSlowPathCall)
    110117            addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
    111         throwStackOverflowError();
     118        setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock));
     119        appendCallWithExceptionCheck(operationThrowStackOverflowError);
    112120
    113121        // FIXME: Implement arity check.
     
    117125        jump(m_beginLabel);
    118126
     127        if (!m_divideErrorJumpList.empty()) {
     128            m_divideErrorJumpList.link(this);
     129
     130            if (maxFrameExtentForSlowPathCall)
     131                addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
     132            setupArgumentsExecState();
     133            appendCallWithExceptionCheck(operationThrowDivideError);
     134        }
     135
     136        if (!m_exceptionChecks.empty()) {
     137            m_exceptionChecks.link(this);
     138
     139            // lookupExceptionHandler is passed two arguments, the VM and the exec (the CallFrame*).
     140            move(TrustedImmPtr(vm()), GPRInfo::argumentGPR0);
     141            move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
     142
     143#if CPU(X86)
     144            // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer!
     145            poke(GPRInfo::argumentGPR0);
     146            poke(GPRInfo::argumentGPR1, 1);
     147#endif
     148            m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value()));
     149            jumpToExceptionHandler();
     150        }
     151
    119152        LinkBuffer patchBuffer(*m_vm, *this, m_codeBlock, JITCompilationMustSucceed);
    120153
     
    190223            sub32(GPRInfo::regT1, GPRInfo::regT0);
    191224            break;
     225        case WASMOpExpressionI32::SDiv: {
     226            m_divideErrorJumpList.append(branchTest32(Zero, GPRInfo::regT1));
     227            Jump denominatorNotNeg1 = branch32(NotEqual, GPRInfo::regT1, TrustedImm32(-1));
     228            m_divideErrorJumpList.append(branch32(Equal, GPRInfo::regT0, TrustedImm32(-2147483647-1)));
     229            denominatorNotNeg1.link(this);
     230#if CPU(X86) || CPU(X86_64)
     231            ASSERT(GPRInfo::regT0 == X86Registers::eax);
     232            move(GPRInfo::regT1, X86Registers::ecx);
     233            m_assembler.cdq();
     234            m_assembler.idivl_r(X86Registers::ecx);
     235#else
     236            if (maxFrameExtentForSlowPathCall)
     237                addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
     238            callOperation(operationDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
     239            if (maxFrameExtentForSlowPathCall)
     240                addPtr(TrustedImm32(maxFrameExtentForSlowPathCall), stackPointerRegister);
     241#endif
     242            break;
     243        }
    192244        default:
    193245            ASSERT_NOT_REACHED();
     
    217269    }
    218270
    219     void throwStackOverflowError()
    220     {
    221         setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock));
    222 
    223         m_calls.append(std::make_pair(call(), FunctionPtr(operationThrowStackOverflowError).value()));
    224 
    225         // lookupExceptionHandlerFromCallerFrame is passed two arguments, the VM and the exec (the CallFrame*).
    226         move(TrustedImmPtr(m_vm), GPRInfo::argumentGPR0);
    227         move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
    228 #if CPU(X86)
    229         // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer!
    230         poke(GPRInfo::argumentGPR0);
    231         poke(GPRInfo::argumentGPR1, 1);
    232 #endif
    233         m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value()));
    234         jumpToExceptionHandler();
     271    void appendCall(const FunctionPtr& function)
     272    {
     273        m_calls.append(std::make_pair(call(), function.value()));
     274    }
     275
     276    void appendCallWithExceptionCheck(const FunctionPtr& function)
     277    {
     278        appendCall(function);
     279        m_exceptionChecks.append(emitExceptionCheck());
     280    }
     281
     282    void callOperation(int32_t JIT_OPERATION (*operation)(int32_t, int32_t), GPRReg src1, GPRReg src2, GPRReg dst)
     283    {
     284        setupArguments(src1, src2);
     285        appendCall(operation);
     286        move(GPRInfo::returnValueGPR, dst);
    235287    }
    236288
     
    241293    Label m_beginLabel;
    242294    Jump m_stackOverflow;
     295    JumpList m_divideErrorJumpList;
     296    JumpList m_exceptionChecks;
    243297
    244298    Vector<std::pair<Call, void*>> m_calls;
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionParser.cpp

    r189382 r189396  
    431431        case WASMOpExpressionI32::Add:
    432432        case WASMOpExpressionI32::Sub:
     433        case WASMOpExpressionI32::SDiv:
    433434            return parseBinaryExpressionI32(context, op);
    434435        case WASMOpExpressionI32::ConstantPoolIndex:
     
    461462        case WASMOpExpressionI32::Negate:
    462463        case WASMOpExpressionI32::Mul:
    463         case WASMOpExpressionI32::SDiv:
    464464        case WASMOpExpressionI32::UDiv:
    465465        case WASMOpExpressionI32::SMod:
Note: See TracChangeset for help on using the changeset viewer.