Bug 1824904 - part 2: make Wasm{Struct,Array}Object point directly at their SuperTypeVectors. r=rhunt.

Currently Wasm{Struct,Array}Object point at their relevant TypeDef, which in
turn points at the associated SuperTypeVector.  This causes all references to
the SuperTypeVector -- particularly for casting -- to need a double
indirection.  This patch makes them point directly at their SuperTypeVectors
instead.

The changes are almost all mechanical.  Points of note are:

* WasmGcObject (hence WSO/WAO) now hold a SuperTypeVector*, not a TypeDef*.

* SuperTypeVector now holds a TypeDef*; and TypeDef already has a pointer to
  its SuperTypeVector.  These two must point at each other, so as to form a
  1:1 relationship.

* TypeDefInstanceData now has both a SuperTypeVector* and TypeDef*, since both
  are needed at run time.

* The point of all the changes is so that
  MacroAssembler::branchWasmTypeDefIsSubtype -- now renamed to
  ::branchWasmSuperTypeVectorIsSubtype -- can omit the load that would chase
  from a TypeDef to its SuperTypeVector.  However, this routine is also
  changed so as to skip the fast-path check for the type defs (as-was) being
  equal; profiling suggests it is perf-neutral, and so generating smaller code
  seems preferable.

* MinSuperTypeVectorLength has been increased from 3 to 8 following profiling
  with Barista-3.  jit-test/tests/wasm/gc/casting.js is updated accordingly.

* The example in the comment explaining type vectors in WasmTypeDef.h has been
  reworked so as to make it clear that the type numbers have no relationship
  to the vector indices (this wasn't clear before).

* SuperTypeVector has been changed from a struct to a class; we seem to mostly
  use classes elsewhere, even for objects accessed from generated code.

Differential Revision: https://phabricator.services.mozilla.com/D174061
This commit is contained in:
Julian Seward 2023-04-04 09:51:33 +00:00
Родитель df5c491264
Коммит adc407f8e4
20 изменённых файлов: 251 добавлений и 140 удалений

Просмотреть файл

@ -21,9 +21,23 @@
// C1 C2 C3
// | \ |
// D1 D2 D3
// | \ |
// E1 E2 E3
// | \ |
// F1 F2 F3
// | \ |
// G1 G2 G3
// | \ |
// H1 H2 H3
// | \ |
// I1 I2 I3
// | \ |
// J1 J2 J3
//
// NOTE: this object will be mutated and needs to be ordered such that parent
// definitions come before children.
// definitions come before children. Note also, to be properly effective,
// these trees need to have a depth of at least MinSuperTypeVectorLength as
// defined in wasm/WasmCodegenConstants.h; keep it in sync with that.
const TYPES = {
'A1': { super: null },
'A2': { super: null },
@ -35,6 +49,24 @@ const TYPES = {
'D1': { super: 'C1' },
'D2': { super: 'C1' },
'D3': { super: 'C3' },
'E1': { super: 'D1' },
'E2': { super: 'D1' },
'E3': { super: 'D3' },
'F1': { super: 'E1' },
'F2': { super: 'E1' },
'F3': { super: 'E3' },
'G1': { super: 'F1' },
'G2': { super: 'F1' },
'G3': { super: 'F3' },
'H1': { super: 'G1' },
'H2': { super: 'G1' },
'H3': { super: 'G3' },
'I1': { super: 'H1' },
'I2': { super: 'H1' },
'I3': { super: 'H3' },
'J1': { super: 'I1' },
'J2': { super: 'I1' },
'J3': { super: 'I3' },
};
// The oracle method for testing the declared subtype relationship.

Просмотреть файл

@ -16655,7 +16655,7 @@ void CodeGenerator::visitWasmGcObjectIsSubtypeOf(
MOZ_ASSERT(gen->compilingWasm());
const MWasmGcObjectIsSubtypeOf* mir = ins->mir();
Register object = ToRegister(ins->object());
Register superTypeDef = ToRegister(ins->superTypeDef());
Register superSuperTypeVector = ToRegister(ins->superSuperTypeVector());
Register scratch1 = ToRegister(ins->temp0());
Register scratch2 = ins->temp1()->isBogusTemp() ? Register::Invalid()
: ToRegister(ins->temp1());
@ -16666,9 +16666,11 @@ void CodeGenerator::visitWasmGcObjectIsSubtypeOf(
masm.branchTestPtr(Assembler::Zero, object, object,
mir->succeedOnNull() ? &success : &failed);
masm.branchTestObjectIsWasmGcObject(false, object, scratch1, &failed);
masm.loadPtr(Address(object, WasmGcObject::offsetOfTypeDef()), scratch1);
masm.branchWasmTypeDefIsSubtype(scratch1, superTypeDef, scratch2,
mir->subTypingDepth(), &success, true);
masm.loadPtr(Address(object, WasmGcObject::offsetOfSuperTypeVector()),
scratch1);
masm.branchWasmSuperTypeVectorIsSubtype(scratch1, superSuperTypeVector,
scratch2, mir->subTypingDepth(),
&success, true);
masm.bind(&failed);
masm.xor32(result, result);
masm.jump(&join);
@ -16681,7 +16683,7 @@ void CodeGenerator::visitWasmGcObjectIsSubtypeOfAndBranch(
LWasmGcObjectIsSubtypeOfAndBranch* ins) {
MOZ_ASSERT(gen->compilingWasm());
Register object = ToRegister(ins->object());
Register superTypeDef = ToRegister(ins->superTypeDef());
Register superSuperTypeVector = ToRegister(ins->superSuperTypeVector());
Register scratch1 = ToRegister(ins->temp0());
Register scratch2 = ins->temp1()->isBogusTemp() ? Register::Invalid()
: ToRegister(ins->temp1());
@ -16690,9 +16692,11 @@ void CodeGenerator::visitWasmGcObjectIsSubtypeOfAndBranch(
Label* onNull = ins->succeedOnNull() ? onSuccess : onFail;
masm.branchTestPtr(Assembler::Zero, object, object, onNull);
masm.branchTestObjectIsWasmGcObject(false, object, scratch1, onFail);
masm.loadPtr(Address(object, WasmGcObject::offsetOfTypeDef()), scratch1);
masm.branchWasmTypeDefIsSubtype(scratch1, superTypeDef, scratch2,
ins->subTypingDepth(), onSuccess, true);
masm.loadPtr(Address(object, WasmGcObject::offsetOfSuperTypeVector()),
scratch1);
masm.branchWasmSuperTypeVectorIsSubtype(scratch1, superSuperTypeVector,
scratch2, ins->subTypingDepth(),
onSuccess, true);
masm.jump(onFail);
}

Просмотреть файл

@ -306,7 +306,7 @@
mir_op: true
operands:
object: WordSized
superTypeDef: WordSized
superSuperTypeVector: WordSized
result_type: WordSized
num_temps: 2

Просмотреть файл

@ -1069,14 +1069,15 @@ void LIRGenerator::visitTest(MTest* test) {
if (opd->isWasmGcObjectIsSubtypeOf() && opd->isEmittedAtUses()) {
MWasmGcObjectIsSubtypeOf* isSubTypeOf = opd->toWasmGcObjectIsSubtypeOf();
LAllocation object = useRegister(isSubTypeOf->object());
LAllocation superTypeDef = useRegister(isSubTypeOf->superTypeDef());
LAllocation superSuperTypeVector =
useRegister(isSubTypeOf->superSuperTypeVector());
uint32_t subTypingDepth = isSubTypeOf->subTypingDepth();
LDefinition subTypeDepth = temp();
LDefinition scratch = subTypingDepth >= wasm::MinSuperTypeVectorLength
? temp()
: LDefinition();
add(new (alloc()) LWasmGcObjectIsSubtypeOfAndBranch(
ifTrue, ifFalse, object, superTypeDef, subTypingDepth,
ifTrue, ifFalse, object, superSuperTypeVector, subTypingDepth,
isSubTypeOf->succeedOnNull(), subTypeDepth, scratch),
test);
return;
@ -7046,12 +7047,12 @@ void LIRGenerator::visitWasmGcObjectIsSubtypeOf(MWasmGcObjectIsSubtypeOf* ins) {
}
LAllocation object = useRegister(ins->object());
LAllocation superTypeDef = useRegister(ins->superTypeDef());
LAllocation superSuperTypeVector = useRegister(ins->superSuperTypeVector());
uint32_t subTypingDepth = ins->subTypingDepth();
LDefinition subTypeDepth = temp();
LDefinition scratch =
subTypingDepth >= wasm::MinSuperTypeVectorLength ? temp() : LDefinition();
define(new (alloc()) LWasmGcObjectIsSubtypeOf(object, superTypeDef,
define(new (alloc()) LWasmGcObjectIsSubtypeOf(object, superSuperTypeVector,
subTypeDepth, scratch),
ins);
}

Просмотреть файл

@ -11380,16 +11380,17 @@ class MWasmStoreFieldRefKA : public MAryInstruction<4>,
#endif
};
// Tests if the WasmGcObject, `object`, is a subtype of `superTypeDef`. The
// actual super type definition must be known at compile time, so that the
// Tests if the WasmGcObject, `object`, is a subtype of `superSuperTypeVector`.
// The actual super type definition must be known at compile time, so that the
// subtyping depth of super type depth can be used.
class MWasmGcObjectIsSubtypeOf : public MBinaryInstruction,
public NoTypePolicy::Data {
uint32_t subTypingDepth_;
bool succeedOnNull_;
MWasmGcObjectIsSubtypeOf(MDefinition* object, MDefinition* superTypeDef,
MWasmGcObjectIsSubtypeOf(MDefinition* object,
MDefinition* superSuperTypeVector,
uint32_t subTypingDepth, bool succeedOnNull)
: MBinaryInstruction(classOpcode, object, superTypeDef),
: MBinaryInstruction(classOpcode, object, superSuperTypeVector),
subTypingDepth_(subTypingDepth),
succeedOnNull_(succeedOnNull) {
setResultType(MIRType::Int32);
@ -11399,7 +11400,7 @@ class MWasmGcObjectIsSubtypeOf : public MBinaryInstruction,
public:
INSTRUCTION_HEADER(WasmGcObjectIsSubtypeOf)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object), (1, superTypeDef))
NAMED_OPERANDS((0, object), (1, superSuperTypeVector))
uint32_t subTypingDepth() const { return subTypingDepth_; }
bool succeedOnNull() const { return succeedOnNull_; }
@ -11408,7 +11409,7 @@ class MWasmGcObjectIsSubtypeOf : public MBinaryInstruction,
return congruentIfOperandsEqual(ins) &&
ins->toWasmGcObjectIsSubtypeOf()->subTypingDepth() ==
subTypingDepth() &&
succeedOnNull_ == ins->toWasmGcObjectIsSubtypeOf()->succeedOnNull();
succeedOnNull() == ins->toWasmGcObjectIsSubtypeOf()->succeedOnNull();
}
HashNumber valueHash() const override {

Просмотреть файл

@ -4758,12 +4758,10 @@ void MacroAssembler::wasmCallRef(const wasm::CallSiteDesc& desc,
bind(&done);
}
void MacroAssembler::branchWasmTypeDefIsSubtype(Register subTypeDef,
Register superTypeDef,
Register scratch,
uint32_t subTypingDepth,
Label* label, bool onSuccess) {
MOZ_ASSERT_IF(subTypingDepth >= wasm::MinSuperTypeVectorLength,
void MacroAssembler::branchWasmSuperTypeVectorIsSubtype(
Register subSuperTypeVector, Register superSuperTypeVector,
Register scratch, uint32_t superTypeDepth, Label* label, bool onSuccess) {
MOZ_ASSERT_IF(superTypeDepth >= wasm::MinSuperTypeVectorLength,
scratch != Register::Invalid());
// We generate just different enough code for 'is' subtype vs 'is not'
@ -4771,53 +4769,55 @@ void MacroAssembler::branchWasmTypeDefIsSubtype(Register subTypeDef,
if (onSuccess) {
Label failed;
// Fast path for the type defs being equal.
branchPtr(Assembler::Equal, subTypeDef, superTypeDef, label);
// Slower path for checking the supertype vector of `subTypeDef`. We don't
// need `subTypeDef` at this point, so we use it as a scratch for storing
// the super type vector and entry from it.
loadPtr(Address(subTypeDef, wasm::TypeDef::offsetOfSuperTypeVector()),
subTypeDef);
// At this point, we could generate a fast success check which jumps to
// `label` if `subSuperTypeVector == superSuperTypeVector`. However,
// profiling of Barista-3 seems to show this is hardly worth anything,
// whereas it is worth us generating smaller code and in particular one
// fewer conditional branch. So it is omitted:
//
// branchPtr(Assembler::Equal, subSuperTypeVector, superSuperTypeVector,
// label);
// Emit a bounds check if the super type depth may be out-of-bounds.
if (subTypingDepth >= wasm::MinSuperTypeVectorLength) {
if (superTypeDepth >= wasm::MinSuperTypeVectorLength) {
// Slowest path for having a bounds check of the super type vector
load32(Address(subTypeDef, wasm::SuperTypeVector::offsetOfLength()),
scratch);
branch32(Assembler::LessThanOrEqual, scratch, Imm32(subTypingDepth),
load32(
Address(subSuperTypeVector, wasm::SuperTypeVector::offsetOfLength()),
scratch);
branch32(Assembler::LessThanOrEqual, scratch, Imm32(superTypeDepth),
&failed);
}
// Load the `subTypingDepth` entry from subTypeDef's super type vector. This
// will be `superTypeDef` if `subTypeDef` is indeed a subtype.
loadPtr(Address(subTypeDef, wasm::SuperTypeVector::offsetOfTypeDefInVector(
subTypingDepth)),
subTypeDef);
branchPtr(Assembler::Equal, subTypeDef, superTypeDef, label);
// Load the `superTypeDepth` entry from subSuperTypeVector. This
// will be `superSuperTypeVector` if `subSuperTypeVector` is indeed a
// subtype.
loadPtr(
Address(subSuperTypeVector,
wasm::SuperTypeVector::offsetOfTypeDefInVector(superTypeDepth)),
subSuperTypeVector);
branchPtr(Assembler::Equal, subSuperTypeVector, superSuperTypeVector,
label);
// Fallthrough to the failed case
bind(&failed);
return;
}
// Load the super type vector from subTypeDef
loadPtr(Address(subTypeDef, wasm::TypeDef::offsetOfSuperTypeVector()),
subTypeDef);
// Emit a bounds check if the super type depth may be out-of-bounds.
if (subTypingDepth >= wasm::MinSuperTypeVectorLength) {
load32(Address(subTypeDef, wasm::SuperTypeVector::offsetOfLength()),
if (superTypeDepth >= wasm::MinSuperTypeVectorLength) {
load32(Address(subSuperTypeVector, wasm::SuperTypeVector::offsetOfLength()),
scratch);
branch32(Assembler::LessThanOrEqual, scratch, Imm32(subTypingDepth), label);
branch32(Assembler::LessThanOrEqual, scratch, Imm32(superTypeDepth), label);
}
// Load the `subTypingDepth` entry from subTypeDef's super type vector. This
// will be `superTypeDef` if `subTypeDef` is indeed a subtype.
loadPtr(Address(subTypeDef, wasm::SuperTypeVector::offsetOfTypeDefInVector(
subTypingDepth)),
subTypeDef);
branchPtr(Assembler::NotEqual, subTypeDef, superTypeDef, label);
// Load the `superTypeDepth` entry from subSuperTypeVector. This will be
// `superSuperTypeVector` if `subSuperTypeVector` is indeed a subtype.
loadPtr(
Address(subSuperTypeVector,
wasm::SuperTypeVector::offsetOfTypeDefInVector(superTypeDepth)),
subSuperTypeVector);
branchPtr(Assembler::NotEqual, subSuperTypeVector, superSuperTypeVector,
label);
// Fallthrough to the success case
}

Просмотреть файл

@ -3834,18 +3834,21 @@ class MacroAssembler : public MacroAssemblerSpecific {
wasm::SymbolicAddress builtin,
wasm::FailureMode failureMode);
// Perform a subtype check that `subTypeDef` is a subtype of `superTypeDef`
// branching to label depending on `onSuccess`. This method is a
// specialization of the general `wasm::TypeDef::isSubTypeOf` method for the
// case where the `superTypeDef` is statically known, which is the case for
// all wasm instructions.
// Perform a subtype check that `subSuperTypeVector` is a subtype of
// `superSuperTypeVector`, branching to `label` depending on `onSuccess`.
// This method is a specialization of the general
// `wasm::TypeDef::isSubTypeOf` method for the case where the
// `superSuperTypeVector` is statically known, which is the case for all
// wasm instructions.
//
// `scratch` is required iff the `subTypeDepth` is >=
// wasm::MinSuperTypeVectorLength. `subTypeDef` is clobbered by this method.
// `superTypeDef` is preserved.
void branchWasmTypeDefIsSubtype(Register subTypeDef, Register superTypeDef,
Register scratch, uint32_t subTypeDepth,
Label* label, bool onSuccess);
// wasm::MinSuperTypeVectorLength. `subSuperTypeVector` is clobbered by this
// method. `superSuperTypeVector` is preserved.
void branchWasmSuperTypeVectorIsSubtype(Register subSuperTypeVector,
Register superSuperTypeVector,
Register scratch,
uint32_t superTypeDepth, Label* label,
bool onSuccess);
// Compute ptr += (indexTemp32 << shift) where shift can be any value < 32.
// May destroy indexTemp32. The value of indexTemp32 must be positive, and it

Просмотреть файл

@ -3757,11 +3757,11 @@ class LWasmGcObjectIsSubtypeOfAndBranch
LIR_HEADER(WasmGcObjectIsSubtypeOfAndBranch)
static constexpr uint32_t Object = 0;
static constexpr uint32_t SuperTypeDef = 1;
static constexpr uint32_t SuperSuperTypeVector = 1;
LWasmGcObjectIsSubtypeOfAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse,
const LAllocation& object,
const LAllocation& superTypeDef,
const LAllocation& superSuperTypeVector,
uint32_t subTypingDepth, bool succeedOnNull,
const LDefinition& temp0,
const LDefinition& temp1)
@ -3771,7 +3771,7 @@ class LWasmGcObjectIsSubtypeOfAndBranch
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setOperand(Object, object);
setOperand(SuperTypeDef, superTypeDef);
setOperand(SuperSuperTypeVector, superSuperTypeVector);
setTemp(0, temp0);
setTemp(1, temp1);
}
@ -3783,7 +3783,9 @@ class LWasmGcObjectIsSubtypeOfAndBranch
MBasicBlock* ifFalse() const { return getSuccessor(1); }
const LAllocation* object() { return getOperand(Object); }
const LAllocation* superTypeDef() { return getOperand(SuperTypeDef); }
const LAllocation* superSuperTypeVector() {
return getOperand(SuperSuperTypeVector);
}
const LDefinition* temp0() { return getTemp(0); }
const LDefinition* temp1() { return getTemp(1); }
};

Просмотреть файл

@ -1668,8 +1668,8 @@ struct BaseCompiler final {
// Load a pointer to the TypeDefInstanceData for a given type index
RegPtr loadTypeDefInstanceData(uint32_t typeIndex);
// Load a pointer to the TypeDef for a given type index
RegPtr loadTypeDef(uint32_t typeIndex);
// Load a pointer to the SuperTypeVector for a given type index
RegPtr loadSuperTypeVector(uint32_t typeIndex);
// Branch to the label if the WasmGcObject `object` is/is not a subtype of
// `typeIndex`.

Просмотреть файл

@ -6377,12 +6377,12 @@ RegPtr BaseCompiler::loadTypeDefInstanceData(uint32_t typeIndex) {
return rp;
}
RegPtr BaseCompiler::loadTypeDef(uint32_t typeIndex) {
RegPtr BaseCompiler::loadSuperTypeVector(uint32_t typeIndex) {
RegPtr rp = needPtr();
# ifndef RABALDR_PIN_INSTANCE
fr.loadInstancePtr(InstanceReg);
# endif
masm.loadWasmGlobalPtr(moduleEnv_.offsetOfTypeDef(typeIndex), rp);
masm.loadWasmGlobalPtr(moduleEnv_.offsetOfSuperTypeVector(typeIndex), rp);
return rp;
}
@ -6407,7 +6407,7 @@ void BaseCompiler::branchGcObjectType(RegRef object, uint32_t typeIndex,
Label* label, bool succeedOnNull,
bool onSuccess) {
const TypeDef& castTypeDef = (*moduleEnv_.types)[typeIndex];
RegPtr superTypeDef = loadTypeDef(typeIndex);
RegPtr superSuperTypeVector = loadSuperTypeVector(typeIndex);
RegPtr scratch1 = needPtr();
RegI32 scratch2;
if (castTypeDef.subTypingDepth() >= MinSuperTypeVectorLength) {
@ -6420,17 +6420,18 @@ void BaseCompiler::branchGcObjectType(RegRef object, uint32_t typeIndex,
Label* nullLabel = succeedOnNull ? successLabel : failLabel;
masm.branchTestPtr(Assembler::Zero, object, object, nullLabel);
masm.branchTestObjectIsWasmGcObject(false, object, scratch1, failLabel);
masm.loadPtr(Address(object, WasmGcObject::offsetOfTypeDef()), scratch1);
masm.branchWasmTypeDefIsSubtype(scratch1, superTypeDef, scratch2,
castTypeDef.subTypingDepth(), label,
onSuccess);
masm.loadPtr(Address(object, WasmGcObject::offsetOfSuperTypeVector()),
scratch1);
masm.branchWasmSuperTypeVectorIsSubtype(
scratch1, superSuperTypeVector, scratch2, castTypeDef.subTypingDepth(),
label, onSuccess);
masm.bind(&fallthrough);
if (castTypeDef.subTypingDepth() >= MinSuperTypeVectorLength) {
freeI32(scratch2);
}
freePtr(scratch1);
freePtr(superTypeDef);
freePtr(superSuperTypeVector);
}
RegPtr BaseCompiler::emitGcArrayGetData(RegRef rp) {

Просмотреть файл

@ -52,11 +52,11 @@ static const uint32_t MaxInlineMemoryFillLength = 0;
#endif
// The size we round all super type vectors to. All accesses below this length
// can avoid bounds checks. The value of three was chosen here to match V8,
// eventually we should get data of our own to support this.
// can avoid bounds checks. The value of 8 was chosen after a bit of profiling
// with the Dart Barista benchmark.
//
// Keep wasm/gc/casting.js in sync with this constant.
static const uint32_t MinSuperTypeVectorLength = 3;
// Keep jit-tests/tests/wasm/gc/casting.js in sync with this constant.
static const uint32_t MinSuperTypeVectorLength = 8;
// An exported wasm function may have a 'jit entry' stub attached that can be
// called using the JS JIT ABI. This relies on the pointer we store in the

Просмотреть файл

@ -367,7 +367,7 @@ inline WasmGcObject* WasmGcObject::create(
}
obj->initShape(typeDefData->shape);
obj->typeDef_ = typeDefData->typeDef;
obj->superTypeVector_ = typeDefData->superTypeVector;
js::gc::gcprobes::CreateObject(obj);
probes::CreateObject(cx, obj);
@ -418,7 +418,8 @@ bool WasmGcObject::loadValue(JSContext* cx,
return ToJSValue(cx, arrayObj.data_ + offset.get(), type, vp);
}
bool WasmGcObject::isRuntimeSubtype(const wasm::TypeDef* parentTypeDef) const {
bool WasmGcObject::isRuntimeSubtypeOf(
const wasm::TypeDef* parentTypeDef) const {
return TypeDef::isSubTypeOf(&typeDef(), parentTypeDef);
}

Просмотреть файл

@ -27,7 +27,7 @@ namespace js {
class WasmGcObject : public JSObject {
protected:
const wasm::TypeDef* typeDef_;
const wasm::SuperTypeVector* superTypeVector_;
static const ObjectOps objectOps_;
@ -100,13 +100,21 @@ class WasmGcObject : public JSObject {
wasm::FieldType type, MutableHandleValue vp);
public:
const wasm::TypeDef& typeDef() const { return *typeDef_; }
const wasm::SuperTypeVector& superTypeVector() const {
return *superTypeVector_;
}
static size_t offsetOfTypeDef() { return offsetof(WasmGcObject, typeDef_); }
static size_t offsetOfSuperTypeVector() {
return offsetof(WasmGcObject, superTypeVector_);
}
wasm::TypeDefKind kind() const { return typeDef().kind(); }
// These are both expensive in that they involve a double indirection.
// Avoid them if possible.
const wasm::TypeDef& typeDef() const { return *superTypeVector().typeDef(); }
wasm::TypeDefKind kind() const { return superTypeVector().typeDef()->kind(); }
[[nodiscard]] bool isRuntimeSubtype(const wasm::TypeDef* parentTypeDef) const;
[[nodiscard]] bool isRuntimeSubtypeOf(
const wasm::TypeDef* parentTypeDef) const;
[[nodiscard]] static bool obj_newEnumerate(JSContext* cx, HandleObject obj,
MutableHandleIdVector properties,

Просмотреть файл

@ -1755,6 +1755,7 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports,
// Store the runtime type for this type index
typeDefData->typeDef = &typeDef;
typeDefData->superTypeVector = typeDef.superTypeVector();
if (typeDef.kind() == TypeDefKind::Struct ||
typeDef.kind() == TypeDefKind::Array) {

Просмотреть файл

@ -49,6 +49,7 @@ using ExportFuncPtr = int32_t (*)(ExportArg*, Instance*);
struct TypeDefInstanceData {
TypeDefInstanceData()
: typeDef(nullptr),
superTypeVector(nullptr),
shape(nullptr),
clasp(nullptr),
allocSite(nullptr),
@ -58,6 +59,11 @@ struct TypeDefInstanceData {
// the type context associated with the instance.
const wasm::TypeDef* typeDef;
// The supertype vector for this type definition. This is also kept alive
// by the type context associated with the instance.
//
const wasm::SuperTypeVector* superTypeVector;
// The remaining fields are only meaningful for, and used by, structs and
// arrays.
GCPtr<Shape*> shape;

Просмотреть файл

@ -3690,13 +3690,14 @@ class FunctionCompiler {
/************************************************ WasmGC: type helpers ***/
// Returns an MDefinition holding the type definition for `typeIndex`.
[[nodiscard]] MDefinition* loadTypeDef(uint32_t typeIndex) {
uint32_t typeDefOffset = moduleEnv().offsetOfTypeDef(typeIndex);
// Returns an MDefinition holding the supertype vector for `typeIndex`.
[[nodiscard]] MDefinition* loadSuperTypeVector(uint32_t typeIndex) {
uint32_t superTypeVectorOffset =
moduleEnv().offsetOfSuperTypeVector(typeIndex);
auto* load =
MWasmLoadGlobalVar::New(alloc(), MIRType::Pointer, typeDefOffset,
/*isConst=*/true, instancePointer_);
auto* load = MWasmLoadGlobalVar::New(alloc(), MIRType::Pointer,
superTypeVectorOffset,
/*isConst=*/true, instancePointer_);
if (!load) {
return nullptr;
}
@ -4207,9 +4208,9 @@ class FunctionCompiler {
[[nodiscard]] MDefinition* isGcObjectSubtypeOf(MDefinition* object,
uint32_t castTypeIndex,
bool succeedOnNull) {
auto* superTypeDef = loadTypeDef(castTypeIndex);
auto* superSuperTypeVector = loadSuperTypeVector(castTypeIndex);
auto* isSubTypeOf = MWasmGcObjectIsSubtypeOf::New(
alloc(), object, superTypeDef,
alloc(), object, superSuperTypeVector,
moduleEnv_.types->type(castTypeIndex).subTypingDepth(), succeedOnNull);
curBlock_->add(isSubTypeOf);
return isSubTypeOf;

Просмотреть файл

@ -354,7 +354,7 @@ size_t TypeDef::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
/* static */
size_t SuperTypeVector::offsetOfTypeDefInVector(uint32_t typeDefDepth) {
return offsetof(SuperTypeVector, types) + sizeof(void*) * typeDefDepth;
return offsetof(SuperTypeVector, types_) + sizeof(void*) * typeDefDepth;
}
/* static */
@ -401,21 +401,22 @@ const SuperTypeVector* SuperTypeVector::createMultipleForRecGroup(
// Compute the size again to know where the next vector can be found.
size_t vectorByteSize = SuperTypeVector::byteSizeForTypeDef(typeDef);
// Link the corresponding typeDef to this vector.
// Make the typedef and the vector point at each other.
typeDef.setSuperTypeVector(currentVector);
currentVector->setTypeDef(&typeDef);
// Every vector stores all ancestor types and itself.
currentVector->length = SuperTypeVector::lengthForTypeDef(typeDef);
currentVector->setLength(SuperTypeVector::lengthForTypeDef(typeDef));
// Initialize the entries in the vector
const TypeDef* currentTypeDef = &typeDef;
for (uint32_t index = 0; index < currentVector->length; index++) {
uint32_t reverseIndex = currentVector->length - index - 1;
for (uint32_t index = 0; index < currentVector->length(); index++) {
uint32_t reverseIndex = currentVector->length() - index - 1;
// If this entry is required just to hit the minimum size, then
// initialize it to null.
if (reverseIndex > typeDef.subTypingDepth()) {
currentVector->types[reverseIndex] = nullptr;
currentVector->setType(reverseIndex, nullptr);
continue;
}
@ -423,7 +424,7 @@ const SuperTypeVector* SuperTypeVector::createMultipleForRecGroup(
// currentTypeDef.
MOZ_ASSERT(reverseIndex == currentTypeDef->subTypingDepth());
currentVector->types[reverseIndex] = currentTypeDef;
currentVector->setType(reverseIndex, currentTypeDef->superTypeVector());
currentTypeDef = currentTypeDef->superTypeDef();
}

Просмотреть файл

@ -476,18 +476,23 @@ using ArrayTypeVector = Vector<ArrayType, 0, SystemAllocPolicy>;
// ## Example
//
// For the following type section:
// 0: (type (struct))
// 1: (type (sub 1 (struct)))
// 2: (type (sub 2 (struct)))
// 3: (type (sub 3 (struct)))
// ..
// 12: (type (struct))
// ..
// 34: (type (sub 12 (struct)))
// ..
// 56: (type (sub 34 (struct)))
// ..
// 78: (type (sub 56 (struct)))
// ..
//
// (type 0) would have the following super type vector:
// [(type 0)]
// (type 12) would have the following super type vector:
// [(type 12)]
//
// (type 3) would have the following super type vector:
// [(type 0), (type 1), (type 2), (type 3)]
// (type 78) would have the following super type vector:
// [(type 12), (type 34), (type 56), (type 78)]
//
// Checking that (type 3) <: (type 0) can use the fact that (type 0) will
// Checking that (type 78) <: (type 12) can use the fact that (type 12) will
// always be present at depth 0 of any super type vector it is in, and
// therefore check the vector at that index.
//
@ -498,29 +503,55 @@ using ArrayTypeVector = Vector<ArrayType, 0, SystemAllocPolicy>;
// against indices that we know statically are at/below that can skip bounds
// checking. Extra entries added to reach the minimum size are initialized to
// null.
struct SuperTypeVector {
class SuperTypeVector {
SuperTypeVector() : typeDef_(nullptr), length_(0) {}
// The TypeDef for which this is the supertype vector. That TypeDef should
// point back to this SuperTypeVector.
const TypeDef* typeDef_;
// The length of types stored inline below.
uint32_t length_;
public:
// Raw pointers to the super types of this type definition. Ordered from
// least-derived to most-derived. Do not add any fields after this point.
const SuperTypeVector* types_[0];
// Batch allocate super type vectors for all the types in a recursion group.
// Returns a pointer to the first super type vector, which can be used to
// free all vectors.
[[nodiscard]] static const SuperTypeVector* createMultipleForRecGroup(
RecGroup* recGroup);
const TypeDef* typeDef() const { return typeDef_; }
void setTypeDef(const TypeDef* typeDef) { typeDef_ = typeDef; }
uint32_t length() const { return length_; }
void setLength(uint32_t length) { length_ = length; }
const SuperTypeVector* type(size_t index) const {
MOZ_ASSERT(index < length_);
return types_[index];
}
void setType(size_t index, const SuperTypeVector* type) {
MOZ_ASSERT(index < length_);
types_[index] = type;
}
// The length of a super type vector for a specific type def.
static size_t lengthForTypeDef(const TypeDef& typeDef);
// The byte size of a super type vector for a specific type def.
static size_t byteSizeForTypeDef(const TypeDef& typeDef);
static size_t offsetOfLength() { return offsetof(SuperTypeVector, length); }
static size_t offsetOfLength() { return offsetof(SuperTypeVector, length_); }
static size_t offsetOfTypeDefInVector(uint32_t typeDefDepth);
// The length of types stored inline below.
uint32_t length;
// Raw pointers to the super types of this type definition. Ordered from
// least-derived to most-derived.
const TypeDef* types[0];
};
// Ensure it is safe to use `sizeof(SuperTypeVector)` to find the offset of
// `types_[0]`.
static_assert(offsetof(SuperTypeVector, types_) == sizeof(SuperTypeVector));
// A tagged container for the various types that can be present in a wasm
// module's type section.
@ -533,7 +564,11 @@ enum class TypeDefKind : uint8_t {
class TypeDef {
uint32_t offsetToRecGroup_;
// The supertype vector for this TypeDef. That SuperTypeVector should point
// back to this TypeDef.
const SuperTypeVector* superTypeVector_;
const TypeDef* superTypeDef_;
uint16_t subTypingDepth_;
TypeDefKind kind_;
@ -736,36 +771,46 @@ class TypeDef {
subTypingDepth_ = superTypeDef_->subTypingDepth_ + 1;
}
// Checks if `subType` is a declared sub type of `superType`.
static bool isSubTypeOf(const TypeDef* subType, const TypeDef* superType) {
// Checks if `subTypeDef` is a declared sub type of `superTypeDef`.
static bool isSubTypeOf(const TypeDef* subTypeDef,
const TypeDef* superTypeDef) {
// Fast path for when the types are equal
if (MOZ_LIKELY(subType == superType)) {
if (MOZ_LIKELY(subTypeDef == superTypeDef)) {
return true;
}
const SuperTypeVector* subSuperTypes = subType->superTypeVector();
const SuperTypeVector* subSuperTypeVector = subTypeDef->superTypeVector();
// During construction of a recursion group, the super type vector may not
// have been computed yet, in which case we need to fall back to a linear
// search.
if (!subSuperTypes) {
while (subType) {
if (subType == superType) {
if (!subSuperTypeVector) {
while (subTypeDef) {
if (subTypeDef == superTypeDef) {
return true;
}
subType = subType->superTypeDef();
subTypeDef = subTypeDef->superTypeDef();
}
return false;
}
// Otherwise, we need to check if `superType` is one of `subType`s super
// types by checking in `subType`s super type vector. We can use the static
// information of the depth of `superType` to index directly into the
// The supertype vector does exist. So check it points back here.
MOZ_ASSERT(subSuperTypeVector->typeDef() == subTypeDef);
// We need to check if `superTypeDef` is one of `subTypeDef`s super types
// by checking in `subTypeDef`s super type vector. We can use the static
// information of the depth of `superTypeDef` to index directly into the
// vector.
uint32_t subTypingDepth = superType->subTypingDepth();
if (subTypingDepth >= subSuperTypes->length) {
uint32_t subTypingDepth = superTypeDef->subTypingDepth();
if (subTypingDepth >= subSuperTypeVector->length()) {
return false;
}
return subSuperTypes->types[subTypingDepth] == superType;
const SuperTypeVector* superSuperTypeVector =
superTypeDef->superTypeVector();
MOZ_ASSERT(superSuperTypeVector);
MOZ_ASSERT(superSuperTypeVector->typeDef() == superTypeDef);
return subSuperTypeVector->type(subTypingDepth) == superSuperTypeVector;
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;

Просмотреть файл

@ -157,6 +157,10 @@ struct ModuleEnvironment {
return offsetOfTypeDefInstanceData(typeIndex) +
offsetof(TypeDefInstanceData, typeDef);
}
uint32_t offsetOfSuperTypeVector(uint32_t typeIndex) const {
return offsetOfTypeDefInstanceData(typeIndex) +
offsetof(TypeDefInstanceData, superTypeVector);
}
};
// ElemSegmentFlags provides methods for decoding and encoding the flags field

Просмотреть файл

@ -267,7 +267,7 @@ bool wasm::CheckTypeRefValue(JSContext* cx, const TypeDef* typeDef,
if (v.isObject()) {
JSObject& obj = v.toObject();
if (obj.is<WasmGcObject>() &&
obj.as<WasmGcObject>().isRuntimeSubtype(typeDef)) {
obj.as<WasmGcObject>().isRuntimeSubtypeOf(typeDef)) {
vp.set(AnyRef::fromJSObject(&obj.as<WasmGcObject>()));
return true;
}