Bug 1874684 - Part 21: Rename SecondsAndNanoseconds::toTotalNanoseconds. r=dminor
[gecko.git] / js / src / wasm / WasmValue.h
blob9a5442fc75bb55318d90831265f7bef14a19e8e8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 * Copyright 2021 Mozilla Foundation
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #ifndef wasm_val_h
20 #define wasm_val_h
22 #include "js/Class.h" // JSClassOps, ClassSpec
23 #include "vm/JSObject.h"
24 #include "vm/NativeObject.h" // NativeObject
25 #include "wasm/WasmAnyRef.h"
26 #include "wasm/WasmSerialize.h"
27 #include "wasm/WasmTypeDef.h"
29 namespace js {
30 namespace wasm {
32 // A V128 value.
34 struct V128 {
35 uint8_t bytes[16] = {}; // Little-endian
37 WASM_CHECK_CACHEABLE_POD(bytes);
39 V128() = default;
41 explicit V128(uint8_t splatValue) {
42 memset(bytes, int(splatValue), sizeof(bytes));
45 template <typename T>
46 void extractLane(unsigned lane, T* result) const {
47 MOZ_ASSERT(lane < 16 / sizeof(T));
48 memcpy(result, bytes + sizeof(T) * lane, sizeof(T));
51 template <typename T>
52 void insertLane(unsigned lane, T value) {
53 MOZ_ASSERT(lane < 16 / sizeof(T));
54 memcpy(bytes + sizeof(T) * lane, &value, sizeof(T));
57 bool operator==(const V128& rhs) const {
58 for (size_t i = 0; i < sizeof(bytes); i++) {
59 if (bytes[i] != rhs.bytes[i]) {
60 return false;
63 return true;
66 bool operator!=(const V128& rhs) const { return !(*this == rhs); }
69 WASM_DECLARE_CACHEABLE_POD(V128);
71 static_assert(sizeof(V128) == 16, "Invariant");
73 // A FuncRef is a JSFunction* and is hence also an AnyRef, and the remarks above
74 // about AnyRef apply also to FuncRef. When 'funcref' is used as a value type
75 // in wasm code, the value that is held is "the canonical function value", which
76 // is a function for which IsWasmExportedFunction() is true, and which has the
77 // correct identity wrt reference equality of functions. Notably, if a function
78 // is imported then its ref.func value compares === in JS to the function that
79 // was passed as an import when the instance was created.
81 // These rules ensure that casts from funcref to anyref are non-converting
82 // (generate no code), and that no wrapping or unwrapping needs to happen when a
83 // funcref or anyref flows across the JS/wasm boundary, and that functions have
84 // the necessary identity when observed from JS, and in the future, from wasm.
86 // Functions stored in tables, whether wasm tables or internal tables, can be
87 // stored in a form that optimizes for eg call speed, however.
89 // Reading a funcref from a funcref table, writing a funcref to a funcref table,
90 // and generating the value for a ref.func instruction are therefore nontrivial
91 // operations that require mapping between the canonical JSFunction and the
92 // optimized table representation. Once we get an instruction to call a
93 // ref.func directly it too will require such a mapping.
95 // In many cases, a FuncRef is exactly the same as AnyRef and we can use AnyRef
96 // functionality on funcref values. The FuncRef class exists mostly to add more
97 // checks and to make it clear, when we need to, that we're manipulating funcref
98 // values. FuncRef does not currently subclass AnyRef because there's been no
99 // need to, but it probably could.
101 class FuncRef {
102 // mutable so that tracing may access a JSFunction* from a `const FuncRef`
103 mutable JSFunction* value_;
105 explicit FuncRef() : value_((JSFunction*)-1) {}
106 explicit FuncRef(JSFunction* p) : value_(p) {
107 MOZ_ASSERT(((uintptr_t)p & 0x03) == 0);
110 public:
111 // Given a void* that comes from compiled wasm code, turn it into FuncRef.
112 static FuncRef fromCompiledCode(void* p) { return FuncRef((JSFunction*)p); }
114 // Given a JSFunction* that comes from JS, turn it into FuncRef.
115 static FuncRef fromJSFunction(JSFunction* p) { return FuncRef(p); }
117 // Given an AnyRef that represents a possibly-null funcref, turn it into a
118 // FuncRef.
119 static FuncRef fromAnyRefUnchecked(AnyRef p);
121 static FuncRef null() { return FuncRef(nullptr); }
123 AnyRef toAnyRef() { return AnyRef::fromJSObjectOrNull((JSObject*)value_); }
125 void* forCompiledCode() const { return value_; }
127 JSFunction* asJSFunction() { return value_; }
129 bool isNull() const { return value_ == nullptr; }
131 void trace(JSTracer* trc) const;
134 using RootedFuncRef = Rooted<FuncRef>;
135 using HandleFuncRef = Handle<FuncRef>;
136 using MutableHandleFuncRef = MutableHandle<FuncRef>;
138 // Given any FuncRef, unbox it as a JS Value -- always a JSFunction*.
140 Value UnboxFuncRef(FuncRef val);
142 // The LitVal class represents a single WebAssembly value of a given value
143 // type, mostly for the purpose of numeric literals and initializers. A LitVal
144 // does not directly map to a JS value since there is not (currently) a precise
145 // representation of i64 values. A LitVal may contain non-canonical NaNs since,
146 // within WebAssembly, floats are not canonicalized. Canonicalization must
147 // happen at the JS boundary.
149 class LitVal {
150 public:
151 union Cell {
152 uint32_t i32_;
153 uint64_t i64_;
154 float f32_;
155 double f64_;
156 wasm::V128 v128_;
157 // Mutable so that it can be traced
158 mutable wasm::AnyRef ref_;
160 Cell() : v128_() {}
161 ~Cell() = default;
163 WASM_CHECK_CACHEABLE_POD(i32_, i64_, f32_, f64_, v128_);
164 WASM_ALLOW_NON_CACHEABLE_POD_FIELD(
165 ref_,
166 "The pointer value in ref_ is guaranteed to always be null in a "
167 "LitVal.");
170 protected:
171 ValType type_;
172 Cell cell_;
174 public:
175 LitVal() = default;
177 explicit LitVal(ValType type) : type_(type) {
178 switch (type.kind()) {
179 case ValType::Kind::I32: {
180 cell_.i32_ = 0;
181 break;
183 case ValType::Kind::I64: {
184 cell_.i64_ = 0;
185 break;
187 case ValType::Kind::F32: {
188 cell_.f32_ = 0;
189 break;
191 case ValType::Kind::F64: {
192 cell_.f64_ = 0;
193 break;
195 case ValType::Kind::V128: {
196 new (&cell_.v128_) V128();
197 break;
199 case ValType::Kind::Ref: {
200 cell_.ref_ = nullptr;
201 break;
206 explicit LitVal(uint32_t i32) : type_(ValType::I32) { cell_.i32_ = i32; }
207 explicit LitVal(uint64_t i64) : type_(ValType::I64) { cell_.i64_ = i64; }
209 explicit LitVal(float f32) : type_(ValType::F32) { cell_.f32_ = f32; }
210 explicit LitVal(double f64) : type_(ValType::F64) { cell_.f64_ = f64; }
212 explicit LitVal(V128 v128) : type_(ValType::V128) { cell_.v128_ = v128; }
214 explicit LitVal(ValType type, AnyRef any) : type_(type) {
215 MOZ_ASSERT(type.isRefRepr());
216 MOZ_ASSERT(any.isNull(),
217 "use Val for non-nullptr ref types to get tracing");
218 cell_.ref_ = any;
221 ValType type() const { return type_; }
222 static constexpr size_t sizeofLargestValue() { return sizeof(cell_); }
224 Cell& cell() { return cell_; }
225 const Cell& cell() const { return cell_; }
227 // Updates the type of the LitVal. Does not check that the type is valid for
228 // the actual value, so make sure the type is definitely correct via
229 // validation or something.
230 void unsafeSetType(ValType type) { type_ = type; }
232 uint32_t i32() const {
233 MOZ_ASSERT(type_ == ValType::I32);
234 return cell_.i32_;
236 uint64_t i64() const {
237 MOZ_ASSERT(type_ == ValType::I64);
238 return cell_.i64_;
240 const float& f32() const {
241 MOZ_ASSERT(type_ == ValType::F32);
242 return cell_.f32_;
244 const double& f64() const {
245 MOZ_ASSERT(type_ == ValType::F64);
246 return cell_.f64_;
248 AnyRef ref() const {
249 MOZ_ASSERT(type_.isRefRepr());
250 return cell_.ref_;
252 const V128& v128() const {
253 MOZ_ASSERT(type_ == ValType::V128);
254 return cell_.v128_;
257 WASM_DECLARE_FRIEND_SERIALIZE(LitVal);
260 WASM_DECLARE_CACHEABLE_POD(LitVal::Cell);
262 // A Val is a LitVal that can contain (non-null) pointers to GC things. All Vals
263 // must be used with the rooting APIs as they may contain JS objects.
265 class MOZ_NON_PARAM Val : public LitVal {
266 public:
267 Val() = default;
268 explicit Val(ValType type) : LitVal(type) {}
269 explicit Val(const LitVal& val);
270 explicit Val(uint32_t i32) : LitVal(i32) {}
271 explicit Val(uint64_t i64) : LitVal(i64) {}
272 explicit Val(float f32) : LitVal(f32) {}
273 explicit Val(double f64) : LitVal(f64) {}
274 explicit Val(V128 v128) : LitVal(v128) {}
275 explicit Val(ValType type, AnyRef val) : LitVal(type, AnyRef::null()) {
276 MOZ_ASSERT(type.isRefRepr());
277 cell_.ref_ = val;
279 explicit Val(ValType type, FuncRef val) : LitVal(type, AnyRef::null()) {
280 MOZ_ASSERT(type.refType().isFuncHierarchy());
281 cell_.ref_ = val.toAnyRef();
284 Val(const Val&) = default;
285 Val& operator=(const Val&) = default;
287 bool operator==(const Val& rhs) const {
288 if (type_ != rhs.type_) {
289 return false;
291 switch (type_.kind()) {
292 case ValType::I32:
293 return cell_.i32_ == rhs.cell_.i32_;
294 case ValType::I64:
295 return cell_.i64_ == rhs.cell_.i64_;
296 case ValType::F32:
297 return cell_.f32_ == rhs.cell_.f32_;
298 case ValType::F64:
299 return cell_.f64_ == rhs.cell_.f64_;
300 case ValType::V128:
301 return cell_.v128_ == rhs.cell_.v128_;
302 case ValType::Ref:
303 return cell_.ref_ == rhs.cell_.ref_;
305 MOZ_ASSERT_UNREACHABLE();
306 return false;
308 bool operator!=(const Val& rhs) const { return !(*this == rhs); }
310 bool isInvalid() const { return !type_.isValid(); }
311 bool isAnyRef() const { return type_.isValid() && type_.isRefRepr(); }
312 AnyRef& toAnyRef() const {
313 MOZ_ASSERT(isAnyRef());
314 return cell_.ref_;
317 // Initialize from `loc` which is a rooted location and needs no barriers.
318 void initFromRootedLocation(ValType type, const void* loc);
319 void initFromHeapLocation(ValType type, const void* loc);
321 // Write to `loc` which is a rooted location and needs no barriers.
322 void writeToRootedLocation(void* loc, bool mustWrite64) const;
324 // Read from `loc` which is in the heap.
325 void readFromHeapLocation(const void* loc);
326 // Write to `loc` which is in the heap and must be barriered.
327 void writeToHeapLocation(void* loc) const;
329 // See the comment for `ToWebAssemblyValue` below.
330 static bool fromJSValue(JSContext* cx, ValType targetType, HandleValue val,
331 MutableHandle<Val> rval);
332 // See the comment for `ToJSValue` below.
333 bool toJSValue(JSContext* cx, MutableHandleValue rval) const;
335 void trace(JSTracer* trc) const;
338 using GCPtrVal = GCPtr<Val>;
339 using RootedVal = Rooted<Val>;
340 using HandleVal = Handle<Val>;
341 using MutableHandleVal = MutableHandle<Val>;
343 using ValVector = GCVector<Val, 0, SystemAllocPolicy>;
344 using RootedValVector = Rooted<ValVector>;
345 using HandleValVector = Handle<ValVector>;
346 using MutableHandleValVector = MutableHandle<ValVector>;
348 template <int N>
349 using ValVectorN = GCVector<Val, N, SystemAllocPolicy>;
350 template <int N>
351 using RootedValVectorN = Rooted<ValVectorN<N>>;
353 // Check a value against the given reference type. If the targetType
354 // is RefType::Extern then the test always passes, but the value may be boxed.
355 // If the test passes then the value is stored either in fnval (for
356 // RefType::Func) or in refval (for other types); this split is not strictly
357 // necessary but is convenient for the users of this function.
359 // This can return false if the type check fails, or if a boxing into AnyRef
360 // throws an OOM.
361 [[nodiscard]] extern bool CheckRefType(JSContext* cx, RefType targetType,
362 HandleValue v,
363 MutableHandleFunction fnval,
364 MutableHandleAnyRef refval);
366 // The same as above for when the target type is 'funcref'.
367 [[nodiscard]] extern bool CheckFuncRefValue(JSContext* cx, HandleValue v,
368 MutableHandleFunction fun);
370 // The same as above for when the target type is 'anyref'.
371 [[nodiscard]] extern bool CheckAnyRefValue(JSContext* cx, HandleValue v,
372 MutableHandleAnyRef vp);
374 // The same as above for when the target type is 'nullexternref'.
375 [[nodiscard]] extern bool CheckNullExternRefValue(JSContext* cx, HandleValue v,
376 MutableHandleAnyRef vp);
378 // The same as above for when the target type is 'nullfuncref'.
379 [[nodiscard]] extern bool CheckNullFuncRefValue(JSContext* cx, HandleValue v,
380 MutableHandleFunction fun);
382 // The same as above for when the target type is 'nullref'.
383 [[nodiscard]] extern bool CheckNullRefValue(JSContext* cx, HandleValue v,
384 MutableHandleAnyRef vp);
386 // The same as above for when the target type is 'eqref'.
387 [[nodiscard]] extern bool CheckEqRefValue(JSContext* cx, HandleValue v,
388 MutableHandleAnyRef vp);
390 // The same as above for when the target type is 'i31ref'.
391 [[nodiscard]] extern bool CheckI31RefValue(JSContext* cx, HandleValue v,
392 MutableHandleAnyRef vp);
394 // The same as above for when the target type is 'structref'.
395 [[nodiscard]] extern bool CheckStructRefValue(JSContext* cx, HandleValue v,
396 MutableHandleAnyRef vp);
398 // The same as above for when the target type is 'arrayref'.
399 [[nodiscard]] extern bool CheckArrayRefValue(JSContext* cx, HandleValue v,
400 MutableHandleAnyRef vp);
402 // The same as above for when the target type is '(ref T)'.
403 [[nodiscard]] extern bool CheckTypeRefValue(JSContext* cx,
404 const TypeDef* typeDef,
405 HandleValue v,
406 MutableHandleAnyRef vp);
407 class NoDebug;
408 class DebugCodegenVal;
410 // The level of coercion to apply in `ToWebAssemblyValue` and `ToJSValue`.
411 enum class CoercionLevel {
412 // The default coercions given by the JS-API specification.
413 Spec,
414 // Allow for the coercions given by `Spec` but also use WebAssembly.Global
415 // as a container for lossless conversions. This is only available through
416 // the wasmLosslessInvoke testing function and is used in tests.
417 Lossless,
420 // Coercion function from a JS value to a WebAssembly value [1].
422 // This function may fail for any of the following reasons:
423 // * The input value has an incorrect type for the targetType
424 // * The targetType is not exposable
425 // * An OOM ocurred
426 // An error will be set upon failure.
428 // [1] https://webassembly.github.io/spec/js-api/index.html#towebassemblyvalue
429 template <typename Debug = NoDebug>
430 extern bool ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type,
431 void* loc, bool mustWrite64,
432 CoercionLevel level = CoercionLevel::Spec);
434 // Coercion function from a WebAssembly value to a JS value [1].
436 // This function will only fail if an OOM ocurred. If the type of WebAssembly
437 // value being coerced is not exposable to JS, then it will be coerced to
438 // 'undefined'. Callers are responsible for guarding against this if this is
439 // not desirable.
441 // [1] https://webassembly.github.io/spec/js-api/index.html#tojsvalue
442 template <typename Debug = NoDebug>
443 extern bool ToJSValue(JSContext* cx, const void* src, StorageType type,
444 MutableHandleValue dst,
445 CoercionLevel level = CoercionLevel::Spec);
446 template <typename Debug = NoDebug>
447 extern bool ToJSValueMayGC(StorageType type);
448 template <typename Debug = NoDebug>
449 extern bool ToJSValue(JSContext* cx, const void* src, ValType type,
450 MutableHandleValue dst,
451 CoercionLevel level = CoercionLevel::Spec);
452 template <typename Debug = NoDebug>
453 extern bool ToJSValueMayGC(ValType type);
454 } // namespace wasm
456 template <>
457 struct InternalBarrierMethods<wasm::AnyRef> {
458 static bool isMarkable(const wasm::AnyRef v) { return v.isGCThing(); }
460 static void preBarrier(const wasm::AnyRef v) {
461 if (v.isGCThing()) {
462 gc::PreWriteBarrierImpl(v.toGCThing());
466 static MOZ_ALWAYS_INLINE void postBarrier(wasm::AnyRef* vp,
467 const wasm::AnyRef prev,
468 const wasm::AnyRef next) {
469 // If the target needs an entry, add it.
470 gc::StoreBuffer* sb;
471 if (next.isGCThing() && (sb = next.toGCThing()->storeBuffer())) {
472 // If we know that the prev has already inserted an entry, we can
473 // skip doing the lookup to add the new entry. Note that we cannot
474 // safely assert the presence of the entry because it may have been
475 // added via a different store buffer.
476 if (prev.isGCThing() && prev.toGCThing()->storeBuffer()) {
477 return;
479 sb->putWasmAnyRef(vp);
480 return;
482 // Remove the prev entry if the new value does not need it.
483 if (prev.isGCThing() && (sb = prev.toGCThing()->storeBuffer())) {
484 sb->unputWasmAnyRef(vp);
488 static void readBarrier(const wasm::AnyRef v) {
489 if (v.isGCThing()) {
490 gc::ReadBarrierImpl(v.toGCThing());
494 #ifdef DEBUG
495 static void assertThingIsNotGray(const wasm::AnyRef v) {
496 if (v.isGCThing()) {
497 JS::AssertCellIsNotGray(v.toGCThing());
500 #endif
503 template <>
504 struct InternalBarrierMethods<wasm::Val> {
505 static bool isMarkable(const wasm::Val& v) { return v.isAnyRef(); }
507 static void preBarrier(const wasm::Val& v) {
508 if (v.isAnyRef()) {
509 InternalBarrierMethods<wasm::AnyRef>::preBarrier(v.toAnyRef());
513 static MOZ_ALWAYS_INLINE void postBarrier(wasm::Val* vp,
514 const wasm::Val& prev,
515 const wasm::Val& next) {
516 // A wasm::Val can transition from being uninitialized to holding an anyref
517 // but cannot change kind after that.
518 MOZ_ASSERT_IF(next.isAnyRef(), prev.isAnyRef() || prev.isInvalid());
519 MOZ_ASSERT_IF(prev.isAnyRef(), next.isAnyRef());
521 if (next.isAnyRef()) {
522 InternalBarrierMethods<wasm::AnyRef>::postBarrier(
523 &vp->toAnyRef(),
524 prev.isAnyRef() ? prev.toAnyRef() : wasm::AnyRef::null(),
525 next.toAnyRef());
526 return;
530 static void readBarrier(const wasm::Val& v) {
531 if (v.isAnyRef()) {
532 InternalBarrierMethods<wasm::AnyRef>::readBarrier(v.toAnyRef());
536 #ifdef DEBUG
537 static void assertThingIsNotGray(const wasm::Val& v) {
538 if (v.isAnyRef()) {
539 InternalBarrierMethods<wasm::AnyRef>::assertThingIsNotGray(v.toAnyRef());
542 #endif
545 } // namespace js
547 template <>
548 struct JS::SafelyInitialized<js::wasm::AnyRef> {
549 static js::wasm::AnyRef create() { return js::wasm::AnyRef::null(); }
552 #endif // wasm_val_h