1 // Copyright 2017 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 #if !V8_ENABLE_WEBASSEMBLY
6 #error This header should only be included if WebAssembly is enabled.
7 #endif // !V8_ENABLE_WEBASSEMBLY
8
9 #ifndef V8_WASM_WASM_VALUE_H_
10 #define V8_WASM_WASM_VALUE_H_
11
12 #include "src/base/memory.h"
13 #include "src/handles/handles.h"
14 #include "src/utils/boxed-float.h"
15 #include "src/wasm/value-type.h"
16 #include "src/zone/zone-containers.h"
17
18 namespace v8 {
19 namespace internal {
20 namespace wasm {
21
22 #define FOREACH_SIMD_TYPE(V) \
23 V(double, float2, f64x2, 2) \
24 V(float, float4, f32x4, 4) \
25 V(int64_t, int2, i64x2, 2) \
26 V(int32_t, int4, i32x4, 4) \
27 V(int16_t, int8, i16x8, 8) \
28 V(int8_t, int16, i8x16, 16)
29
30 #define DEFINE_SIMD_TYPE(cType, sType, name, kSize) \
31 struct sType { \
32 cType val[kSize]; \
33 };
FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE)34 FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE)
35 #undef DEFINE_SIMD_TYPE
36
37 class Simd128 {
38 public:
39 Simd128() = default;
40
41 #define DEFINE_SIMD_TYPE_SPECIFIC_METHODS(cType, sType, name, size) \
42 explicit Simd128(sType val) { \
43 base::WriteUnalignedValue<sType>(reinterpret_cast<Address>(val_), val); \
44 } \
45 sType to_##name() const { \
46 return base::ReadUnalignedValue<sType>(reinterpret_cast<Address>(val_)); \
47 }
48 FOREACH_SIMD_TYPE(DEFINE_SIMD_TYPE_SPECIFIC_METHODS)
49 #undef DEFINE_SIMD_TYPE_SPECIFIC_METHODS
50
51 explicit Simd128(byte* bytes) {
52 memcpy(static_cast<void*>(val_), reinterpret_cast<void*>(bytes),
53 kSimd128Size);
54 }
55
56 const uint8_t* bytes() { return val_; }
57
58 template <typename T>
59 inline T to() const;
60
61 private:
62 uint8_t val_[16] = {0};
63 };
64
65 #define DECLARE_CAST(cType, sType, name, size) \
66 template <> \
67 inline sType Simd128::to() const { \
68 return to_##name(); \
69 }
70 FOREACH_SIMD_TYPE(DECLARE_CAST)
71 #undef DECLARE_CAST
72
73 // Macro for defining WasmValue methods for different types.
74 // Elements:
75 // - name (for to_<name>() method)
76 // - wasm type
77 // - c type
78 #define FOREACH_PRIMITIVE_WASMVAL_TYPE(V) \
79 V(i8, kWasmI8, int8_t) \
80 V(i16, kWasmI16, int16_t) \
81 V(i32, kWasmI32, int32_t) \
82 V(u32, kWasmI32, uint32_t) \
83 V(i64, kWasmI64, int64_t) \
84 V(u64, kWasmI64, uint64_t) \
85 V(f32, kWasmF32, float) \
86 V(f32_boxed, kWasmF32, Float32) \
87 V(f64, kWasmF64, double) \
88 V(f64_boxed, kWasmF64, Float64) \
89 V(s128, kWasmS128, Simd128)
90
91 ASSERT_TRIVIALLY_COPYABLE(Handle<Object>);
92
93 // A wasm value with type information.
94 class WasmValue {
95 public:
WasmValue()96 WasmValue() : type_(kWasmVoid), bit_pattern_{} {}
97
98 #define DEFINE_TYPE_SPECIFIC_METHODS(name, localtype, ctype) \
99 explicit WasmValue(ctype v) : type_(localtype), bit_pattern_{} { \
100 static_assert(sizeof(ctype) <= sizeof(bit_pattern_), \
101 "size too big for WasmValue"); \
102 base::WriteUnalignedValue<ctype>(reinterpret_cast<Address>(bit_pattern_), \
103 v); \
104 } \
105 ctype to_##name() const { \
106 DCHECK_EQ(localtype, type_); \
107 return to_##name##_unchecked(); \
108 } \
109 ctype to_##name##_unchecked() const { \
110 return base::ReadUnalignedValue<ctype>( \
111 reinterpret_cast<Address>(bit_pattern_)); \
112 }
113
114 FOREACH_PRIMITIVE_WASMVAL_TYPE(DEFINE_TYPE_SPECIFIC_METHODS)
115 #undef DEFINE_TYPE_SPECIFIC_METHODS
116
WasmValue(byte * raw_bytes,ValueType type)117 WasmValue(byte* raw_bytes, ValueType type) : type_(type), bit_pattern_{} {
118 DCHECK(type_.is_numeric());
119 memcpy(bit_pattern_, raw_bytes, type.value_kind_size());
120 }
121
WasmValue(Handle<Object> ref,ValueType type)122 WasmValue(Handle<Object> ref, ValueType type) : type_(type), bit_pattern_{} {
123 static_assert(sizeof(Handle<Object>) <= sizeof(bit_pattern_),
124 "bit_pattern_ must be large enough to fit a Handle");
125 DCHECK(type.is_reference());
126 base::WriteUnalignedValue<Handle<Object>>(
127 reinterpret_cast<Address>(bit_pattern_), ref);
128 }
129
to_ref()130 Handle<Object> to_ref() const {
131 DCHECK(type_.is_reference());
132 return base::ReadUnalignedValue<Handle<Object>>(
133 reinterpret_cast<Address>(bit_pattern_));
134 }
135
type()136 ValueType type() const { return type_; }
137
138 // Checks equality of type and bit pattern (also for float and double values).
139 bool operator==(const WasmValue& other) const {
140 return type_ == other.type_ &&
141 !memcmp(bit_pattern_, other.bit_pattern_,
142 type_.is_reference() ? sizeof(Handle<Object>)
143 : type_.value_kind_size());
144 }
145
CopyTo(byte * to)146 void CopyTo(byte* to) const {
147 STATIC_ASSERT(sizeof(float) == sizeof(Float32));
148 STATIC_ASSERT(sizeof(double) == sizeof(Float64));
149 DCHECK(type_.is_numeric());
150 memcpy(to, bit_pattern_, type_.value_kind_size());
151 }
152
153 // If {packed_type.is_packed()}, create a new value of {packed_type()}.
154 // Otherwise, return this object.
Packed(ValueType packed_type)155 WasmValue Packed(ValueType packed_type) const {
156 if (packed_type == kWasmI8) {
157 DCHECK_EQ(type_, kWasmI32);
158 return WasmValue(static_cast<int8_t>(to_i32()));
159 }
160 if (packed_type == kWasmI16) {
161 DCHECK_EQ(type_, kWasmI32);
162 return WasmValue(static_cast<int16_t>(to_i32()));
163 }
164 return *this;
165 }
166
167 template <typename T>
168 inline T to() const;
169
170 template <typename T>
171 inline T to_unchecked() const;
172
ForUintPtr(uintptr_t value)173 static WasmValue ForUintPtr(uintptr_t value) {
174 using type =
175 std::conditional<kSystemPointerSize == 8, uint64_t, uint32_t>::type;
176 return WasmValue{type{value}};
177 }
178
to_string()179 inline std::string to_string() const {
180 switch (type_.kind()) {
181 case kI8:
182 return std::to_string(to_i8());
183 case kI16:
184 return std::to_string(to_i16());
185 case kI32:
186 return std::to_string(to_i32());
187 case kI64:
188 return std::to_string(to_i64());
189 case kF32:
190 return std::to_string(to_f32());
191 case kF64:
192 return std::to_string(to_f64());
193 case kS128: {
194 std::stringstream stream;
195 stream << "0x" << std::hex;
196 for (int8_t byte : bit_pattern_) {
197 if (!(byte & 0xf0)) stream << '0';
198 stream << byte;
199 }
200 return stream.str();
201 }
202 case kOptRef:
203 case kRef:
204 case kRtt:
205 return "Handle [" + std::to_string(to_ref().address()) + "]";
206 case kVoid:
207 case kBottom:
208 UNREACHABLE();
209 }
210 }
211
212 private:
213 ValueType type_;
214 uint8_t bit_pattern_[16];
215 };
216
217 #define DECLARE_CAST(name, localtype, ctype, ...) \
218 template <> \
219 inline ctype WasmValue::to_unchecked() const { \
220 return to_##name##_unchecked(); \
221 } \
222 template <> \
223 inline ctype WasmValue::to() const { \
224 return to_##name(); \
225 }
226 FOREACH_PRIMITIVE_WASMVAL_TYPE(DECLARE_CAST)
227 #undef DECLARE_CAST
228
229 } // namespace wasm
230 } // namespace internal
231 } // namespace v8
232
233 #endif // V8_WASM_WASM_VALUE_H_
234