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 2021 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_valtype_h
20 #define wasm_valtype_h
21 
22 #include "mozilla/Maybe.h"
23 
24 #include <type_traits>
25 
26 #include "jit/IonTypes.h"
27 #include "wasm/WasmConstants.h"
28 #include "wasm/WasmTypeDecls.h"
29 
30 namespace js {
31 namespace wasm {
32 
33 using mozilla::Maybe;
34 
35 // A PackedTypeCode represents any value type in an compact POD format.
36 union PackedTypeCode {
37  public:
38   using PackedRepr = uintptr_t;
39 
40  private:
41 #ifdef JS_64BIT
42   static constexpr size_t PointerTagBits = 2;
43   static constexpr size_t TypeCodeBits = 8;
44   static constexpr size_t TypeIndexBits = 21;
45   static constexpr size_t NullableBits = 1;
46   static constexpr size_t RttDepthBits = 10;
47 #else
48   static constexpr size_t PointerTagBits = 2;
49   static constexpr size_t TypeCodeBits = 8;
50   static constexpr size_t TypeIndexBits = 14;
51   static constexpr size_t NullableBits = 1;
52   static constexpr size_t RttDepthBits = 7;
53 #endif
54 
55   static_assert(PointerTagBits + TypeCodeBits + TypeIndexBits + NullableBits +
56                         RttDepthBits <=
57                     (sizeof(PackedRepr) * 8),
58                 "enough bits");
59   static_assert(MaxTypeIndex < (1 << TypeIndexBits), "enough bits");
60   static_assert(MaxRttDepth < (1 << RttDepthBits), "enough bits");
61 
62   PackedRepr bits_;
63   struct {
64     PackedRepr pointerTag_ : PointerTagBits;
65     PackedRepr typeCode_ : TypeCodeBits;
66     PackedRepr typeIndex_ : TypeIndexBits;
67     PackedRepr nullable_ : NullableBits;
68     PackedRepr rttDepth_ : RttDepthBits;
69   };
70 
71  public:
72   static constexpr uint32_t NoTypeCode = (1 << TypeCodeBits) - 1;
73   static constexpr uint32_t NoTypeIndex = (1 << TypeIndexBits) - 1;
74 
invalid()75   static PackedTypeCode invalid() {
76     PackedTypeCode ptc = {};
77     ptc.typeCode_ = NoTypeCode;
78     return ptc;
79   }
80 
fromBits(PackedRepr bits)81   static constexpr PackedTypeCode fromBits(PackedRepr bits) {
82     PackedTypeCode ptc = {};
83     ptc.bits_ = bits;
84     return ptc;
85   }
86 
pack(TypeCode tc,uint32_t refTypeIndex,bool isNullable,uint32_t rttDepth)87   static constexpr PackedTypeCode pack(TypeCode tc, uint32_t refTypeIndex,
88                                        bool isNullable, uint32_t rttDepth) {
89     MOZ_ASSERT(uint32_t(tc) <= ((1 << TypeCodeBits) - 1));
90     MOZ_ASSERT_IF(tc != AbstractReferenceTypeIndexCode && tc != TypeCode::Rtt,
91                   refTypeIndex == NoTypeIndex);
92     MOZ_ASSERT_IF(tc == AbstractReferenceTypeIndexCode || tc == TypeCode::Rtt,
93                   refTypeIndex <= MaxTypeIndex);
94     MOZ_ASSERT_IF(tc != TypeCode::Rtt, rttDepth == 0);
95     MOZ_ASSERT_IF(tc == TypeCode::Rtt, rttDepth <= MaxRttDepth);
96     PackedTypeCode ptc = {};
97     ptc.typeCode_ = PackedRepr(tc);
98     ptc.typeIndex_ = refTypeIndex;
99     ptc.nullable_ = isNullable;
100     ptc.rttDepth_ = rttDepth;
101     return ptc;
102   }
103 
pack(TypeCode tc,bool nullable)104   static constexpr PackedTypeCode pack(TypeCode tc, bool nullable) {
105     return pack(tc, PackedTypeCode::NoTypeIndex, nullable, 0);
106   }
107 
pack(TypeCode tc)108   static constexpr PackedTypeCode pack(TypeCode tc) {
109     return pack(tc, PackedTypeCode::NoTypeIndex, false, 0);
110   }
111 
isValid()112   bool isValid() const { return typeCode_ != NoTypeCode; }
113 
isReference()114   bool isReference() const {
115     return typeCodeAbstracted() == AbstractReferenceTypeCode;
116   }
117 
bits()118   PackedRepr bits() const { return bits_; }
119 
typeCode()120   TypeCode typeCode() const {
121     MOZ_ASSERT(isValid());
122     return TypeCode(typeCode_);
123   }
124 
125   // Return the TypeCode, but return AbstractReferenceTypeCode for any reference
126   // type.
127   //
128   // This function is very, very hot, hence what would normally be a switch on
129   // the value `c` to map the reference types to AbstractReferenceTypeCode has
130   // been distilled into a simple comparison; this is fastest.  Should type
131   // codes become too complicated for this to work then a lookup table also has
132   // better performance than a switch.
133   //
134   // An alternative is for the PackedTypeCode to represent something closer to
135   // what ValType needs, so that this decoding step is not necessary, but that
136   // moves complexity elsewhere, and the perf gain here would be only about 1%
137   // for baseline compilation throughput.
138   //
139   // TODO: with rtt types this is no longer a simple comparison, we should
140   // re-evaluate the performance of this function.
typeCodeAbstracted()141   TypeCode typeCodeAbstracted() const {
142     MOZ_ASSERT(isValid());
143     TypeCode tc = TypeCode(typeCode_);
144     return (tc < LowestPrimitiveTypeCode && tc != TypeCode::Rtt)
145                ? AbstractReferenceTypeCode
146                : tc;
147   }
148 
typeIndex()149   uint32_t typeIndex() const {
150     MOZ_ASSERT(isValid());
151     return uint32_t(typeIndex_);
152   }
153 
typeIndexUnchecked()154   uint32_t typeIndexUnchecked() const {
155     MOZ_ASSERT(isValid());
156     return uint32_t(typeIndex_);
157   }
158 
isNullable()159   bool isNullable() const {
160     MOZ_ASSERT(isValid());
161     return bool(nullable_);
162   }
163 
rttDepth()164   uint32_t rttDepth() const {
165     MOZ_ASSERT(isValid());
166     return uint32_t(rttDepth_);
167   }
168 
asNonNullable()169   PackedTypeCode asNonNullable() const {
170     MOZ_ASSERT(isReference());
171     PackedTypeCode mutated = *this;
172     mutated.nullable_ = 0;
173     return mutated;
174   }
175 
176   bool operator==(const PackedTypeCode& rhs) const {
177     return bits_ == rhs.bits_;
178   }
179   bool operator!=(const PackedTypeCode& rhs) const {
180     return bits_ != rhs.bits_;
181   }
182 };
183 
184 static_assert(sizeof(PackedTypeCode) == sizeof(uintptr_t), "packed");
185 static_assert(std::is_pod_v<PackedTypeCode>,
186               "must be POD to be simply serialized/deserialized");
187 
188 // An enum that describes the representation classes for tables; The table
189 // element type is mapped into this by Table::repr().
190 
191 enum class TableRepr { Ref, Func };
192 
193 // The RefType carries more information about types t for which t.isReference()
194 // is true.
195 
196 class RefType {
197  public:
198   enum Kind {
199     Func = uint8_t(TypeCode::FuncRef),
200     Extern = uint8_t(TypeCode::ExternRef),
201     Eq = uint8_t(TypeCode::EqRef),
202     TypeIndex = uint8_t(AbstractReferenceTypeIndexCode)
203   };
204 
205  private:
206   PackedTypeCode ptc_;
207 
208 #ifdef DEBUG
isValid()209   bool isValid() const {
210     switch (ptc_.typeCode()) {
211       case TypeCode::FuncRef:
212       case TypeCode::ExternRef:
213       case TypeCode::EqRef:
214         MOZ_ASSERT(ptc_.typeIndex() == PackedTypeCode::NoTypeIndex);
215         return true;
216       case AbstractReferenceTypeIndexCode:
217         MOZ_ASSERT(ptc_.typeIndex() != PackedTypeCode::NoTypeIndex);
218         return true;
219       default:
220         return false;
221     }
222   }
223 #endif
RefType(Kind kind,bool nullable)224   RefType(Kind kind, bool nullable)
225       : ptc_(PackedTypeCode::pack(TypeCode(kind), nullable)) {
226     MOZ_ASSERT(isValid());
227   }
228 
RefType(uint32_t refTypeIndex,bool nullable)229   RefType(uint32_t refTypeIndex, bool nullable)
230       : ptc_(PackedTypeCode::pack(AbstractReferenceTypeIndexCode, refTypeIndex,
231                                   nullable, 0)) {
232     MOZ_ASSERT(isValid());
233   }
234 
235  public:
RefType()236   RefType() : ptc_(PackedTypeCode::invalid()) {}
RefType(PackedTypeCode ptc)237   explicit RefType(PackedTypeCode ptc) : ptc_(ptc) { MOZ_ASSERT(isValid()); }
238 
fromTypeCode(TypeCode tc,bool nullable)239   static RefType fromTypeCode(TypeCode tc, bool nullable) {
240     MOZ_ASSERT(tc != AbstractReferenceTypeIndexCode);
241     return RefType(Kind(tc), nullable);
242   }
243 
fromTypeIndex(uint32_t refTypeIndex,bool nullable)244   static RefType fromTypeIndex(uint32_t refTypeIndex, bool nullable) {
245     return RefType(refTypeIndex, nullable);
246   }
247 
kind()248   Kind kind() const { return Kind(ptc_.typeCode()); }
249 
typeIndex()250   uint32_t typeIndex() const { return ptc_.typeIndex(); }
251 
packed()252   PackedTypeCode packed() const { return ptc_; }
253 
func()254   static RefType func() { return RefType(Func, true); }
extern_()255   static RefType extern_() { return RefType(Extern, true); }
eq()256   static RefType eq() { return RefType(Eq, true); }
257 
isFunc()258   bool isFunc() const { return kind() == RefType::Func; }
isExtern()259   bool isExtern() const { return kind() == RefType::Extern; }
isEq()260   bool isEq() const { return kind() == RefType::Eq; }
isTypeIndex()261   bool isTypeIndex() const { return kind() == RefType::TypeIndex; }
262 
isNullable()263   bool isNullable() const { return bool(ptc_.isNullable()); }
asNonNullable()264   RefType asNonNullable() const { return RefType(ptc_.asNonNullable()); }
265 
tableRepr()266   TableRepr tableRepr() const {
267     switch (kind()) {
268       case RefType::Func:
269         return TableRepr::Func;
270       case RefType::Extern:
271       case RefType::Eq:
272         return TableRepr::Ref;
273       case RefType::TypeIndex:
274         MOZ_CRASH("NYI");
275     }
276     MOZ_CRASH("switch is exhaustive");
277   }
278 
279   bool operator==(const RefType& that) const { return ptc_ == that.ptc_; }
280   bool operator!=(const RefType& that) const { return ptc_ != that.ptc_; }
281 };
282 
283 class FieldTypeTraits {
284  public:
285   enum Kind {
286     I8 = uint8_t(TypeCode::I8),
287     I16 = uint8_t(TypeCode::I16),
288     I32 = uint8_t(TypeCode::I32),
289     I64 = uint8_t(TypeCode::I64),
290     F32 = uint8_t(TypeCode::F32),
291     F64 = uint8_t(TypeCode::F64),
292     V128 = uint8_t(TypeCode::V128),
293     Rtt = uint8_t(TypeCode::Rtt),
294     Ref = uint8_t(AbstractReferenceTypeCode),
295   };
296 
isValidTypeCode(TypeCode tc)297   static bool isValidTypeCode(TypeCode tc) {
298     switch (tc) {
299 #ifdef ENABLE_WASM_GC
300       case TypeCode::I8:
301       case TypeCode::I16:
302 #endif
303       case TypeCode::I32:
304       case TypeCode::I64:
305       case TypeCode::F32:
306       case TypeCode::F64:
307 #ifdef ENABLE_WASM_SIMD
308       case TypeCode::V128:
309 #endif
310       case TypeCode::FuncRef:
311       case TypeCode::ExternRef:
312 #ifdef ENABLE_WASM_GC
313       case TypeCode::EqRef:
314       case TypeCode::Rtt:
315 #endif
316 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
317       case AbstractReferenceTypeIndexCode:
318 #endif
319         return true;
320       default:
321         return false;
322     }
323   }
324 };
325 
326 class ValTypeTraits {
327  public:
328   enum Kind {
329     I32 = uint8_t(TypeCode::I32),
330     I64 = uint8_t(TypeCode::I64),
331     F32 = uint8_t(TypeCode::F32),
332     F64 = uint8_t(TypeCode::F64),
333     V128 = uint8_t(TypeCode::V128),
334     Rtt = uint8_t(TypeCode::Rtt),
335     Ref = uint8_t(AbstractReferenceTypeCode),
336   };
337 
isValidTypeCode(TypeCode tc)338   static bool isValidTypeCode(TypeCode tc) {
339     switch (tc) {
340       case TypeCode::I32:
341       case TypeCode::I64:
342       case TypeCode::F32:
343       case TypeCode::F64:
344 #ifdef ENABLE_WASM_SIMD
345       case TypeCode::V128:
346 #endif
347       case TypeCode::FuncRef:
348       case TypeCode::ExternRef:
349 #ifdef ENABLE_WASM_GC
350       case TypeCode::EqRef:
351       case TypeCode::Rtt:
352 #endif
353 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
354       case AbstractReferenceTypeIndexCode:
355 #endif
356         return true;
357       default:
358         return false;
359     }
360   }
361 };
362 
363 // The PackedType represents the storage type of a WebAssembly location, whether
364 // parameter, local, field, or global. See specializations below for ValType and
365 // FieldType.
366 
367 template <class T>
368 class PackedType : public T {
369  public:
370   using Kind = typename T::Kind;
371 
372  protected:
373   PackedTypeCode tc_;
374 
PackedType(TypeCode c)375   explicit PackedType(TypeCode c) : tc_(PackedTypeCode::pack(c)) {
376     MOZ_ASSERT(c != AbstractReferenceTypeIndexCode);
377     MOZ_ASSERT(isValid());
378   }
379 
typeCode()380   TypeCode typeCode() const {
381     MOZ_ASSERT(isValid());
382     return tc_.typeCode();
383   }
384 
385  public:
PackedType()386   PackedType() : tc_(PackedTypeCode::invalid()) {}
387 
PackedType(Kind c)388   MOZ_IMPLICIT PackedType(Kind c) : tc_(PackedTypeCode::pack(TypeCode(c))) {
389     MOZ_ASSERT(c != Kind::Ref);
390     MOZ_ASSERT(isValid());
391   }
392 
PackedType(RefType rt)393   MOZ_IMPLICIT PackedType(RefType rt) : tc_(rt.packed()) {
394     MOZ_ASSERT(isValid());
395   }
396 
PackedType(PackedTypeCode ptc)397   explicit PackedType(PackedTypeCode ptc) : tc_(ptc) { MOZ_ASSERT(isValid()); }
398 
PackedType(jit::MIRType mty)399   explicit PackedType(jit::MIRType mty) {
400     switch (mty) {
401       case jit::MIRType::Int32:
402         tc_ = PackedTypeCode::pack(TypeCode::I32);
403         break;
404       case jit::MIRType::Int64:
405         tc_ = PackedTypeCode::pack(TypeCode::I64);
406         break;
407       case jit::MIRType::Float32:
408         tc_ = PackedTypeCode::pack(TypeCode::F32);
409         break;
410       case jit::MIRType::Double:
411         tc_ = PackedTypeCode::pack(TypeCode::F64);
412         break;
413       case jit::MIRType::Simd128:
414         tc_ = PackedTypeCode::pack(TypeCode::V128);
415         break;
416       default:
417         MOZ_CRASH("PackedType(MIRType): unexpected type");
418     }
419   }
420 
fromNonRefTypeCode(TypeCode tc)421   static PackedType fromNonRefTypeCode(TypeCode tc) {
422 #ifdef DEBUG
423     switch (tc) {
424       case TypeCode::I8:
425       case TypeCode::I16:
426       case TypeCode::I32:
427       case TypeCode::I64:
428       case TypeCode::F32:
429       case TypeCode::F64:
430       case TypeCode::V128:
431         break;
432       default:
433         MOZ_CRASH("Bad type code");
434     }
435 #endif
436     return PackedType(tc);
437   }
438 
fromRtt(uint32_t typeIndex,uint32_t rttDepth)439   static PackedType fromRtt(uint32_t typeIndex, uint32_t rttDepth) {
440     return PackedType(
441         PackedTypeCode::pack(TypeCode::Rtt, typeIndex, false, rttDepth));
442   }
443 
fromBitsUnsafe(uint64_t bits)444   static PackedType fromBitsUnsafe(uint64_t bits) {
445     return PackedType(PackedTypeCode::fromBits(bits));
446   }
447 
hostPtr()448   static constexpr PackedType hostPtr() {
449 #ifdef JS_64BIT
450     return PackedType::I64;
451 #else
452     return PackedType::I32;
453 #endif
454   }
455 
isValid()456   bool isValid() const {
457     if (!tc_.isValid()) {
458       return false;
459     }
460     return T::isValidTypeCode(tc_.typeCode());
461   }
462 
packed()463   PackedTypeCode packed() const {
464     MOZ_ASSERT(isValid());
465     return tc_;
466   }
467 
bitsUnsafe()468   uint64_t bitsUnsafe() const {
469     MOZ_ASSERT(isValid());
470     return tc_.bits();
471   }
472 
isFuncRef()473   bool isFuncRef() const { return tc_.typeCode() == TypeCode::FuncRef; }
474 
isExternRef()475   bool isExternRef() const { return tc_.typeCode() == TypeCode::ExternRef; }
476 
isEqRef()477   bool isEqRef() const { return tc_.typeCode() == TypeCode::EqRef; }
478 
isTypeIndex()479   bool isTypeIndex() const {
480     MOZ_ASSERT(isValid());
481     return tc_.typeCode() == AbstractReferenceTypeIndexCode;
482   }
483 
isReference()484   bool isReference() const {
485     MOZ_ASSERT(isValid());
486     return tc_.isReference();
487   }
488 
isRtt()489   bool isRtt() const { return tc_.typeCode() == TypeCode::Rtt; }
490 
491   // Returns whether the type has a default value.
isDefaultable()492   bool isDefaultable() const {
493     MOZ_ASSERT(isValid());
494     return !(isRtt() || (isReference() && !isNullable()));
495   }
496 
497   // Returns whether the type has a representation in JS.
isExposable()498   bool isExposable() const {
499     MOZ_ASSERT(isValid());
500 #if defined(ENABLE_WASM_SIMD) || defined(ENABLE_WASM_GC)
501     return !(kind() == Kind::V128 || isRtt() || isTypeIndex());
502 #else
503     return true;
504 #endif
505   }
506 
isNullable()507   bool isNullable() const {
508     MOZ_ASSERT(isValid());
509     return tc_.isNullable();
510   }
511 
typeIndex()512   uint32_t typeIndex() const {
513     MOZ_ASSERT(isValid());
514     return tc_.typeIndex();
515   }
516 
rttDepth()517   uint32_t rttDepth() const {
518     MOZ_ASSERT(isValid());
519     return tc_.rttDepth();
520   }
521 
kind()522   Kind kind() const {
523     MOZ_ASSERT(isValid());
524     return Kind(tc_.typeCodeAbstracted());
525   }
526 
refType()527   RefType refType() const {
528     MOZ_ASSERT(isReference());
529     return RefType(tc_);
530   }
531 
refTypeKind()532   RefType::Kind refTypeKind() const {
533     MOZ_ASSERT(isReference());
534     return RefType(tc_).kind();
535   }
536 
renumber(const RenumberMap & map)537   void renumber(const RenumberMap& map) {
538     if (!isTypeIndex()) {
539       return;
540     }
541 
542     if (RenumberMap::Ptr p = map.lookup(refType().typeIndex())) {
543       *this = RefType::fromTypeIndex(p->value(), isNullable());
544     }
545   }
546 
offsetTypeIndex(uint32_t offsetBy)547   void offsetTypeIndex(uint32_t offsetBy) {
548     if (!isTypeIndex()) {
549       return;
550     }
551     *this =
552         RefType::fromTypeIndex(refType().typeIndex() + offsetBy, isNullable());
553   }
554 
555   // Some types are encoded as JS::Value when they escape from Wasm (when passed
556   // as parameters to imports or returned from exports).  For ExternRef the
557   // Value encoding is pretty much a requirement.  For other types it's a choice
558   // that may (temporarily) simplify some code.
isEncodedAsJSValueOnEscape()559   bool isEncodedAsJSValueOnEscape() const {
560     switch (typeCode()) {
561       case TypeCode::FuncRef:
562       case TypeCode::ExternRef:
563       case TypeCode::EqRef:
564         return true;
565       default:
566         return false;
567     }
568   }
569 
size()570   uint32_t size() const {
571     switch (tc_.typeCodeAbstracted()) {
572       case TypeCode::I8:
573         return 1;
574       case TypeCode::I16:
575         return 2;
576       case TypeCode::I32:
577         return 4;
578       case TypeCode::I64:
579         return 8;
580       case TypeCode::F32:
581         return 4;
582       case TypeCode::F64:
583         return 8;
584       case TypeCode::V128:
585         return 16;
586       case TypeCode::Rtt:
587       case AbstractReferenceTypeCode:
588         return sizeof(void*);
589       default:
590         MOZ_ASSERT_UNREACHABLE();
591         return 0;
592     }
593   }
alignmentInStruct()594   uint32_t alignmentInStruct() const { return size(); }
indexingShift()595   uint32_t indexingShift() const {
596     switch (size()) {
597       case 1:
598         return 0;
599       case 2:
600         return 1;
601       case 4:
602         return 2;
603       case 8:
604         return 3;
605       case 16:
606         return 4;
607       default:
608         MOZ_ASSERT_UNREACHABLE();
609         return 0;
610     }
611   }
612 
widenToValType()613   PackedType<ValTypeTraits> widenToValType() const {
614     switch (tc_.typeCodeAbstracted()) {
615       case TypeCode::I8:
616       case TypeCode::I16:
617         return PackedType<ValTypeTraits>::I32;
618       default:
619         return PackedType<ValTypeTraits>(tc_);
620     }
621   }
622 
valType()623   PackedType<ValTypeTraits> valType() const {
624     MOZ_ASSERT(isValType());
625     return PackedType<ValTypeTraits>(tc_);
626   }
627 
isValType()628   bool isValType() const {
629     switch (tc_.typeCode()) {
630       case TypeCode::I8:
631       case TypeCode::I16:
632         return false;
633       default:
634         return true;
635     }
636   }
637 
638   bool operator==(const PackedType& that) const {
639     MOZ_ASSERT(isValid() && that.isValid());
640     return tc_ == that.tc_;
641   }
642 
643   bool operator!=(const PackedType& that) const {
644     MOZ_ASSERT(isValid() && that.isValid());
645     return tc_ != that.tc_;
646   }
647 
648   bool operator==(Kind that) const {
649     MOZ_ASSERT(isValid());
650     MOZ_ASSERT(that != Kind::Ref);
651     return Kind(typeCode()) == that;
652   }
653 
654   bool operator!=(Kind that) const { return !(*this == that); }
655 };
656 
657 using ValType = PackedType<ValTypeTraits>;
658 using FieldType = PackedType<FieldTypeTraits>;
659 
660 // The dominant use of this data type is for locals and args, and profiling
661 // with ZenGarden and Tanks suggests an initial size of 16 minimises heap
662 // allocation, both in terms of blocks and bytes.
663 using ValTypeVector = Vector<ValType, 16, SystemAllocPolicy>;
664 
665 // ValType utilities
666 
SizeOf(ValType vt)667 static inline unsigned SizeOf(ValType vt) {
668   switch (vt.kind()) {
669     case ValType::I32:
670     case ValType::F32:
671       return 4;
672     case ValType::I64:
673     case ValType::F64:
674       return 8;
675     case ValType::V128:
676       return 16;
677     case ValType::Rtt:
678     case ValType::Ref:
679       return sizeof(intptr_t);
680   }
681   MOZ_CRASH("Invalid ValType");
682 }
683 
684 // Note, ToMIRType is only correct within Wasm, where an AnyRef is represented
685 // as a pointer.  At the JS/wasm boundary, an AnyRef can be represented as a
686 // JS::Value, and the type translation may have to be handled specially and on a
687 // case-by-case basis.
688 
ToMIRType(ValType vt)689 static inline jit::MIRType ToMIRType(ValType vt) {
690   switch (vt.kind()) {
691     case ValType::I32:
692       return jit::MIRType::Int32;
693     case ValType::I64:
694       return jit::MIRType::Int64;
695     case ValType::F32:
696       return jit::MIRType::Float32;
697     case ValType::F64:
698       return jit::MIRType::Double;
699     case ValType::V128:
700       return jit::MIRType::Simd128;
701     case ValType::Rtt:
702     case ValType::Ref:
703       return jit::MIRType::RefOrNull;
704   }
705   MOZ_CRASH("bad type");
706 }
707 
IsNumberType(ValType vt)708 static inline bool IsNumberType(ValType vt) { return !vt.isReference(); }
709 
ToMIRType(const Maybe<ValType> & t)710 static inline jit::MIRType ToMIRType(const Maybe<ValType>& t) {
711   return t ? ToMIRType(ValType(t.ref())) : jit::MIRType::None;
712 }
713 
714 extern bool ToValType(JSContext* cx, HandleValue v, ValType* out);
715 
716 extern UniqueChars ToString(ValType type);
717 
718 extern UniqueChars ToString(const Maybe<ValType>& type);
719 
720 }  // namespace wasm
721 }  // namespace js
722 
723 #endif  // wasm_valtype_h
724