value-type.h 18.6 KB
Newer Older
1 2 3 4 5 6 7
// 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_

8
#include "src/base/bit-field.h"
9
#include "src/codegen/machine-type.h"
10 11 12 13
#include "src/wasm/wasm-constants.h"

namespace v8 {
namespace internal {
14 15 16 17

template <typename T>
class Signature;

18 19
namespace wasm {

20 21 22
// Type for holding simd values, defined in wasm-value.h.
class Simd128;

23
// Format: kind, log2Size, code, machineType, shortName, typeName
24 25
//
// Some of these types are from proposals that are not standardized yet:
26 27 28
// - "ref"/"optref" (a.k.a. "ref null") per
//   https://github.com/WebAssembly/function-references
// - "rtt" per https://github.com/WebAssembly/gc
29 30 31 32 33 34 35 36 37
#define FOREACH_NUMERIC_VALUE_TYPE(V)    \
  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(I8, 0, I8, Int8, 'b', "i8")          \
  V(I16, 1, I16, Int16, 'h', "i16")

38 39
#define FOREACH_VALUE_TYPE(V)                                               \
  V(Stmt, -1, Void, None, 'v', "<stmt>")                                    \
40
  FOREACH_NUMERIC_VALUE_TYPE(V)                                             \
41 42 43
  V(Rtt, kSystemPointerSizeLog2, Rtt, TaggedPointer, 't', "rtt")            \
  V(Ref, kSystemPointerSizeLog2, Ref, TaggedPointer, 'r', "ref")            \
  V(OptRef, kSystemPointerSizeLog2, OptRef, TaggedPointer, 'n', "ref null") \
44 45
  V(Bottom, -1, Void, None, '*', "<bot>")

46 47
class HeapType {
 public:
48 49 50 51 52 53
  enum Representation : uint32_t {
    kFunc = kV8MaxWasmTypes,  // shorthand: c
    kExtern,                  // shorthand: e
    kEq,                      // shorthand: q
    kExn,                     // shorthand: x
    kI31,                     // shorthand: j
54
    // This value is used to represent failures in the parsing of heap types and
55 56 57
    // does not correspond to a wasm heap type.
    kBottom
  };
58 59
  // Internal use only; defined in the public section to make it easy to
  // check that they are defined correctly:
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
  static constexpr Representation kFirstSentinel = kFunc;
  static constexpr Representation kLastSentinel = kBottom;

  static constexpr HeapType from_code(uint8_t code) {
    switch (code) {
      case ValueTypeCode::kLocalFuncRef:
        return HeapType(kFunc);
      case ValueTypeCode::kLocalExternRef:
        return HeapType(kExtern);
      case ValueTypeCode::kLocalEqRef:
        return HeapType(kEq);
      case ValueTypeCode::kLocalExnRef:
        return HeapType(kExn);
      case ValueTypeCode::kLocalI31Ref:
        return HeapType(kI31);
      default:
        return HeapType(kBottom);
    }
  }
79

80
  explicit constexpr HeapType(Representation repr) : representation_(repr) {
81
    CONSTEXPR_DCHECK(is_bottom() || is_valid());
82
  }
83 84
  explicit constexpr HeapType(uint32_t repr)
      : HeapType(static_cast<Representation>(repr)) {}
85 86

  constexpr bool operator==(HeapType other) const {
87
    return representation_ == other.representation_;
88 89
  }
  constexpr bool operator!=(HeapType other) const {
90
    return representation_ != other.representation_;
91 92
  }

93
  constexpr Representation representation() const { return representation_; }
94
  constexpr uint32_t ref_index() const {
95
    CONSTEXPR_DCHECK(is_index());
96 97 98 99
    return representation_;
  }

  constexpr bool is_generic() const {
100
    return !is_bottom() && representation_ >= kFirstSentinel;
101 102
  }

103
  constexpr bool is_index() const { return representation_ < kFirstSentinel; }
104

105
  constexpr bool is_bottom() const { return representation_ == kBottom; }
106

107 108
  std::string name() const {
    switch (representation_) {
109 110 111 112 113 114 115 116
      case kFunc:
        return std::string("func");
      case kExtern:
        return std::string("extern");
      case kEq:
        return std::string("eq");
      case kExn:
        return std::string("exn");
117 118
      case kI31:
        return std::string("i31");
119
      default:
120
        return std::to_string(representation_);
121 122 123
    }
  }

124 125 126 127 128
  constexpr int32_t code() const {
    // kLocal* codes represent the first byte of the LEB128 encoding. To get the
    // int32 represented by a code, we need to sign-extend it from 7 to 32 bits.
    int32_t mask = 0xFFFFFF80;
    switch (representation_) {
129
      case kFunc:
130
        return mask | kLocalFuncRef;
131
      case kExn:
132
        return mask | kLocalExnRef;
133
      case kExtern:
134
        return mask | kLocalExternRef;
135
      case kEq:
136
        return mask | kLocalEqRef;
137
      case kI31:
138
        return mask | kLocalI31Ref;
139
      default:
140
        return static_cast<int32_t>(representation_);
141 142 143 144 145
    }
  }

 private:
  friend class ValueType;
146
  Representation representation_;
147 148 149
  constexpr bool is_valid() const {
    return !is_bottom() && representation_ <= kLastSentinel;
  }
150 151 152
};
enum Nullability : bool { kNonNullable, kNullable };

153 154 155 156 157
class ValueType {
 public:
  enum Kind : uint8_t {
#define DEF_ENUM(kind, ...) k##kind,
    FOREACH_VALUE_TYPE(DEF_ENUM)
158
#undef DEF_ENUM
159 160
  };

161 162 163 164 165 166 167 168
  constexpr bool is_reference_type() const {
    return kind() == kRef || kind() == kOptRef || kind() == kRtt;
  }

  constexpr bool is_packed() const { return kind() == kI8 || kind() == kI16; }

  constexpr bool is_nullable() const { return kind() == kOptRef; }

169
  constexpr bool is_reference_to(uint32_t htype) const {
170 171
    return (kind() == kRef || kind() == kOptRef) &&
           heap_representation() == htype;
172 173
  }

174 175 176 177 178
  constexpr bool is_defaultable() const {
    CONSTEXPR_DCHECK(kind() != kBottom && kind() != kStmt);
    return kind() != kRef && kind() != kRtt;
  }

179 180 181 182 183
  constexpr ValueType Unpacked() const {
    return is_packed() ? Primitive(kI32) : *this;
  }

  constexpr bool has_index() const {
184
    return is_reference_type() && heap_type().is_index();
185
  }
186 187
  constexpr bool is_rtt() const { return kind() == kRtt; }
  constexpr bool has_depth() const { return is_rtt(); }
188

189
  constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {}
190 191 192
  static constexpr ValueType Primitive(Kind kind) {
    CONSTEXPR_DCHECK(kind == kBottom || kind <= kI16);
    return ValueType(KindField::encode(kind));
193
  }
194
  static constexpr ValueType Ref(uint32_t heap_type, Nullability nullability) {
195 196
    CONSTEXPR_DCHECK(heap_type != HeapType::kBottom &&
                     HeapType(heap_type).is_valid());
197 198 199 200
    return ValueType(
        KindField::encode(nullability == kNullable ? kOptRef : kRef) |
        HeapTypeField::encode(heap_type));
  }
201
  static constexpr ValueType Ref(HeapType heap_type, Nullability nullability) {
202
    return Ref(heap_type.representation(), nullability);
203
  }
204

205
  static constexpr ValueType Rtt(uint32_t heap_type,
206
                                 uint8_t inheritance_depth) {
207 208
    CONSTEXPR_DCHECK(heap_type != HeapType::kBottom &&
                     HeapType(heap_type).is_valid());
209 210 211 212
    return ValueType(KindField::encode(kRtt) |
                     HeapTypeField::encode(heap_type) |
                     DepthField::encode(inheritance_depth));
  }
213 214
  static constexpr ValueType Rtt(HeapType heap_type,
                                 uint8_t inheritance_depth) {
215
    return Rtt(heap_type.representation(), inheritance_depth);
216
  }
217 218 219

  static constexpr ValueType FromRawBitField(uint32_t bit_field) {
    return ValueType(bit_field);
220
  }
221

222
  constexpr Kind kind() const { return KindField::decode(bit_field_); }
223
  constexpr HeapType::Representation heap_representation() const {
224
    CONSTEXPR_DCHECK(is_reference_type());
225 226
    return static_cast<HeapType::Representation>(
        HeapTypeField::decode(bit_field_));
227
  }
228 229 230
  constexpr HeapType heap_type() const {
    return HeapType(heap_representation());
  }
231 232 233 234
  constexpr uint8_t depth() const {
    CONSTEXPR_DCHECK(has_depth());
    return DepthField::decode(bit_field_);
  }
235
  constexpr uint32_t ref_index() const {
236
    CONSTEXPR_DCHECK(has_index());
237
    return heap_type().ref_index();
238
  }
239

240 241
  constexpr uint32_t raw_bit_field() const { return bit_field_; }

242
  constexpr int element_size_log2() const {
243
    constexpr int8_t kElementSizeLog2[] = {
244 245 246 247 248
#define ELEM_SIZE_LOG2(kind, log2Size, ...) log2Size,
        FOREACH_VALUE_TYPE(ELEM_SIZE_LOG2)
#undef ELEM_SIZE_LOG2
    };

249 250 251
    int size_log_2 = kElementSizeLog2[kind()];
    CONSTEXPR_DCHECK(size_log_2 >= 0);
    return size_log_2;
252 253
  }

254 255 256 257 258 259 260 261 262 263 264 265
  constexpr int element_size_bytes() const {
    constexpr int8_t kElementSize[] = {
#define ELEM_SIZE_LOG2(kind, log2Size, ...) \
  log2Size == -1 ? -1 : (1 << std::max(0, log2Size)),
        FOREACH_VALUE_TYPE(ELEM_SIZE_LOG2)
#undef ELEM_SIZE_LOG2
    };

    int size = kElementSize[kind()];
    CONSTEXPR_DCHECK(size > 0);
    return size;
  }
266 267

  constexpr bool operator==(ValueType other) const {
268
    return bit_field_ == other.bit_field_;
269 270
  }
  constexpr bool operator!=(ValueType other) const {
271
    return bit_field_ != other.bit_field_;
272 273
  }

274
  constexpr MachineType machine_type() const {
275
    CONSTEXPR_DCHECK(kBottom != kind());
276 277 278 279 280 281 282 283

    constexpr MachineType kMachineType[] = {
#define MACH_TYPE(kind, log2Size, code, machineType, ...) \
  MachineType::machineType(),
        FOREACH_VALUE_TYPE(MACH_TYPE)
#undef MACH_TYPE
    };

284
    return kMachineType[kind()];
285 286
  }

287
  constexpr MachineRepresentation machine_representation() const {
288 289 290
    return machine_type().representation();
  }

291 292 293 294
  constexpr ValueTypeCode value_type_code() const {
    CONSTEXPR_DCHECK(kind() != kBottom);
    switch (kind()) {
      case kOptRef:
295
        switch (heap_representation()) {
296
          case HeapType::kFunc:
297
            return kLocalFuncRef;
298
          case HeapType::kExtern:
299
            return kLocalExternRef;
300
          case HeapType::kEq:
301
            return kLocalEqRef;
302
          case HeapType::kExn:
303 304
            return kLocalExnRef;
          default:
305
            return kLocalOptRef;
306 307
        }
      case kRef:
308
        if (heap_representation() == HeapType::kI31) return kLocalI31Ref;
309
        return kLocalRef;
310 311
      case kStmt:
        return kLocalVoid;
312 313
      case kRtt:
        return kLocalRtt;
314 315 316 317 318 319 320 321
#define NUMERIC_TYPE_CASE(kind, ...) \
  case k##kind:                      \
    return kLocal##kind;
        FOREACH_NUMERIC_VALUE_TYPE(NUMERIC_TYPE_CASE)
#undef NUMERIC_TYPE_CASE
      case kBottom:
        // Unreachable code
        return kLocalVoid;
322 323 324
    }
  }

325
  constexpr bool encoding_needs_heap_type() const {
326 327 328 329
    return (kind() == kRef && heap_representation() != HeapType::kI31) ||
           kind() == kRtt ||
           (kind() == kOptRef && (!heap_type().is_generic() ||
                                  heap_representation() == HeapType::kI31));
330 331
  }

332 333 334 335 336
  static ValueType For(MachineType type) {
    switch (type.representation()) {
      case MachineRepresentation::kWord8:
      case MachineRepresentation::kWord16:
      case MachineRepresentation::kWord32:
337
        return Primitive(kI32);
338
      case MachineRepresentation::kWord64:
339
        return Primitive(kI64);
340
      case MachineRepresentation::kFloat32:
341
        return Primitive(kF32);
342
      case MachineRepresentation::kFloat64:
343
        return Primitive(kF64);
344
      case MachineRepresentation::kTaggedPointer:
345
        return Ref(HeapType::kExtern, kNullable);
346
      case MachineRepresentation::kSimd128:
347
        return Primitive(kS128);
348 349 350 351 352 353 354 355 356 357 358 359
      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
    };

360
    return kShortName[kind()];
361 362
  }

363
  std::string type_name() const {
364 365 366
    std::ostringstream buf;
    switch (kind()) {
      case kRef:
367
        if (heap_representation() == HeapType::kI31) {
368 369 370 371
          buf << "i31ref";
        } else {
          buf << "(ref " << heap_type().name() << ")";
        }
372 373
        break;
      case kOptRef:
374 375
        if (heap_type().is_generic() &&
            heap_representation() != HeapType::kI31) {
376 377
          // We prefer the shorthand to be backwards-compatible with previous
          // proposals.
378
          buf << heap_type().name() << "ref";
379
        } else {
380
          buf << "(ref null " << heap_type().name() << ")";
381 382 383
        }
        break;
      case kRtt:
384 385
        buf << "(rtt " << static_cast<uint32_t>(depth()) << " "
            << heap_type().name() + ")";
386 387 388 389 390 391 392 393 394
        break;
      default:
        buf << kind_name();
    }
    return buf.str();
  }

 private:
  using KindField = base::BitField<Kind, 0, 5>;
395
  using HeapTypeField = base::BitField<uint32_t, 5, 20>;
396 397 398 399 400
  using DepthField = base::BitField<uint8_t, 25, 7>;

  constexpr explicit ValueType(uint32_t bit_field) : bit_field_(bit_field) {}

  constexpr const char* kind_name() const {
401
    constexpr const char* kTypeName[] = {
402
#define KIND_NAME(kind, log2Size, code, machineType, shortName, typeName, ...) \
403
  typeName,
404
        FOREACH_VALUE_TYPE(KIND_NAME)
405 406 407
#undef TYPE_NAME
    };

408
    return kTypeName[kind()];
409 410
  }

411
  uint32_t bit_field_;
412
};
413

414 415 416 417 418 419 420
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());
}

421 422 423 424 425
// Output operator, useful for DCHECKS and others.
inline std::ostream& operator<<(std::ostream& oss, ValueType type) {
  return oss << type.type_name();
}

426 427 428 429 430 431 432 433 434 435
constexpr ValueType kWasmI32 = ValueType::Primitive(ValueType::kI32);
constexpr ValueType kWasmI64 = ValueType::Primitive(ValueType::kI64);
constexpr ValueType kWasmF32 = ValueType::Primitive(ValueType::kF32);
constexpr ValueType kWasmF64 = ValueType::Primitive(ValueType::kF64);
constexpr ValueType kWasmS128 = ValueType::Primitive(ValueType::kS128);
constexpr ValueType kWasmI8 = ValueType::Primitive(ValueType::kI8);
constexpr ValueType kWasmI16 = ValueType::Primitive(ValueType::kI16);
constexpr ValueType kWasmStmt = ValueType::Primitive(ValueType::kStmt);
constexpr ValueType kWasmBottom = ValueType::Primitive(ValueType::kBottom);
// Established wasm shorthands:
436 437 438 439 440
constexpr ValueType kWasmFuncRef = ValueType::Ref(HeapType::kFunc, kNullable);
constexpr ValueType kWasmExnRef = ValueType::Ref(HeapType::kExn, kNullable);
constexpr ValueType kWasmExternRef =
    ValueType::Ref(HeapType::kExtern, kNullable);
constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
441
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
442

443
#define FOREACH_WASMVALUE_CTYPES(V) \
444 445 446 447 448
  V(kI32, int32_t)                  \
  V(kI64, int64_t)                  \
  V(kF32, float)                    \
  V(kF64, double)                   \
  V(kS128, Simd128)
449

450 451
using FunctionSig = Signature<ValueType>;

452
#define FOREACH_LOAD_TYPE(V) \
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
  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)
468 469 470 471 472 473 474 475 476

class LoadType {
 public:
  enum LoadTypeValue : uint8_t {
#define DEF_ENUM(type, suffix, ...) k##type##Load##suffix,
    FOREACH_LOAD_TYPE(DEF_ENUM)
#undef DEF_ENUM
  };

477
  // Allow implicit conversion of the enum value to this wrapper.
478 479 480 481 482 483 484 485 486 487
  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) {
488 489
    switch (type.kind()) {
      case ValueType::kI32:
490
        return kI32Load;
491
      case ValueType::kI64:
492
        return kI64Load;
493
      case ValueType::kF32:
494
        return kF32Load;
495
      case ValueType::kF64:
496
        return kF64Load;
497
      case ValueType::kS128:
498
        return kS128Load;
499 500 501 502 503 504 505 506 507
      default:
        UNREACHABLE();
    }
  }

 private:
  const LoadTypeValue val_;

  static constexpr uint8_t kLoadSizeLog2[] = {
508
  // MSVC wants a static_cast here.
509
#define LOAD_SIZE(_, __, memtype) \
510 511
  static_cast<uint8_t>(           \
      ElementSizeLog2Of(MachineType::memtype().representation())),
512 513 514 515 516
      FOREACH_LOAD_TYPE(LOAD_SIZE)
#undef LOAD_SIZE
  };

  static constexpr ValueType kValueType[] = {
517
#define VALUE_TYPE(type, ...) ValueType::Primitive(ValueType::k##type),
518 519 520 521 522
      FOREACH_LOAD_TYPE(VALUE_TYPE)
#undef VALUE_TYPE
  };

  static constexpr MachineType kMemType[] = {
523
#define MEMTYPE(_, __, memtype) MachineType::memtype(),
524 525 526 527 528 529
      FOREACH_LOAD_TYPE(MEMTYPE)
#undef MEMTYPE
  };
};

#define FOREACH_STORE_TYPE(V) \
530 531 532 533 534 535 536 537 538 539
  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)
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556

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_]; }
557
  constexpr MachineRepresentation mem_rep() const { return kMemRep[val_]; }
558 559

  static StoreType ForValueType(ValueType type) {
560 561
    switch (type.kind()) {
      case ValueType::kI32:
562
        return kI32Store;
563
      case ValueType::kI64:
564
        return kI64Store;
565
      case ValueType::kF32:
566
        return kF32Store;
567
      case ValueType::kF64:
568
        return kF64Store;
569
      case ValueType::kS128:
570
        return kS128Store;
571 572 573 574 575 576 577 578 579
      default:
        UNREACHABLE();
    }
  }

 private:
  const StoreTypeValue val_;

  static constexpr uint8_t kStoreSizeLog2[] = {
580
  // MSVC wants a static_cast here.
581
#define STORE_SIZE(_, __, memrep) \
582
  static_cast<uint8_t>(ElementSizeLog2Of(MachineRepresentation::k##memrep)),
583 584 585 586 587
      FOREACH_STORE_TYPE(STORE_SIZE)
#undef STORE_SIZE
  };

  static constexpr ValueType kValueType[] = {
588
#define VALUE_TYPE(type, ...) ValueType::Primitive(ValueType::k##type),
589 590 591 592 593
      FOREACH_STORE_TYPE(VALUE_TYPE)
#undef VALUE_TYPE
  };

  static constexpr MachineRepresentation kMemRep[] = {
594
#define MEMREP(_, __, memrep) MachineRepresentation::k##memrep,
595 596 597 598 599 600 601 602 603 604
      FOREACH_STORE_TYPE(MEMREP)
#undef MEMREP
  };
};

}  // namespace wasm
}  // namespace internal
}  // namespace v8

#endif  // V8_WASM_VALUE_TYPE_H_