// Copyright 2017 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. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_WASM_VALUE_H_ #define V8_WASM_WASM_VALUE_H_ #include "src/base/memory.h" #include "src/handles/handles.h" #include "src/utils/boxed-float.h" #include "src/wasm/value-type.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { namespace wasm { #define FOREACH_SIMD_TYPE(V) \ V(double, float2, f64x2, 2) \ V(float, float4, f32x4, 4) \ V(int64_t, int2, i64x2, 2) \ V(int32_t, int4, i32x4, 4) \ V(int16_t, int8, i16x8, 8) \ V(int8_t, int16, i8x16, 16) #define DEFINE_SIMD_TYPE(cType, sType, name, kSize) \ struct sType { \ cType val[kSize]; \ }; FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE) #undef DEFINE_SIMD_TYPE class Simd128 { public: Simd128() = default; #define DEFINE_SIMD_TYPE_SPECIFIC_METHODS(cType, sType, name, size) \ explicit Simd128(sType val) { \ base::WriteUnalignedValue<sType>(reinterpret_cast<Address>(val_), val); \ } \ sType to_##name() const { \ return base::ReadUnalignedValue<sType>(reinterpret_cast<Address>(val_)); \ } FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE_SPECIFIC_METHODS) #undef DEFINE_SIMD_TYPE_SPECIFIC_METHODS explicit Simd128(byte* bytes) { memcpy(static_cast<void*>(val_), reinterpret_cast<void*>(bytes), kSimd128Size); } const uint8_t* bytes() { return val_; } template <typename T> inline T to() const; private: uint8_t val_[16] = {0}; }; #define DECLARE_CAST(cType, sType, name, size) \ template <> \ inline sType Simd128::to() const { \ return to_##name(); \ } FOREACH_SIMD_TYPE(DECLARE_CAST) #undef DECLARE_CAST // Macro for defining WasmValue methods for different types. // Elements: // - name (for to_<name>() method) // - wasm type // - c type #define FOREACH_PRIMITIVE_WASMVAL_TYPE(V) \ V(i8, kWasmI8, int8_t) \ V(i16, kWasmI16, int16_t) \ V(i32, kWasmI32, int32_t) \ V(u32, kWasmI32, uint32_t) \ V(i64, kWasmI64, int64_t) \ V(u64, kWasmI64, uint64_t) \ V(f32, kWasmF32, float) \ V(f32_boxed, kWasmF32, Float32) \ V(f64, kWasmF64, double) \ V(f64_boxed, kWasmF64, Float64) \ V(s128, kWasmS128, Simd128) ASSERT_TRIVIALLY_COPYABLE(Handle<Object>); // A wasm value with type information. class WasmValue { public: WasmValue() : type_(kWasmVoid), bit_pattern_{} {} #define DEFINE_TYPE_SPECIFIC_METHODS(name, localtype, ctype) \ explicit WasmValue(ctype v) : type_(localtype), bit_pattern_{} { \ static_assert(sizeof(ctype) <= sizeof(bit_pattern_), \ "size too big for WasmValue"); \ base::WriteUnalignedValue<ctype>(reinterpret_cast<Address>(bit_pattern_), \ v); \ } \ ctype to_##name() const { \ DCHECK_EQ(localtype, type_); \ return to_##name##_unchecked(); \ } \ ctype to_##name##_unchecked() const { \ return base::ReadUnalignedValue<ctype>( \ reinterpret_cast<Address>(bit_pattern_)); \ } FOREACH_PRIMITIVE_WASMVAL_TYPE(DEFINE_TYPE_SPECIFIC_METHODS) #undef DEFINE_TYPE_SPECIFIC_METHODS WasmValue(byte* raw_bytes, ValueType type) : type_(type), bit_pattern_{} { DCHECK(type_.is_numeric()); memcpy(bit_pattern_, raw_bytes, type.element_size_bytes()); } WasmValue(Handle<Object> ref, ValueType type) : type_(type), bit_pattern_{} { static_assert(sizeof(Handle<Object>) <= sizeof(bit_pattern_), "bit_pattern_ must be large enough to fit a Handle"); DCHECK(type.is_reference()); base::WriteUnalignedValue<Handle<Object>>( reinterpret_cast<Address>(bit_pattern_), ref); } Handle<Object> to_ref() const { DCHECK(type_.is_reference()); return base::ReadUnalignedValue<Handle<Object>>( reinterpret_cast<Address>(bit_pattern_)); } ValueType type() const { return type_; } // Checks equality of type and bit pattern (also for float and double values). bool operator==(const WasmValue& other) const { return type_ == other.type_ && !memcmp(bit_pattern_, other.bit_pattern_, type_.is_reference() ? sizeof(Handle<Object>) : type_.element_size_bytes()); } void CopyTo(byte* to) const { STATIC_ASSERT(sizeof(float) == sizeof(Float32)); STATIC_ASSERT(sizeof(double) == sizeof(Float64)); DCHECK(type_.is_numeric()); memcpy(to, bit_pattern_, type_.element_size_bytes()); } // If {packed_type.is_packed()}, create a new value of {packed_type()}. // Otherwise, return this object. WasmValue Packed(ValueType packed_type) const { if (packed_type == kWasmI8) { DCHECK_EQ(type_, kWasmI32); return WasmValue(static_cast<int8_t>(to_i32())); } if (packed_type == kWasmI16) { DCHECK_EQ(type_, kWasmI32); return WasmValue(static_cast<int16_t>(to_i32())); } return *this; } template <typename T> inline T to() const; template <typename T> inline T to_unchecked() const; static WasmValue ForUintPtr(uintptr_t value) { using type = std::conditional<kSystemPointerSize == 8, uint64_t, uint32_t>::type; return WasmValue{type{value}}; } inline std::string to_string() const { switch (type_.kind()) { case kI8: return std::to_string(to_i8()); case kI16: return std::to_string(to_i16()); case kI32: return std::to_string(to_i32()); case kI64: return std::to_string(to_i64()); case kF32: return std::to_string(to_f32()); case kF64: return std::to_string(to_f64()); case kS128: { std::stringstream stream; stream << "0x" << std::hex; for (int8_t byte : bit_pattern_) { if (!(byte & 0xf0)) stream << '0'; stream << byte; } return stream.str(); } case kOptRef: case kRef: case kRtt: case kRttWithDepth: return "Handle [" + std::to_string(to_ref().address()) + "]"; case kVoid: case kBottom: UNREACHABLE(); } } private: ValueType type_; uint8_t bit_pattern_[16]; }; #define DECLARE_CAST(name, localtype, ctype, ...) \ template <> \ inline ctype WasmValue::to_unchecked() const { \ return to_##name##_unchecked(); \ } \ template <> \ inline ctype WasmValue::to() const { \ return to_##name(); \ } FOREACH_PRIMITIVE_WASMVAL_TYPE(DECLARE_CAST) #undef DECLARE_CAST } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_WASM_VALUE_H_