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 #include "wasm/WasmTypeDef.h"
20
21 #include "mozilla/CheckedInt.h"
22 #include "mozilla/MathAlgorithms.h"
23
24 #include "jit/JitOptions.h"
25 #include "js/friend/ErrorMessages.h" // JSMSG_*
26 #include "js/Printf.h"
27 #include "js/Value.h"
28 #include "vm/StringType.h"
29 #include "wasm/WasmJS.h"
30
31 using namespace js;
32 using namespace js::jit;
33 using namespace js::wasm;
34
35 using mozilla::CheckedInt32;
36 using mozilla::IsPowerOfTwo;
37
canHaveJitEntry() const38 bool FuncType::canHaveJitEntry() const {
39 return !hasUnexposableArgOrRet() &&
40 !temporarilyUnsupportedReftypeForEntry() &&
41 !temporarilyUnsupportedResultCountForJitEntry() &&
42 JitOptions.enableWasmJitEntry;
43 }
44
canHaveJitExit() const45 bool FuncType::canHaveJitExit() const {
46 return !hasUnexposableArgOrRet() && !temporarilyUnsupportedReftypeForExit() &&
47 !temporarilyUnsupportedResultCountForJitExit() &&
48 JitOptions.enableWasmJitExit;
49 }
50
serializedSize() const51 size_t FuncType::serializedSize() const {
52 return SerializedPodVectorSize(results_) + SerializedPodVectorSize(args_);
53 }
54
serialize(uint8_t * cursor) const55 uint8_t* FuncType::serialize(uint8_t* cursor) const {
56 cursor = SerializePodVector(cursor, results_);
57 cursor = SerializePodVector(cursor, args_);
58 return cursor;
59 }
60
deserialize(const uint8_t * cursor)61 const uint8_t* FuncType::deserialize(const uint8_t* cursor) {
62 cursor = DeserializePodVector(cursor, &results_);
63 if (!cursor) {
64 return nullptr;
65 }
66 return DeserializePodVector(cursor, &args_);
67 }
68
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const69 size_t FuncType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
70 return args_.sizeOfExcludingThis(mallocSizeOf);
71 }
72
RoundUpToAlignment(CheckedInt32 address,uint32_t align)73 static inline CheckedInt32 RoundUpToAlignment(CheckedInt32 address,
74 uint32_t align) {
75 MOZ_ASSERT(IsPowerOfTwo(align));
76
77 // Note: Be careful to order operators such that we first make the
78 // value smaller and then larger, so that we don't get false
79 // overflow errors due to (e.g.) adding `align` and then
80 // subtracting `1` afterwards when merely adding `align-1` would
81 // not have overflowed. Note that due to the nature of two's
82 // complement representation, if `address` is already aligned,
83 // then adding `align-1` cannot itself cause an overflow.
84
85 return ((address + (align - 1)) / align) * align;
86 }
87
88 class StructLayout {
89 CheckedInt32 sizeSoFar = 0;
90 uint32_t structAlignment = 1;
91
92 public:
93 // The field adders return the offset of the the field.
addField(FieldType type)94 CheckedInt32 addField(FieldType type) {
95 uint32_t fieldSize = type.size();
96 uint32_t fieldAlignment = type.alignmentInStruct();
97
98 // Alignment of the struct is the max of the alignment of its fields.
99 structAlignment = std::max(structAlignment, fieldAlignment);
100
101 // Align the pointer.
102 CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldAlignment);
103 if (!offset.isValid()) {
104 return offset;
105 }
106
107 // Allocate space.
108 sizeSoFar = offset + fieldSize;
109 if (!sizeSoFar.isValid()) {
110 return sizeSoFar;
111 }
112
113 return offset;
114 }
115
116 // The close method rounds up the structure size to the appropriate
117 // alignment and returns that size.
close()118 CheckedInt32 close() {
119 return RoundUpToAlignment(sizeSoFar, structAlignment);
120 }
121 };
122
computeLayout()123 bool StructType::computeLayout() {
124 StructLayout layout;
125 for (StructField& field : fields_) {
126 CheckedInt32 offset = layout.addField(field.type);
127 if (!offset.isValid()) {
128 return false;
129 }
130 field.offset = offset.value();
131 }
132
133 CheckedInt32 size = layout.close();
134 if (!size.isValid()) {
135 return false;
136 }
137 size_ = size.value();
138
139 return true;
140 }
141
serializedSize() const142 size_t StructType::serializedSize() const {
143 return SerializedPodVectorSize(fields_) + sizeof(size_);
144 }
145
serialize(uint8_t * cursor) const146 uint8_t* StructType::serialize(uint8_t* cursor) const {
147 cursor = SerializePodVector(cursor, fields_);
148 cursor = WriteBytes(cursor, &size_, sizeof(size_));
149 return cursor;
150 }
151
deserialize(const uint8_t * cursor)152 const uint8_t* StructType::deserialize(const uint8_t* cursor) {
153 (cursor = DeserializePodVector(cursor, &fields_)) &&
154 (cursor = ReadBytes(cursor, &size_, sizeof(size_)));
155 return cursor;
156 }
157
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const158 size_t StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
159 return fields_.sizeOfExcludingThis(mallocSizeOf);
160 }
161
serializedSize() const162 size_t TypeDef::serializedSize() const {
163 size_t size = sizeof(kind_);
164 switch (kind_) {
165 case TypeDefKind::Struct: {
166 size += structType_.serializedSize();
167 break;
168 }
169 case TypeDefKind::Func: {
170 size += funcType_.serializedSize();
171 break;
172 }
173 case TypeDefKind::None: {
174 break;
175 }
176 default:
177 MOZ_ASSERT_UNREACHABLE();
178 }
179 return size;
180 }
181
serialize(uint8_t * cursor) const182 uint8_t* TypeDef::serialize(uint8_t* cursor) const {
183 cursor = WriteBytes(cursor, &kind_, sizeof(kind_));
184 switch (kind_) {
185 case TypeDefKind::Struct: {
186 cursor = structType_.serialize(cursor);
187 break;
188 }
189 case TypeDefKind::Func: {
190 cursor = funcType_.serialize(cursor);
191 break;
192 }
193 case TypeDefKind::None: {
194 break;
195 }
196 default:
197 MOZ_ASSERT_UNREACHABLE();
198 }
199 return cursor;
200 }
201
deserialize(const uint8_t * cursor)202 const uint8_t* TypeDef::deserialize(const uint8_t* cursor) {
203 cursor = ReadBytes(cursor, &kind_, sizeof(kind_));
204 // kind_ was replaced -- call in-place constructors for union members.
205 switch (kind_) {
206 case TypeDefKind::Struct: {
207 StructType* structType = new (&structType_) StructType();
208 cursor = structType->deserialize(cursor);
209 break;
210 }
211 case TypeDefKind::Func: {
212 FuncType* funcType = new (&funcType_) FuncType();
213 cursor = funcType->deserialize(cursor);
214 break;
215 }
216 case TypeDefKind::None: {
217 break;
218 }
219 default:
220 MOZ_ASSERT_UNREACHABLE();
221 }
222 return cursor;
223 }
224
sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const225 size_t TypeDef::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
226 switch (kind_) {
227 case TypeDefKind::Struct: {
228 return structType_.sizeOfExcludingThis(mallocSizeOf);
229 }
230 case TypeDefKind::Func: {
231 return funcType_.sizeOfExcludingThis(mallocSizeOf);
232 }
233 case TypeDefKind::None: {
234 return 0;
235 }
236 default:
237 break;
238 }
239 MOZ_ASSERT_UNREACHABLE();
240 return 0;
241 }
242
isRefEquivalent(RefType one,RefType two,TypeCache * cache) const243 TypeResult TypeContext::isRefEquivalent(RefType one, RefType two,
244 TypeCache* cache) const {
245 // Anything's equal to itself.
246 if (one == two) {
247 return TypeResult::True;
248 }
249
250 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
251 if (features_.functionReferences) {
252 // Two references must have the same nullability to be equal
253 if (one.isNullable() != two.isNullable()) {
254 return TypeResult::False;
255 }
256
257 // Non type-index references are equal if they have the same kind
258 if (!one.isTypeIndex() && !two.isTypeIndex() && one.kind() == two.kind()) {
259 return TypeResult::True;
260 }
261
262 // Type-index references can be equal
263 if (one.isTypeIndex() && two.isTypeIndex()) {
264 return isTypeIndexEquivalent(one.typeIndex(), two.typeIndex(), cache);
265 }
266 }
267 #endif
268 return TypeResult::False;
269 }
270
271 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
isTypeIndexEquivalent(uint32_t one,uint32_t two,TypeCache * cache) const272 TypeResult TypeContext::isTypeIndexEquivalent(uint32_t one, uint32_t two,
273 TypeCache* cache) const {
274 MOZ_ASSERT(features_.functionReferences);
275
276 // Anything's equal to itself.
277 if (one == two) {
278 return TypeResult::True;
279 }
280
281 # ifdef ENABLE_WASM_GC
282 if (features_.gc) {
283 // A struct may be equal to a struct
284 if (isStructType(one) && isStructType(two)) {
285 return isStructEquivalent(one, two, cache);
286 }
287
288 // An array may be equal to an array
289 if (isArrayType(one) && isArrayType(two)) {
290 return isArrayEquivalent(one, two, cache);
291 }
292 }
293 # endif
294
295 return TypeResult::False;
296 }
297 #endif
298
299 #ifdef ENABLE_WASM_GC
isStructEquivalent(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const300 TypeResult TypeContext::isStructEquivalent(uint32_t oneIndex, uint32_t twoIndex,
301 TypeCache* cache) const {
302 if (cache->isEquivalent(oneIndex, twoIndex)) {
303 return TypeResult::True;
304 }
305
306 const StructType& one = structType(oneIndex);
307 const StructType& two = structType(twoIndex);
308
309 // Structs must have the same number of fields to be equal
310 if (one.fields_.length() != two.fields_.length()) {
311 return TypeResult::False;
312 }
313
314 // Assume these structs are equal while checking fields. If any field is
315 // not equal then we remove the assumption.
316 if (!cache->markEquivalent(oneIndex, twoIndex)) {
317 return TypeResult::OOM;
318 }
319
320 for (uint32_t i = 0; i < two.fields_.length(); i++) {
321 TypeResult result =
322 isStructFieldEquivalent(one.fields_[i], two.fields_[i], cache);
323 if (result != TypeResult::True) {
324 cache->unmarkEquivalent(oneIndex, twoIndex);
325 return result;
326 }
327 }
328 return TypeResult::True;
329 }
330
isStructFieldEquivalent(const StructField one,const StructField two,TypeCache * cache) const331 TypeResult TypeContext::isStructFieldEquivalent(const StructField one,
332 const StructField two,
333 TypeCache* cache) const {
334 // Struct fields must share the same mutability to equal
335 if (one.isMutable != two.isMutable) {
336 return TypeResult::False;
337 }
338 // Struct field types must be equal
339 return isEquivalent(one.type, two.type, cache);
340 }
341
isArrayEquivalent(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const342 TypeResult TypeContext::isArrayEquivalent(uint32_t oneIndex, uint32_t twoIndex,
343 TypeCache* cache) const {
344 if (cache->isEquivalent(oneIndex, twoIndex)) {
345 return TypeResult::True;
346 }
347
348 const ArrayType& one = arrayType(oneIndex);
349 const ArrayType& two = arrayType(twoIndex);
350
351 // Assume these arrays are equal while checking fields. If the array
352 // element is not equal then we remove the assumption.
353 if (!cache->markEquivalent(oneIndex, twoIndex)) {
354 return TypeResult::OOM;
355 }
356
357 TypeResult result = isArrayElementEquivalent(one, two, cache);
358 if (result != TypeResult::True) {
359 cache->unmarkEquivalent(oneIndex, twoIndex);
360 }
361 return result;
362 }
363
isArrayElementEquivalent(const ArrayType & one,const ArrayType & two,TypeCache * cache) const364 TypeResult TypeContext::isArrayElementEquivalent(const ArrayType& one,
365 const ArrayType& two,
366 TypeCache* cache) const {
367 // Array elements must share the same mutability to be equal
368 if (one.isMutable_ != two.isMutable_) {
369 return TypeResult::False;
370 }
371 // Array elements must be equal
372 return isEquivalent(one.elementType_, two.elementType_, cache);
373 }
374 #endif
375
isRefSubtypeOf(RefType one,RefType two,TypeCache * cache) const376 TypeResult TypeContext::isRefSubtypeOf(RefType one, RefType two,
377 TypeCache* cache) const {
378 // Anything's a subtype of itself.
379 if (one == two) {
380 return TypeResult::True;
381 }
382
383 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
384 if (features_.functionReferences) {
385 // A subtype must have the same nullability as the supertype or the
386 // supertype must be nullable.
387 if (!(one.isNullable() == two.isNullable() || two.isNullable())) {
388 return TypeResult::False;
389 }
390
391 // Non type-index references are subtypes if they have the same kind
392 if (!one.isTypeIndex() && !two.isTypeIndex() && one.kind() == two.kind()) {
393 return TypeResult::True;
394 }
395
396 // Structs are subtypes of eqref
397 if (isStructType(one) && two.isEq()) {
398 return TypeResult::True;
399 }
400
401 // Arrays are subtypes of eqref
402 if (isArrayType(one) && two.isEq()) {
403 return TypeResult::True;
404 }
405
406 // Type-index references can be subtypes
407 if (one.isTypeIndex() && two.isTypeIndex()) {
408 return isTypeIndexSubtypeOf(one.typeIndex(), two.typeIndex(), cache);
409 }
410 }
411 #endif
412 return TypeResult::False;
413 }
414
415 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
isTypeIndexSubtypeOf(uint32_t one,uint32_t two,TypeCache * cache) const416 TypeResult TypeContext::isTypeIndexSubtypeOf(uint32_t one, uint32_t two,
417 TypeCache* cache) const {
418 MOZ_ASSERT(features_.functionReferences);
419
420 // Anything's a subtype of itself.
421 if (one == two) {
422 return TypeResult::True;
423 }
424
425 # ifdef ENABLE_WASM_GC
426 if (features_.gc) {
427 // Structs may be subtypes of structs
428 if (isStructType(one) && isStructType(two)) {
429 return isStructSubtypeOf(one, two, cache);
430 }
431
432 // Arrays may be subtypes of arrays
433 if (isArrayType(one) && isArrayType(two)) {
434 return isArraySubtypeOf(one, two, cache);
435 }
436 }
437 # endif
438 return TypeResult::False;
439 }
440 #endif
441
442 #ifdef ENABLE_WASM_GC
isStructSubtypeOf(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const443 TypeResult TypeContext::isStructSubtypeOf(uint32_t oneIndex, uint32_t twoIndex,
444 TypeCache* cache) const {
445 if (cache->isSubtypeOf(oneIndex, twoIndex)) {
446 return TypeResult::True;
447 }
448
449 const StructType& one = structType(oneIndex);
450 const StructType& two = structType(twoIndex);
451
452 // A subtype must have at least as many fields as its supertype
453 if (one.fields_.length() < two.fields_.length()) {
454 return TypeResult::False;
455 }
456
457 // Assume these structs are subtypes while checking fields. If any field
458 // fails a check then we remove the assumption.
459 if (!cache->markSubtypeOf(oneIndex, twoIndex)) {
460 return TypeResult::OOM;
461 }
462
463 for (uint32_t i = 0; i < two.fields_.length(); i++) {
464 TypeResult result =
465 isStructFieldSubtypeOf(one.fields_[i], two.fields_[i], cache);
466 if (result != TypeResult::True) {
467 cache->unmarkSubtypeOf(oneIndex, twoIndex);
468 return result;
469 }
470 }
471 return TypeResult::True;
472 }
473
isStructFieldSubtypeOf(const StructField one,const StructField two,TypeCache * cache) const474 TypeResult TypeContext::isStructFieldSubtypeOf(const StructField one,
475 const StructField two,
476 TypeCache* cache) const {
477 // Mutable fields are invariant w.r.t. field types
478 if (one.isMutable && two.isMutable) {
479 return isEquivalent(one.type, two.type, cache);
480 }
481 // Immutable fields are covariant w.r.t. field types
482 if (!one.isMutable && !two.isMutable) {
483 return isSubtypeOf(one.type, two.type, cache);
484 }
485 return TypeResult::False;
486 }
487
isArraySubtypeOf(uint32_t oneIndex,uint32_t twoIndex,TypeCache * cache) const488 TypeResult TypeContext::isArraySubtypeOf(uint32_t oneIndex, uint32_t twoIndex,
489 TypeCache* cache) const {
490 if (cache->isSubtypeOf(oneIndex, twoIndex)) {
491 return TypeResult::True;
492 }
493
494 const ArrayType& one = arrayType(oneIndex);
495 const ArrayType& two = arrayType(twoIndex);
496
497 // Assume these arrays are subtypes while checking elements. If the elements
498 // fail the check then we remove the assumption.
499 if (!cache->markSubtypeOf(oneIndex, twoIndex)) {
500 return TypeResult::OOM;
501 }
502
503 TypeResult result = isArrayElementSubtypeOf(one, two, cache);
504 if (result != TypeResult::True) {
505 cache->unmarkSubtypeOf(oneIndex, twoIndex);
506 }
507 return result;
508 }
509
isArrayElementSubtypeOf(const ArrayType & one,const ArrayType & two,TypeCache * cache) const510 TypeResult TypeContext::isArrayElementSubtypeOf(const ArrayType& one,
511 const ArrayType& two,
512 TypeCache* cache) const {
513 // Mutable elements are invariant w.r.t. field types
514 if (one.isMutable_ && two.isMutable_) {
515 return isEquivalent(one.elementType_, two.elementType_, cache);
516 }
517 // Immutable elements are covariant w.r.t. field types
518 if (!one.isMutable_ && !two.isMutable_) {
519 return isSubtypeOf(one.elementType_, two.elementType_, cache);
520 }
521 return TypeResult::False;
522 }
523 #endif
524