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