Source code

Revision control

Other Tools

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:
3
*
4
* Copyright 2015 Mozilla Foundation
5
*
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
9
*
11
*
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.
17
*/
18
19
#ifndef wasm_stubs_h
20
#define wasm_stubs_h
21
22
#include "wasm/WasmFrameIter.h" // js::wasm::ExitReason
23
#include "wasm/WasmGenerator.h"
24
#include "wasm/WasmOpIter.h"
25
26
namespace js {
27
namespace wasm {
28
29
// ValType and location for a single result: either in a register or on the
30
// stack.
31
32
class ABIResult {
33
ValType type_;
34
enum class Location { Gpr, Gpr64, Fpr, Stack } loc_;
35
union {
36
Register gpr_;
37
Register64 gpr64_;
38
FloatRegister fpr_;
39
uint32_t stackOffset_;
40
};
41
42
void validate() {
43
#ifdef DEBUG
44
if (onStack()) {
45
return;
46
}
47
MOZ_ASSERT(inRegister());
48
switch (type_.code()) {
49
case ValType::I32:
50
MOZ_ASSERT(loc_ == Location::Gpr);
51
break;
52
case ValType::I64:
53
MOZ_ASSERT(loc_ == Location::Gpr64);
54
break;
55
case ValType::F32:
56
case ValType::F64:
57
MOZ_ASSERT(loc_ == Location::Fpr);
58
break;
59
case ValType::AnyRef:
60
case ValType::FuncRef:
61
case ValType::Ref:
62
MOZ_ASSERT(loc_ == Location::Gpr);
63
break;
64
default:
65
MOZ_CRASH("bad value type");
66
}
67
#endif
68
}
69
70
friend class ABIResultIter;
71
ABIResult(){};
72
73
public:
74
// Sizes of items in the stack area.
75
//
76
// The size values come from the implementations of Push() in
77
// MacroAssembler-x86-shared.cpp and MacroAssembler-arm-shared.cpp, and from
78
// VFPRegister::size() in Architecture-arm.h.
79
//
80
// On ARM unlike on x86 we push a single for float.
81
82
static constexpr size_t StackSizeOfPtr = sizeof(intptr_t);
83
static constexpr size_t StackSizeOfInt32 = StackSizeOfPtr;
84
static constexpr size_t StackSizeOfInt64 = sizeof(int64_t);
85
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
86
static constexpr size_t StackSizeOfFloat = sizeof(float);
87
#else
88
static constexpr size_t StackSizeOfFloat = sizeof(double);
89
#endif
90
static constexpr size_t StackSizeOfDouble = sizeof(double);
91
92
ABIResult(ValType type, Register gpr)
93
: type_(type), loc_(Location::Gpr), gpr_(gpr) {
94
validate();
95
}
96
ABIResult(ValType type, Register64 gpr64)
97
: type_(type), loc_(Location::Gpr64), gpr64_(gpr64) {
98
validate();
99
}
100
ABIResult(ValType type, FloatRegister fpr)
101
: type_(type), loc_(Location::Fpr), fpr_(fpr) {
102
validate();
103
}
104
ABIResult(ValType type, uint32_t stackOffset)
105
: type_(type), loc_(Location::Stack), stackOffset_(stackOffset) {
106
validate();
107
}
108
109
ValType type() const { return type_; }
110
bool onStack() const { return loc_ == Location::Stack; }
111
bool inRegister() const { return !onStack(); }
112
Register gpr() const {
113
MOZ_ASSERT(loc_ == Location::Gpr);
114
return gpr_;
115
}
116
Register64 gpr64() const {
117
MOZ_ASSERT(loc_ == Location::Gpr64);
118
return gpr64_;
119
}
120
FloatRegister fpr() const {
121
MOZ_ASSERT(loc_ == Location::Fpr);
122
return fpr_;
123
}
124
// Offset from SP.
125
uint32_t stackOffset() const {
126
MOZ_ASSERT(loc_ == Location::Stack);
127
return stackOffset_;
128
}
129
uint32_t size() const;
130
};
131
132
// Just as WebAssembly functions can take multiple arguments, they can also
133
// return multiple results. As with a call, a limited number of results will be
134
// located in registers, and the rest will be stored in a stack area. The
135
// |ABIResultIter| computes result locations, given a |ResultType|.
136
//
137
// Recall that a |ResultType| represents a sequence of value types t1..tN,
138
// indexed from 1 to N. In principle it doesn't matter how we decide which
139
// results get to be in registers and which go to the stack. To better
140
// harmonize with WebAssembly's abstract stack machine, whose properties are
141
// taken advantage of by the baseline compiler, our strategy is to start
142
// allocating result locations in "reverse" order: from result N down to 1.
143
//
144
// If a result with index I is in a register, then all results with index J > I
145
// are also in registers. If a result I is on the stack, then all results with
146
// index K < I are also on the stack, farther away from the stack pointer than
147
// result I.
148
//
149
// Currently only a single result is ever stored in a register, though this may
150
// change in the future on register-rich platforms.
151
//
152
// NB: The baseline compiler also uses thie ABI for locations of block
153
// parameters and return values, within individual WebAssembly functions.
154
155
class ABIResultIter {
156
ResultType type_;
157
uint32_t count_;
158
uint32_t index_;
159
uint32_t nextStackOffset_;
160
enum { Next, Prev } direction_;
161
ABIResult cur_;
162
163
void settleRegister(ValType type);
164
void settleNext();
165
void settlePrev();
166
167
static constexpr size_t RegisterResultCount = 1;
168
169
public:
170
explicit ABIResultIter(const ResultType& type)
171
: type_(type), count_(type.length()) {
172
reset();
173
}
174
175
void reset() {
176
index_ = nextStackOffset_ = 0;
177
direction_ = Next;
178
if (!done()) {
179
settleNext();
180
}
181
}
182
bool done() const { return index_ == count_; }
183
uint32_t index() const { return index_; }
184
uint32_t count() const { return count_; }
185
uint32_t remaining() const { return count_ - index_; }
186
void switchToNext() {
187
MOZ_ASSERT(direction_ == Prev);
188
if (!done() && cur().onStack()) {
189
nextStackOffset_ += cur().size();
190
}
191
index_ = count_ - index_;
192
direction_ = Next;
193
if (!done()) {
194
settleNext();
195
}
196
}
197
void switchToPrev() {
198
MOZ_ASSERT(direction_ == Next);
199
if (!done() && cur().onStack()) {
200
nextStackOffset_ -= cur().size();
201
}
202
index_ = count_ - index_;
203
direction_ = Prev;
204
if (!done()) settlePrev();
205
}
206
void next() {
207
MOZ_ASSERT(direction_ == Next);
208
MOZ_ASSERT(!done());
209
index_++;
210
if (!done()) {
211
settleNext();
212
}
213
}
214
void prev() {
215
MOZ_ASSERT(direction_ == Prev);
216
MOZ_ASSERT(!done());
217
index_++;
218
if (!done()) {
219
settlePrev();
220
}
221
}
222
const ABIResult& cur() const {
223
MOZ_ASSERT(!done());
224
return cur_;
225
}
226
227
uint32_t stackBytesConsumedSoFar() const { return nextStackOffset_; }
228
229
static inline bool HasStackResults(const ResultType& type) {
230
return type.length() > RegisterResultCount;
231
}
232
233
static uint32_t MeasureStackBytes(const ResultType& type) {
234
if (!HasStackResults(type)) {
235
return 0;
236
}
237
ABIResultIter iter(type);
238
while (!iter.done()) {
239
iter.next();
240
}
241
return iter.stackBytesConsumedSoFar();
242
}
243
};
244
245
extern bool GenerateBuiltinThunk(jit::MacroAssembler& masm,
246
jit::ABIFunctionType abiType,
247
ExitReason exitReason, void* funcPtr,
248
CallableOffsets* offsets);
249
250
extern bool GenerateImportFunctions(const ModuleEnvironment& env,
251
const FuncImportVector& imports,
252
CompiledCode* code);
253
254
extern bool GenerateStubs(const ModuleEnvironment& env,
255
const FuncImportVector& imports,
256
const FuncExportVector& exports, CompiledCode* code);
257
258
extern bool GenerateEntryStubs(jit::MacroAssembler& masm,
259
size_t funcExportIndex,
260
const FuncExport& funcExport,
261
const Maybe<jit::ImmPtr>& callee, bool isAsmJS,
262
bool bigIntEnabled, CodeRangeVector* codeRanges);
263
264
extern void GenerateTrapExitMachineState(jit::MachineState* machine,
265
size_t* numWords);
266
267
// A value that is written into the trap exit frame, which is useful for
268
// cross-checking during garbage collection.
269
static constexpr uintptr_t TrapExitDummyValue = 1337;
270
271
// And its offset, in words, down from the highest-addressed word of the trap
272
// exit frame. The value is written into the frame using WasmPush. In the
273
// case where WasmPush allocates more than one word, the value will therefore
274
// be written at the lowest-addressed word.
275
#ifdef JS_CODEGEN_ARM64
276
static constexpr size_t TrapExitDummyValueOffsetFromTop = 1;
277
#else
278
static constexpr size_t TrapExitDummyValueOffsetFromTop = 0;
279
#endif
280
281
// An argument that will end up on the stack according to the system ABI, to be
282
// passed to GenerateDirectCallFromJit. Since the direct JIT call creates its
283
// own frame, it is its responsibility to put stack arguments to their expected
284
// locations; so the caller of GenerateDirectCallFromJit can put them anywhere.
285
286
class JitCallStackArg {
287
public:
288
enum class Tag {
289
Imm32,
290
GPR,
291
FPU,
292
Address,
293
Undefined,
294
};
295
296
private:
297
Tag tag_;
298
union U {
299
int32_t imm32_;
300
jit::Register gpr_;
301
jit::FloatRegister fpu_;
302
jit::Address addr_;
303
U() {}
304
} arg;
305
306
public:
307
JitCallStackArg() : tag_(Tag::Undefined) {}
308
explicit JitCallStackArg(int32_t imm32) : tag_(Tag::Imm32) {
309
arg.imm32_ = imm32;
310
}
311
explicit JitCallStackArg(jit::Register gpr) : tag_(Tag::GPR) {
312
arg.gpr_ = gpr;
313
}
314
explicit JitCallStackArg(jit::FloatRegister fpu) : tag_(Tag::FPU) {
315
new (&arg) jit::FloatRegister(fpu);
316
}
317
explicit JitCallStackArg(const jit::Address& addr) : tag_(Tag::Address) {
318
new (&arg) jit::Address(addr);
319
}
320
321
Tag tag() const { return tag_; }
322
int32_t imm32() const {
323
MOZ_ASSERT(tag_ == Tag::Imm32);
324
return arg.imm32_;
325
}
326
jit::Register gpr() const {
327
MOZ_ASSERT(tag_ == Tag::GPR);
328
return arg.gpr_;
329
}
330
jit::FloatRegister fpu() const {
331
MOZ_ASSERT(tag_ == Tag::FPU);
332
return arg.fpu_;
333
}
334
const jit::Address& addr() const {
335
MOZ_ASSERT(tag_ == Tag::Address);
336
return arg.addr_;
337
}
338
};
339
340
using JitCallStackArgVector = Vector<JitCallStackArg, 4, SystemAllocPolicy>;
341
342
// Generates an inline wasm call (during jit compilation) to a specific wasm
343
// function (as specifed by the given FuncExport).
344
// This call doesn't go through a wasm entry, but rather creates its own
345
// inlined exit frame.
346
// Assumes:
347
// - all the registers have been preserved by the caller,
348
// - all arguments passed in registers have been set up at the expected
349
// locations,
350
// - all arguments passed on stack slot are alive as defined by a corresponding
351
// JitCallStackArg.
352
353
extern void GenerateDirectCallFromJit(
354
jit::MacroAssembler& masm, const FuncExport& fe, const Instance& inst,
355
const JitCallStackArgVector& stackArgs, bool profilingEnabled,
356
jit::Register scratch, uint32_t* callOffset);
357
358
} // namespace wasm
359
} // namespace js
360
361
#endif // wasm_stubs_h