7376fd92fc0085696fbc61535edab25ada11bf76
[WebKit-https.git] / Source / JavaScriptCore / wasm / WASMFunctionCompiler.h
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #ifndef WASMFunctionCompiler_h
27 #define WASMFunctionCompiler_h
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CCallHelpers.h"
32 #include "JITOperations.h"
33 #include "LinkBuffer.h"
34 #include "MaxFrameExtentForSlowPathCall.h"
35
36 #define UNUSED 0
37
38 namespace JSC {
39
40 class WASMFunctionCompiler : private CCallHelpers {
41 public:
42     typedef int Expression;
43     typedef int Statement;
44
45     WASMFunctionCompiler(VM& vm, CodeBlock* codeBlock, unsigned stackHeight)
46         : CCallHelpers(&vm, codeBlock)
47         , m_stackHeight(stackHeight)
48     {
49     }
50
51     void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables)
52     {
53         emitFunctionPrologue();
54         emitPutImmediateToCallFrameHeader(m_codeBlock, JSStack::CodeBlock);
55
56         m_beginLabel = label();
57
58         addPtr(TrustedImm32(-WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot)), GPRInfo::callFrameRegister, GPRInfo::regT1);
59         m_stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1);
60
61         move(GPRInfo::regT1, stackPointerRegister);
62         checkStackPointerAlignment();
63
64         m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables;
65
66         unsigned localIndex = 0;
67         for (size_t i = 0; i < arguments.size(); ++i) {
68             Address address(GPRInfo::callFrameRegister, CallFrame::argumentOffset(i) * sizeof(Register));
69             switch (arguments[i]) {
70             case WASMType::I32:
71                 load32(address, GPRInfo::regT0);
72                 store32(GPRInfo::regT0, localAddress(localIndex++));
73                 break;
74             case WASMType::F32:
75                 load64(address, GPRInfo::regT0);
76                 unboxDoubleWithoutAssertions(GPRInfo::regT0, FPRInfo::fpRegT0);
77                 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
78                 storeDouble(FPRInfo::fpRegT0, localAddress(localIndex++));
79                 break;
80             case WASMType::F64:
81                 load64(address, GPRInfo::regT0);
82                 unboxDoubleWithoutAssertions(GPRInfo::regT0, FPRInfo::fpRegT0);
83                 storeDouble(FPRInfo::fpRegT0, localAddress(localIndex++));
84                 break;
85             default:
86                 ASSERT_NOT_REACHED();
87             }
88         }
89         for (uint32_t i = 0; i < numberOfI32LocalVariables; ++i)
90             store32(TrustedImm32(0), localAddress(localIndex++));
91         for (uint32_t i = 0; i < numberOfF32LocalVariables; ++i)
92             store32(TrustedImm32(0), localAddress(localIndex++));
93         for (uint32_t i = 0; i < numberOfF64LocalVariables; ++i)
94             store64(TrustedImm64(0), localAddress(localIndex++));
95
96         m_codeBlock->setNumParameters(1 + arguments.size());
97     }
98
99     void endFunction()
100     {
101         ASSERT(!m_tempStackTop);
102
103         // FIXME: Remove these if the last statement is a return statement.
104         move(TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
105         emitFunctionEpilogue();
106         ret();
107
108         m_stackOverflow.link(this);
109         if (maxFrameExtentForSlowPathCall)
110             addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
111         throwStackOverflowError();
112
113         // FIXME: Implement arity check.
114         Label arityCheck = label();
115         emitFunctionPrologue();
116         emitPutImmediateToCallFrameHeader(m_codeBlock, JSStack::CodeBlock);
117         jump(m_beginLabel);
118
119         LinkBuffer patchBuffer(*m_vm, *this, m_codeBlock, JITCompilationMustSucceed);
120
121         for (auto iterator : m_calls)
122             patchBuffer.link(iterator.first, FunctionPtr(iterator.second));
123
124         MacroAssemblerCodePtr withArityCheck = patchBuffer.locationOf(arityCheck);
125         CodeRef result = FINALIZE_CODE(patchBuffer, ("Baseline JIT code for WebAssembly"));
126         m_codeBlock->setJITCode(adoptRef(new DirectJITCode(result, withArityCheck, JITCode::BaselineJIT)));
127         m_codeBlock->capabilityLevel();
128     }
129
130     void buildReturn(int, WASMExpressionType returnType)
131     {
132         switch (returnType) {
133         case WASMExpressionType::I32:
134             load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::returnValueGPR);
135             or64(GPRInfo::tagTypeNumberRegister, GPRInfo::returnValueGPR);
136             m_tempStackTop--;
137             break;
138         case WASMExpressionType::Void:
139             move(TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR);
140             break;
141         default:
142             ASSERT_NOT_REACHED();
143         }
144         emitFunctionEpilogue();
145         ret();
146     }
147
148     int buildImmediateI32(uint32_t immediate)
149     {
150         store32(TrustedImm32(immediate), temporaryAddress(m_tempStackTop++));
151         return UNUSED;
152     }
153
154     int buildBinaryI32(int, int, WASMOpExpressionI32 op)
155     {
156         load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0);
157         load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
158         switch (op) {
159         case WASMOpExpressionI32::Add:
160             add32(GPRInfo::regT1, GPRInfo::regT0);
161             break;
162         case WASMOpExpressionI32::Sub:
163             sub32(GPRInfo::regT1, GPRInfo::regT0);
164             break;
165         default:
166             ASSERT_NOT_REACHED();
167         }
168         m_tempStackTop--;
169         store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
170         return UNUSED;
171     }
172
173 private:
174     union StackSlot {
175         int32_t intValue;
176         float floatValue;
177         double doubleValue;
178     };
179
180     Address localAddress(unsigned localIndex) const
181     {
182         ASSERT(localIndex < m_numberOfLocals);
183         return Address(GPRInfo::callFrameRegister, -(localIndex + 1) * sizeof(StackSlot));
184     }
185
186     Address temporaryAddress(unsigned temporaryIndex) const
187     {
188         ASSERT(m_numberOfLocals + temporaryIndex < m_stackHeight);
189         return Address(GPRInfo::callFrameRegister, -(m_numberOfLocals + temporaryIndex + 1) * sizeof(StackSlot));
190     }
191
192     void throwStackOverflowError()
193     {
194         setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock));
195
196         m_calls.append(std::make_pair(call(), FunctionPtr(operationThrowStackOverflowError).value()));
197
198         // lookupExceptionHandlerFromCallerFrame is passed two arguments, the VM and the exec (the CallFrame*).
199         move(TrustedImmPtr(m_vm), GPRInfo::argumentGPR0);
200         move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
201 #if CPU(X86)
202         // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer!
203         poke(GPRInfo::argumentGPR0);
204         poke(GPRInfo::argumentGPR1, 1);
205 #endif
206         m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value()));
207         jumpToExceptionHandler();
208     }
209
210     unsigned m_stackHeight;
211     unsigned m_numberOfLocals;
212     unsigned m_tempStackTop { 0 };
213
214     Label m_beginLabel;
215     Jump m_stackOverflow;
216
217     Vector<std::pair<Call, void*>> m_calls;
218 };
219
220 } // namespace JSC
221
222 #endif // ENABLE(WEBASSEMBLY)
223
224 #endif // WASMFunctionCompiler_h