Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2016 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 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
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_code_h
20 : #define wasm_code_h
21 :
22 : #include "js/HashTable.h"
23 : #include "threading/ExclusiveData.h"
24 : #include "wasm/WasmTypes.h"
25 :
26 : namespace js {
27 :
28 : struct AsmJSMetadata;
29 : class WasmInstanceObject;
30 :
31 : namespace wasm {
32 :
33 : struct LinkData;
34 : struct LinkDataTier;
35 : struct Metadata;
36 : struct MetadataTier;
37 : class FrameIterator;
38 :
39 : // ShareableBytes is a reference-counted Vector of bytes.
40 :
41 0 : struct ShareableBytes : ShareableBase<ShareableBytes>
42 : {
43 : // Vector is 'final', so instead make Vector a member and add boilerplate.
44 : Bytes bytes;
45 0 : size_t sizeOfExcludingThis(MallocSizeOf m) const { return bytes.sizeOfExcludingThis(m); }
46 0 : const uint8_t* begin() const { return bytes.begin(); }
47 0 : const uint8_t* end() const { return bytes.end(); }
48 0 : size_t length() const { return bytes.length(); }
49 0 : bool append(const uint8_t *p, uint32_t ct) { return bytes.append(p, ct); }
50 : };
51 :
52 : typedef RefPtr<ShareableBytes> MutableBytes;
53 : typedef RefPtr<const ShareableBytes> SharedBytes;
54 :
55 : // A wasm CodeSegment owns the allocated executable code for a wasm module.
56 :
57 : class CodeSegment;
58 : typedef UniquePtr<CodeSegment> UniqueCodeSegment;
59 : typedef UniquePtr<const CodeSegment> UniqueConstCodeSegment;
60 :
61 0 : class CodeSegment
62 : {
63 : // Executable code must be deallocated specially.
64 : struct FreeCode {
65 : uint32_t codeLength;
66 0 : FreeCode() : codeLength(0) {}
67 0 : explicit FreeCode(uint32_t codeLength) : codeLength(codeLength) {}
68 : void operator()(uint8_t* codeBytes);
69 : };
70 : typedef UniquePtr<uint8_t, FreeCode> UniqueCodeBytes;
71 : static UniqueCodeBytes AllocateCodeBytes(uint32_t codeLength);
72 :
73 : // How this code was compiled.
74 : Tier tier_;
75 :
76 : // bytes_ points to a single allocation of executable machine code in
77 : // the range [0, length_). The range [0, functionLength_) is
78 : // the subrange of [0, length_) which contains function code.
79 : UniqueCodeBytes bytes_;
80 : uint32_t functionLength_;
81 : uint32_t length_;
82 :
83 : // These are pointers into code for stubs used for asynchronous
84 : // signal-handler control-flow transfer.
85 : uint8_t* interruptCode_;
86 : uint8_t* outOfBoundsCode_;
87 : uint8_t* unalignedAccessCode_;
88 :
89 : bool initialize(Tier tier,
90 : UniqueCodeBytes bytes,
91 : uint32_t codeLength,
92 : const ShareableBytes& bytecode,
93 : const LinkDataTier& linkData,
94 : const Metadata& metadata);
95 :
96 : static UniqueConstCodeSegment create(Tier tier,
97 : UniqueCodeBytes bytes,
98 : uint32_t codeLength,
99 : const ShareableBytes& bytecode,
100 : const LinkDataTier& linkData,
101 : const Metadata& metadata);
102 : public:
103 : CodeSegment(const CodeSegment&) = delete;
104 : void operator=(const CodeSegment&) = delete;
105 :
106 0 : CodeSegment()
107 0 : : tier_(Tier(-1)),
108 : functionLength_(0),
109 : length_(0),
110 : interruptCode_(nullptr),
111 : outOfBoundsCode_(nullptr),
112 0 : unalignedAccessCode_(nullptr)
113 0 : {}
114 :
115 : static UniqueConstCodeSegment create(Tier tier,
116 : jit::MacroAssembler& masm,
117 : const ShareableBytes& bytecode,
118 : const LinkDataTier& linkData,
119 : const Metadata& metadata);
120 :
121 : static UniqueConstCodeSegment create(Tier tier,
122 : const Bytes& unlinkedBytes,
123 : const ShareableBytes& bytecode,
124 : const LinkDataTier& linkData,
125 : const Metadata& metadata);
126 :
127 0 : Tier tier() const { return tier_; }
128 :
129 0 : uint8_t* base() const { return bytes_.get(); }
130 0 : uint32_t length() const { return length_; }
131 :
132 0 : uint8_t* interruptCode() const { return interruptCode_; }
133 0 : uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
134 : uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
135 :
136 : // The range [0, functionBytes) is a subrange of [0, codeBytes) that
137 : // contains only function body code, not the stub code. This distinction is
138 : // used by the async interrupt handler to only interrupt when the pc is in
139 : // function code which, in turn, simplifies reasoning about how stubs
140 : // enter/exit.
141 :
142 0 : bool containsFunctionPC(const void* pc) const {
143 0 : return pc >= base() && pc < (base() + functionLength_);
144 : }
145 0 : bool containsCodePC(const void* pc) const {
146 0 : return pc >= base() && pc < (base() + length_);
147 : }
148 :
149 : // Structured clone support:
150 :
151 : size_t serializedSize() const;
152 : uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkData) const;
153 : const uint8_t* deserialize(const uint8_t* cursor, const ShareableBytes& bytecode,
154 : const LinkDataTier& linkData, const Metadata& metadata);
155 :
156 : void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const;
157 : };
158 :
159 : // A FuncExport represents a single function definition inside a wasm Module
160 : // that has been exported one or more times. A FuncExport represents an
161 : // internal entry point that can be called via function definition index by
162 : // Instance::callExport(). To allow O(log(n)) lookup of a FuncExport by
163 : // function definition index, the FuncExportVector is stored sorted by
164 : // function definition index.
165 :
166 0 : class FuncExport
167 : {
168 : Sig sig_;
169 : MOZ_INIT_OUTSIDE_CTOR struct CacheablePod {
170 : uint32_t funcIndex_;
171 : uint32_t codeRangeIndex_;
172 : uint32_t entryOffset_; // Machine code offset
173 : } pod;
174 :
175 : public:
176 0 : FuncExport() = default;
177 0 : explicit FuncExport(Sig&& sig,
178 : uint32_t funcIndex,
179 : uint32_t codeRangeIndex)
180 0 : : sig_(Move(sig))
181 : {
182 0 : pod.funcIndex_ = funcIndex;
183 0 : pod.codeRangeIndex_ = codeRangeIndex;
184 0 : pod.entryOffset_ = UINT32_MAX;
185 0 : }
186 0 : void initEntryOffset(uint32_t entryOffset) {
187 0 : MOZ_ASSERT(pod.entryOffset_ == UINT32_MAX);
188 0 : pod.entryOffset_ = entryOffset;
189 0 : }
190 :
191 0 : const Sig& sig() const {
192 0 : return sig_;
193 : }
194 0 : uint32_t funcIndex() const {
195 0 : return pod.funcIndex_;
196 : }
197 0 : uint32_t codeRangeIndex() const {
198 0 : return pod.codeRangeIndex_;
199 : }
200 0 : uint32_t entryOffset() const {
201 0 : MOZ_ASSERT(pod.entryOffset_ != UINT32_MAX);
202 0 : return pod.entryOffset_;
203 : }
204 :
205 : WASM_DECLARE_SERIALIZABLE(FuncExport)
206 : };
207 :
208 : typedef Vector<FuncExport, 0, SystemAllocPolicy> FuncExportVector;
209 :
210 : // An FuncImport contains the runtime metadata needed to implement a call to an
211 : // imported function. Each function import has two call stubs: an optimized path
212 : // into JIT code and a slow path into the generic C++ js::Invoke and these
213 : // offsets of these stubs are stored so that function-import callsites can be
214 : // dynamically patched at runtime.
215 :
216 0 : class FuncImport
217 : {
218 : Sig sig_;
219 : struct CacheablePod {
220 : uint32_t tlsDataOffset_;
221 : uint32_t interpExitCodeOffset_; // Machine code offset
222 : uint32_t jitExitCodeOffset_; // Machine code offset
223 : } pod;
224 :
225 : public:
226 0 : FuncImport() {
227 0 : memset(&pod, 0, sizeof(CacheablePod));
228 0 : }
229 :
230 0 : FuncImport(Sig&& sig, uint32_t tlsDataOffset)
231 0 : : sig_(Move(sig))
232 : {
233 0 : pod.tlsDataOffset_ = tlsDataOffset;
234 0 : pod.interpExitCodeOffset_ = 0;
235 0 : pod.jitExitCodeOffset_ = 0;
236 0 : }
237 :
238 0 : void initInterpExitOffset(uint32_t off) {
239 0 : MOZ_ASSERT(!pod.interpExitCodeOffset_);
240 0 : pod.interpExitCodeOffset_ = off;
241 0 : }
242 0 : void initJitExitOffset(uint32_t off) {
243 0 : MOZ_ASSERT(!pod.jitExitCodeOffset_);
244 0 : pod.jitExitCodeOffset_ = off;
245 0 : }
246 :
247 0 : const Sig& sig() const {
248 0 : return sig_;
249 : }
250 0 : uint32_t tlsDataOffset() const {
251 0 : return pod.tlsDataOffset_;
252 : }
253 0 : uint32_t interpExitCodeOffset() const {
254 0 : return pod.interpExitCodeOffset_;
255 : }
256 0 : uint32_t jitExitCodeOffset() const {
257 0 : return pod.jitExitCodeOffset_;
258 : }
259 :
260 : WASM_DECLARE_SERIALIZABLE(FuncImport)
261 : };
262 :
263 : typedef Vector<FuncImport, 0, SystemAllocPolicy> FuncImportVector;
264 :
265 : // A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
266 : // shared memory (SharedArrayBuffer).
267 :
268 : enum class MemoryUsage
269 : {
270 : None = false,
271 : Unshared = 1,
272 : Shared = 2
273 : };
274 :
275 : static inline bool
276 0 : UsesMemory(MemoryUsage memoryUsage)
277 : {
278 0 : return bool(memoryUsage);
279 : }
280 :
281 : // NameInBytecode represents a name that is embedded in the wasm bytecode.
282 : // The presence of NameInBytecode implies that bytecode has been kept.
283 :
284 : struct NameInBytecode
285 : {
286 : uint32_t offset;
287 : uint32_t length;
288 :
289 0 : NameInBytecode()
290 0 : : offset(UINT32_MAX), length(0)
291 0 : {}
292 : NameInBytecode(uint32_t offset, uint32_t length)
293 : : offset(offset), length(length)
294 : {}
295 : };
296 :
297 : typedef Vector<NameInBytecode, 0, SystemAllocPolicy> NameInBytecodeVector;
298 :
299 : // CustomSection represents a custom section in the bytecode which can be
300 : // extracted via Module.customSections. The (offset, length) pair does not
301 : // include the custom section name.
302 :
303 : struct CustomSection
304 : {
305 : NameInBytecode name;
306 : uint32_t offset;
307 : uint32_t length;
308 :
309 : CustomSection() = default;
310 0 : CustomSection(NameInBytecode name, uint32_t offset, uint32_t length)
311 0 : : name(name), offset(offset), length(length)
312 0 : {}
313 : };
314 :
315 : typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
316 : typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
317 : typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
318 :
319 : // Metadata holds all the data that is needed to describe compiled wasm code
320 : // at runtime (as opposed to data that is only used to statically link or
321 : // instantiate a module).
322 : //
323 : // Metadata is built incrementally by ModuleGenerator and then shared immutably
324 : // between modules.
325 : //
326 : // The Metadata structure is split into tier-invariant and tier-variant parts;
327 : // the former points to instances of the latter. Additionally, the asm.js
328 : // subsystem subclasses the Metadata, adding more tier-invariant data, some of
329 : // which is serialized. See AsmJS.cpp.
330 :
331 0 : struct MetadataCacheablePod
332 : {
333 : ModuleKind kind;
334 : MemoryUsage memoryUsage;
335 : uint32_t minMemoryLength;
336 : uint32_t globalDataLength;
337 : Maybe<uint32_t> maxMemoryLength;
338 : Maybe<uint32_t> startFuncIndex;
339 :
340 0 : explicit MetadataCacheablePod(ModuleKind kind)
341 0 : : kind(kind),
342 : memoryUsage(MemoryUsage::None),
343 : minMemoryLength(0),
344 0 : globalDataLength(0)
345 0 : {}
346 : };
347 :
348 : typedef uint8_t ModuleHash[8];
349 :
350 0 : struct MetadataTier
351 : {
352 0 : explicit MetadataTier(Tier tier)
353 0 : : tier(tier)
354 : {
355 0 : MOZ_ASSERT(tier == Tier::Baseline || tier == Tier::Ion);
356 0 : }
357 :
358 : const Tier tier;
359 :
360 : MemoryAccessVector memoryAccesses;
361 : CodeRangeVector codeRanges;
362 : CallSiteVector callSites;
363 : FuncImportVector funcImports;
364 : FuncExportVector funcExports;
365 :
366 : // Debug information, not serialized.
367 : Uint32Vector debugTrapFarJumpOffsets;
368 : Uint32Vector debugFuncToCodeRange;
369 :
370 : const FuncExport& lookupFuncExport(uint32_t funcIndex) const;
371 :
372 : WASM_DECLARE_SERIALIZABLE(MetadataTier);
373 : };
374 :
375 : typedef UniquePtr<MetadataTier> UniqueMetadataTier;
376 :
377 : struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
378 : {
379 : // `tier_` will become more complicated when tiering is implemented.
380 : UniqueMetadataTier tier_;
381 :
382 : Tiers tiers() const;
383 : const MetadataTier& metadata(Tier t) const;
384 : MetadataTier& metadata(Tier t);
385 :
386 0 : explicit Metadata(UniqueMetadataTier tier, ModuleKind kind = ModuleKind::Wasm)
387 0 : : MetadataCacheablePod(kind),
388 0 : tier_(Move(tier))
389 0 : {}
390 0 : virtual ~Metadata() {}
391 :
392 0 : MetadataCacheablePod& pod() { return *this; }
393 0 : const MetadataCacheablePod& pod() const { return *this; }
394 :
395 : SigWithIdVector sigIds;
396 : GlobalDescVector globals;
397 : TableDescVector tables;
398 : NameInBytecodeVector funcNames;
399 : CustomSectionVector customSections;
400 : CacheableChars filename;
401 : ModuleHash hash;
402 :
403 : // Debug-enabled code is not serialized.
404 : bool debugEnabled;
405 : FuncArgTypesVector debugFuncArgTypes;
406 : FuncReturnTypesVector debugFuncReturnTypes;
407 :
408 0 : bool usesMemory() const { return UsesMemory(memoryUsage); }
409 : bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
410 :
411 : // AsmJSMetadata derives Metadata iff isAsmJS(). Mostly this distinction is
412 : // encapsulated within AsmJS.cpp, but the additional virtual functions allow
413 : // asm.js to override wasm behavior in the handful of cases that can't be
414 : // easily encapsulated by AsmJS.cpp.
415 :
416 0 : bool isAsmJS() const {
417 0 : return kind == ModuleKind::AsmJS;
418 : }
419 0 : const AsmJSMetadata& asAsmJS() const {
420 0 : MOZ_ASSERT(isAsmJS());
421 0 : return *(const AsmJSMetadata*)this;
422 : }
423 0 : virtual bool mutedErrors() const {
424 0 : return false;
425 : }
426 0 : virtual const char16_t* displayURL() const {
427 0 : return nullptr;
428 : }
429 0 : virtual ScriptSource* maybeScriptSource() const {
430 0 : return nullptr;
431 : }
432 : virtual bool getFuncName(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const;
433 :
434 : WASM_DECLARE_SERIALIZABLE_VIRTUAL(Metadata);
435 : };
436 :
437 : typedef RefPtr<Metadata> MutableMetadata;
438 : typedef RefPtr<const Metadata> SharedMetadata;
439 :
440 : // Code objects own executable code and the metadata that describe it. A single
441 : // Code object is normally shared between a module and all its instances.
442 : //
443 : // profilingLabels_ is lazily initialized, but behind a lock.
444 :
445 0 : class Code : public ShareableBase<Code>
446 : {
447 : // `tier_` will become more complicated when tiering is implemented.
448 : UniqueConstCodeSegment tier_;
449 : SharedMetadata metadata_;
450 : ExclusiveData<CacheableCharsVector> profilingLabels_;
451 :
452 : public:
453 : Code();
454 :
455 : Code(UniqueConstCodeSegment tier, const Metadata& metadata);
456 :
457 : Tier anyTier() const;
458 : Tiers tiers() const;
459 :
460 : const CodeSegment& segment(Tier tier) const;
461 0 : const MetadataTier& metadata(Tier tier) const { return metadata_->metadata(tier); }
462 0 : const Metadata& metadata() const { return *metadata_; }
463 :
464 : // Frame iterator support:
465 :
466 : const CallSite* lookupCallSite(void* returnAddress, const CodeSegment** segment = nullptr) const;
467 : const CodeRange* lookupRange(void* pc, const CodeSegment** segment = nullptr) const;
468 : const MemoryAccess* lookupMemoryAccess(void* pc, const CodeSegment** segment = nullptr) const;
469 :
470 : // Signal handling support:
471 :
472 : bool containsFunctionPC(const void* pc, const CodeSegment** segmentp = nullptr) const;
473 : bool containsCodePC(const void* pc, const CodeSegment** segmentp = nullptr) const;
474 :
475 : // To save memory, profilingLabels_ are generated lazily when profiling mode
476 : // is enabled.
477 :
478 : void ensureProfilingLabels(const Bytes* maybeBytecode, bool profilingEnabled) const;
479 : const char* profilingLabel(uint32_t funcIndex) const;
480 :
481 : // about:memory reporting:
482 :
483 : void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf,
484 : Metadata::SeenSet* seenMetadata,
485 : Code::SeenSet* seenCode,
486 : size_t* code,
487 : size_t* data) const;
488 :
489 : // A Code object is serialized as the length and bytes of the machine code
490 : // after statically unlinking it; the Code is then later recreated from the
491 : // machine code and other parts.
492 :
493 : size_t serializedSize() const;
494 : uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
495 : const uint8_t* deserialize(const uint8_t* cursor, const SharedBytes& bytecode,
496 : const LinkData& linkData, Metadata* maybeMetadata);
497 : };
498 :
499 : typedef RefPtr<const Code> SharedCode;
500 : typedef RefPtr<Code> MutableCode;
501 :
502 : } // namespace wasm
503 : } // namespace js
504 :
505 : #endif // wasm_code_h
|