v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-subtyping.cc
Go to the documentation of this file.
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
9
10namespace v8::internal::wasm {
11
12namespace {
13
14V8_INLINE bool EquivalentIndices(ModuleTypeIndex index1, ModuleTypeIndex index2,
15 const WasmModule* module1,
16 const WasmModule* module2) {
17 DCHECK(index1 != index2 || module1 != module2);
18 return module1->canonical_type_id(index1) ==
19 module2->canonical_type_id(index2);
20}
21
22bool ValidStructSubtypeDefinition(ModuleTypeIndex subtype_index,
23 ModuleTypeIndex supertype_index,
24 const WasmModule* sub_module,
25 const WasmModule* super_module) {
26 const TypeDefinition& sub_def = sub_module->type(subtype_index);
27 const TypeDefinition& super_def = super_module->type(supertype_index);
28 const StructType* sub_struct = sub_def.struct_type;
29 const StructType* super_struct = super_def.struct_type;
30
31 if (sub_struct->field_count() < super_struct->field_count()) {
32 return false;
33 }
34
35 for (uint32_t i = 0; i < super_struct->field_count(); i++) {
36 bool sub_mut = sub_struct->mutability(i);
37 bool super_mut = super_struct->mutability(i);
38 if (sub_mut != super_mut ||
39 (sub_mut &&
40 !EquivalentTypes(sub_struct->field(i), super_struct->field(i),
41 sub_module, super_module)) ||
42 (!sub_mut && !IsSubtypeOf(sub_struct->field(i), super_struct->field(i),
43 sub_module, super_module))) {
44 return false;
45 }
46 }
47 if (sub_def.descriptor.valid()) {
48 // If a type has a descriptor, its supertype must either have no descriptor,
49 // or the supertype's descriptor must be a supertype of the subtype's
50 // descriptor.
51 if (super_def.descriptor.valid() &&
52 !IsHeapSubtypeOf(sub_module->heap_type(sub_def.descriptor),
53 super_module->heap_type(super_def.descriptor),
54 sub_module, super_module)) {
55 return false;
56 }
57 } else {
58 // If a type has no descriptor, its supertype must not have one either.
59 if (super_def.descriptor.valid()) return false;
60 }
61 // A type and its supertype must either both be descriptors, or both not.
62 if (sub_def.describes.valid() != super_def.describes.valid()) {
63 return false;
64 }
65 return true;
66}
67
68bool ValidArraySubtypeDefinition(ModuleTypeIndex subtype_index,
69 ModuleTypeIndex supertype_index,
70 const WasmModule* sub_module,
71 const WasmModule* super_module) {
72 const ArrayType* sub_array = sub_module->type(subtype_index).array_type;
73 const ArrayType* super_array = super_module->type(supertype_index).array_type;
74 bool sub_mut = sub_array->mutability();
75 bool super_mut = super_array->mutability();
76
77 return (sub_mut && super_mut &&
78 EquivalentTypes(sub_array->element_type(),
79 super_array->element_type(), sub_module,
80 super_module)) ||
81 (!sub_mut && !super_mut &&
82 IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
83 sub_module, super_module));
84}
85
86bool ValidFunctionSubtypeDefinition(ModuleTypeIndex subtype_index,
87 ModuleTypeIndex supertype_index,
88 const WasmModule* sub_module,
89 const WasmModule* super_module) {
90 const FunctionSig* sub_func = sub_module->type(subtype_index).function_sig;
91 const FunctionSig* super_func =
92 super_module->type(supertype_index).function_sig;
93
94 if (sub_func->parameter_count() != super_func->parameter_count() ||
95 sub_func->return_count() != super_func->return_count()) {
96 return false;
97 }
98
99 for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
100 // Contravariance for params.
101 if (!IsSubtypeOf(super_func->parameters()[i], sub_func->parameters()[i],
102 super_module, sub_module)) {
103 return false;
104 }
105 }
106 for (uint32_t i = 0; i < sub_func->return_count(); i++) {
107 // Covariance for returns.
108 if (!IsSubtypeOf(sub_func->returns()[i], super_func->returns()[i],
109 sub_module, super_module)) {
110 return false;
111 }
112 }
113
114 return true;
115}
116
117bool ValidContinuationSubtypeDefinition(ModuleTypeIndex subtype_index,
118 ModuleTypeIndex supertype_index,
119 const WasmModule* sub_module,
120 const WasmModule* super_module) {
121 const ContType* sub_cont = sub_module->type(subtype_index).cont_type;
122 const ContType* super_cont = super_module->type(supertype_index).cont_type;
123
124 return IsHeapSubtypeOf(
125 sub_module->heap_type(sub_cont->contfun_typeindex()),
126 super_module->heap_type(super_cont->contfun_typeindex()), sub_module,
127 super_module);
128}
129
130// For some purposes, we can treat all custom structs like the generic
131// "structref", etc.
132StandardType UpcastToStandardType(ValueTypeBase type) {
133 if (!type.has_index()) return type.standard_type();
134 switch (type.ref_type_kind()) {
136 return StandardType::kStruct;
138 return StandardType::kArray;
140 return StandardType::kFunc;
142 return StandardType::kCont;
144 UNREACHABLE();
145 }
146}
147
148// Format: subtype, supertype
149#define FOREACH_SUBTYPING(V) /* force 80 cols */ \
150 /* anyref hierarchy */ \
151 V(Eq, Any) \
152 V(Struct, Eq) \
153 V(None, Struct) \
154 V(Array, Eq) \
155 V(None, Array) \
156 V(I31, Eq) \
157 V(None, I31) \
158 V(String, Any) \
159 V(None, String) \
160 /* funcref hierarchy */ \
161 V(NoFunc, Func) \
162 /* extern hierarchy */ \
163 V(ExternString, Extern) \
164 V(NoExtern, ExternString) \
165 /* exnref hierarchy */ \
166 V(NoExn, Exn) \
167 /* cont hierarchy */ \
168 V(NoCont, Cont)
169
170static constexpr uint32_t kNumStandardTypes =
172
173static constexpr std::array<GenericKind, kNumStandardTypes>
174 kValueTypeLookupMap = base::make_array<kNumStandardTypes>([](size_t i) {
175#define ENTRY(name, ...) \
176 if (static_cast<size_t>(StandardType::k##name) == i) { \
177 return GenericKind::k##name; \
178 }
180#undef ENTRY
181 // Numeric types fall through to here. We won't use these values.
182 return GenericKind::kVoid;
183 });
184constexpr GenericKind ToGenericKind(StandardType type) {
185 DCHECK_LT(static_cast<int>(type), static_cast<int>(StandardType::kI32));
186 return kValueTypeLookupMap[static_cast<size_t>(type)];
187}
188
190// Construct a dense index space for nontrivial subtypes, to save space in
191// the 2D lookup maps we'll build for them.
192
193constexpr int NotInSubtypeRelation(StandardType type) {
194#define CASE(sub, super) \
195 if (type == StandardType::k##sub || type == StandardType::k##super) return 0;
197#undef CASE
198 return 1;
199}
200static constexpr uint32_t kNumNonTrivial =
201#define CASE(name, ...) (NotInSubtypeRelation(StandardType::k##name) ? 0 : 1) +
203// We could include numeric types here, but they don't have nontrivial
204// subtyping relations anyway.
205#undef CASE
206 0;
207enum class CondensedIndices : int {
208// "kFooAdjustNext" is a dummy entry to control whether the next "kBar" will
209// get its own unique value: when NotInSubtypeRelation(kFoo), then we'll end
210// up with kFoo = N, kFooAdJustNext = N-1, kBar = N, and we'll never use
211// kFoo later, so have effectively skipped it from the condensed space.
212#define ENTRY(name, ...) \
213 k##name, \
214 k##name##AdjustNext = \
215 k##name - NotInSubtypeRelation(StandardType::k##name),
217#undef ENTRY
218};
219static constexpr uint8_t kNotRelatedSentinel = 0xFF;
220static_assert(kNumStandardTypes < kNotRelatedSentinel);
221
222constexpr uint8_t ComputeCondensedIndex(StandardType type) {
223 switch (type) {
224#define BAILOUT(name, ...) case StandardType::k##name:
226 return kNotRelatedSentinel;
227#undef BAILOUT
228#define CASE(name, ...) \
229 case StandardType::k##name: \
230 if (NotInSubtypeRelation(type)) return kNotRelatedSentinel; \
231 return static_cast<uint8_t>(CondensedIndices::k##name);
233#undef CASE
234 }
235}
236constexpr StandardType ComputeStandardType(uint8_t condensed_index) {
237#define CASE(name, ...) \
238 if (ComputeCondensedIndex(StandardType::k##name) == condensed_index) \
239 return StandardType::k##name;
241#undef CASE
242 UNREACHABLE();
243}
244
245static constexpr std::array<uint8_t, kNumStandardTypes>
246 kCondensedIndexLookupMap =
248 return ComputeCondensedIndex(static_cast<StandardType>(i));
249 });
250static constexpr std::array<uint8_t, kNumNonTrivial> kCondensedToStandardMap =
252 [](size_t i) { return static_cast<uint8_t>(ComputeStandardType(i)); });
253
254constexpr uint8_t CondensedIndex(StandardType type) {
255 return kCondensedIndexLookupMap[static_cast<size_t>(type)];
256}
257constexpr StandardType CondensedToStandard(uint8_t condensed) {
258 return static_cast<StandardType>(kCondensedToStandardMap[condensed]);
259}
260
262// Statically compute transitive subtype relations.
263
264// Inputs are "condensed".
265constexpr bool ComputeIsSubtype(size_t sub, size_t super) {
266 if (sub == super) return true;
267#define CASE(a, b) \
268 { \
269 size_t raw_a = static_cast<size_t>(CondensedIndices::k##a); \
270 size_t raw_b = static_cast<size_t>(CondensedIndices::k##b); \
271 if (sub == raw_a) { \
272 if (super == raw_b) return true; \
273 if (ComputeIsSubtype(raw_b, super)) return true; \
274 } \
275 }
277#undef CASE
278 return false;
279}
280
281// A 2D map (type, type) -> bool indicating transitive subtype relationships.
282// Keyed by "condensed" indices to save space.
283static constexpr std::array<std::array<bool, kNumNonTrivial>, kNumNonTrivial>
284 kSubtypeLookupMap2 = base::make_array<kNumNonTrivial>([](size_t sub) {
286 [sub](size_t super) { return ComputeIsSubtype(sub, super); });
287 });
288
289// The "public" accessor for looking up subtyping of standard types.
290constexpr bool SubtypeLookup(StandardType sub, StandardType super) {
291 // Note: this implementation strikes a compromise between time and space.
292 // We could put everything into the lookup map, but that would significantly
293 // increase its size (at the time of this writing: by about 4x), with
294 // mostly-sparse additional entries.
295 if (sub == StandardType::kBottom) return true;
296 if (super == StandardType::kTop) return true;
297 uint8_t sub_condensed = CondensedIndex(sub);
298 if (sub_condensed == kNotRelatedSentinel) return sub == super;
299 uint8_t super_condensed = CondensedIndex(super);
300 if (super_condensed == kNotRelatedSentinel) return false;
301 return kSubtypeLookupMap2[sub_condensed][super_condensed];
302}
303
305// Statically compute common ancestors.
306
307// Inputs are "condensed", result is a full StandardType because it might
308// be kTop.
309constexpr StandardType ComputeCommonAncestor(size_t t1, size_t t2) {
310 if (kSubtypeLookupMap2[t1][t2]) return CondensedToStandard(t2);
311 if (kSubtypeLookupMap2[t2][t1]) return CondensedToStandard(t1);
312#define CASE(a, b) \
313 if (t1 == static_cast<size_t>(CondensedIndices::k##a)) { \
314 return ComputeCommonAncestor(static_cast<size_t>(CondensedIndices::k##b), \
315 t2); \
316 }
318#undef CASE
319 return StandardType::kTop;
320}
321
322// A 2D map (type, type) -> type caching the lowest common supertype.
323// Keyed by "condensed" indices to save space.
324static constexpr std::array<std::array<StandardType, kNumNonTrivial>,
325 kNumNonTrivial>
326 kCommonAncestorLookupMap = base::make_array<kNumNonTrivial>([](size_t sub) {
328 [sub](size_t super) { return ComputeCommonAncestor(sub, super); });
329 });
330
331// The "public" accessor for looking up common supertypes of standard types.
332constexpr StandardType CommonAncestorLookup(StandardType t1, StandardType t2) {
333 if (t1 == StandardType::kBottom) return t2;
334 if (t2 == StandardType::kBottom) return t1;
335 if (t1 == StandardType::kTop) return t1;
336 if (t2 == StandardType::kTop) return t2;
337 uint8_t t1_condensed = CondensedIndex(t1);
338 if (t1_condensed == kNotRelatedSentinel) {
339 return t2 == t1 ? t1 : StandardType::kTop;
340 }
341 uint8_t t2_condensed = CondensedIndex(t2);
342 if (t2_condensed == kNotRelatedSentinel) return StandardType::kTop;
343 return kCommonAncestorLookupMap[t1_condensed][t2_condensed];
344}
345
347// Null-related things.
348
349HeapType NullSentinelImpl(HeapType type) {
350 StandardType standard = UpcastToStandardType(type);
351 constexpr StandardType candidates[] = {
352#define NULLTYPE(name, ...) StandardType::k##name,
354#undef NULLTYPE
355 };
356 for (StandardType candidate : candidates) {
357 if (SubtypeLookup(candidate, standard)) {
358 return HeapType::Generic(ToGenericKind(candidate), type.is_shared());
359 }
360 }
361 if (type.is_string_view()) {
362 // TODO(12868): This special case reflects unresolved discussion. If string
363 // views aren't nullable, they shouldn't really show up here at all.
364 return HeapType::Generic(GenericKind::kNone, type.is_shared());
365 }
366 UNREACHABLE();
367}
368
369bool IsNullSentinel(HeapType type) {
370 if (type.has_index()) return false;
371 return IsNullKind(type.generic_kind());
372}
373
374bool IsGenericSubtypeOfIndexedTypes(ValueTypeBase type) {
375 DCHECK(type.is_generic());
376 GenericKind kind = type.generic_kind();
377 return IsNullKind(kind) || kind == GenericKind::kBottom;
378}
379
380} // namespace
381
383 ModuleTypeIndex supertype_index,
384 const WasmModule* sub_module,
385 const WasmModule* super_module) {
386 const TypeDefinition& subtype = sub_module->type(subtype_index);
387 const TypeDefinition& supertype = super_module->type(supertype_index);
388 if (subtype.kind != supertype.kind) return false;
389 if (supertype.is_final) return false;
390 if (subtype.is_shared != supertype.is_shared) return false;
391 switch (subtype.kind) {
393 return ValidFunctionSubtypeDefinition(subtype_index, supertype_index,
394 sub_module, super_module);
396 return ValidStructSubtypeDefinition(subtype_index, supertype_index,
397 sub_module, super_module);
399 return ValidArraySubtypeDefinition(subtype_index, supertype_index,
400 sub_module, super_module);
402 return ValidContinuationSubtypeDefinition(subtype_index, supertype_index,
403 sub_module, super_module);
404 }
405}
406
407namespace {
408// Common parts of the implementation for ValueType and CanonicalValueType.
409std::optional<bool> IsSubtypeOf_Abstract(ValueTypeBase subtype,
410 ValueTypeBase supertype) {
411 DCHECK(!subtype.is_numeric() && !supertype.is_numeric());
412
413 if (subtype.is_shared() != supertype.is_shared()) return false;
414 if (supertype.is_exact()) {
415 if (!subtype.is_exact()) return false;
416 if (subtype.is_bottom()) return true;
417 if (supertype.has_index()) {
418 if (!subtype.has_index()) return false;
419 return {};
420 }
421 // supertype is a none-type. Subtype must be the same.
422 return !subtype.has_index() &&
423 subtype.generic_kind() == supertype.generic_kind();
424 }
425 if (supertype.has_index()) {
426 // If both types are indexed, the specialized implementations need to
427 // take care of it.
428 if (subtype.has_index()) return {};
429 // Subtype is generic. It can only be a subtype if it is a none-type.
430 if (!IsGenericSubtypeOfIndexedTypes(subtype)) return false;
431 }
432 return SubtypeLookup(UpcastToStandardType(subtype),
433 UpcastToStandardType(supertype));
434}
435
436} // namespace
437
439 HeapType subtype, HeapType supertype, const WasmModule* sub_module,
440 const WasmModule* super_module) {
441 DCHECK(subtype != supertype || sub_module != super_module);
442
443 std::optional<bool> result = IsSubtypeOf_Abstract(subtype, supertype);
444 if (result.has_value()) return result.value();
445 DCHECK(subtype.has_index() && supertype.has_index());
446 ModuleTypeIndex sub_index = subtype.ref_index();
447 CanonicalTypeIndex super_canon =
448 super_module->canonical_type_id(supertype.ref_index());
449 // Comparing canonicalized type indices handles both different modules
450 // and different recgroups in the same module.
451 if (supertype.is_exact()) {
452 return sub_module->canonical_type_id(sub_index) == super_canon;
453 }
454 do {
455 if (sub_module->canonical_type_id(sub_index) == super_canon) return true;
456 sub_index = sub_module->supertype(sub_index);
457 } while (sub_index.valid());
458 return false;
459}
460
462 ValueType subtype, ValueType supertype, const WasmModule* sub_module,
463 const WasmModule* super_module) {
464 // Note: it seems tempting to handle <top> and the numeric types
465 // with the lookup-based mechanism we'll ultimately call. But that would
466 // require guarding the checks for nullability and sharedness behind
467 // checks for being reftypes, so it would end up being more complicated,
468 // not less.
469 if (supertype.is_top()) return true;
470 if (subtype.is_numeric()) return subtype == supertype;
471 if (supertype.is_numeric()) return subtype.is_bottom();
472 if (subtype.is_nullable() && !supertype.is_nullable()) return false;
473 HeapType sub_heap = subtype.heap_type();
474 HeapType super_heap = supertype.heap_type();
475 if (sub_heap == super_heap && sub_module == super_module) return true;
476 return IsSubtypeOfImpl(sub_heap, super_heap, sub_module, super_module);
477}
478
480 CanonicalValueType subtype, CanonicalValueType supertype) {
481 DCHECK_NE(subtype, supertype); // Caller has checked.
482 if (supertype.is_top()) return true;
483 if (subtype.is_numeric()) return false;
484 if (supertype.is_numeric()) return subtype.is_bottom();
485 if (subtype.is_nullable() && !supertype.is_nullable()) return false;
486
487 std::optional<bool> result = IsSubtypeOf_Abstract(subtype, supertype);
488 if (result.has_value()) return result.value();
489 DCHECK(subtype.has_index() && supertype.has_index());
490 CanonicalTypeIndex sub_index = subtype.ref_index();
491 CanonicalTypeIndex super_index = supertype.ref_index();
492 // Can happen despite subtype != supertype, e.g. when nullability differs.
493 if (sub_index == super_index) return true;
494 if (supertype.is_exact()) return false;
495 return GetTypeCanonicalizer()->IsHeapSubtype(sub_index, super_index);
496}
497
499 const WasmModule* module1,
500 const WasmModule* module2) {
501 if (type1 == type2 && module1 == module2) return true;
502 if (!type1.has_index() || !type2.has_index()) return type1 == type2;
503 if (type1.nullability() != type2.nullability()) return false;
504 if (type1.is_exact() != type2.is_exact()) return false;
505
506 DCHECK(type1.has_index() && module1->has_type(type1.ref_index()) &&
507 type2.has_index() && module2->has_type(type2.ref_index()));
508
509 return EquivalentIndices(type1.ref_index(), type2.ref_index(), module1,
510 module2);
511}
512
513namespace {
514// Returns the least common ancestor of two type indices, as a type index in
515// {module1}.
516HeapType CommonAncestor(HeapType type1, HeapType type2,
517 const WasmModule* module1, const WasmModule* module2) {
518 DCHECK(type1.has_index() && type2.has_index());
519 bool both_shared = type1.is_shared();
520 if (both_shared != type2.is_shared()) return HeapType{kWasmTop};
521
522 ModuleTypeIndex type_index1 = type1.ref_index();
523 ModuleTypeIndex type_index2 = type2.ref_index();
524 {
525 int depth1 = GetSubtypingDepth(module1, type_index1);
526 int depth2 = GetSubtypingDepth(module2, type_index2);
527 while (depth1 > depth2) {
528 type_index1 = module1->supertype(type_index1);
529 depth1--;
530 }
531 while (depth2 > depth1) {
532 type_index2 = module2->supertype(type_index2);
533 depth2--;
534 }
535 }
536 DCHECK_NE(type_index1, kNoSuperType);
537 DCHECK_NE(type_index2, kNoSuperType);
538 while (type_index1 != kNoSuperType &&
539 !(type_index1 == type_index2 && module1 == module2) &&
540 !EquivalentIndices(type_index1, type_index2, module1, module2)) {
541 type_index1 = module1->supertype(type_index1);
542 type_index2 = module2->supertype(type_index2);
543 }
544 DCHECK_EQ(type_index1 == kNoSuperType, type_index2 == kNoSuperType);
545 RefTypeKind kind1 = type1.ref_type_kind();
546 if (type_index1 != kNoSuperType) {
547 DCHECK_EQ(kind1, type2.ref_type_kind());
548 return HeapType::Index(type_index1, both_shared, kind1);
549 }
550 // No indexed type was found as common ancestor, so we can treat both types
551 // as generic.
552 StandardType generic_ancestor = CommonAncestorLookup(
553 UpcastToStandardType(type1), UpcastToStandardType(type2));
554 return HeapType::Generic(ToGenericKind(generic_ancestor), both_shared);
555}
556
557// Returns the least common ancestor of an abstract heap type {type1}, and
558// another heap type {type2}.
559HeapType CommonAncestorWithAbstract(HeapType heap1, HeapType heap2,
560 const WasmModule* module2) {
561 DCHECK(heap1.is_abstract_ref());
562 bool is_shared = heap1.is_shared();
563 if (is_shared != heap2.is_shared()) return HeapType{kWasmTop};
564
565 // If {heap2} is an indexed type, then {heap1} could be a subtype of it if
566 // it is a none-type. In that case, {heap2} is the common ancestor.
567 std::optional<bool> is_sub = IsSubtypeOf_Abstract(heap1, heap2);
568 DCHECK(is_sub.has_value()); // Guaranteed by {heap1.is_abstract_ref()}.
569 if (is_sub.value()) return heap2;
570
571 // Otherwise, we can treat {heap2} like its generic supertype (e.g. any
572 // indexed struct is "rounded up" to structref).
573 StandardType generic_ancestor = CommonAncestorLookup(
574 UpcastToStandardType(heap1), UpcastToStandardType(heap2));
575 return HeapType::Generic(ToGenericKind(generic_ancestor), is_shared);
576}
577
578Exactness UnionExactness(ValueType type1, ValueType type2,
579 const WasmModule* module1, const WasmModule* module2) {
580 if (!type1.is_exact() || !type2.is_exact()) return Exactness::kAnySubtype;
581 // <top> and <bottom> are ruled out by the caller, non-null abstract
582 // types were {NormalizeUninhabited()} before.
583 if (!type1.has_index()) {
584 DCHECK(IsNullSentinel(type1.heap_type()));
585 return Exactness::kExact;
586 }
587 if (!type2.has_index()) {
588 DCHECK(IsNullSentinel(type2.heap_type()));
589 return Exactness::kExact;
590 }
591 bool same =
592 EquivalentIndices(type1.ref_index(), type2.ref_index(), module1, module2);
594}
595
596} // namespace
597
599 const WasmModule* module1,
600 const WasmModule* module2) {
601 if (type1 == kWasmTop || type2 == kWasmTop) return {kWasmTop, module1};
602 if (type1 == kWasmBottom) return {type2, module2};
603 if (type2 == kWasmBottom) return {type1, module1};
604 if (!type1.is_ref() || !type2.is_ref()) {
605 return {type1 == type2 ? type1 : kWasmTop, module1};
606 }
607 Nullability nullability =
608 type1.is_nullable() || type2.is_nullable() ? kNullable : kNonNullable;
609 Exactness exactness = UnionExactness(type1, type2, module1, module2);
610 HeapType heap1 = type1.heap_type();
611 HeapType heap2 = type2.heap_type();
612 if (heap1 == heap2 && module1 == module2) {
613 return {type1.AsNullable(nullability).AsExact(exactness), module1};
614 }
615 HeapType result_type = kWasmBottom;
616 const WasmModule* result_module;
617 if (heap1.is_abstract_ref()) {
618 result_type = CommonAncestorWithAbstract(heap1, heap2, module2);
619 result_module = module2;
620 } else if (heap2.is_abstract_ref()) {
621 result_type = CommonAncestorWithAbstract(heap2, heap1, module1);
622 result_module = module1;
623 } else {
624 result_type = CommonAncestor(heap1, heap2, module1, module2);
625 result_module = module1;
626 }
627 // The type could only be kBottom if the input was kBottom but any kBottom
628 // HeapType should be "normalized" to kWasmBottom ValueType.
629 DCHECK_NE(result_type, kWasmBottom);
630 if (result_type.is_top()) return {kWasmTop, result_module};
631 return {ValueType::RefMaybeNull(result_type, nullability).AsExact(exactness),
632 result_module};
633}
634
636 const WasmModule* module1,
637 const WasmModule* module2) {
638 if (type1 == kWasmTop) return {type2, module2};
639 if (type2 == kWasmTop) return {type1, module1};
640 if (!type1.is_ref() || !type2.is_ref()) {
641 return {type1 == type2 ? type1 : kWasmBottom, module1};
642 }
643 Nullability nullability =
644 type1.is_nullable() && type2.is_nullable() ? kNullable : kNonNullable;
645 Exactness exactness = type1.is_exact() || type2.is_exact()
648 // non-nullable null type is not a valid type.
649 if (nullability == kNonNullable && (IsNullSentinel(type1.heap_type()) ||
650 IsNullSentinel(type2.heap_type()))) {
651 return {kWasmBottom, module1};
652 }
653 if (IsHeapSubtypeOf(type1.heap_type(), type2.heap_type(), module1, module2)) {
654 return TypeInModule{type1.AsNullable(nullability).AsExact(exactness),
655 module1};
656 }
657 if (IsHeapSubtypeOf(type2.heap_type(), type1.heap_type(), module2, module1)) {
658 return TypeInModule{type2.AsNullable(nullability).AsExact(exactness),
659 module2};
660 }
661 if (nullability == kNonNullable) {
662 return {kWasmBottom, module1};
663 }
664 // Check for common null representation.
665 ValueType null_type1 = ToNullSentinel({type1, module1});
666 if (null_type1 == ToNullSentinel({type2, module2})) {
667 return {null_type1, module1};
668 }
669 return {kWasmBottom, module1};
670}
671
673 HeapType null_heap = NullSentinelImpl(type.type.heap_type());
674 DCHECK(IsHeapSubtypeOf(null_heap, type.type.heap_type(), type.module));
675 return ValueType::RefNull(null_heap);
676}
677
679 const WasmModule* module) {
680 return NullSentinelImpl(type1) == NullSentinelImpl(type2);
681}
682
683} // namespace v8::internal::wasm
Builtins::Kind kind
Definition builtins.cc:40
#define ENTRY(Name,...)
constexpr CanonicalTypeIndex ref_index() const
static constexpr HeapType Generic(GenericKind kind, bool shared)
Definition value-type.h:713
static constexpr HeapType Index(ModuleTypeIndex index, bool shared, RefTypeKind kind, Exactness exact=Exactness::kAnySubtype)
Definition value-type.h:716
constexpr ModuleTypeIndex ref_index() const
Definition value-type.h:762
bool IsHeapSubtype(CanonicalTypeIndex sub, CanonicalTypeIndex super) const
constexpr bool is_ref() const
Definition value-type.h:380
constexpr bool is_bottom() const
Definition value-type.h:426
constexpr bool has_index() const
Definition value-type.h:367
constexpr Nullability nullability() const
Definition value-type.h:389
constexpr bool is_numeric() const
Definition value-type.h:373
constexpr bool is_nullable() const
Definition value-type.h:393
constexpr bool is_exact() const
Definition value-type.h:402
constexpr bool is_abstract_ref() const
Definition value-type.h:376
constexpr bool is_top() const
Definition value-type.h:430
constexpr ValueType AsExact(Exactness exact=Exactness::kExact) const
Definition value-type.h:924
constexpr HeapType heap_type() const
static constexpr ValueType RefNull(ModuleTypeIndex index, bool shared, RefTypeKind kind)
Definition value-type.h:895
constexpr ModuleTypeIndex ref_index() const
static constexpr ValueType RefMaybeNull(ModuleTypeIndex index, Nullability nullable, bool shared, RefTypeKind kind)
Definition value-type.h:903
constexpr ValueType AsNullable(Nullability nullable=kNullable) const
Definition value-type.h:918
ZoneVector< OpIndex > candidates
ZoneVector< RpoNumber > & result
#define BAILOUT(name,...)
constexpr auto make_array(Function f)
static constexpr uint32_t kNumberOfStandardTypes
Definition value-type.h:258
constexpr bool IsNullKind(GenericKind kind)
Definition value-type.h:321
ValueType ToNullSentinel(TypeInModule type)
int GetSubtypingDepth(const WasmModule *module, ModuleTypeIndex type_index)
bool IsSameTypeHierarchy(HeapType type1, HeapType type2, const WasmModule *module)
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(HeapType subtype, HeapType supertype, const WasmModule *sub_module, const WasmModule *super_module)
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2, const WasmModule *module1, const WasmModule *module2)
V8_INLINE bool IsHeapSubtypeOf(HeapType subtype, HeapType supertype, const WasmModule *sub_module, const WasmModule *super_module)
TypeCanonicalizer * GetTypeCanonicalizer()
TypeInModule Intersection(ValueType type1, ValueType type2, const WasmModule *module1, const WasmModule *module2)
V8_EXPORT_PRIVATE TypeInModule Union(ValueType type1, ValueType type2, const WasmModule *module1, const WasmModule *module2)
constexpr ModuleTypeIndex kNoSuperType
constexpr IndependentHeapType kWasmTop
bool ValidSubtypeDefinition(ModuleTypeIndex subtype_index, ModuleTypeIndex supertype_index, const WasmModule *sub_module, const WasmModule *super_module)
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype, const WasmModule *sub_module, const WasmModule *super_module)
constexpr IndependentHeapType kWasmBottom
Signature< ValueType > FunctionSig
wasm::WasmModule WasmModule
JSArrayBuffer::IsDetachableBit is_shared
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
constexpr bool valid() const
Definition value-type.h:58
CanonicalTypeIndex canonical_type_id(ModuleTypeIndex index) const
const TypeDefinition & type(ModuleTypeIndex index) const
ModuleTypeIndex supertype(ModuleTypeIndex index) const
bool has_type(ModuleTypeIndex index) const
#define V8_INLINE
Definition v8config.h:500
#define V8_NOINLINE
Definition v8config.h:586
#define NULLTYPE(name,...)
#define FOREACH_NUMERIC_VALUE_TYPE(V)
Definition value-type.h:31
#define FOREACH_NONE_TYPE(V)
Definition value-type.h:168
wasm::ValueType type
#define FOREACH_SUBTYPING(V)