зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
df5c491264
Коммит
adc407f8e4
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче