1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
|
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_WASM_VALUE_TYPE_H_
#define V8_WASM_VALUE_TYPE_H_
#include "src/base/bit-field.h"
#include "src/codegen/machine-type.h"
#include "src/wasm/wasm-constants.h"
namespace v8 {
namespace internal {
template <typename T>
class Signature;
namespace wasm {
// Type for holding simd values, defined in wasm-value.h.
class Simd128;
// Type lattice: Given a fixed struct type S, the following lattice
// defines the subtyping relation among types:
// For every two types connected by a line, the top type is a
// (direct) subtype of the bottom type.
//
// AnyRef
// / \
// / EqRef
// / / \
// FuncRef ExnRef OptRef(S)
// \ | / \
// I32 I64 F32 F64 NullRef Ref(S)
// \ \ \ \ | /
// ---------------------- Bottom ---------
// Format: kind, log2Size, code, machineType, shortName, typeName
//
// Some of these types are from proposals that are not standardized yet:
// - "ref" types per https://github.com/WebAssembly/function-references
// - "optref"/"eqref" per https://github.com/WebAssembly/gc
//
// TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping.
#define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \
V(I32, 2, I32, Int32, 'i', "i32") \
V(I64, 3, I64, Int64, 'l', "i64") \
V(F32, 2, F32, Float32, 'f', "f32") \
V(F64, 3, F64, Float64, 'd', "f64") \
V(S128, 4, S128, Simd128, 's', "s128") \
V(AnyRef, kSystemPointerSizeLog2, AnyRef, TaggedPointer, 'r', "anyref") \
V(FuncRef, kSystemPointerSizeLog2, FuncRef, TaggedPointer, 'a', "funcref") \
V(NullRef, kSystemPointerSizeLog2, NullRef, TaggedPointer, 'n', "nullref") \
V(ExnRef, kSystemPointerSizeLog2, ExnRef, TaggedPointer, 'e', "exn") \
V(Ref, kSystemPointerSizeLog2, Ref, TaggedPointer, '*', "ref") \
V(OptRef, kSystemPointerSizeLog2, OptRef, TaggedPointer, 'o', "optref") \
V(EqRef, kSystemPointerSizeLog2, EqRef, TaggedPointer, 'q', "eqref") \
V(Bottom, -1, Void, None, '*', "<bot>")
class ValueType {
public:
enum Kind : uint8_t {
#define DEF_ENUM(kind, ...) k##kind,
FOREACH_VALUE_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
constexpr bool has_immediate() const {
return kind() == kRef || kind() == kOptRef;
}
constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {}
explicit constexpr ValueType(Kind kind)
: bit_field_(KindField::encode(kind)) {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK(!has_immediate());
#endif
}
constexpr ValueType(Kind kind, uint32_t ref_index)
: bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK(has_immediate());
#endif
}
constexpr Kind kind() const { return KindField::decode(bit_field_); }
constexpr uint32_t ref_index() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK(has_immediate());
#endif
return RefIndexField::decode(bit_field_);
}
constexpr int element_size_log2() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK_NE(kStmt, kind());
DCHECK_NE(kBottom, kind());
#endif
constexpr int kElementSizeLog2[] = {
#define ELEM_SIZE_LOG2(kind, log2Size, ...) log2Size,
FOREACH_VALUE_TYPE(ELEM_SIZE_LOG2)
#undef ELEM_SIZE_LOG2
};
return kElementSizeLog2[kind()];
}
constexpr int element_size_bytes() const { return 1 << element_size_log2(); }
constexpr bool operator==(ValueType other) const {
return bit_field_ == other.bit_field_;
}
constexpr bool operator!=(ValueType other) const {
return bit_field_ != other.bit_field_;
}
// TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping.
constexpr bool IsSubTypeOf(ValueType other) const {
return (*this == other) || (other.kind() == kAnyRef && IsReferenceType()) ||
(kind() == kNullRef && other.kind() != kRef &&
other.IsReferenceType()) ||
(other.kind() == kEqRef &&
(kind() == kExnRef || kind() == kOptRef || kind() == kRef)) ||
(kind() == kRef && other.kind() == kOptRef &&
ref_index() == other.ref_index());
}
constexpr bool IsReferenceType() const {
return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef ||
kind() == kExnRef || kind() == kRef || kind() == kOptRef ||
kind() == kEqRef;
}
// TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping.
static ValueType CommonSubType(ValueType a, ValueType b) {
if (a == b) return a;
// The only sub type of any value type is {bot}.
if (!a.IsReferenceType() || !b.IsReferenceType()) {
return ValueType(kBottom);
}
if (a.IsSubTypeOf(b)) return a;
if (b.IsSubTypeOf(a)) return b;
// {a} and {b} are not each other's subtype.
// If one of them is not nullable, their greatest subtype is bottom,
// otherwise null.
if (a.kind() == kRef || b.kind() == kRef) return ValueType(kBottom);
return ValueType(kNullRef);
}
constexpr ValueTypeCode value_type_code() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK_NE(kBottom, kind());
#endif
constexpr ValueTypeCode kValueTypeCode[] = {
#define TYPE_CODE(kind, log2Size, code, ...) kLocal##code,
FOREACH_VALUE_TYPE(TYPE_CODE)
#undef TYPE_CODE
};
return kValueTypeCode[kind()];
}
constexpr MachineType machine_type() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK_NE(kBottom, kind());
#endif
constexpr MachineType kMachineType[] = {
#define MACH_TYPE(kind, log2Size, code, machineType, ...) \
MachineType::machineType(),
FOREACH_VALUE_TYPE(MACH_TYPE)
#undef MACH_TYPE
};
return kMachineType[kind()];
}
constexpr MachineRepresentation machine_representation() const {
return machine_type().representation();
}
static ValueType For(MachineType type) {
switch (type.representation()) {
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
return ValueType(kI32);
case MachineRepresentation::kWord64:
return ValueType(kI64);
case MachineRepresentation::kFloat32:
return ValueType(kF32);
case MachineRepresentation::kFloat64:
return ValueType(kF64);
case MachineRepresentation::kTaggedPointer:
return ValueType(kAnyRef);
case MachineRepresentation::kSimd128:
return ValueType(kS128);
default:
UNREACHABLE();
}
}
constexpr char short_name() const {
constexpr char kShortName[] = {
#define SHORT_NAME(kind, log2Size, code, machineType, shortName, ...) shortName,
FOREACH_VALUE_TYPE(SHORT_NAME)
#undef SHORT_NAME
};
return kShortName[kind()];
}
constexpr const char* type_name() const {
constexpr const char* kTypeName[] = {
#define TYPE_NAME(kind, log2Size, code, machineType, shortName, typeName, ...) \
typeName,
FOREACH_VALUE_TYPE(TYPE_NAME)
#undef TYPE_NAME
};
return kTypeName[kind()];
}
private:
using KindField = base::BitField<Kind, 0, 8>;
using RefIndexField = base::BitField<uint32_t, 8, 24>;
uint32_t bit_field_;
};
static_assert(sizeof(ValueType) <= kUInt32Size,
"ValueType is small and can be passed by value");
inline size_t hash_value(ValueType type) {
return static_cast<size_t>(type.kind());
}
// Output operator, useful for DCHECKS and others.
inline std::ostream& operator<<(std::ostream& oss, ValueType type) {
return oss << type.type_name();
}
constexpr ValueType kWasmI32 = ValueType(ValueType::kI32);
constexpr ValueType kWasmI64 = ValueType(ValueType::kI64);
constexpr ValueType kWasmF32 = ValueType(ValueType::kF32);
constexpr ValueType kWasmF64 = ValueType(ValueType::kF64);
constexpr ValueType kWasmAnyRef = ValueType(ValueType::kAnyRef);
constexpr ValueType kWasmEqRef = ValueType(ValueType::kEqRef);
constexpr ValueType kWasmExnRef = ValueType(ValueType::kExnRef);
constexpr ValueType kWasmFuncRef = ValueType(ValueType::kFuncRef);
constexpr ValueType kWasmNullRef = ValueType(ValueType::kNullRef);
constexpr ValueType kWasmS128 = ValueType(ValueType::kS128);
constexpr ValueType kWasmStmt = ValueType(ValueType::kStmt);
constexpr ValueType kWasmBottom = ValueType(ValueType::kBottom);
#define FOREACH_WASMVALUE_CTYPES(V) \
V(kI32, int32_t) \
V(kI64, int64_t) \
V(kF32, float) \
V(kF64, double) \
V(kS128, Simd128)
using FunctionSig = Signature<ValueType>;
#define FOREACH_LOAD_TYPE(V) \
V(I32, , Int32) \
V(I32, 8S, Int8) \
V(I32, 8U, Uint8) \
V(I32, 16S, Int16) \
V(I32, 16U, Uint16) \
V(I64, , Int64) \
V(I64, 8S, Int8) \
V(I64, 8U, Uint8) \
V(I64, 16S, Int16) \
V(I64, 16U, Uint16) \
V(I64, 32S, Int32) \
V(I64, 32U, Uint32) \
V(F32, , Float32) \
V(F64, , Float64) \
V(S128, , Simd128)
class LoadType {
public:
enum LoadTypeValue : uint8_t {
#define DEF_ENUM(type, suffix, ...) k##type##Load##suffix,
FOREACH_LOAD_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
// Allow implicit conversion of the enum value to this wrapper.
constexpr LoadType(LoadTypeValue val) // NOLINT(runtime/explicit)
: val_(val) {}
constexpr LoadTypeValue value() const { return val_; }
constexpr unsigned size_log_2() const { return kLoadSizeLog2[val_]; }
constexpr unsigned size() const { return 1 << size_log_2(); }
constexpr ValueType value_type() const { return kValueType[val_]; }
constexpr MachineType mem_type() const { return kMemType[val_]; }
static LoadType ForValueType(ValueType type) {
switch (type.kind()) {
case ValueType::kI32:
return kI32Load;
case ValueType::kI64:
return kI64Load;
case ValueType::kF32:
return kF32Load;
case ValueType::kF64:
return kF64Load;
case ValueType::kS128:
return kS128Load;
default:
UNREACHABLE();
}
}
private:
const LoadTypeValue val_;
static constexpr uint8_t kLoadSizeLog2[] = {
// MSVC wants a static_cast here.
#define LOAD_SIZE(_, __, memtype) \
static_cast<uint8_t>( \
ElementSizeLog2Of(MachineType::memtype().representation())),
FOREACH_LOAD_TYPE(LOAD_SIZE)
#undef LOAD_SIZE
};
static constexpr ValueType kValueType[] = {
#define VALUE_TYPE(type, ...) ValueType(ValueType::k##type),
FOREACH_LOAD_TYPE(VALUE_TYPE)
#undef VALUE_TYPE
};
static constexpr MachineType kMemType[] = {
#define MEMTYPE(_, __, memtype) MachineType::memtype(),
FOREACH_LOAD_TYPE(MEMTYPE)
#undef MEMTYPE
};
};
#define FOREACH_STORE_TYPE(V) \
V(I32, , Word32) \
V(I32, 8, Word8) \
V(I32, 16, Word16) \
V(I64, , Word64) \
V(I64, 8, Word8) \
V(I64, 16, Word16) \
V(I64, 32, Word32) \
V(F32, , Float32) \
V(F64, , Float64) \
V(S128, , Simd128)
class StoreType {
public:
enum StoreTypeValue : uint8_t {
#define DEF_ENUM(type, suffix, ...) k##type##Store##suffix,
FOREACH_STORE_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
// Allow implicit convertion of the enum value to this wrapper.
constexpr StoreType(StoreTypeValue val) // NOLINT(runtime/explicit)
: val_(val) {}
constexpr StoreTypeValue value() const { return val_; }
constexpr unsigned size_log_2() const { return kStoreSizeLog2[val_]; }
constexpr unsigned size() const { return 1 << size_log_2(); }
constexpr ValueType value_type() const { return kValueType[val_]; }
constexpr MachineRepresentation mem_rep() const { return kMemRep[val_]; }
static StoreType ForValueType(ValueType type) {
switch (type.kind()) {
case ValueType::kI32:
return kI32Store;
case ValueType::kI64:
return kI64Store;
case ValueType::kF32:
return kF32Store;
case ValueType::kF64:
return kF64Store;
case ValueType::kS128:
return kS128Store;
default:
UNREACHABLE();
}
}
private:
const StoreTypeValue val_;
static constexpr uint8_t kStoreSizeLog2[] = {
// MSVC wants a static_cast here.
#define STORE_SIZE(_, __, memrep) \
static_cast<uint8_t>(ElementSizeLog2Of(MachineRepresentation::k##memrep)),
FOREACH_STORE_TYPE(STORE_SIZE)
#undef STORE_SIZE
};
static constexpr ValueType kValueType[] = {
#define VALUE_TYPE(type, ...) ValueType(ValueType::k##type),
FOREACH_STORE_TYPE(VALUE_TYPE)
#undef VALUE_TYPE
};
static constexpr MachineRepresentation kMemRep[] = {
#define MEMREP(_, __, memrep) MachineRepresentation::k##memrep,
FOREACH_STORE_TYPE(MEMREP)
#undef MEMREP
};
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_VALUE_TYPE_H_
|