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 2023 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.
22 #include "mozilla/FloatingPoint.h"
26 #include "js/HeapAPI.h"
27 #include "js/RootingAPI.h"
28 #include "js/TypeDecls.h"
31 // #include "NamespaceImports.h"
45 // An AnyRef is a boxed value that can represent any wasm reference type and any
46 // host type that the host system allows to flow into and out of wasm
47 // transparently. It is a pointer-sized datum that has the same representation
48 // as all its subtypes (funcref, externref, eqref, (ref T), et al) due to the
49 // non-coercive subtyping of the wasm type system.
51 // The C++/wasm boundary always uses a 'void*' type to express AnyRef values, to
52 // emphasize the pointer-ness of the value. The C++ code must transform the
53 // void* into an AnyRef by calling AnyRef::fromCompiledCode(), and transform an
54 // AnyRef into a void* by calling AnyRef::toCompiledCode(). Once in C++, we use
55 // AnyRef everywhere. A JS Value is transformed into an AnyRef by calling
56 // AnyRef::fromJSValue(), and the AnyRef is transformed into a JS Value by
57 // calling AnyRef::toJSValue().
59 // NOTE that AnyRef values may point to GC'd storage and as such need to be
60 // rooted if they are kept live in boxed form across code that may cause GC!
61 // Use RootedAnyRef / HandleAnyRef / MutableHandleAnyRef where necessary.
63 // The lowest bits of the pointer value are used for tagging, to allow for some
64 // representation optimizations and to distinguish various types.
66 // The current tagging scheme is:
67 // if (pointer == 0) then 'null'
68 // if (pointer & 0x1) then 'i31'
69 // if (pointer & 0x2) then 'string'
72 // NOTE: there is sequencing required when checking tags. If bit 0x1 is set,
73 // then bit 0x2 is part of the i31 value and does not imply string.
75 // An i31ref value has no sign interpretation within wasm, where instructions
76 // specify the signedness. When converting to/from a JS value, an i31ref value
77 // is treated as a signed 31-bit value.
79 // The kind of value stored in an AnyRef. This is not 1:1 with the pointer tag
80 // of AnyRef as this separates the 'Null' and 'Object' cases which are
81 // collapsed in the pointer tag.
82 enum class AnyRefKind
: uint8_t {
89 // The pointer tag of an AnyRef.
90 enum class AnyRefTag
: uint8_t {
91 // This value is either a JSObject& or a null pointer.
93 // This value is a 31-bit integer.
95 // This value is a JSString*.
99 // A reference to any wasm reference type or host (JS) value. AnyRef is
100 // optimized for efficient access to objects, strings, and 31-bit integers.
102 // See the above documentation comment for more details.
106 // Get the pointer tag stored in value_.
107 AnyRefTag
pointerTag() const { return GetUintptrTag(value_
); }
109 explicit AnyRef(uintptr_t value
) : value_(value
) {}
111 static constexpr uintptr_t TagUintptr(uintptr_t value
, AnyRefTag tag
) {
112 MOZ_ASSERT(!(value
& TagMask
));
113 return value
| uintptr_t(tag
);
115 static constexpr uintptr_t UntagUintptr(uintptr_t value
) {
116 return value
& ~TagMask
;
118 static constexpr AnyRefTag
GetUintptrTag(uintptr_t value
) {
119 // Mask off all but the lowest two-bits (the tag)
120 uintptr_t rawTag
= value
& TagMask
;
121 // If the lowest bit is set, we want to normalize and only return
122 // AnyRefTag::I31. Mask off the high-bit iff the low-bit was set.
123 uintptr_t normalizedI31
= rawTag
& ~(value
<< 1);
124 return AnyRefTag(normalizedI31
);
127 // Given a 32-bit signed integer within 31-bit signed bounds, turn it into
129 static AnyRef
fromInt32(int32_t value
) {
130 MOZ_ASSERT(!int32NeedsBoxing(value
));
131 return AnyRef::fromUint32Truncate(uint32_t(value
));
135 static constexpr uintptr_t TagMask
= 0x3;
136 static constexpr uintptr_t TagShift
= 2;
137 static_assert(TagShift
<= gc::CellAlignShift
, "not enough free bits");
138 // A mask for getting the GC thing an AnyRef represents.
139 static constexpr uintptr_t GCThingMask
= ~TagMask
;
140 // A combined mask for getting the gc::Chunk for an AnyRef that is a GC
142 static constexpr uintptr_t GCThingChunkMask
=
143 GCThingMask
& ~js::gc::ChunkMask
;
145 // The representation of a null reference value throughout the compiler for
146 // when we need an integer constant. This is asserted to be equivalent to
147 // nullptr in wasm::Init.
148 static constexpr uintptr_t NullRefValue
= 0;
149 static constexpr uintptr_t InvalidRefValue
= UINTPTR_MAX
<< TagShift
;
151 // The inclusive maximum 31-bit signed integer, 2^30 - 1.
152 static constexpr int32_t MaxI31Value
= (2 << 29) - 1;
153 // The inclusive minimum 31-bit signed integer, -2^30.
154 static constexpr int32_t MinI31Value
= -(2 << 29);
156 explicit AnyRef() : value_(NullRefValue
) {}
157 MOZ_IMPLICIT
AnyRef(std::nullptr_t
) : value_(NullRefValue
) {}
159 // The null AnyRef value.
160 static AnyRef
null() { return AnyRef(NullRefValue
); }
162 // An invalid AnyRef cannot arise naturally from wasm and so can be used as
163 // a sentinel value to indicate failure from an AnyRef-returning function.
164 static AnyRef
invalid() { return AnyRef(InvalidRefValue
); }
166 // Given a JSObject* that comes from JS, turn it into AnyRef.
167 static AnyRef
fromJSObjectOrNull(JSObject
* objectOrNull
) {
168 MOZ_ASSERT(GetUintptrTag((uintptr_t)objectOrNull
) ==
169 AnyRefTag::ObjectOrNull
);
170 return AnyRef((uintptr_t)objectOrNull
);
173 // Given a JSObject& that comes from JS, turn it into AnyRef.
174 static AnyRef
fromJSObject(JSObject
& object
) {
175 MOZ_ASSERT(GetUintptrTag((uintptr_t)&object
) == AnyRefTag::ObjectOrNull
);
176 return AnyRef((uintptr_t)&object
);
179 // Given a JSString* that comes from JS, turn it into AnyRef.
180 static AnyRef
fromJSString(JSString
* string
) {
181 return AnyRef(TagUintptr((uintptr_t)string
, AnyRefTag::String
));
184 // Given a void* that comes from compiled wasm code, turn it into AnyRef.
185 static AnyRef
fromCompiledCode(void* pointer
) {
186 return AnyRef((uintptr_t)pointer
);
189 // Given a JS value, turn it into AnyRef. This returns false if boxing the
190 // value failed due to an OOM.
191 static bool fromJSValue(JSContext
* cx
, JS::HandleValue value
,
192 JS::MutableHandle
<AnyRef
> result
);
194 // fromUint32Truncate will produce an i31 from an int32 by truncating the
195 // highest bit. For values in the 31-bit range, this losslessly preserves the
196 // value. For values outside the 31-bit range, this performs 31-bit
199 // There are four cases here based on the two high bits:
200 // 00 - [0, MaxI31Value]
201 // 01 - (MaxI31Value, INT32_MAX]
202 // 10 - [INT32_MIN, MinI31Value)
203 // 11 - [MinI31Value, -1]
205 // The middle two cases can be ruled out if the value is guaranteed to be
206 // within the i31 range. Therefore if we truncate the high bit upon converting
207 // to i31 and perform a signed widening upon converting back to i32, we can
208 // losslessly represent all i31 values.
209 static AnyRef
fromUint32Truncate(uint32_t value
) {
210 // See 64-bit GPRs carrying 32-bit values invariants in MacroAssember.h
211 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
212 // Truncate the value to the 31-bit value size.
213 uintptr_t wideValue
= uintptr_t(value
& 0x7FFFFFFF);
214 #elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \
215 defined(JS_CODEGEN_RISCV64)
216 // Sign extend the value to the native pointer size.
217 uintptr_t wideValue
= uintptr_t(int64_t((uint64_t(value
) << 33)) >> 33);
219 // Transfer 32-bit value as is.
220 uintptr_t wideValue
= (uintptr_t)value
;
223 // Left shift the value by 1, truncating the high bit.
224 uintptr_t shiftedValue
= wideValue
<< 1;
225 uintptr_t taggedValue
= shiftedValue
| (uintptr_t)AnyRefTag::I31
;
226 return AnyRef(taggedValue
);
229 static bool int32NeedsBoxing(int32_t value
) {
230 // We can represent every signed 31-bit number without boxing
231 return value
< MinI31Value
|| value
> MaxI31Value
;
234 static bool doubleNeedsBoxing(double value
) {
236 if (!mozilla::NumberIsInt32(value
, &intValue
)) {
239 return int32NeedsBoxing(value
);
242 // Returns whether a JS value will need to be boxed.
243 static bool valueNeedsBoxing(JS::HandleValue value
) {
244 if (value
.isObjectOrNull() || value
.isString()) {
247 if (value
.isInt32()) {
248 return int32NeedsBoxing(value
.toInt32());
250 if (value
.isDouble()) {
251 return doubleNeedsBoxing(value
.toDouble());
256 // Box a JS Value that needs boxing.
257 static JSObject
* boxValue(JSContext
* cx
, JS::HandleValue value
);
259 bool operator==(const AnyRef
& rhs
) const {
260 return this->value_
== rhs
.value_
;
262 bool operator!=(const AnyRef
& rhs
) const { return !(*this == rhs
); }
264 // Check if this AnyRef is the invalid value.
265 bool isInvalid() const { return *this == AnyRef::invalid(); }
267 AnyRefKind
kind() const {
268 if (value_
== NullRefValue
) {
269 return AnyRefKind::Null
;
271 switch (pointerTag()) {
272 case AnyRefTag::ObjectOrNull
: {
273 // The invalid pattern uses the ObjectOrNull tag, check for it here.
274 MOZ_ASSERT(!isInvalid());
275 // We ruled out the null case above
276 return AnyRefKind::Object
;
278 case AnyRefTag::String
: {
279 return AnyRefKind::String
;
281 case AnyRefTag::I31
: {
282 return AnyRefKind::I31
;
285 MOZ_CRASH("unknown AnyRef tag");
290 bool isNull() const { return value_
== NullRefValue
; }
291 bool isGCThing() const { return !isNull() && !isI31(); }
292 bool isJSObject() const { return kind() == AnyRefKind::Object
; }
293 bool isJSString() const { return kind() == AnyRefKind::String
; }
294 bool isI31() const { return kind() == AnyRefKind::I31
; }
296 gc::Cell
* toGCThing() const {
297 MOZ_ASSERT(isGCThing());
298 return (gc::Cell
*)UntagUintptr(value_
);
300 JSObject
& toJSObject() const {
301 MOZ_ASSERT(isJSObject());
302 return *(JSObject
*)value_
;
304 JSObject
* toJSObjectOrNull() const {
305 MOZ_ASSERT(!isInvalid());
306 MOZ_ASSERT(pointerTag() == AnyRefTag::ObjectOrNull
);
307 return (JSObject
*)value_
;
309 JSString
* toJSString() const {
310 MOZ_ASSERT(isJSString());
311 return (JSString
*)UntagUintptr(value_
);
313 // Unpack an i31, interpreting the integer as signed.
314 int32_t toI31() const {
316 // On 64-bit targets, we only care about the low 4-bytes.
317 uint32_t truncatedValue
= *reinterpret_cast<const uint32_t*>(&value_
);
318 // Perform a right arithmetic shift (see AnyRef::fromI31 for more details),
319 // avoiding undefined behavior by using an unsigned type.
320 uint32_t shiftedValue
= value_
>> 1;
321 if ((truncatedValue
& (1 << 31)) != 0) {
322 shiftedValue
|= (1 << 31);
324 // Perform a bitwise cast to see the result as a signed value.
325 return *reinterpret_cast<int32_t*>(&shiftedValue
);
328 // Convert from AnyRef to a JS Value. This currently does not require any
329 // allocation. If this changes in the future, this function will become
331 JS::Value
toJSValue() const;
333 // Get the raw value for returning to wasm code.
334 void* forCompiledCode() const { return (void*)value_
; }
336 // Get the raw value for diagnostics.
337 uintptr_t rawValue() const { return value_
; }
339 // Internal details of the boxing format used by WasmStubs.cpp
340 static const JSClass
* valueBoxClass();
341 static size_t valueBoxOffsetOfValue();
344 using RootedAnyRef
= JS::Rooted
<AnyRef
>;
345 using HandleAnyRef
= JS::Handle
<AnyRef
>;
346 using MutableHandleAnyRef
= JS::MutableHandle
<AnyRef
>;
350 template <class Wrapper
>
351 class WrappedPtrOperations
<wasm::AnyRef
, Wrapper
> {
352 const wasm::AnyRef
& value() const {
353 return static_cast<const Wrapper
*>(this)->get();
357 bool isNull() const { return value().isNull(); }
358 bool isI31() const { return value().isI31(); }
359 bool isJSObject() const { return value().isJSObject(); }
360 bool isJSString() const { return value().isJSString(); }
361 JSObject
& toJSObject() const { return value().toJSObject(); }
362 JSString
* toJSString() const { return value().toJSString(); }
365 // If the Value is a GC pointer type, call |f| with the pointer cast to that
366 // type and return the result wrapped in a Maybe, otherwise return None().
367 template <typename F
>
368 auto MapGCThingTyped(const wasm::AnyRef
& val
, F
&& f
) {
369 switch (val
.kind()) {
370 case wasm::AnyRefKind::Object
:
371 return mozilla::Some(f(&val
.toJSObject()));
372 case wasm::AnyRefKind::String
:
373 return mozilla::Some(f(val
.toJSString()));
374 case wasm::AnyRefKind::I31
:
375 case wasm::AnyRefKind::Null
: {
376 using ReturnType
= decltype(f(static_cast<JSObject
*>(nullptr)));
377 return mozilla::Maybe
<ReturnType
>();
383 template <typename F
>
384 bool ApplyGCThingTyped(const wasm::AnyRef
& val
, F
&& f
) {
385 return MapGCThingTyped(val
,
398 struct GCPolicy
<js::wasm::AnyRef
> {
399 static void trace(JSTracer
* trc
, js::wasm::AnyRef
* v
, const char* name
) {
400 // This should only be called as part of root marking since that's the only
401 // time we should trace unbarriered GC thing pointers. This will assert if
402 // called at other times.
403 TraceRoot(trc
, v
, name
);
405 static bool isValid(const js::wasm::AnyRef
& v
) {
406 return !v
.isGCThing() || js::gc::IsCellPointerValid(v
.toGCThing());
412 #endif // wasm_anyref_h