no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / js / src / wasm / WasmStubs.h
blob6a6622fdf2dd0a22204cf31d3991087b6acf4773
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 * Copyright 2015 Mozilla Foundation
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #ifndef wasm_stubs_h
20 #define wasm_stubs_h
22 #include "wasm/WasmFrameIter.h" // js::wasm::ExitReason
23 #include "wasm/WasmGenerator.h"
24 #include "wasm/WasmOpIter.h"
26 namespace js {
27 namespace wasm {
29 using jit::FloatRegister;
30 using jit::Register;
31 using jit::Register64;
33 // ValType and location for a single result: either in a register or on the
34 // stack.
36 class ABIResult {
37 ValType type_;
38 enum class Location { Gpr, Gpr64, Fpr, Stack } loc_;
39 union {
40 Register gpr_;
41 Register64 gpr64_;
42 FloatRegister fpr_;
43 uint32_t stackOffset_;
46 void validate() {
47 #ifdef DEBUG
48 if (onStack()) {
49 return;
51 MOZ_ASSERT(inRegister());
52 switch (type_.kind()) {
53 case ValType::I32:
54 MOZ_ASSERT(loc_ == Location::Gpr);
55 break;
56 case ValType::I64:
57 MOZ_ASSERT(loc_ == Location::Gpr64);
58 break;
59 case ValType::F32:
60 case ValType::F64:
61 MOZ_ASSERT(loc_ == Location::Fpr);
62 break;
63 case ValType::Ref:
64 MOZ_ASSERT(loc_ == Location::Gpr);
65 break;
66 case ValType::V128:
67 MOZ_ASSERT(loc_ == Location::Fpr);
68 break;
70 #endif
73 friend class ABIResultIter;
74 ABIResult() {}
76 public:
77 // Sizes of items in the stack area.
79 // The size values come from the implementations of Push() in
80 // MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp, and from
81 // VFPRegister::size() in Architecture-arm.h.
83 // On ARM unlike on x86 we push a single for float.
85 static constexpr size_t StackSizeOfPtr = sizeof(intptr_t);
86 static constexpr size_t StackSizeOfInt32 = StackSizeOfPtr;
87 static constexpr size_t StackSizeOfInt64 = sizeof(int64_t);
88 #if defined(JS_CODEGEN_ARM)
89 static constexpr size_t StackSizeOfFloat = sizeof(float);
90 #else
91 static constexpr size_t StackSizeOfFloat = sizeof(double);
92 #endif
93 static constexpr size_t StackSizeOfDouble = sizeof(double);
94 #ifdef ENABLE_WASM_SIMD
95 static constexpr size_t StackSizeOfV128 = sizeof(V128);
96 #endif
98 ABIResult(ValType type, Register gpr)
99 : type_(type), loc_(Location::Gpr), gpr_(gpr) {
100 validate();
102 ABIResult(ValType type, Register64 gpr64)
103 : type_(type), loc_(Location::Gpr64), gpr64_(gpr64) {
104 validate();
106 ABIResult(ValType type, FloatRegister fpr)
107 : type_(type), loc_(Location::Fpr), fpr_(fpr) {
108 validate();
110 ABIResult(ValType type, uint32_t stackOffset)
111 : type_(type), loc_(Location::Stack), stackOffset_(stackOffset) {
112 validate();
115 ValType type() const { return type_; }
116 bool onStack() const { return loc_ == Location::Stack; }
117 bool inRegister() const { return !onStack(); }
118 Register gpr() const {
119 MOZ_ASSERT(loc_ == Location::Gpr);
120 return gpr_;
122 Register64 gpr64() const {
123 MOZ_ASSERT(loc_ == Location::Gpr64);
124 return gpr64_;
126 FloatRegister fpr() const {
127 MOZ_ASSERT(loc_ == Location::Fpr);
128 return fpr_;
130 // Offset from SP.
131 uint32_t stackOffset() const {
132 MOZ_ASSERT(loc_ == Location::Stack);
133 return stackOffset_;
135 uint32_t size() const;
138 // Just as WebAssembly functions can take multiple arguments, they can also
139 // return multiple results. As with a call, a limited number of results will be
140 // located in registers, and the rest will be stored in a stack area. The
141 // |ABIResultIter| computes result locations, given a |ResultType|.
143 // Recall that a |ResultType| represents a sequence of value types t1..tN,
144 // indexed from 1 to N. In principle it doesn't matter how we decide which
145 // results get to be in registers and which go to the stack. To better
146 // harmonize with WebAssembly's abstract stack machine, whose properties are
147 // taken advantage of by the baseline compiler, our strategy is to start
148 // allocating result locations in "reverse" order: from result N down to 1.
150 // If a result with index I is in a register, then all results with index J > I
151 // are also in registers. If a result I is on the stack, then all results with
152 // index K < I are also on the stack, farther away from the stack pointer than
153 // result I.
155 // Currently only a single result is ever stored in a register, though this may
156 // change in the future on register-rich platforms.
158 // NB: The baseline compiler also uses thie ABI for locations of block
159 // parameters and return values, within individual WebAssembly functions.
161 class ABIResultIter {
162 ResultType type_;
163 uint32_t count_;
164 uint32_t index_;
165 uint32_t nextStackOffset_;
166 enum { Next, Prev } direction_;
167 ABIResult cur_;
169 void settleRegister(ValType type);
170 void settleNext();
171 void settlePrev();
173 public:
174 explicit ABIResultIter(const ResultType& type)
175 : type_(type), count_(type.length()) {
176 reset();
179 void reset() {
180 index_ = nextStackOffset_ = 0;
181 direction_ = Next;
182 if (!done()) {
183 settleNext();
186 bool done() const { return index_ == count_; }
187 uint32_t index() const { return index_; }
188 uint32_t count() const { return count_; }
189 uint32_t remaining() const { return count_ - index_; }
190 void switchToNext() {
191 MOZ_ASSERT(direction_ == Prev);
192 if (!done() && cur().onStack()) {
193 nextStackOffset_ += cur().size();
195 index_ = count_ - index_;
196 direction_ = Next;
197 if (!done()) {
198 settleNext();
201 void switchToPrev() {
202 MOZ_ASSERT(direction_ == Next);
203 if (!done() && cur().onStack()) {
204 nextStackOffset_ -= cur().size();
206 index_ = count_ - index_;
207 direction_ = Prev;
208 if (!done()) settlePrev();
210 void next() {
211 MOZ_ASSERT(direction_ == Next);
212 MOZ_ASSERT(!done());
213 index_++;
214 if (!done()) {
215 settleNext();
218 void prev() {
219 MOZ_ASSERT(direction_ == Prev);
220 MOZ_ASSERT(!done());
221 index_++;
222 if (!done()) {
223 settlePrev();
226 const ABIResult& cur() const {
227 MOZ_ASSERT(!done());
228 return cur_;
231 uint32_t stackBytesConsumedSoFar() const { return nextStackOffset_; }
233 static inline bool HasStackResults(const ResultType& type) {
234 return type.length() > MaxRegisterResults;
237 static uint32_t MeasureStackBytes(const ResultType& type) {
238 if (!HasStackResults(type)) {
239 return 0;
241 ABIResultIter iter(type);
242 while (!iter.done()) {
243 iter.next();
245 return iter.stackBytesConsumedSoFar();
249 extern bool GenerateBuiltinThunk(jit::MacroAssembler& masm,
250 jit::ABIFunctionType abiType,
251 ExitReason exitReason, void* funcPtr,
252 CallableOffsets* offsets);
254 extern bool GenerateImportFunctions(const ModuleEnvironment& env,
255 const FuncImportVector& imports,
256 CompiledCode* code);
258 extern bool GenerateStubs(const ModuleEnvironment& env,
259 const FuncImportVector& imports,
260 const FuncExportVector& exports, CompiledCode* code);
262 extern bool GenerateEntryStubs(jit::MacroAssembler& masm,
263 size_t funcExportIndex, const FuncExport& fe,
264 const FuncType& funcType,
265 const Maybe<jit::ImmPtr>& callee, bool isAsmJS,
266 CodeRangeVector* codeRanges);
268 extern void GenerateTrapExitRegisterOffsets(jit::RegisterOffsets* offsets,
269 size_t* numWords);
271 extern bool GenerateProvisionalLazyJitEntryStub(jit::MacroAssembler& masm,
272 Offsets* offsets);
274 // A value that is written into the trap exit frame, which is useful for
275 // cross-checking during garbage collection.
276 static constexpr uintptr_t TrapExitDummyValue = 1337;
278 // And its offset, in words, down from the highest-addressed word of the trap
279 // exit frame. The value is written into the frame using WasmPush. In the
280 // case where WasmPush allocates more than one word, the value will therefore
281 // be written at the lowest-addressed word.
282 #ifdef JS_CODEGEN_ARM64
283 static constexpr size_t TrapExitDummyValueOffsetFromTop = 1;
284 #else
285 static constexpr size_t TrapExitDummyValueOffsetFromTop = 0;
286 #endif
288 // An argument that will end up on the stack according to the system ABI, to be
289 // passed to GenerateDirectCallFromJit. Since the direct JIT call creates its
290 // own frame, it is its responsibility to put stack arguments to their expected
291 // locations; so the caller of GenerateDirectCallFromJit can put them anywhere.
293 class JitCallStackArg {
294 public:
295 enum class Tag {
296 Imm32,
297 GPR,
298 FPU,
299 Address,
300 Undefined,
303 private:
304 Tag tag_;
305 union U {
306 int32_t imm32_;
307 jit::Register gpr_;
308 jit::FloatRegister fpu_;
309 jit::Address addr_;
310 U() {}
311 } arg;
313 public:
314 JitCallStackArg() : tag_(Tag::Undefined) {}
315 explicit JitCallStackArg(int32_t imm32) : tag_(Tag::Imm32) {
316 arg.imm32_ = imm32;
318 explicit JitCallStackArg(jit::Register gpr) : tag_(Tag::GPR) {
319 arg.gpr_ = gpr;
321 explicit JitCallStackArg(jit::FloatRegister fpu) : tag_(Tag::FPU) {
322 new (&arg) jit::FloatRegister(fpu);
324 explicit JitCallStackArg(const jit::Address& addr) : tag_(Tag::Address) {
325 new (&arg) jit::Address(addr);
328 Tag tag() const { return tag_; }
329 int32_t imm32() const {
330 MOZ_ASSERT(tag_ == Tag::Imm32);
331 return arg.imm32_;
333 jit::Register gpr() const {
334 MOZ_ASSERT(tag_ == Tag::GPR);
335 return arg.gpr_;
337 jit::FloatRegister fpu() const {
338 MOZ_ASSERT(tag_ == Tag::FPU);
339 return arg.fpu_;
341 const jit::Address& addr() const {
342 MOZ_ASSERT(tag_ == Tag::Address);
343 return arg.addr_;
347 using JitCallStackArgVector = Vector<JitCallStackArg, 4, SystemAllocPolicy>;
349 // Generates an inline wasm call (during jit compilation) to a specific wasm
350 // function (as specifed by the given FuncExport).
351 // This call doesn't go through a wasm entry, but rather creates its own
352 // inlined exit frame.
353 // Assumes:
354 // - all the registers have been preserved by the caller,
355 // - all arguments passed in registers have been set up at the expected
356 // locations,
357 // - all arguments passed on stack slot are alive as defined by a corresponding
358 // JitCallStackArg.
360 extern void GenerateDirectCallFromJit(jit::MacroAssembler& masm,
361 const FuncExport& fe,
362 const Instance& inst,
363 const JitCallStackArgVector& stackArgs,
364 jit::Register scratch,
365 uint32_t* callOffset);
367 } // namespace wasm
368 } // namespace js
370 #endif // wasm_stubs_h