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.
22 #include "wasm/WasmFrameIter.h" // js::wasm::ExitReason
23 #include "wasm/WasmGenerator.h"
24 #include "wasm/WasmOpIter.h"
29 using jit::FloatRegister
;
31 using jit::Register64
;
33 // ValType and location for a single result: either in a register or on the
38 enum class Location
{ Gpr
, Gpr64
, Fpr
, Stack
} loc_
;
43 uint32_t stackOffset_
;
51 MOZ_ASSERT(inRegister());
52 switch (type_
.kind()) {
54 MOZ_ASSERT(loc_
== Location::Gpr
);
57 MOZ_ASSERT(loc_
== Location::Gpr64
);
61 MOZ_ASSERT(loc_
== Location::Fpr
);
64 MOZ_ASSERT(loc_
== Location::Gpr
);
67 MOZ_ASSERT(loc_
== Location::Fpr
);
73 friend class ABIResultIter
;
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);
91 static constexpr size_t StackSizeOfFloat
= sizeof(double);
93 static constexpr size_t StackSizeOfDouble
= sizeof(double);
94 #ifdef ENABLE_WASM_SIMD
95 static constexpr size_t StackSizeOfV128
= sizeof(V128
);
98 ABIResult(ValType type
, Register gpr
)
99 : type_(type
), loc_(Location::Gpr
), gpr_(gpr
) {
102 ABIResult(ValType type
, Register64 gpr64
)
103 : type_(type
), loc_(Location::Gpr64
), gpr64_(gpr64
) {
106 ABIResult(ValType type
, FloatRegister fpr
)
107 : type_(type
), loc_(Location::Fpr
), fpr_(fpr
) {
110 ABIResult(ValType type
, uint32_t stackOffset
)
111 : type_(type
), loc_(Location::Stack
), stackOffset_(stackOffset
) {
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
);
122 Register64
gpr64() const {
123 MOZ_ASSERT(loc_
== Location::Gpr64
);
126 FloatRegister
fpr() const {
127 MOZ_ASSERT(loc_
== Location::Fpr
);
131 uint32_t stackOffset() const {
132 MOZ_ASSERT(loc_
== Location::Stack
);
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
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
{
165 uint32_t nextStackOffset_
;
166 enum { Next
, Prev
} direction_
;
169 void settleRegister(ValType type
);
174 explicit ABIResultIter(const ResultType
& type
)
175 : type_(type
), count_(type
.length()) {
180 index_
= nextStackOffset_
= 0;
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_
;
201 void switchToPrev() {
202 MOZ_ASSERT(direction_
== Next
);
203 if (!done() && cur().onStack()) {
204 nextStackOffset_
-= cur().size();
206 index_
= count_
- index_
;
208 if (!done()) settlePrev();
211 MOZ_ASSERT(direction_
== Next
);
219 MOZ_ASSERT(direction_
== Prev
);
226 const ABIResult
& cur() const {
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
)) {
241 ABIResultIter
iter(type
);
242 while (!iter
.done()) {
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
,
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
,
271 extern bool GenerateProvisionalLazyJitEntryStub(jit::MacroAssembler
& masm
,
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;
285 static constexpr size_t TrapExitDummyValueOffsetFromTop
= 0;
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
{
308 jit::FloatRegister fpu_
;
314 JitCallStackArg() : tag_(Tag::Undefined
) {}
315 explicit JitCallStackArg(int32_t imm32
) : tag_(Tag::Imm32
) {
318 explicit JitCallStackArg(jit::Register gpr
) : tag_(Tag::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
);
333 jit::Register
gpr() const {
334 MOZ_ASSERT(tag_
== Tag::GPR
);
337 jit::FloatRegister
fpu() const {
338 MOZ_ASSERT(tag_
== Tag::FPU
);
341 const jit::Address
& addr() const {
342 MOZ_ASSERT(tag_
== Tag::Address
);
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.
354 // - all the registers have been preserved by the caller,
355 // - all arguments passed in registers have been set up at the expected
357 // - all arguments passed on stack slot are alive as defined by a corresponding
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
);
370 #endif // wasm_stubs_h