• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 #include "src/wasm/wasm-subtyping.h"
6 
7 #include "src/base/platform/mutex.h"
8 #include "src/wasm/wasm-module.h"
9 #include "src/zone/zone-containers.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14 
15 namespace {
16 
17 using CacheKey =
18     std::tuple<uint32_t, uint32_t, const WasmModule*, const WasmModule*>;
19 
20 struct CacheKeyHasher {
operator ()v8::internal::wasm::__anon9d5c4ab50111::CacheKeyHasher21   size_t operator()(CacheKey key) const {
22     static constexpr size_t large_prime = 14887;
23     return std::get<0>(key) + (std::get<1>(key) * large_prime) +
24            (reinterpret_cast<size_t>(std::get<2>(key)) * large_prime *
25             large_prime) +
26            (reinterpret_cast<size_t>(std::get<3>(key)) * large_prime *
27             large_prime * large_prime);
28   }
29 };
30 
31 class TypeJudgementCache {
32  public:
TypeJudgementCache()33   TypeJudgementCache()
34       : zone_(new AccountingAllocator(), "type judgement zone"),
35         subtyping_cache_(&zone_),
36         type_equivalence_cache_(&zone_) {}
37 
instance()38   static TypeJudgementCache* instance() {
39     static base::LazyInstance<TypeJudgementCache>::type instance_ =
40         LAZY_INSTANCE_INITIALIZER;
41     return instance_.Pointer();
42   }
43 
type_cache_mutex()44   base::RecursiveMutex* type_cache_mutex() { return &type_cache_mutex_; }
is_cached_subtype(uint32_t subtype,uint32_t supertype,const WasmModule * sub_module,const WasmModule * super_module) const45   bool is_cached_subtype(uint32_t subtype, uint32_t supertype,
46                          const WasmModule* sub_module,
47                          const WasmModule* super_module) const {
48     return subtyping_cache_.count(std::make_tuple(
49                subtype, supertype, sub_module, super_module)) == 1;
50   }
cache_subtype(uint32_t subtype,uint32_t supertype,const WasmModule * sub_module,const WasmModule * super_module)51   void cache_subtype(uint32_t subtype, uint32_t supertype,
52                      const WasmModule* sub_module,
53                      const WasmModule* super_module) {
54     subtyping_cache_.emplace(subtype, supertype, sub_module, super_module);
55   }
uncache_subtype(uint32_t subtype,uint32_t supertype,const WasmModule * sub_module,const WasmModule * super_module)56   void uncache_subtype(uint32_t subtype, uint32_t supertype,
57                        const WasmModule* sub_module,
58                        const WasmModule* super_module) {
59     subtyping_cache_.erase(
60         std::make_tuple(subtype, supertype, sub_module, super_module));
61   }
is_cached_equivalent_type(uint32_t type1,uint32_t type2,const WasmModule * module1,const WasmModule * module2) const62   bool is_cached_equivalent_type(uint32_t type1, uint32_t type2,
63                                  const WasmModule* module1,
64                                  const WasmModule* module2) const {
65     if (type1 > type2) std::swap(type1, type2);
66     if (reinterpret_cast<uintptr_t>(module1) >
67         reinterpret_cast<uintptr_t>(module2)) {
68       std::swap(module1, module2);
69     }
70     return type_equivalence_cache_.count(
71                std::make_tuple(type1, type2, module1, module2)) == 1;
72   }
cache_type_equivalence(uint32_t type1,uint32_t type2,const WasmModule * module1,const WasmModule * module2)73   void cache_type_equivalence(uint32_t type1, uint32_t type2,
74                               const WasmModule* module1,
75                               const WasmModule* module2) {
76     if (type1 > type2) std::swap(type1, type2);
77     if (reinterpret_cast<uintptr_t>(module1) >
78         reinterpret_cast<uintptr_t>(module2)) {
79       std::swap(module1, module2);
80     }
81     type_equivalence_cache_.emplace(type1, type2, module1, module2);
82   }
uncache_type_equivalence(uint32_t type1,uint32_t type2,const WasmModule * module1,const WasmModule * module2)83   void uncache_type_equivalence(uint32_t type1, uint32_t type2,
84                                 const WasmModule* module1,
85                                 const WasmModule* module2) {
86     if (type1 > type2) std::swap(type1, type2);
87     if (reinterpret_cast<uintptr_t>(module1) >
88         reinterpret_cast<uintptr_t>(module2)) {
89       std::swap(module1, module2);
90     }
91     type_equivalence_cache_.erase(
92         std::make_tuple(type1, type2, module1, module2));
93   }
94 
95  private:
96   Zone zone_;
97   ZoneUnorderedSet<CacheKey, CacheKeyHasher>
98       // Cache for discovered subtyping pairs.
99       subtyping_cache_,
100       // Cache for discovered equivalent type pairs.
101       // Indexes and modules are stored in increasing order.
102       type_equivalence_cache_;
103   // The above two caches are used from background compile jobs, so they
104   // must be protected from concurrent modifications:
105   base::RecursiveMutex type_cache_mutex_;
106 };
107 
ArrayEquivalentIndices(uint32_t type_index_1,uint32_t type_index_2,const WasmModule * module1,const WasmModule * module2)108 bool ArrayEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
109                             const WasmModule* module1,
110                             const WasmModule* module2) {
111   const ArrayType* sub_array = module1->types[type_index_1].array_type;
112   const ArrayType* super_array = module2->types[type_index_2].array_type;
113   if (sub_array->mutability() != super_array->mutability()) return false;
114 
115   // Temporarily cache type equivalence for the recursive call.
116   TypeJudgementCache::instance()->cache_type_equivalence(
117       type_index_1, type_index_2, module1, module2);
118   if (EquivalentTypes(sub_array->element_type(), super_array->element_type(),
119                       module1, module2)) {
120     return true;
121   } else {
122     TypeJudgementCache::instance()->uncache_type_equivalence(
123         type_index_1, type_index_2, module1, module2);
124     // TODO(7748): Consider caching negative results as well.
125     return false;
126   }
127 }
128 
StructEquivalentIndices(uint32_t type_index_1,uint32_t type_index_2,const WasmModule * module1,const WasmModule * module2)129 bool StructEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
130                              const WasmModule* module1,
131                              const WasmModule* module2) {
132   const StructType* sub_struct = module1->types[type_index_1].struct_type;
133   const StructType* super_struct = module2->types[type_index_2].struct_type;
134 
135   if (sub_struct->field_count() != super_struct->field_count()) {
136     return false;
137   }
138 
139   // Temporarily cache type equivalence for the recursive call.
140   TypeJudgementCache::instance()->cache_type_equivalence(
141       type_index_1, type_index_2, module1, module2);
142   for (uint32_t i = 0; i < sub_struct->field_count(); i++) {
143     if (sub_struct->mutability(i) != super_struct->mutability(i) ||
144         !EquivalentTypes(sub_struct->field(i), super_struct->field(i), module1,
145                          module2)) {
146       TypeJudgementCache::instance()->uncache_type_equivalence(
147           type_index_1, type_index_2, module1, module2);
148       return false;
149     }
150   }
151   return true;
152 }
153 
FunctionEquivalentIndices(uint32_t type_index_1,uint32_t type_index_2,const WasmModule * module1,const WasmModule * module2)154 bool FunctionEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
155                                const WasmModule* module1,
156                                const WasmModule* module2) {
157   const FunctionSig* sig1 = module1->types[type_index_1].function_sig;
158   const FunctionSig* sig2 = module2->types[type_index_2].function_sig;
159 
160   if (sig1->parameter_count() != sig2->parameter_count() ||
161       sig1->return_count() != sig2->return_count()) {
162     return false;
163   }
164 
165   auto iter1 = sig1->all();
166   auto iter2 = sig2->all();
167 
168   // Temporarily cache type equivalence for the recursive call.
169   TypeJudgementCache::instance()->cache_type_equivalence(
170       type_index_1, type_index_2, module1, module2);
171   for (int i = 0; i < iter1.size(); i++) {
172     if (!EquivalentTypes(iter1[i], iter2[i], module1, module2)) {
173       TypeJudgementCache::instance()->uncache_type_equivalence(
174           type_index_1, type_index_2, module1, module2);
175       return false;
176     }
177   }
178   return true;
179 }
180 
EquivalentIndices(uint32_t index1,uint32_t index2,const WasmModule * module1,const WasmModule * module2)181 V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
182                                  const WasmModule* module1,
183                                  const WasmModule* module2) {
184   DCHECK(index1 != index2 || module1 != module2);
185   uint8_t kind1 = module1->type_kinds[index1];
186 
187   if (kind1 != module2->type_kinds[index2]) return false;
188 
189   base::RecursiveMutexGuard type_cache_access(
190       TypeJudgementCache::instance()->type_cache_mutex());
191   if (TypeJudgementCache::instance()->is_cached_equivalent_type(
192           index1, index2, module1, module2)) {
193     return true;
194   }
195 
196   if (kind1 == kWasmStructTypeCode) {
197     return StructEquivalentIndices(index1, index2, module1, module2);
198   } else if (kind1 == kWasmArrayTypeCode) {
199     return ArrayEquivalentIndices(index1, index2, module1, module2);
200   } else {
201     DCHECK_EQ(kind1, kWasmFunctionTypeCode);
202     return FunctionEquivalentIndices(index1, index2, module1, module2);
203   }
204 }
205 
StructIsSubtypeOf(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)206 bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
207                        const WasmModule* sub_module,
208                        const WasmModule* super_module) {
209   const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
210   const StructType* super_struct =
211       super_module->types[supertype_index].struct_type;
212 
213   if (sub_struct->field_count() < super_struct->field_count()) {
214     return false;
215   }
216 
217   TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
218                                                 sub_module, super_module);
219   for (uint32_t i = 0; i < super_struct->field_count(); i++) {
220     bool sub_mut = sub_struct->mutability(i);
221     bool super_mut = super_struct->mutability(i);
222     if (sub_mut != super_mut ||
223         (sub_mut &&
224          !EquivalentTypes(sub_struct->field(i), super_struct->field(i),
225                           sub_module, super_module)) ||
226         (!sub_mut && !IsSubtypeOf(sub_struct->field(i), super_struct->field(i),
227                                   sub_module, super_module))) {
228       TypeJudgementCache::instance()->uncache_subtype(
229           subtype_index, supertype_index, sub_module, super_module);
230       return false;
231     }
232   }
233   return true;
234 }
235 
ArrayIsSubtypeOf(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)236 bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
237                       const WasmModule* sub_module,
238                       const WasmModule* super_module) {
239   const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
240   const ArrayType* super_array =
241       super_module->types[supertype_index].array_type;
242   bool sub_mut = sub_array->mutability();
243   bool super_mut = super_array->mutability();
244   TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
245                                                 sub_module, super_module);
246   if (sub_mut != super_mut ||
247       (sub_mut &&
248        !EquivalentTypes(sub_array->element_type(), super_array->element_type(),
249                         sub_module, super_module)) ||
250       (!sub_mut &&
251        !IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
252                     sub_module, super_module))) {
253     TypeJudgementCache::instance()->uncache_subtype(
254         subtype_index, supertype_index, sub_module, super_module);
255     return false;
256   } else {
257     return true;
258   }
259 }
260 
261 // TODO(7748): Expand this with function subtyping once the hiccups
262 // with 'exact types' have been cleared.
FunctionIsSubtypeOf(uint32_t subtype_index,uint32_t supertype_index,const WasmModule * sub_module,const WasmModule * super_module)263 bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
264                          const WasmModule* sub_module,
265                          const WasmModule* super_module) {
266   return FunctionEquivalentIndices(subtype_index, supertype_index, sub_module,
267                                    super_module);
268 }
269 
270 }  // namespace
271 
272 // TODO(7748): Extend this with any-heap subtyping.
IsSubtypeOfImpl(ValueType subtype,ValueType supertype,const WasmModule * sub_module,const WasmModule * super_module)273 V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
274     ValueType subtype, ValueType supertype, const WasmModule* sub_module,
275     const WasmModule* super_module) {
276   DCHECK(subtype != supertype || sub_module != super_module);
277 
278   // This function checks for subtyping based on the kind of subtype.
279 
280   if (!subtype.is_reference_type()) return subtype == supertype;
281 
282   if (subtype.kind() == ValueType::kRtt) {
283     return subtype.heap_type().is_generic()
284                ? subtype == supertype
285                : (supertype.kind() == ValueType::kRtt &&
286                   subtype.depth() == supertype.depth() &&
287                   EquivalentIndices(subtype.ref_index(), supertype.ref_index(),
288                                     sub_module, super_module));
289   }
290 
291   DCHECK(subtype.is_object_reference_type());
292 
293   bool compatible_references = subtype.kind() == ValueType::kOptRef
294                                    ? supertype.kind() == ValueType::kOptRef
295                                    : supertype.is_object_reference_type();
296   if (!compatible_references) return false;
297 
298   DCHECK(supertype.is_object_reference_type());
299 
300   // Now check that sub_heap and super_heap are subtype-related.
301 
302   HeapType sub_heap = subtype.heap_type();
303   HeapType super_heap = supertype.heap_type();
304 
305   if (sub_heap.representation() == HeapType::kI31 &&
306       super_heap.representation() == HeapType::kEq) {
307     return true;
308   }
309   if (sub_heap.is_generic()) return sub_heap == super_heap;
310 
311   DCHECK(sub_heap.is_index());
312   uint32_t sub_index = sub_heap.ref_index();
313   DCHECK(sub_module->has_type(sub_index));
314 
315   if (super_heap.representation() == HeapType::kEq) {
316     return !sub_module->has_signature(sub_heap.ref_index());
317   }
318   if (super_heap.representation() == HeapType::kFunc) {
319     return sub_module->has_signature(sub_heap.ref_index());
320   }
321   if (super_heap.is_generic()) return false;
322 
323   DCHECK(super_heap.is_index());
324   uint32_t super_index = super_heap.ref_index();
325   DCHECK(super_module->has_type(super_index));
326 
327   uint8_t sub_kind = sub_module->type_kinds[sub_index];
328 
329   if (sub_kind != super_module->type_kinds[super_index]) return false;
330 
331   // Accessing the caches for subtyping and equivalence from multiple background
332   // threads is protected by a lock.
333   base::RecursiveMutexGuard type_cache_access(
334       TypeJudgementCache::instance()->type_cache_mutex());
335   if (TypeJudgementCache::instance()->is_cached_subtype(
336           sub_index, super_index, sub_module, super_module)) {
337     return true;
338   }
339 
340   if (sub_kind == kWasmStructTypeCode) {
341     return StructIsSubtypeOf(sub_index, super_index, sub_module, super_module);
342   } else if (sub_kind == kWasmArrayTypeCode) {
343     return ArrayIsSubtypeOf(sub_index, super_index, sub_module, super_module);
344   } else {
345     DCHECK_EQ(sub_kind, kWasmFunctionTypeCode);
346     return FunctionIsSubtypeOf(sub_index, super_index, sub_module,
347                                super_module);
348   }
349 }
350 
EquivalentTypes(ValueType type1,ValueType type2,const WasmModule * module1,const WasmModule * module2)351 V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
352                                  const WasmModule* module1,
353                                  const WasmModule* module2) {
354   if (type1 == type2 && module1 == module2) return true;
355   if (!type1.has_index()) return type1 == type2;
356   if (type1.kind() != type2.kind()) return false;
357 
358   DCHECK(type1.has_index() && type2.has_index() &&
359          (type1 != type2 || module1 != module2));
360 
361   DCHECK_IMPLIES(type1.has_depth(), type2.has_depth());  // Due to 'if' above
362   if (type1.has_depth() && type1.depth() != type2.depth()) return false;
363 
364   DCHECK(type1.has_index() && module1->has_type(type1.ref_index()) &&
365          type2.has_index() && module2->has_type(type2.ref_index()));
366 
367   return EquivalentIndices(type1.ref_index(), type2.ref_index(), module1,
368                            module2);
369 }
370 
CommonSubtype(ValueType a,ValueType b,const WasmModule * module)371 ValueType CommonSubtype(ValueType a, ValueType b, const WasmModule* module) {
372   if (a == b) return a;
373   if (IsSubtypeOf(a, b, module)) return a;
374   if (IsSubtypeOf(b, a, module)) return b;
375   return kWasmBottom;
376 }
377 
378 }  // namespace wasm
379 }  // namespace internal
380 }  // namespace v8
381