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  *
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_types_h
20 #define wasm_types_h
21 
22 #include "mozilla/Alignment.h"
23 #include "mozilla/Atomics.h"
24 #include "mozilla/BinarySearch.h"
25 #include "mozilla/EnumeratedArray.h"
26 #include "mozilla/HashFunctions.h"
27 #include "mozilla/Maybe.h"
28 #include "mozilla/RefPtr.h"
29 
30 #include <type_traits>
31 
32 #include "NamespaceImports.h"
33 
34 #include "ds/LifoAlloc.h"
35 #include "jit/IonTypes.h"
36 #include "js/RefCounted.h"
37 #include "js/UniquePtr.h"
38 #include "js/Utility.h"
39 #include "js/Vector.h"
40 #include "vm/MallocProvider.h"
41 #include "vm/NativeObject.h"
42 #include "wasm/WasmBuiltins.h"
43 #include "wasm/WasmConstants.h"
44 #include "wasm/WasmInitExpr.h"
45 #include "wasm/WasmPages.h"
46 #include "wasm/WasmSerialize.h"
47 #include "wasm/WasmShareable.h"
48 #include "wasm/WasmTlsData.h"
49 #include "wasm/WasmTypeDecls.h"
50 #include "wasm/WasmTypeDef.h"
51 #include "wasm/WasmUtility.h"
52 #include "wasm/WasmValType.h"
53 #include "wasm/WasmValue.h"
54 
55 namespace js {
56 
57 namespace jit {
58 enum class RoundingMode;
59 template <class VecT, class ABIArgGeneratorT>
60 class ABIArgIterBase;
61 }  // namespace jit
62 
63 namespace wasm {
64 
65 using mozilla::Atomic;
66 using mozilla::DebugOnly;
67 using mozilla::EnumeratedArray;
68 using mozilla::MallocSizeOf;
69 using mozilla::Maybe;
70 using mozilla::Nothing;
71 using mozilla::PodCopy;
72 using mozilla::PodZero;
73 using mozilla::Some;
74 
75 class Code;
76 class DebugState;
77 class GeneratedSourceMap;
78 class Memory;
79 class Module;
80 class Instance;
81 class Table;
82 
83 // Exception tags are used to uniquely identify exceptions. They are stored
84 // in a vector in Instances and used by both WebAssembly.Exception for import
85 // and export, and by the representation of thrown exceptions.
86 //
87 // Since an exception tag is a (trivial) substructure of AtomicRefCounted, the
88 // RefPtr SharedExceptionTag can have many instances/modules referencing a
89 // single constant exception tag.
90 
91 struct ExceptionTag : AtomicRefCounted<ExceptionTag> {
92   ExceptionTag() = default;
93 };
94 using SharedExceptionTag = RefPtr<ExceptionTag>;
95 using SharedExceptionTagVector =
96     Vector<SharedExceptionTag, 0, SystemAllocPolicy>;
97 
98 // WasmJSExceptionObject wraps a JS Value in order to provide a uniform
99 // method of handling JS thrown exceptions. Exceptions originating in Wasm
100 // are WebAssemby.RuntimeException objects, whereas exceptions from JS are
101 // wrapped as WasmJSExceptionObject objects.
102 class WasmJSExceptionObject : public NativeObject {
103   static const unsigned VALUE_SLOT = 0;
104 
105  public:
106   static const unsigned RESERVED_SLOTS = 1;
107   static const JSClass class_;
value()108   const Value& value() const { return getFixedSlot(VALUE_SLOT); }
109 
110   static WasmJSExceptionObject* create(JSContext* cx, MutableHandleValue value);
111 };
112 
113 // A Module can either be asm.js or wasm.
114 
115 enum ModuleKind { Wasm, AsmJS };
116 
117 // ArgTypeVector type.
118 //
119 // Functions usually receive one ABI argument per WebAssembly argument.  However
120 // if a function has multiple results and some of those results go to the stack,
121 // then it additionally receives a synthetic ABI argument holding a pointer to
122 // the stack result area.
123 //
124 // Given the presence of synthetic arguments, sometimes we need a name for
125 // non-synthetic arguments.  We call those "natural" arguments.
126 
127 enum class StackResults { HasStackResults, NoStackResults };
128 
129 class ArgTypeVector {
130   const ValTypeVector& args_;
131   bool hasStackResults_;
132 
133   // To allow ABIArgIterBase<VecT, ABIArgGeneratorT>, we define a private
134   // length() method.  To prevent accidental errors, other users need to be
135   // explicit and call lengthWithStackResults() or
136   // lengthWithoutStackResults().
length()137   size_t length() const { return args_.length() + size_t(hasStackResults_); }
138   template <class VecT, class ABIArgGeneratorT>
139   friend class jit::ABIArgIterBase;
140 
141  public:
ArgTypeVector(const ValTypeVector & args,StackResults stackResults)142   ArgTypeVector(const ValTypeVector& args, StackResults stackResults)
143       : args_(args),
144         hasStackResults_(stackResults == StackResults::HasStackResults) {}
145   explicit ArgTypeVector(const FuncType& funcType);
146 
hasSyntheticStackResultPointerArg()147   bool hasSyntheticStackResultPointerArg() const { return hasStackResults_; }
stackResults()148   StackResults stackResults() const {
149     return hasSyntheticStackResultPointerArg() ? StackResults::HasStackResults
150                                                : StackResults::NoStackResults;
151   }
lengthWithoutStackResults()152   size_t lengthWithoutStackResults() const { return args_.length(); }
isSyntheticStackResultPointerArg(size_t idx)153   bool isSyntheticStackResultPointerArg(size_t idx) const {
154     // The pointer to stack results area, if present, is a synthetic argument
155     // tacked on at the end.
156     MOZ_ASSERT(idx < lengthWithStackResults());
157     return idx == args_.length();
158   }
isNaturalArg(size_t idx)159   bool isNaturalArg(size_t idx) const {
160     return !isSyntheticStackResultPointerArg(idx);
161   }
naturalIndex(size_t idx)162   size_t naturalIndex(size_t idx) const {
163     MOZ_ASSERT(isNaturalArg(idx));
164     // Because the synthetic argument, if present, is tacked on the end, an
165     // argument index that isn't synthetic is natural.
166     return idx;
167   }
168 
lengthWithStackResults()169   size_t lengthWithStackResults() const { return length(); }
170   jit::MIRType operator[](size_t i) const {
171     MOZ_ASSERT(i < lengthWithStackResults());
172     if (isSyntheticStackResultPointerArg(i)) {
173       return jit::MIRType::StackResults;
174     }
175     return ToMIRType(args_[naturalIndex(i)]);
176   }
177 };
178 
179 template <typename PointerType>
180 class TaggedValue {
181  public:
182   enum Kind {
183     ImmediateKind1 = 0,
184     ImmediateKind2 = 1,
185     PointerKind1 = 2,
186     PointerKind2 = 3
187   };
188   using PackedRepr = uintptr_t;
189 
190  private:
191   PackedRepr bits_;
192 
193   static constexpr PackedRepr PayloadShift = 2;
194   static constexpr PackedRepr KindMask = 0x3;
195   static constexpr PackedRepr PointerKindBit = 0x2;
196 
IsPointerKind(Kind kind)197   constexpr static bool IsPointerKind(Kind kind) {
198     return PackedRepr(kind) & PointerKindBit;
199   }
IsImmediateKind(Kind kind)200   constexpr static bool IsImmediateKind(Kind kind) {
201     return !IsPointerKind(kind);
202   }
203 
204   static_assert(IsImmediateKind(ImmediateKind1), "immediate kind 1");
205   static_assert(IsImmediateKind(ImmediateKind2), "immediate kind 2");
206   static_assert(IsPointerKind(PointerKind1), "pointer kind 1");
207   static_assert(IsPointerKind(PointerKind2), "pointer kind 2");
208 
PackImmediate(Kind kind,PackedRepr imm)209   static PackedRepr PackImmediate(Kind kind, PackedRepr imm) {
210     MOZ_ASSERT(IsImmediateKind(kind));
211     MOZ_ASSERT((PackedRepr(kind) & KindMask) == kind);
212     MOZ_ASSERT((imm & (PackedRepr(KindMask)
213                        << ((sizeof(PackedRepr) * 8) - PayloadShift))) == 0);
214     return PackedRepr(kind) | (PackedRepr(imm) << PayloadShift);
215   }
216 
PackPointer(Kind kind,PointerType * ptr)217   static PackedRepr PackPointer(Kind kind, PointerType* ptr) {
218     PackedRepr ptrBits = reinterpret_cast<PackedRepr>(ptr);
219     MOZ_ASSERT(IsPointerKind(kind));
220     MOZ_ASSERT((PackedRepr(kind) & KindMask) == kind);
221     MOZ_ASSERT((ptrBits & KindMask) == 0);
222     return PackedRepr(kind) | ptrBits;
223   }
224 
225  public:
TaggedValue(Kind kind,PackedRepr imm)226   TaggedValue(Kind kind, PackedRepr imm) : bits_(PackImmediate(kind, imm)) {}
TaggedValue(Kind kind,PointerType * ptr)227   TaggedValue(Kind kind, PointerType* ptr) : bits_(PackPointer(kind, ptr)) {}
228 
bits()229   PackedRepr bits() const { return bits_; }
kind()230   Kind kind() const { return Kind(bits() & KindMask); }
immediate()231   PackedRepr immediate() const {
232     MOZ_ASSERT(IsImmediateKind(kind()));
233     return mozilla::AssertedCast<PackedRepr>(bits() >> PayloadShift);
234   }
pointer()235   PointerType* pointer() const {
236     MOZ_ASSERT(IsPointerKind(kind()));
237     return reinterpret_cast<PointerType*>(bits() & ~KindMask);
238   }
239 };
240 
241 static_assert(
242     std::is_same<TaggedValue<void*>::PackedRepr, PackedTypeCode::PackedRepr>(),
243     "can use pointer tagging with PackedTypeCode");
244 
245 // ResultType represents the WebAssembly spec's `resulttype`. Semantically, a
246 // result type is just a vec(valtype).  For effiency, though, the ResultType
247 // value is packed into a word, with separate encodings for these 3 cases:
248 //  []
249 //  [valtype]
250 //  pointer to ValTypeVector
251 //
252 // Additionally there is an encoding indicating uninitialized ResultType
253 // values.
254 //
255 // Generally in the latter case the ValTypeVector is the args() or results() of
256 // a FuncType in the compilation unit, so as long as the lifetime of the
257 // ResultType value is less than the OpIter, we can just borrow the pointer
258 // without ownership or copying.
259 class ResultType {
260   using Tagged = TaggedValue<const ValTypeVector>;
261   Tagged tagged_;
262 
263   enum Kind {
264     EmptyKind = Tagged::ImmediateKind1,
265     SingleKind = Tagged::ImmediateKind2,
266     VectorKind = Tagged::PointerKind1,
267     InvalidKind = Tagged::PointerKind2,
268   };
269 
ResultType(Kind kind,uint32_t imm)270   ResultType(Kind kind, uint32_t imm) : tagged_(Tagged::Kind(kind), imm) {}
ResultType(const ValTypeVector * ptr)271   explicit ResultType(const ValTypeVector* ptr)
272       : tagged_(Tagged::Kind(VectorKind), ptr) {}
273 
kind()274   Kind kind() const { return Kind(tagged_.kind()); }
275 
singleValType()276   ValType singleValType() const {
277     MOZ_ASSERT(kind() == SingleKind);
278     return ValType(PackedTypeCode::fromBits(tagged_.immediate()));
279   }
280 
values()281   const ValTypeVector& values() const {
282     MOZ_ASSERT(kind() == VectorKind);
283     return *tagged_.pointer();
284   }
285 
286  public:
ResultType()287   ResultType() : tagged_(Tagged::Kind(InvalidKind), nullptr) {}
288 
Empty()289   static ResultType Empty() { return ResultType(EmptyKind, uint32_t(0)); }
Single(ValType vt)290   static ResultType Single(ValType vt) {
291     return ResultType(SingleKind, vt.bitsUnsafe());
292   }
Vector(const ValTypeVector & vals)293   static ResultType Vector(const ValTypeVector& vals) {
294     switch (vals.length()) {
295       case 0:
296         return Empty();
297       case 1:
298         return Single(vals[0]);
299       default:
300         return ResultType(&vals);
301     }
302   }
303 
cloneToVector(ValTypeVector * out)304   [[nodiscard]] bool cloneToVector(ValTypeVector* out) {
305     MOZ_ASSERT(out->empty());
306     switch (kind()) {
307       case EmptyKind:
308         return true;
309       case SingleKind:
310         return out->append(singleValType());
311       case VectorKind:
312         return out->appendAll(values());
313       default:
314         MOZ_CRASH("bad resulttype");
315     }
316   }
317 
empty()318   bool empty() const { return kind() == EmptyKind; }
319 
length()320   size_t length() const {
321     switch (kind()) {
322       case EmptyKind:
323         return 0;
324       case SingleKind:
325         return 1;
326       case VectorKind:
327         return values().length();
328       default:
329         MOZ_CRASH("bad resulttype");
330     }
331   }
332 
333   ValType operator[](size_t i) const {
334     switch (kind()) {
335       case SingleKind:
336         MOZ_ASSERT(i == 0);
337         return singleValType();
338       case VectorKind:
339         return values()[i];
340       default:
341         MOZ_CRASH("bad resulttype");
342     }
343   }
344 
345   bool operator==(ResultType rhs) const {
346     switch (kind()) {
347       case EmptyKind:
348       case SingleKind:
349       case InvalidKind:
350         return tagged_.bits() == rhs.tagged_.bits();
351       case VectorKind: {
352         if (rhs.kind() != VectorKind) {
353           return false;
354         }
355         return EqualContainers(values(), rhs.values());
356       }
357       default:
358         MOZ_CRASH("bad resulttype");
359     }
360   }
361   bool operator!=(ResultType rhs) const { return !(*this == rhs); }
362 };
363 
364 // BlockType represents the WebAssembly spec's `blocktype`. Semantically, a
365 // block type is just a (vec(valtype) -> vec(valtype)) with four special
366 // encodings which are represented explicitly in BlockType:
367 //  [] -> []
368 //  [] -> [valtype]
369 //  [params] -> [results] via pointer to FuncType
370 //  [] -> [results] via pointer to FuncType (ignoring [params])
371 
372 class BlockType {
373   using Tagged = TaggedValue<const FuncType>;
374   Tagged tagged_;
375 
376   enum Kind {
377     VoidToVoidKind = Tagged::ImmediateKind1,
378     VoidToSingleKind = Tagged::ImmediateKind2,
379     FuncKind = Tagged::PointerKind1,
380     FuncResultsKind = Tagged::PointerKind2
381   };
382 
BlockType(Kind kind,uint32_t imm)383   BlockType(Kind kind, uint32_t imm) : tagged_(Tagged::Kind(kind), imm) {}
BlockType(Kind kind,const FuncType & type)384   BlockType(Kind kind, const FuncType& type)
385       : tagged_(Tagged::Kind(kind), &type) {}
386 
kind()387   Kind kind() const { return Kind(tagged_.kind()); }
singleValType()388   ValType singleValType() const {
389     MOZ_ASSERT(kind() == VoidToSingleKind);
390     return ValType(PackedTypeCode::fromBits(tagged_.immediate()));
391   }
392 
funcType()393   const FuncType& funcType() const { return *tagged_.pointer(); }
394 
395  public:
BlockType()396   BlockType()
397       : tagged_(Tagged::Kind(VoidToVoidKind),
398                 PackedTypeCode::invalid().bits()) {}
399 
VoidToVoid()400   static BlockType VoidToVoid() {
401     return BlockType(VoidToVoidKind, uint32_t(0));
402   }
VoidToSingle(ValType vt)403   static BlockType VoidToSingle(ValType vt) {
404     return BlockType(VoidToSingleKind, vt.bitsUnsafe());
405   }
Func(const FuncType & type)406   static BlockType Func(const FuncType& type) {
407     if (type.args().length() == 0) {
408       return FuncResults(type);
409     }
410     return BlockType(FuncKind, type);
411   }
FuncResults(const FuncType & type)412   static BlockType FuncResults(const FuncType& type) {
413     switch (type.results().length()) {
414       case 0:
415         return VoidToVoid();
416       case 1:
417         return VoidToSingle(type.results()[0]);
418       default:
419         return BlockType(FuncResultsKind, type);
420     }
421   }
422 
params()423   ResultType params() const {
424     switch (kind()) {
425       case VoidToVoidKind:
426       case VoidToSingleKind:
427       case FuncResultsKind:
428         return ResultType::Empty();
429       case FuncKind:
430         return ResultType::Vector(funcType().args());
431       default:
432         MOZ_CRASH("unexpected kind");
433     }
434   }
435 
results()436   ResultType results() const {
437     switch (kind()) {
438       case VoidToVoidKind:
439         return ResultType::Empty();
440       case VoidToSingleKind:
441         return ResultType::Single(singleValType());
442       case FuncKind:
443       case FuncResultsKind:
444         return ResultType::Vector(funcType().results());
445       default:
446         MOZ_CRASH("unexpected kind");
447     }
448   }
449 
450   bool operator==(BlockType rhs) const {
451     if (kind() != rhs.kind()) {
452       return false;
453     }
454     switch (kind()) {
455       case VoidToVoidKind:
456       case VoidToSingleKind:
457         return tagged_.bits() == rhs.tagged_.bits();
458       case FuncKind:
459         return funcType() == rhs.funcType();
460       case FuncResultsKind:
461         return EqualContainers(funcType().results(), rhs.funcType().results());
462       default:
463         MOZ_CRASH("unexpected kind");
464     }
465   }
466 
467   bool operator!=(BlockType rhs) const { return !(*this == rhs); }
468 };
469 
470 // CacheableChars is used to cacheably store UniqueChars.
471 
472 struct CacheableChars : UniqueChars {
473   CacheableChars() = default;
CacheableCharsCacheableChars474   explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
CacheableCharsCacheableChars475   MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs)
476       : UniqueChars(std::move(rhs)) {}
477   WASM_DECLARE_SERIALIZABLE(CacheableChars)
478 };
479 
480 using CacheableCharsVector = Vector<CacheableChars, 0, SystemAllocPolicy>;
481 
482 // Import describes a single wasm import. An ImportVector describes all
483 // of a single module's imports.
484 //
485 // ImportVector is built incrementally by ModuleGenerator and then stored
486 // immutably by Module.
487 
488 struct Import {
489   CacheableChars module;
490   CacheableChars field;
491   DefinitionKind kind;
492 
493   Import() = default;
ImportImport494   Import(UniqueChars&& module, UniqueChars&& field, DefinitionKind kind)
495       : module(std::move(module)), field(std::move(field)), kind(kind) {}
496 
497   WASM_DECLARE_SERIALIZABLE(Import)
498 };
499 
500 using ImportVector = Vector<Import, 0, SystemAllocPolicy>;
501 
502 // Export describes the export of a definition in a Module to a field in the
503 // export object. The Export stores the index of the exported item in the
504 // appropriate type-specific module data structure (function table, global
505 // table, table table, and - eventually - memory table).
506 //
507 // Note a single definition can be exported by multiple Exports in the
508 // ExportVector.
509 //
510 // ExportVector is built incrementally by ModuleGenerator and then stored
511 // immutably by Module.
512 
513 class Export {
514   CacheableChars fieldName_;
515   struct CacheablePod {
516     DefinitionKind kind_;
517     uint32_t index_;
518   } pod;
519 
520  public:
521   Export() = default;
522   explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind);
523   explicit Export(UniqueChars fieldName, DefinitionKind kind);
524 
fieldName()525   const char* fieldName() const { return fieldName_.get(); }
526 
kind()527   DefinitionKind kind() const { return pod.kind_; }
528   uint32_t funcIndex() const;
529 #ifdef ENABLE_WASM_EXCEPTIONS
530   uint32_t eventIndex() const;
531 #endif
532   uint32_t globalIndex() const;
533   uint32_t tableIndex() const;
534 
535   WASM_DECLARE_SERIALIZABLE(Export)
536 };
537 
538 using ExportVector = Vector<Export, 0, SystemAllocPolicy>;
539 
540 // FuncFlags provides metadata for a function definition.
541 
542 enum class FuncFlags : uint8_t {
543   None = 0x0,
544   // The function maybe be accessible by JS and needs thunks generated for it.
545   // See `[SMDOC] Exported wasm functions and the jit-entry stubs` in
546   // WasmJS.cpp for more information.
547   Exported = 0x1,
548   // The function should have thunks generated upon instantiation, not upon
549   // first call. May only be set if `Exported` is set.
550   Eager = 0x2,
551   // The function can be the target of a ref.func instruction in the code
552   // section. May only be set if `Exported` is set.
553   CanRefFunc = 0x4,
554 };
555 
556 // A FuncDesc describes a single function definition.
557 
558 class TypeIdDesc;
559 
560 struct FuncDesc {
561   FuncType* type;
562   TypeIdDesc* typeId;
563   // Bit pack to keep this struct small on 32-bit systems
564   uint32_t typeIndex : 24;
565   FuncFlags flags : 8;
566 
567   // Assert that the bit packing scheme is viable
568   static_assert(MaxTypes <= (1 << 24) - 1);
569   static_assert(sizeof(FuncFlags) == sizeof(uint8_t));
570 
571   FuncDesc() = default;
FuncDescFuncDesc572   FuncDesc(FuncType* type, TypeIdDesc* typeId, uint32_t typeIndex)
573       : type(type),
574         typeId(typeId),
575         typeIndex(typeIndex),
576         flags(FuncFlags::None) {}
577 
isExportedFuncDesc578   bool isExported() const {
579     return uint8_t(flags) & uint8_t(FuncFlags::Exported);
580   }
isEagerFuncDesc581   bool isEager() const { return uint8_t(flags) & uint8_t(FuncFlags::Eager); }
canRefFuncFuncDesc582   bool canRefFunc() const {
583     return uint8_t(flags) & uint8_t(FuncFlags::CanRefFunc);
584   }
585 };
586 
587 using FuncDescVector = Vector<FuncDesc, 0, SystemAllocPolicy>;
588 
589 // A GlobalDesc describes a single global variable.
590 //
591 // wasm can import and export mutable and immutable globals.
592 //
593 // asm.js can import mutable and immutable globals, but a mutable global has a
594 // location that is private to the module, and its initial value is copied into
595 // that cell from the environment.  asm.js cannot export globals.
596 
597 enum class GlobalKind { Import, Constant, Variable };
598 
599 class GlobalDesc {
600   GlobalKind kind_;
601   // Stores the value type of this global for all kinds, and the initializer
602   // expression when `constant` or `variable`.
603   InitExpr initial_;
604   // Metadata for the global when `variable` or `import`.
605   unsigned offset_;
606   bool isMutable_;
607   bool isWasm_;
608   bool isExport_;
609   // Metadata for the global when `import`.
610   uint32_t importIndex_;
611 
612   // Private, as they have unusual semantics.
613 
isExport()614   bool isExport() const { return !isConstant() && isExport_; }
isWasm()615   bool isWasm() const { return !isConstant() && isWasm_; }
616 
617  public:
618   GlobalDesc() = default;
619 
620   explicit GlobalDesc(InitExpr&& initial, bool isMutable,
621                       ModuleKind kind = ModuleKind::Wasm)
622       : kind_((isMutable || !initial.isLiteral()) ? GlobalKind::Variable
623                                                   : GlobalKind::Constant) {
624     initial_ = std::move(initial);
625     if (isVariable()) {
626       isMutable_ = isMutable;
627       isWasm_ = kind == Wasm;
628       isExport_ = false;
629       offset_ = UINT32_MAX;
630     }
631   }
632 
633   explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex,
634                       ModuleKind kind = ModuleKind::Wasm)
kind_(GlobalKind::Import)635       : kind_(GlobalKind::Import) {
636     initial_ = InitExpr(LitVal(type));
637     importIndex_ = importIndex;
638     isMutable_ = isMutable;
639     isWasm_ = kind == Wasm;
640     isExport_ = false;
641     offset_ = UINT32_MAX;
642   }
643 
setOffset(unsigned offset)644   void setOffset(unsigned offset) {
645     MOZ_ASSERT(!isConstant());
646     MOZ_ASSERT(offset_ == UINT32_MAX);
647     offset_ = offset;
648   }
offset()649   unsigned offset() const {
650     MOZ_ASSERT(!isConstant());
651     MOZ_ASSERT(offset_ != UINT32_MAX);
652     return offset_;
653   }
654 
setIsExport()655   void setIsExport() {
656     if (!isConstant()) {
657       isExport_ = true;
658     }
659   }
660 
kind()661   GlobalKind kind() const { return kind_; }
isVariable()662   bool isVariable() const { return kind_ == GlobalKind::Variable; }
isConstant()663   bool isConstant() const { return kind_ == GlobalKind::Constant; }
isImport()664   bool isImport() const { return kind_ == GlobalKind::Import; }
665 
isMutable()666   bool isMutable() const { return !isConstant() && isMutable_; }
initExpr()667   const InitExpr& initExpr() const {
668     MOZ_ASSERT(!isImport());
669     return initial_;
670   }
importIndex()671   uint32_t importIndex() const {
672     MOZ_ASSERT(isImport());
673     return importIndex_;
674   }
675 
constantValue()676   LitVal constantValue() const { return initial_.literal(); }
677 
678   // If isIndirect() is true then storage for the value is not in the
679   // instance's global area, but in a WasmGlobalObject::Cell hanging off a
680   // WasmGlobalObject; the global area contains a pointer to the Cell.
681   //
682   // We don't want to indirect unless we must, so only mutable, exposed
683   // globals are indirected - in all other cases we copy values into and out
684   // of their module.
685   //
686   // Note that isIndirect() isn't equivalent to getting a WasmGlobalObject:
687   // an immutable exported global will still get an object, but will not be
688   // indirect.
isIndirect()689   bool isIndirect() const {
690     return isMutable() && isWasm() && (isImport() || isExport());
691   }
692 
type()693   ValType type() const { return initial_.type(); }
694 
695   WASM_DECLARE_SERIALIZABLE(GlobalDesc)
696 };
697 
698 using GlobalDescVector = Vector<GlobalDesc, 0, SystemAllocPolicy>;
699 
700 // An EventDesc describes a single event for non-local control flow, such as
701 // for exceptions.
702 
703 #ifdef ENABLE_WASM_EXCEPTIONS
704 struct EventDesc {
705   EventKind kind;
706   ValTypeVector type;
707   bool isExport;
708 
709   EventDesc(EventKind kind, ValTypeVector&& type, bool isExport = false)
kindEventDesc710       : kind(kind), type(std::move(type)), isExport(isExport) {}
711 
resultTypeEventDesc712   ResultType resultType() const { return ResultType::Vector(type); }
713 };
714 
715 using EventDescVector = Vector<EventDesc, 0, SystemAllocPolicy>;
716 #endif
717 
718 // When a ElemSegment is "passive" it is shared between a wasm::Module and its
719 // wasm::Instances. To allow each segment to be released as soon as the last
720 // Instance elem.drops it and the Module is destroyed, each ElemSegment is
721 // individually atomically ref-counted.
722 
723 struct ElemSegment : AtomicRefCounted<ElemSegment> {
724   enum class Kind {
725     Active,
726     Passive,
727     Declared,
728   };
729 
730   Kind kind;
731   uint32_t tableIndex;
732   RefType elemType;
733   Maybe<InitExpr> offsetIfActive;
734   Uint32Vector elemFuncIndices;  // Element may be NullFuncIndex
735 
activeElemSegment736   bool active() const { return kind == Kind::Active; }
737 
offsetElemSegment738   const InitExpr& offset() const { return *offsetIfActive; }
739 
lengthElemSegment740   size_t length() const { return elemFuncIndices.length(); }
741 
742   WASM_DECLARE_SERIALIZABLE(ElemSegment)
743 };
744 
745 // NullFuncIndex represents the case when an element segment (of type funcref)
746 // contains a null element.
747 constexpr uint32_t NullFuncIndex = UINT32_MAX;
748 static_assert(NullFuncIndex > MaxFuncs, "Invariant");
749 
750 using MutableElemSegment = RefPtr<ElemSegment>;
751 using SharedElemSegment = SerializableRefPtr<const ElemSegment>;
752 using ElemSegmentVector = Vector<SharedElemSegment, 0, SystemAllocPolicy>;
753 
754 // DataSegmentEnv holds the initial results of decoding a data segment from the
755 // bytecode and is stored in the ModuleEnvironment during compilation. When
756 // compilation completes, (non-Env) DataSegments are created and stored in
757 // the wasm::Module which contain copies of the data segment payload. This
758 // allows non-compilation uses of wasm validation to avoid expensive copies.
759 //
760 // When a DataSegment is "passive" it is shared between a wasm::Module and its
761 // wasm::Instances. To allow each segment to be released as soon as the last
762 // Instance mem.drops it and the Module is destroyed, each DataSegment is
763 // individually atomically ref-counted.
764 
765 struct DataSegmentEnv {
766   Maybe<InitExpr> offsetIfActive;
767   uint32_t bytecodeOffset;
768   uint32_t length;
769 };
770 
771 using DataSegmentEnvVector = Vector<DataSegmentEnv, 0, SystemAllocPolicy>;
772 
773 struct DataSegment : AtomicRefCounted<DataSegment> {
774   Maybe<InitExpr> offsetIfActive;
775   Bytes bytes;
776 
777   DataSegment() = default;
778 
activeDataSegment779   bool active() const { return !!offsetIfActive; }
780 
offsetDataSegment781   const InitExpr& offset() const { return *offsetIfActive; }
782 
initDataSegment783   [[nodiscard]] bool init(const ShareableBytes& bytecode,
784                           const DataSegmentEnv& src) {
785     if (src.offsetIfActive) {
786       offsetIfActive.emplace();
787       if (!offsetIfActive->clone(*src.offsetIfActive)) {
788         return false;
789       }
790     }
791     return bytes.append(bytecode.begin() + src.bytecodeOffset, src.length);
792   }
793 
794   WASM_DECLARE_SERIALIZABLE(DataSegment)
795 };
796 
797 using MutableDataSegment = RefPtr<DataSegment>;
798 using SharedDataSegment = SerializableRefPtr<const DataSegment>;
799 using DataSegmentVector = Vector<SharedDataSegment, 0, SystemAllocPolicy>;
800 
801 // The CustomSection(Env) structs are like DataSegment(Env): CustomSectionEnv is
802 // stored in the ModuleEnvironment and CustomSection holds a copy of the payload
803 // and is stored in the wasm::Module.
804 
805 struct CustomSectionEnv {
806   uint32_t nameOffset;
807   uint32_t nameLength;
808   uint32_t payloadOffset;
809   uint32_t payloadLength;
810 };
811 
812 using CustomSectionEnvVector = Vector<CustomSectionEnv, 0, SystemAllocPolicy>;
813 
814 struct CustomSection {
815   Bytes name;
816   SharedBytes payload;
817 
818   WASM_DECLARE_SERIALIZABLE(CustomSection)
819 };
820 
821 using CustomSectionVector = Vector<CustomSection, 0, SystemAllocPolicy>;
822 
823 // A Name represents a string of utf8 chars embedded within the name custom
824 // section. The offset of a name is expressed relative to the beginning of the
825 // name section's payload so that Names can stored in wasm::Code, which only
826 // holds the name section's bytes, not the whole bytecode.
827 
828 struct Name {
829   // All fields are treated as cacheable POD:
830   uint32_t offsetInNamePayload;
831   uint32_t length;
832 
NameName833   Name() : offsetInNamePayload(UINT32_MAX), length(0) {}
834 };
835 
836 using NameVector = Vector<Name, 0, SystemAllocPolicy>;
837 
838 // TypeIdDesc describes the runtime representation of a TypeDef suitable for
839 // type equality checks. The kind of representation depends on whether the type
840 // is a function or a struct. This will likely be simplified in the future once
841 // mutually recursives types are able to be collected.
842 //
843 // For functions, a FuncType is allocated and stored in a process-wide hash
844 // table, so that pointer equality implies structural equality. As an
845 // optimization for the 99% case where the FuncType has a small number of
846 // parameters, the FuncType is bit-packed into a uint32 immediate value so that
847 // integer equality implies structural equality. Both cases can be handled with
848 // a single comparison by always setting the LSB for the immediates
849 // (the LSB is necessarily 0 for allocated FuncType pointers due to alignment).
850 //
851 // TODO: Write description for StructTypes once it is well formed.
852 
853 class TypeIdDesc {
854  public:
855   static const uintptr_t ImmediateBit = 0x1;
856 
857  private:
858   TypeIdDescKind kind_;
859   size_t bits_;
860 
TypeIdDesc(TypeIdDescKind kind,size_t bits)861   TypeIdDesc(TypeIdDescKind kind, size_t bits) : kind_(kind), bits_(bits) {}
862 
863  public:
kind()864   TypeIdDescKind kind() const { return kind_; }
865   static bool isGlobal(const TypeDef& type);
866 
TypeIdDesc()867   TypeIdDesc() : kind_(TypeIdDescKind::None), bits_(0) {}
868   static TypeIdDesc global(const TypeDef& type, uint32_t globalDataOffset);
869   static TypeIdDesc immediate(const TypeDef& type);
870 
isGlobal()871   bool isGlobal() const { return kind_ == TypeIdDescKind::Global; }
872 
immediate()873   size_t immediate() const {
874     MOZ_ASSERT(kind_ == TypeIdDescKind::Immediate);
875     return bits_;
876   }
globalDataOffset()877   uint32_t globalDataOffset() const {
878     MOZ_ASSERT(kind_ == TypeIdDescKind::Global);
879     return bits_;
880   }
881 };
882 
883 using TypeIdDescVector = Vector<TypeIdDesc, 0, SystemAllocPolicy>;
884 
885 // TypeDefWithId pairs a FuncType with TypeIdDesc, describing either how to
886 // compile code that compares this signature's id or, at instantiation what
887 // signature ids to allocate in the global hash and where to put them.
888 
889 struct TypeDefWithId : public TypeDef {
890   TypeIdDesc id;
891 
892   TypeDefWithId() = default;
TypeDefWithIdTypeDefWithId893   explicit TypeDefWithId(TypeDef&& typeDef)
894       : TypeDef(std::move(typeDef)), id() {}
TypeDefWithIdTypeDefWithId895   TypeDefWithId(TypeDef&& typeDef, TypeIdDesc id)
896       : TypeDef(std::move(typeDef)), id(id) {}
897 
898   WASM_DECLARE_SERIALIZABLE(TypeDefWithId)
899 };
900 
901 using TypeDefWithIdVector = Vector<TypeDefWithId, 0, SystemAllocPolicy>;
902 using TypeDefWithIdPtrVector =
903     Vector<const TypeDefWithId*, 0, SystemAllocPolicy>;
904 
905 // A wrapper around the bytecode offset of a wasm instruction within a whole
906 // module, used for trap offsets or call offsets. These offsets should refer to
907 // the first byte of the instruction that triggered the trap / did the call and
908 // should ultimately derive from OpIter::bytecodeOffset.
909 
910 class BytecodeOffset {
911   static const uint32_t INVALID = -1;
912   uint32_t offset_;
913 
914  public:
BytecodeOffset()915   BytecodeOffset() : offset_(INVALID) {}
BytecodeOffset(uint32_t offset)916   explicit BytecodeOffset(uint32_t offset) : offset_(offset) {}
917 
isValid()918   bool isValid() const { return offset_ != INVALID; }
offset()919   uint32_t offset() const {
920     MOZ_ASSERT(isValid());
921     return offset_;
922   }
923 };
924 
925 // A TrapSite (in the TrapSiteVector for a given Trap code) represents a wasm
926 // instruction at a given bytecode offset that can fault at the given pc offset.
927 // When such a fault occurs, a signal/exception handler looks up the TrapSite to
928 // confirm the fault is intended/safe and redirects pc to the trap stub.
929 
930 struct TrapSite {
931   uint32_t pcOffset;
932   BytecodeOffset bytecode;
933 
TrapSiteTrapSite934   TrapSite() : pcOffset(-1), bytecode() {}
TrapSiteTrapSite935   TrapSite(uint32_t pcOffset, BytecodeOffset bytecode)
936       : pcOffset(pcOffset), bytecode(bytecode) {}
937 
offsetByTrapSite938   void offsetBy(uint32_t offset) { pcOffset += offset; }
939 };
940 
941 WASM_DECLARE_POD_VECTOR(TrapSite, TrapSiteVector)
942 
943 struct TrapSiteVectorArray
944     : EnumeratedArray<Trap, Trap::Limit, TrapSiteVector> {
945   bool empty() const;
946   void clear();
947   void swap(TrapSiteVectorArray& rhs);
948   void shrinkStorageToFit();
949 
950   WASM_DECLARE_SERIALIZABLE(TrapSiteVectorArray)
951 };
952 
953 // On trap, the bytecode offset to be reported in callstacks is saved.
954 
955 struct TrapData {
956   // The resumePC indicates where, if the trap doesn't throw, the trap stub
957   // should jump to after restoring all register state.
958   void* resumePC;
959 
960   // The unwoundPC is the PC after adjustment by wasm::StartUnwinding(), which
961   // basically unwinds partially-construted wasm::Frames when pc is in the
962   // prologue/epilogue. Stack traces during a trap should use this PC since
963   // it corresponds to the JitActivation::wasmExitFP.
964   void* unwoundPC;
965 
966   Trap trap;
967   uint32_t bytecodeOffset;
968 };
969 
970 // The (,Callable,Func)Offsets classes are used to record the offsets of
971 // different key points in a CodeRange during compilation.
972 
973 struct Offsets {
974   explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
beginOffsets975       : begin(begin), end(end) {}
976 
977   // These define a [begin, end) contiguous range of instructions compiled
978   // into a CodeRange.
979   uint32_t begin;
980   uint32_t end;
981 };
982 
983 struct CallableOffsets : Offsets {
OffsetsCallableOffsets984   MOZ_IMPLICIT CallableOffsets(uint32_t ret = 0) : Offsets(), ret(ret) {}
985 
986   // The offset of the return instruction precedes 'end' by a variable number
987   // of instructions due to out-of-line codegen.
988   uint32_t ret;
989 };
990 
991 struct JitExitOffsets : CallableOffsets {
JitExitOffsetsJitExitOffsets992   MOZ_IMPLICIT JitExitOffsets()
993       : CallableOffsets(), untrustedFPStart(0), untrustedFPEnd(0) {}
994 
995   // There are a few instructions in the Jit exit where FP may be trash
996   // (because it may have been clobbered by the JS Jit), known as the
997   // untrusted FP zone.
998   uint32_t untrustedFPStart;
999   uint32_t untrustedFPEnd;
1000 };
1001 
1002 struct FuncOffsets : CallableOffsets {
FuncOffsetsFuncOffsets1003   MOZ_IMPLICIT FuncOffsets()
1004       : CallableOffsets(), uncheckedCallEntry(0), tierEntry(0) {}
1005 
1006   // Function CodeRanges have a checked call entry which takes an extra
1007   // signature argument which is checked against the callee's signature before
1008   // falling through to the normal prologue. The checked call entry is thus at
1009   // the beginning of the CodeRange and the unchecked call entry is at some
1010   // offset after the checked call entry.
1011   uint32_t uncheckedCallEntry;
1012 
1013   // The tierEntry is the point within a function to which the patching code
1014   // within a Tier-1 function jumps.  It could be the instruction following
1015   // the jump in the Tier-1 function, or the point following the standard
1016   // prologue within a Tier-2 function.
1017   uint32_t tierEntry;
1018 };
1019 
1020 using FuncOffsetsVector = Vector<FuncOffsets, 0, SystemAllocPolicy>;
1021 
1022 // A CodeRange describes a single contiguous range of code within a wasm
1023 // module's code segment. A CodeRange describes what the code does and, for
1024 // function bodies, the name and source coordinates of the function.
1025 
1026 class CodeRange {
1027  public:
1028   enum Kind {
1029     Function,          // function definition
1030     InterpEntry,       // calls into wasm from C++
1031     JitEntry,          // calls into wasm from jit code
1032     ImportInterpExit,  // slow-path calling from wasm into C++ interp
1033     ImportJitExit,     // fast-path calling from wasm into jit code
1034     BuiltinThunk,      // fast-path calling from wasm into a C++ native
1035     TrapExit,          // calls C++ to report and jumps to throw stub
1036     DebugTrap,         // calls C++ to handle debug event
1037     FarJumpIsland,     // inserted to connect otherwise out-of-range insns
1038     Throw              // special stack-unwinding stub jumped to by other stubs
1039   };
1040 
1041  private:
1042   // All fields are treated as cacheable POD:
1043   uint32_t begin_;
1044   uint32_t ret_;
1045   uint32_t end_;
1046   union {
1047     struct {
1048       uint32_t funcIndex_;
1049       union {
1050         struct {
1051           uint32_t lineOrBytecode_;
1052           uint8_t beginToUncheckedCallEntry_;
1053           uint8_t beginToTierEntry_;
1054         } func;
1055         struct {
1056           uint16_t beginToUntrustedFPStart_;
1057           uint16_t beginToUntrustedFPEnd_;
1058         } jitExit;
1059       };
1060     };
1061     Trap trap_;
1062   } u;
1063   Kind kind_ : 8;
1064 
1065  public:
1066   CodeRange() = default;
1067   CodeRange(Kind kind, Offsets offsets);
1068   CodeRange(Kind kind, uint32_t funcIndex, Offsets offsets);
1069   CodeRange(Kind kind, CallableOffsets offsets);
1070   CodeRange(Kind kind, uint32_t funcIndex, CallableOffsets);
1071   CodeRange(uint32_t funcIndex, JitExitOffsets offsets);
1072   CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
1073 
offsetBy(uint32_t offset)1074   void offsetBy(uint32_t offset) {
1075     begin_ += offset;
1076     end_ += offset;
1077     if (hasReturn()) {
1078       ret_ += offset;
1079     }
1080   }
1081 
1082   // All CodeRanges have a begin and end.
1083 
begin()1084   uint32_t begin() const { return begin_; }
end()1085   uint32_t end() const { return end_; }
1086 
1087   // Other fields are only available for certain CodeRange::Kinds.
1088 
kind()1089   Kind kind() const { return kind_; }
1090 
isFunction()1091   bool isFunction() const { return kind() == Function; }
isImportExit()1092   bool isImportExit() const {
1093     return kind() == ImportJitExit || kind() == ImportInterpExit ||
1094            kind() == BuiltinThunk;
1095   }
isImportInterpExit()1096   bool isImportInterpExit() const { return kind() == ImportInterpExit; }
isImportJitExit()1097   bool isImportJitExit() const { return kind() == ImportJitExit; }
isTrapExit()1098   bool isTrapExit() const { return kind() == TrapExit; }
isDebugTrap()1099   bool isDebugTrap() const { return kind() == DebugTrap; }
isThunk()1100   bool isThunk() const { return kind() == FarJumpIsland; }
1101 
1102   // Function, import exits and trap exits have standard callable prologues
1103   // and epilogues. Asynchronous frame iteration needs to know the offset of
1104   // the return instruction to calculate the frame pointer.
1105 
hasReturn()1106   bool hasReturn() const {
1107     return isFunction() || isImportExit() || isDebugTrap();
1108   }
ret()1109   uint32_t ret() const {
1110     MOZ_ASSERT(hasReturn());
1111     return ret_;
1112   }
1113 
1114   // Functions, export stubs and import stubs all have an associated function
1115   // index.
1116 
isJitEntry()1117   bool isJitEntry() const { return kind() == JitEntry; }
isInterpEntry()1118   bool isInterpEntry() const { return kind() == InterpEntry; }
isEntry()1119   bool isEntry() const { return isInterpEntry() || isJitEntry(); }
hasFuncIndex()1120   bool hasFuncIndex() const {
1121     return isFunction() || isImportExit() || isEntry();
1122   }
funcIndex()1123   uint32_t funcIndex() const {
1124     MOZ_ASSERT(hasFuncIndex());
1125     return u.funcIndex_;
1126   }
1127 
1128   // TrapExit CodeRanges have a Trap field.
1129 
trap()1130   Trap trap() const {
1131     MOZ_ASSERT(isTrapExit());
1132     return u.trap_;
1133   }
1134 
1135   // Function CodeRanges have two entry points: one for normal calls (with a
1136   // known signature) and one for table calls (which involves dynamic
1137   // signature checking).
1138 
funcCheckedCallEntry()1139   uint32_t funcCheckedCallEntry() const {
1140     MOZ_ASSERT(isFunction());
1141     return begin_;
1142   }
funcUncheckedCallEntry()1143   uint32_t funcUncheckedCallEntry() const {
1144     MOZ_ASSERT(isFunction());
1145     return begin_ + u.func.beginToUncheckedCallEntry_;
1146   }
funcTierEntry()1147   uint32_t funcTierEntry() const {
1148     MOZ_ASSERT(isFunction());
1149     return begin_ + u.func.beginToTierEntry_;
1150   }
funcLineOrBytecode()1151   uint32_t funcLineOrBytecode() const {
1152     MOZ_ASSERT(isFunction());
1153     return u.func.lineOrBytecode_;
1154   }
1155 
1156   // ImportJitExit have a particular range where the value of FP can't be
1157   // trusted for profiling and thus must be ignored.
1158 
jitExitUntrustedFPStart()1159   uint32_t jitExitUntrustedFPStart() const {
1160     MOZ_ASSERT(isImportJitExit());
1161     return begin_ + u.jitExit.beginToUntrustedFPStart_;
1162   }
jitExitUntrustedFPEnd()1163   uint32_t jitExitUntrustedFPEnd() const {
1164     MOZ_ASSERT(isImportJitExit());
1165     return begin_ + u.jitExit.beginToUntrustedFPEnd_;
1166   }
1167 
1168   // A sorted array of CodeRanges can be looked up via BinarySearch and
1169   // OffsetInCode.
1170 
1171   struct OffsetInCode {
1172     size_t offset;
OffsetInCodeOffsetInCode1173     explicit OffsetInCode(size_t offset) : offset(offset) {}
1174     bool operator==(const CodeRange& rhs) const {
1175       return offset >= rhs.begin() && offset < rhs.end();
1176     }
1177     bool operator<(const CodeRange& rhs) const { return offset < rhs.begin(); }
1178   };
1179 };
1180 
1181 WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
1182 
1183 extern const CodeRange* LookupInSorted(const CodeRangeVector& codeRanges,
1184                                        CodeRange::OffsetInCode target);
1185 
1186 // While the frame-pointer chain allows the stack to be unwound without
1187 // metadata, Error.stack still needs to know the line/column of every call in
1188 // the chain. A CallSiteDesc describes a single callsite to which CallSite adds
1189 // the metadata necessary to walk up to the next frame. Lastly CallSiteAndTarget
1190 // adds the function index of the callee.
1191 
1192 class CallSiteDesc {
1193   static constexpr size_t LINE_OR_BYTECODE_BITS_SIZE = 29;
1194   uint32_t lineOrBytecode_ : LINE_OR_BYTECODE_BITS_SIZE;
1195   uint32_t kind_ : 3;
1196 
1197  public:
1198   static constexpr uint32_t MAX_LINE_OR_BYTECODE_VALUE =
1199       (1 << LINE_OR_BYTECODE_BITS_SIZE) - 1;
1200 
1201   enum Kind {
1202     Func,        // pc-relative call to a specific function
1203     Dynamic,     // dynamic callee called via register
1204     Symbolic,    // call to a single symbolic callee
1205     EnterFrame,  // call to a enter frame handler
1206     LeaveFrame,  // call to a leave frame handler
1207     Breakpoint   // call to instruction breakpoint
1208   };
CallSiteDesc()1209   CallSiteDesc() : lineOrBytecode_(0), kind_(0) {}
CallSiteDesc(Kind kind)1210   explicit CallSiteDesc(Kind kind) : lineOrBytecode_(0), kind_(kind) {
1211     MOZ_ASSERT(kind == Kind(kind_));
1212   }
CallSiteDesc(uint32_t lineOrBytecode,Kind kind)1213   CallSiteDesc(uint32_t lineOrBytecode, Kind kind)
1214       : lineOrBytecode_(lineOrBytecode), kind_(kind) {
1215     MOZ_ASSERT(kind == Kind(kind_));
1216     MOZ_ASSERT(lineOrBytecode == lineOrBytecode_);
1217   }
lineOrBytecode()1218   uint32_t lineOrBytecode() const { return lineOrBytecode_; }
kind()1219   Kind kind() const { return Kind(kind_); }
mightBeCrossInstance()1220   bool mightBeCrossInstance() const { return kind() == CallSiteDesc::Dynamic; }
1221 };
1222 
1223 class CallSite : public CallSiteDesc {
1224   uint32_t returnAddressOffset_;
1225 
1226  public:
CallSite()1227   CallSite() : returnAddressOffset_(0) {}
1228 
CallSite(CallSiteDesc desc,uint32_t returnAddressOffset)1229   CallSite(CallSiteDesc desc, uint32_t returnAddressOffset)
1230       : CallSiteDesc(desc), returnAddressOffset_(returnAddressOffset) {}
1231 
offsetBy(int32_t delta)1232   void offsetBy(int32_t delta) { returnAddressOffset_ += delta; }
returnAddressOffset()1233   uint32_t returnAddressOffset() const { return returnAddressOffset_; }
1234 };
1235 
WASM_DECLARE_POD_VECTOR(CallSite,CallSiteVector)1236 WASM_DECLARE_POD_VECTOR(CallSite, CallSiteVector)
1237 
1238 // A CallSiteTarget describes the callee of a CallSite, either a function or a
1239 // trap exit. Although checked in debug builds, a CallSiteTarget doesn't
1240 // officially know whether it targets a function or trap, relying on the Kind of
1241 // the CallSite to discriminate.
1242 
1243 class CallSiteTarget {
1244   uint32_t packed_;
1245 #ifdef DEBUG
1246   enum Kind { None, FuncIndex, TrapExit } kind_;
1247 #endif
1248 
1249  public:
1250   explicit CallSiteTarget()
1251       : packed_(UINT32_MAX)
1252 #ifdef DEBUG
1253         ,
1254         kind_(None)
1255 #endif
1256   {
1257   }
1258 
1259   explicit CallSiteTarget(uint32_t funcIndex)
1260       : packed_(funcIndex)
1261 #ifdef DEBUG
1262         ,
1263         kind_(FuncIndex)
1264 #endif
1265   {
1266   }
1267 
1268   explicit CallSiteTarget(Trap trap)
1269       : packed_(uint32_t(trap))
1270 #ifdef DEBUG
1271         ,
1272         kind_(TrapExit)
1273 #endif
1274   {
1275   }
1276 
1277   uint32_t funcIndex() const {
1278     MOZ_ASSERT(kind_ == FuncIndex);
1279     return packed_;
1280   }
1281 
1282   Trap trap() const {
1283     MOZ_ASSERT(kind_ == TrapExit);
1284     MOZ_ASSERT(packed_ < uint32_t(Trap::Limit));
1285     return Trap(packed_);
1286   }
1287 };
1288 
1289 using CallSiteTargetVector = Vector<CallSiteTarget, 0, SystemAllocPolicy>;
1290 
1291 // WasmTryNotes are stored in a vector that acts as an exception table for
1292 // wasm try-catch blocks. These represent the information needed to take
1293 // exception handling actions after a throw is executed.
1294 struct WasmTryNote {
1295   explicit WasmTryNote(uint32_t begin = 0, uint32_t end = 0,
1296                        uint32_t framePushed = 0)
beginWasmTryNote1297       : begin(begin), end(end), framePushed(framePushed) {}
1298 
1299   uint32_t begin;        // Begin code offset of try instructions.
1300   uint32_t end;          // End code offset of try instructions.
1301   uint32_t entryPoint;   // The offset of the landing pad.
1302   uint32_t framePushed;  // Track offset from frame of stack pointer.
1303 
offsetByWasmTryNote1304   void offsetBy(uint32_t offset) {
1305     begin += offset;
1306     end += offset;
1307     entryPoint += offset;
1308   }
1309 
1310   bool operator<(const WasmTryNote& other) const {
1311     if (end == other.end) {
1312       return begin > other.begin;
1313     }
1314     return end < other.end;
1315   }
1316 };
1317 
1318 WASM_DECLARE_POD_VECTOR(WasmTryNote, WasmTryNoteVector)
1319 
1320 // Represents the resizable limits of memories and tables.
1321 
1322 struct Limits {
1323   uint64_t initial;
1324   Maybe<uint64_t> maximum;
1325 
1326   // `shared` is Shareable::False for tables but may be Shareable::True for
1327   // memories.
1328   Shareable shared;
1329 
1330   Limits() = default;
1331   explicit Limits(uint64_t initial, const Maybe<uint64_t>& maximum = Nothing(),
1332                   Shareable shared = Shareable::False)
initialLimits1333       : initial(initial), maximum(maximum), shared(shared) {}
1334 };
1335 
1336 // Memories can be 32-bit (indices are 32 bits and the max is 4GB) or 64-bit
1337 // (indices are 64 bits and the max is XXX).
1338 
1339 enum class MemoryKind { Memory32, Memory64 };
1340 
1341 // MemoryDesc describes a memory.
1342 
1343 struct MemoryDesc {
1344   MemoryKind kind;
1345   Limits limits;
1346 
isSharedMemoryDesc1347   bool isShared() const { return limits.shared == Shareable::True; }
1348 
1349   // Whether a backing store for this memory may move when grown.
canMovingGrowMemoryDesc1350   bool canMovingGrow() const { return limits.maximum.isNothing(); }
1351 
1352   // Whether the bounds check limit (see the doc comment in
1353   // ArrayBufferObject.cpp regarding linear memory structure) can ever be
1354   // larger than 32-bits.
boundsCheckLimitIs32BitsMemoryDesc1355   bool boundsCheckLimitIs32Bits() const {
1356     return limits.maximum.isSome() &&
1357            limits.maximum.value() < (0x100000000 / PageSize);
1358   }
1359 
1360   // The initial length of this memory in pages.
initialPagesMemoryDesc1361   Pages initialPages() const { return Pages(limits.initial); }
1362 
1363   // The maximum length of this memory in pages.
maximumPagesMemoryDesc1364   Maybe<Pages> maximumPages() const {
1365     return limits.maximum.map([](uint64_t x) { return Pages(x); });
1366   }
1367 
1368   // The initial length of this memory in bytes. Only valid for memory32.
initialLength32MemoryDesc1369   uint64_t initialLength32() const {
1370     MOZ_ASSERT(kind == MemoryKind::Memory32);
1371     // See static_assert after MemoryDesc for why this is safe.
1372     return limits.initial * PageSize;
1373   }
1374 
1375   // The maximum length of this memory in bytes. Only valid for memory32.
maximumLength32MemoryDesc1376   Maybe<uint64_t> maximumLength32() const {
1377     MOZ_ASSERT(kind == MemoryKind::Memory32);
1378     if (limits.maximum) {
1379       // See static_assert after MemoryDesc for why this is safe.
1380       return Some(*limits.maximum * PageSize);
1381     }
1382     return Nothing();
1383   }
1384 
1385   MemoryDesc() = default;
MemoryDescMemoryDesc1386   MemoryDesc(MemoryKind kind, Limits limits) : kind(kind), limits(limits) {}
1387 };
1388 
1389 // We don't need to worry about overflow with a Memory32 field when
1390 // using a uint64_t.
1391 static_assert(MaxMemory32LimitField <= UINT64_MAX / PageSize);
1392 
1393 // TableDesc describes a table as well as the offset of the table's base pointer
1394 // in global memory.
1395 //
1396 // A TableDesc contains the element type and whether the table is for asm.js,
1397 // which determines the table representation.
1398 //  - ExternRef: a wasm anyref word (wasm::AnyRef)
1399 //  - FuncRef: a two-word FunctionTableElem (wasm indirect call ABI)
1400 //  - FuncRef (if `isAsmJS`): a two-word FunctionTableElem (asm.js ABI)
1401 // Eventually there should be a single unified AnyRef representation.
1402 
1403 struct TableDesc {
1404   RefType elemType;
1405   bool importedOrExported;
1406   bool isAsmJS;
1407   uint32_t globalDataOffset;
1408   uint32_t initialLength;
1409   Maybe<uint32_t> maximumLength;
1410 
1411   TableDesc() = default;
1412   TableDesc(RefType elemType, uint32_t initialLength,
1413             Maybe<uint32_t> maximumLength, bool isAsmJS,
1414             bool importedOrExported = false)
elemTypeTableDesc1415       : elemType(elemType),
1416         importedOrExported(importedOrExported),
1417         isAsmJS(isAsmJS),
1418         globalDataOffset(UINT32_MAX),
1419         initialLength(initialLength),
1420         maximumLength(maximumLength) {}
1421 };
1422 
1423 using TableDescVector = Vector<TableDesc, 0, SystemAllocPolicy>;
1424 
1425 // CalleeDesc describes how to compile one of the variety of asm.js/wasm calls.
1426 // This is hoisted into WasmTypes.h for sharing between Ion and Baseline.
1427 
1428 class CalleeDesc {
1429  public:
1430   enum Which {
1431     // Calls a function defined in the same module by its index.
1432     Func,
1433 
1434     // Calls the import identified by the offset of its FuncImportTls in
1435     // thread-local data.
1436     Import,
1437 
1438     // Calls a WebAssembly table (heterogeneous, index must be bounds
1439     // checked, callee instance depends on TableDesc).
1440     WasmTable,
1441 
1442     // Calls an asm.js table (homogeneous, masked index, same-instance).
1443     AsmJSTable,
1444 
1445     // Call a C++ function identified by SymbolicAddress.
1446     Builtin,
1447 
1448     // Like Builtin, but automatically passes Instance* as first argument.
1449     BuiltinInstanceMethod
1450   };
1451 
1452  private:
1453   // which_ shall be initialized in the static constructors
1454   MOZ_INIT_OUTSIDE_CTOR Which which_;
1455   union U {
U()1456     U() : funcIndex_(0) {}
1457     uint32_t funcIndex_;
1458     struct {
1459       uint32_t globalDataOffset_;
1460     } import;
1461     struct {
1462       uint32_t globalDataOffset_;
1463       uint32_t minLength_;
1464       TypeIdDesc funcTypeId_;
1465     } table;
1466     SymbolicAddress builtin_;
1467   } u;
1468 
1469  public:
1470   CalleeDesc() = default;
function(uint32_t funcIndex)1471   static CalleeDesc function(uint32_t funcIndex) {
1472     CalleeDesc c;
1473     c.which_ = Func;
1474     c.u.funcIndex_ = funcIndex;
1475     return c;
1476   }
import(uint32_t globalDataOffset)1477   static CalleeDesc import(uint32_t globalDataOffset) {
1478     CalleeDesc c;
1479     c.which_ = Import;
1480     c.u.import.globalDataOffset_ = globalDataOffset;
1481     return c;
1482   }
wasmTable(const TableDesc & desc,TypeIdDesc funcTypeId)1483   static CalleeDesc wasmTable(const TableDesc& desc, TypeIdDesc funcTypeId) {
1484     CalleeDesc c;
1485     c.which_ = WasmTable;
1486     c.u.table.globalDataOffset_ = desc.globalDataOffset;
1487     c.u.table.minLength_ = desc.initialLength;
1488     c.u.table.funcTypeId_ = funcTypeId;
1489     return c;
1490   }
asmJSTable(const TableDesc & desc)1491   static CalleeDesc asmJSTable(const TableDesc& desc) {
1492     CalleeDesc c;
1493     c.which_ = AsmJSTable;
1494     c.u.table.globalDataOffset_ = desc.globalDataOffset;
1495     return c;
1496   }
builtin(SymbolicAddress callee)1497   static CalleeDesc builtin(SymbolicAddress callee) {
1498     CalleeDesc c;
1499     c.which_ = Builtin;
1500     c.u.builtin_ = callee;
1501     return c;
1502   }
builtinInstanceMethod(SymbolicAddress callee)1503   static CalleeDesc builtinInstanceMethod(SymbolicAddress callee) {
1504     CalleeDesc c;
1505     c.which_ = BuiltinInstanceMethod;
1506     c.u.builtin_ = callee;
1507     return c;
1508   }
which()1509   Which which() const { return which_; }
funcIndex()1510   uint32_t funcIndex() const {
1511     MOZ_ASSERT(which_ == Func);
1512     return u.funcIndex_;
1513   }
importGlobalDataOffset()1514   uint32_t importGlobalDataOffset() const {
1515     MOZ_ASSERT(which_ == Import);
1516     return u.import.globalDataOffset_;
1517   }
isTable()1518   bool isTable() const { return which_ == WasmTable || which_ == AsmJSTable; }
tableLengthGlobalDataOffset()1519   uint32_t tableLengthGlobalDataOffset() const {
1520     MOZ_ASSERT(isTable());
1521     return u.table.globalDataOffset_ + offsetof(TableTls, length);
1522   }
tableFunctionBaseGlobalDataOffset()1523   uint32_t tableFunctionBaseGlobalDataOffset() const {
1524     MOZ_ASSERT(isTable());
1525     return u.table.globalDataOffset_ + offsetof(TableTls, functionBase);
1526   }
wasmTableSigId()1527   TypeIdDesc wasmTableSigId() const {
1528     MOZ_ASSERT(which_ == WasmTable);
1529     return u.table.funcTypeId_;
1530   }
wasmTableMinLength()1531   uint32_t wasmTableMinLength() const {
1532     MOZ_ASSERT(which_ == WasmTable);
1533     return u.table.minLength_;
1534   }
builtin()1535   SymbolicAddress builtin() const {
1536     MOZ_ASSERT(which_ == Builtin || which_ == BuiltinInstanceMethod);
1537     return u.builtin_;
1538   }
1539 };
1540 
1541 // Because ARM has a fixed-width instruction encoding, ARM can only express a
1542 // limited subset of immediates (in a single instruction).
1543 
1544 static const uint64_t HighestValidARMImmediate = 0xff000000;
1545 
1546 extern bool IsValidARMImmediate(uint32_t i);
1547 
1548 extern uint64_t RoundUpToNextValidARMImmediate(uint64_t i);
1549 
1550 // Bounds checks always compare the base of the memory access with the bounds
1551 // check limit. If the memory access is unaligned, this means that, even if the
1552 // bounds check succeeds, a few bytes of the access can extend past the end of
1553 // memory. To guard against this, extra space is included in the guard region to
1554 // catch the overflow. MaxMemoryAccessSize is a conservative approximation of
1555 // the maximum guard space needed to catch all unaligned overflows.
1556 
1557 static const unsigned MaxMemoryAccessSize = LitVal::sizeofLargestValue();
1558 
1559 #ifdef WASM_SUPPORTS_HUGE_MEMORY
1560 
1561 // On WASM_SUPPORTS_HUGE_MEMORY platforms, every asm.js or WebAssembly 32-bit
1562 // memory unconditionally allocates a huge region of virtual memory of size
1563 // wasm::HugeMappedSize. This allows all memory resizing to work without
1564 // reallocation and provides enough guard space for all offsets to be folded
1565 // into memory accesses.
1566 
1567 static const uint64_t HugeIndexRange = uint64_t(UINT32_MAX) + 1;
1568 static const uint64_t HugeOffsetGuardLimit = uint64_t(INT32_MAX) + 1;
1569 static const uint64_t HugeUnalignedGuardPage = PageSize;
1570 static const uint64_t HugeMappedSize =
1571     HugeIndexRange + HugeOffsetGuardLimit + HugeUnalignedGuardPage;
1572 
1573 static_assert(MaxMemoryAccessSize <= HugeUnalignedGuardPage,
1574               "rounded up to static page size");
1575 static_assert(HugeOffsetGuardLimit < UINT32_MAX,
1576               "checking for overflow against OffsetGuardLimit is enough.");
1577 
1578 #endif
1579 
1580 // On !WASM_SUPPORTS_HUGE_MEMORY platforms:
1581 //  - To avoid OOM in ArrayBuffer::prepareForAsmJS, asm.js continues to use the
1582 //    original ArrayBuffer allocation which has no guard region at all.
1583 //  - For WebAssembly memories, an additional GuardSize is mapped after the
1584 //    accessible region of the memory to catch folded (base+offset) accesses
1585 //    where `offset < OffsetGuardLimit` as well as the overflow from unaligned
1586 //    accesses, as described above for MaxMemoryAccessSize.
1587 
1588 static const size_t OffsetGuardLimit = PageSize - MaxMemoryAccessSize;
1589 static const size_t GuardSize = PageSize;
1590 
1591 static_assert(MaxMemoryAccessSize < GuardSize,
1592               "Guard page handles partial out-of-bounds");
1593 static_assert(OffsetGuardLimit < UINT32_MAX,
1594               "checking for overflow against OffsetGuardLimit is enough.");
1595 
GetMaxOffsetGuardLimit(bool hugeMemory)1596 static constexpr size_t GetMaxOffsetGuardLimit(bool hugeMemory) {
1597 #ifdef WASM_SUPPORTS_HUGE_MEMORY
1598   return hugeMemory ? HugeOffsetGuardLimit : OffsetGuardLimit;
1599 #else
1600   return OffsetGuardLimit;
1601 #endif
1602 }
1603 
1604 static const size_t MinOffsetGuardLimit = OffsetGuardLimit;
1605 
1606 // Return whether the given immediate satisfies the constraints of the platform
1607 // (viz. that, on ARM, IsValidARMImmediate).
1608 
1609 extern bool IsValidBoundsCheckImmediate(uint32_t i);
1610 
1611 // For a given WebAssembly/asm.js max pages, return the number of bytes to
1612 // map which will necessarily be a multiple of the system page size and greater
1613 // than maxPages in bytes. For a returned mappedSize:
1614 //   boundsCheckLimit = mappedSize - GuardSize
1615 //   IsValidBoundsCheckImmediate(boundsCheckLimit)
1616 
1617 extern size_t ComputeMappedSize(Pages maxPages);
1618 
1619 // The following thresholds were derived from a microbenchmark. If we begin to
1620 // ship this optimization for more platforms, we will need to extend this list.
1621 
1622 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
1623 static const uint32_t MaxInlineMemoryCopyLength = 64;
1624 static const uint32_t MaxInlineMemoryFillLength = 64;
1625 #elif defined(JS_CODEGEN_X86)
1626 static const uint32_t MaxInlineMemoryCopyLength = 32;
1627 static const uint32_t MaxInlineMemoryFillLength = 32;
1628 #else
1629 static const uint32_t MaxInlineMemoryCopyLength = 0;
1630 static const uint32_t MaxInlineMemoryFillLength = 0;
1631 #endif
1632 
1633 static_assert(MaxInlineMemoryCopyLength < MinOffsetGuardLimit, "precondition");
1634 static_assert(MaxInlineMemoryFillLength < MinOffsetGuardLimit, "precondition");
1635 
1636 // Verbose logging support.
1637 
1638 extern void Log(JSContext* cx, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
1639 
1640 // Codegen debug support.
1641 
1642 enum class DebugChannel {
1643   Function,
1644   Import,
1645 };
1646 
1647 #ifdef WASM_CODEGEN_DEBUG
1648 bool IsCodegenDebugEnabled(DebugChannel channel);
1649 #endif
1650 
1651 void DebugCodegen(DebugChannel channel, const char* fmt, ...)
1652     MOZ_FORMAT_PRINTF(2, 3);
1653 
1654 using PrintCallback = void (*)(const char*);
1655 
1656 #ifdef ENABLE_WASM_SIMD_WORMHOLE
1657 bool IsWormholeTrigger(const V128& shuffleMask);
1658 jit::SimdConstant WormholeSignature();
1659 #endif
1660 
1661 }  // namespace wasm
1662 }  // namespace js
1663 
1664 #endif  // wasm_types_h
1665