зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1810090 - Disallow uses of MWasmDerived{,Index}Pointer with reftyped-bases. r=rhunt.
MWasmDerivedPointer denotes a base-plus-constant-offset value. In the presence of wasm-gc types, this is dangerous when the base value is reftyped. This is because MWasmDerivedPointer makes it possible to construct values that we have an obligation to modify at GC time, yet it does not give us any mechanism to do so, since they are not valid object pointers from the GC's point of view. MWasmDerivedIndexPointer is similarly dangerous. Hence if these values are live across a GC, they will be invalid after the GC, and hence cause a crash if used after the GC. This has been observed to happen. A reasonable fix seems to be to restrict their base types to be non-ref. For cases where the base type is ref, instead pass around a pair of the base and the (constant) offset, to the place where the access actually happens. In effect what this does is to make it impossible to create these "sort-of but not really a ref" values. The idea is simple in theory but gives rise to quite a large patch. Main changes are: * MWasmDerivedPointer::New, MWasmDerivedIndexPointer::New: assert the base type to exclude reftypes, and add comments. * change from a single `address` to a `base + constant offset` formulation, in: - wasm::EmitWasmPreBarrierGuard - wasm::EmitWasmPreBarrierCall In places where we have a single `address` but know it is "safe", because either it's non-reftyped, or isn't live across any potential GC event, but we need to use a `base + constant offset` formulation, zero is passed for the offset. * add a `base + constant offset` formulation for the following, but leave the existing `address`-only formulation in place: - Instance::postBarrierPreciseWithOffset * remove FunctionCompiler::writeGcValueAtAddress and route all such traffic through FunctionCompiler::writeGcValueAtBasePlusOffset * For constructors of MWasmStoreRef, MWasmLoadField, MWasmLoadFieldKA, MWasmStoreFieldKA, MWasmStoreFieldRefKA, accept an offset of type size_t, but restrict it to 0 .. 2^31-1 (a pre-existing limitation from MWasmDerivedPointer) so as to ensure that it is a valid offset to give to a macroassembler `Address` constructor. However, actually store such values in the MIR as a uint32_t so as not to waste space. * [ridealong] add some missing `getExtras` (debug-printing) methods for MWasmStoreRef MWasmLoadField MWasmLoadFieldKA MWasmStoreFieldKA MWasmStoreFieldRefKA. * [ridealong] some minor rearranging of access method orderings for MWasmLoadField et al to make them more consistent. Differential Revision: https://phabricator.services.mozilla.com/D166899
This commit is contained in:
Родитель
2de4d5e5b0
Коммит
114a9dd6fc
|
@ -8373,17 +8373,18 @@ void CodeGenerator::visitWasmDerivedIndexPointer(
|
|||
|
||||
void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
|
||||
Register instance = ToRegister(ins->instance());
|
||||
Register valueAddr = ToRegister(ins->valueAddr());
|
||||
Register valueBase = ToRegister(ins->valueBase());
|
||||
size_t offset = ins->offset();
|
||||
Register value = ToRegister(ins->value());
|
||||
Register temp = ToRegister(ins->temp0());
|
||||
|
||||
Label skipPreBarrier;
|
||||
wasm::EmitWasmPreBarrierGuard(masm, instance, temp, valueAddr,
|
||||
wasm::EmitWasmPreBarrierGuard(masm, instance, temp, valueBase, offset,
|
||||
&skipPreBarrier);
|
||||
wasm::EmitWasmPreBarrierCall(masm, instance, temp, valueAddr);
|
||||
wasm::EmitWasmPreBarrierCall(masm, instance, temp, valueBase, offset);
|
||||
masm.bind(&skipPreBarrier);
|
||||
|
||||
masm.storePtr(value, Address(valueAddr, 0));
|
||||
masm.storePtr(value, Address(valueBase, offset));
|
||||
// The postbarrier is handled separately.
|
||||
}
|
||||
|
||||
|
|
|
@ -898,6 +898,9 @@ enum ABIFunctionType : uint64_t {
|
|||
Args_Int32_GeneralInt32Int32Int32Int32General = detail::MakeABIFunctionType(
|
||||
ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32,
|
||||
ArgType_Int32, ArgType_Int32, ArgType_General}),
|
||||
Args_Int32_GeneralGeneralInt32General = detail::MakeABIFunctionType(
|
||||
ArgType_Int32,
|
||||
{ArgType_General, ArgType_General, ArgType_Int32, ArgType_General}),
|
||||
Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 =
|
||||
detail::MakeABIFunctionType(
|
||||
ArgType_Int32,
|
||||
|
|
|
@ -2919,8 +2919,10 @@
|
|||
- name: WasmStoreRef
|
||||
operands:
|
||||
instance: WordSized
|
||||
valueAddr: WordSized
|
||||
valueBase: WordSized
|
||||
value: WordSized
|
||||
arguments:
|
||||
offset: uint32_t
|
||||
num_temps: 1
|
||||
mir_op: true
|
||||
|
||||
|
|
|
@ -5483,9 +5483,12 @@ void LIRGenerator::visitWasmDerivedIndexPointer(MWasmDerivedIndexPointer* ins) {
|
|||
|
||||
void LIRGenerator::visitWasmStoreRef(MWasmStoreRef* ins) {
|
||||
LAllocation instance = useRegister(ins->instance());
|
||||
LAllocation valueAddr = useFixed(ins->valueAddr(), PreBarrierReg);
|
||||
LAllocation valueBase = useFixed(ins->valueBase(), PreBarrierReg);
|
||||
LAllocation value = useRegister(ins->value());
|
||||
add(new (alloc()) LWasmStoreRef(instance, valueAddr, value, temp()), ins);
|
||||
uint32_t valueOffset = ins->offset();
|
||||
add(new (alloc())
|
||||
LWasmStoreRef(instance, valueBase, value, temp(), valueOffset),
|
||||
ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmParameter(MWasmParameter* ins) {
|
||||
|
@ -6884,7 +6887,7 @@ void LIRGenerator::visitWasmFence(MWasmFence* ins) {
|
|||
}
|
||||
|
||||
void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) {
|
||||
size_t offs = ins->offset();
|
||||
uint32_t offs = ins->offset();
|
||||
LAllocation obj = useRegister(ins->obj());
|
||||
MWideningOp wideningOp = ins->wideningOp();
|
||||
if (ins->type() == MIRType::Int64) {
|
||||
|
@ -6899,7 +6902,7 @@ void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) {
|
|||
}
|
||||
|
||||
void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) {
|
||||
size_t offs = ins->offset();
|
||||
uint32_t offs = ins->offset();
|
||||
LAllocation obj = useRegister(ins->obj());
|
||||
MWideningOp wideningOp = ins->wideningOp();
|
||||
if (ins->type() == MIRType::Int64) {
|
||||
|
@ -6916,7 +6919,7 @@ void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) {
|
|||
|
||||
void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) {
|
||||
MDefinition* value = ins->value();
|
||||
size_t offs = ins->offset();
|
||||
uint32_t offs = ins->offset();
|
||||
MNarrowingOp narrowingOp = ins->narrowingOp();
|
||||
LAllocation obj = useRegister(ins->obj());
|
||||
LInstruction* lir;
|
||||
|
@ -6935,9 +6938,10 @@ void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) {
|
|||
|
||||
void LIRGenerator::visitWasmStoreFieldRefKA(MWasmStoreFieldRefKA* ins) {
|
||||
LAllocation instance = useRegister(ins->instance());
|
||||
LAllocation valueAddr = useFixed(ins->valueAddr(), PreBarrierReg);
|
||||
LAllocation obj = useFixed(ins->obj(), PreBarrierReg);
|
||||
LAllocation value = useRegister(ins->value());
|
||||
add(new (alloc()) LWasmStoreRef(instance, valueAddr, value, temp()), ins);
|
||||
uint32_t offset = ins->offset();
|
||||
add(new (alloc()) LWasmStoreRef(instance, obj, value, temp(), offset), ins);
|
||||
add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
|
||||
}
|
||||
|
||||
|
|
185
js/src/jit/MIR.h
185
js/src/jit/MIR.h
|
@ -10003,25 +10003,33 @@ class MWasmStoreStackResult : public MBinaryInstruction,
|
|||
// Represents a known-good derived pointer into an object or memory region (in
|
||||
// the most general sense) that will not move while the derived pointer is live.
|
||||
// The `offset` *must* be a valid offset into the object represented by `base`;
|
||||
// hence overflow in the address calculation will never be an issue.
|
||||
// hence overflow in the address calculation will never be an issue. `offset`
|
||||
// must be representable as a 31-bit unsigned integer.
|
||||
//
|
||||
// DO NOT use this with a base value of any JS-heap-resident object type.
|
||||
// Such a value would need to be adjusted during GC, yet we have no mechanism
|
||||
// to do that. See bug 1810090.
|
||||
|
||||
class MWasmDerivedPointer : public MUnaryInstruction,
|
||||
public NoTypePolicy::Data {
|
||||
MWasmDerivedPointer(MDefinition* base, size_t offset)
|
||||
: MUnaryInstruction(classOpcode, base), offset_(offset) {
|
||||
: MUnaryInstruction(classOpcode, base), offset_(uint32_t(offset)) {
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
// Do not change this to allow `base` to be a GC-heap allocated type.
|
||||
MOZ_ASSERT(base->type() == MIRType::Pointer ||
|
||||
base->type() == TargetWordMIRType());
|
||||
setResultType(MIRType::Pointer);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
size_t offset_;
|
||||
uint32_t offset_;
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(WasmDerivedPointer)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, base))
|
||||
|
||||
size_t offset() const { return offset_; }
|
||||
uint32_t offset() const { return offset_; }
|
||||
|
||||
AliasSet getAliasSet() const override { return AliasSet::None(); }
|
||||
|
||||
|
@ -10041,10 +10049,14 @@ class MWasmDerivedPointer : public MUnaryInstruction,
|
|||
ALLOW_CLONE(MWasmDerivedPointer)
|
||||
};
|
||||
|
||||
// As with MWasmDerivedPointer, DO NOT use this with a base value of any
|
||||
// JS-heap-resident object type.
|
||||
class MWasmDerivedIndexPointer : public MBinaryInstruction,
|
||||
public NoTypePolicy::Data {
|
||||
MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale)
|
||||
: MBinaryInstruction(classOpcode, base, index), scale_(scale) {
|
||||
// Do not change this to allow `base` to be a GC-heap allocated type.
|
||||
MOZ_ASSERT(base->type() == MIRType::Pointer);
|
||||
setResultType(MIRType::Pointer);
|
||||
setMovable();
|
||||
}
|
||||
|
@ -10071,27 +10083,42 @@ class MWasmDerivedIndexPointer : public MBinaryInstruction,
|
|||
|
||||
// Stores a reference to an address. This performs a pre-barrier on the address,
|
||||
// but not a post-barrier. A post-barrier must be performed separately, if it's
|
||||
// required.
|
||||
// required. The accessed location is `valueBase + valueOffset`. The latter
|
||||
// must be be representable as a 31-bit unsigned integer.
|
||||
|
||||
class MWasmStoreRef : public MAryInstruction<3>, public NoTypePolicy::Data {
|
||||
uint32_t offset_;
|
||||
AliasSet::Flag aliasSet_;
|
||||
|
||||
MWasmStoreRef(MDefinition* instance, MDefinition* valueAddr,
|
||||
MDefinition* value, AliasSet::Flag aliasSet)
|
||||
: MAryInstruction<3>(classOpcode), aliasSet_(aliasSet) {
|
||||
MOZ_ASSERT(valueAddr->type() == MIRType::Pointer);
|
||||
MWasmStoreRef(MDefinition* instance, MDefinition* valueBase,
|
||||
size_t valueOffset, MDefinition* value, AliasSet::Flag aliasSet)
|
||||
: MAryInstruction<3>(classOpcode),
|
||||
offset_(uint32_t(valueOffset)),
|
||||
aliasSet_(aliasSet) {
|
||||
MOZ_ASSERT(valueOffset <= INT32_MAX);
|
||||
MOZ_ASSERT(valueBase->type() == MIRType::Pointer ||
|
||||
valueBase->type() == MIRType::StackResults);
|
||||
MOZ_ASSERT(value->type() == MIRType::RefOrNull);
|
||||
initOperand(0, instance);
|
||||
initOperand(1, valueAddr);
|
||||
initOperand(1, valueBase);
|
||||
initOperand(2, value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(WasmStoreRef)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, instance), (1, valueAddr), (2, value))
|
||||
NAMED_OPERANDS((0, instance), (1, valueBase), (2, value))
|
||||
|
||||
uint32_t offset() const { return offset_; }
|
||||
AliasSet getAliasSet() const override { return AliasSet::Store(aliasSet_); }
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
void getExtras(ExtrasCollector* extras) override {
|
||||
char buf[64];
|
||||
SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_);
|
||||
extras->add(buf);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class MWasmParameter : public MNullaryInstruction {
|
||||
|
@ -10960,10 +10987,46 @@ class MIonToWasmCall final : public MVariadicInstruction,
|
|||
// Indicates how to widen an 8- or 16-bit value (when it is read from memory).
|
||||
enum class MWideningOp : uint8_t { None, FromU16, FromS16, FromU8, FromS8 };
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
static inline const char* StringFromMWideningOp(MWideningOp op) {
|
||||
switch (op) {
|
||||
case MWideningOp::None:
|
||||
return "None";
|
||||
case MWideningOp::FromU16:
|
||||
return "FromU16";
|
||||
case MWideningOp::FromS16:
|
||||
return "FromS16";
|
||||
case MWideningOp::FromU8:
|
||||
return "FromU8";
|
||||
case MWideningOp::FromS8:
|
||||
return "FromS8";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("Unknown MWideningOp");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Indicates how to narrow a 32-bit value (when it is written to memory). The
|
||||
// operation is a simple truncate.
|
||||
enum class MNarrowingOp : uint8_t { None, To16, To8 };
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
static inline const char* StringFromMNarrowingOp(MNarrowingOp op) {
|
||||
switch (op) {
|
||||
case MNarrowingOp::None:
|
||||
return "None";
|
||||
case MNarrowingOp::To16:
|
||||
return "To16";
|
||||
case MNarrowingOp::To8:
|
||||
return "To8";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("Unknown MNarrowingOp");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Provide information about potential trap at the instruction machine code,
|
||||
// e.g. null pointer dereference.
|
||||
struct TrapSiteInfo {
|
||||
|
@ -10975,7 +11038,7 @@ typedef mozilla::Maybe<TrapSiteInfo> MaybeTrapSiteInfo;
|
|||
|
||||
// Load an object field stored at a fixed offset from a base pointer. This
|
||||
// field may be any value type, including references. No barriers are
|
||||
// performed.
|
||||
// performed. The offset must be representable as a 31-bit unsigned integer.
|
||||
class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
|
||||
uint32_t offset_;
|
||||
MWideningOp wideningOp_;
|
||||
|
@ -10986,10 +11049,11 @@ class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
|
|||
MWideningOp wideningOp, AliasSet aliases,
|
||||
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
|
||||
: MUnaryInstruction(classOpcode, obj),
|
||||
offset_(offset),
|
||||
offset_(uint32_t(offset)),
|
||||
wideningOp_(wideningOp),
|
||||
aliases_(aliases),
|
||||
maybeTrap_(maybeTrap) {
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
// "if you want to widen the value when it is loaded, the destination type
|
||||
// must be Int32".
|
||||
MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32);
|
||||
|
@ -11014,8 +11078,9 @@ class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
|
|||
|
||||
uint32_t offset() const { return offset_; }
|
||||
MWideningOp wideningOp() const { return wideningOp_; }
|
||||
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
// In the limited case where this insn is used to read
|
||||
// WasmStructObject::outlineData_ (the field itself, not what it points
|
||||
|
@ -11032,32 +11097,43 @@ class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
|
|||
getAliasSet().flags() ==
|
||||
AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).flags();
|
||||
}
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
void getExtras(ExtrasCollector* extras) override {
|
||||
char buf[96];
|
||||
SprintfLiteral(buf, "(offs=%lld, wideningOp=%s)", (long long int)offset_,
|
||||
StringFromMWideningOp(wideningOp_));
|
||||
extras->add(buf);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Load a object field stored at a fixed offset from a base pointer. This
|
||||
// field may be any value type, including references. No barriers are
|
||||
// performed.
|
||||
// Loads a value from a location, denoted as a fixed offset from a base
|
||||
// pointer, which (it is assumed) is within a wasm object. This field may be
|
||||
// any value type, including references. No barriers are performed.
|
||||
//
|
||||
// This instruction takes a pointer to a second object `ka`, which it is
|
||||
// necessary to keep alive. It is expected that `ka` holds a reference to
|
||||
// `obj`, but this is not enforced and no code is generated to access `ka`.
|
||||
// This instruction extends the lifetime of `ka` so that it, and hence `obj`,
|
||||
// cannot be collected while `obj` is live. This is necessary if `obj` does
|
||||
// not point to a GC-managed object.
|
||||
// not point to a GC-managed object. `offset` must be representable as a
|
||||
// 31-bit unsigned integer.
|
||||
class MWasmLoadFieldKA : public MBinaryInstruction, public NoTypePolicy::Data {
|
||||
uint32_t offset_;
|
||||
MWideningOp wideningOp_;
|
||||
AliasSet aliases_;
|
||||
MaybeTrapSiteInfo maybeTrap_;
|
||||
|
||||
MWasmLoadFieldKA(MDefinition* ka, MDefinition* obj, uint32_t offset,
|
||||
MWasmLoadFieldKA(MDefinition* ka, MDefinition* obj, size_t offset,
|
||||
MIRType type, MWideningOp wideningOp, AliasSet aliases,
|
||||
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
|
||||
: MBinaryInstruction(classOpcode, ka, obj),
|
||||
offset_(offset),
|
||||
offset_(uint32_t(offset)),
|
||||
wideningOp_(wideningOp),
|
||||
aliases_(aliases),
|
||||
maybeTrap_(maybeTrap) {
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32);
|
||||
MOZ_ASSERT(
|
||||
aliases.flags() ==
|
||||
|
@ -11080,14 +11156,24 @@ class MWasmLoadFieldKA : public MBinaryInstruction, public NoTypePolicy::Data {
|
|||
|
||||
uint32_t offset() const { return offset_; }
|
||||
MWideningOp wideningOp() const { return wideningOp_; }
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
|
||||
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
#ifdef JS_JITSPEW
|
||||
void getExtras(ExtrasCollector* extras) override {
|
||||
char buf[96];
|
||||
SprintfLiteral(buf, "(offs=%lld, wideningOp=%s)", (long long int)offset_,
|
||||
StringFromMWideningOp(wideningOp_));
|
||||
extras->add(buf);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Store a value to an object field at a fixed offset from a base pointer.
|
||||
// This field may be any value type, _excluding_ references. References
|
||||
// _must_ use the 'Ref' variant of this instruction.
|
||||
// Stores a non-reference value to anlocation, denoted as a fixed offset from
|
||||
// a base pointer, which (it is assumed) is within a wasm object. This field
|
||||
// may be any value type, _excluding_ references. References _must_ use the
|
||||
// 'Ref' variant of this instruction. The offset must be representable as a
|
||||
// 31-bit unsigned integer.
|
||||
//
|
||||
// This instruction takes a second object `ka` that must be kept alive, as
|
||||
// described for MWasmLoadFieldKA above.
|
||||
|
@ -11098,15 +11184,16 @@ class MWasmStoreFieldKA : public MTernaryInstruction,
|
|||
AliasSet aliases_;
|
||||
MaybeTrapSiteInfo maybeTrap_;
|
||||
|
||||
MWasmStoreFieldKA(MDefinition* ka, MDefinition* obj, uint32_t offset,
|
||||
MWasmStoreFieldKA(MDefinition* ka, MDefinition* obj, size_t offset,
|
||||
MDefinition* value, MNarrowingOp narrowingOp,
|
||||
AliasSet aliases,
|
||||
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
|
||||
: MTernaryInstruction(classOpcode, ka, obj, value),
|
||||
offset_(offset),
|
||||
offset_(uint32_t(offset)),
|
||||
narrowingOp_(narrowingOp),
|
||||
aliases_(aliases),
|
||||
maybeTrap_(maybeTrap) {
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
MOZ_ASSERT(value->type() != MIRType::RefOrNull);
|
||||
// "if you want to narrow the value when it is stored, the source type
|
||||
// must be Int32".
|
||||
|
@ -11132,27 +11219,40 @@ class MWasmStoreFieldKA : public MTernaryInstruction,
|
|||
|
||||
uint32_t offset() const { return offset_; }
|
||||
MNarrowingOp narrowingOp() const { return narrowingOp_; }
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
|
||||
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
#ifdef JS_JITSPEW
|
||||
void getExtras(ExtrasCollector* extras) override {
|
||||
char buf[96];
|
||||
SprintfLiteral(buf, "(offs=%lld, narrowingOp=%s)", (long long int)offset_,
|
||||
StringFromMNarrowingOp(narrowingOp_));
|
||||
extras->add(buf);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Store a reference value to a location which (it is assumed) is within a
|
||||
// wasm object. This instruction emits a pre-barrier. A post barrier _must_
|
||||
// be performed separately.
|
||||
// Stores a reference value to a location, denoted as a fixed offset from a
|
||||
// base pointer, which (it is assumed) is within a wasm object. This
|
||||
// instruction emits a pre-barrier. A post barrier _must_ be performed
|
||||
// separately. The offset must be representable as a 31-bit unsigned integer.
|
||||
//
|
||||
// This instruction takes a second object `ka` that must be kept alive, as
|
||||
// described for MWasmLoadFieldKA above.
|
||||
class MWasmStoreFieldRefKA : public MAryInstruction<4>,
|
||||
public NoTypePolicy::Data {
|
||||
uint32_t offset_;
|
||||
AliasSet aliases_;
|
||||
|
||||
MWasmStoreFieldRefKA(MDefinition* instance, MDefinition* ka,
|
||||
MDefinition* valueAddr, MDefinition* value,
|
||||
AliasSet aliases)
|
||||
: MAryInstruction<4>(classOpcode), aliases_(aliases) {
|
||||
MOZ_ASSERT(valueAddr->type() == MIRType::Pointer ||
|
||||
valueAddr->type() == TargetWordMIRType());
|
||||
MWasmStoreFieldRefKA(MDefinition* instance, MDefinition* ka, MDefinition* obj,
|
||||
size_t offset, MDefinition* value, AliasSet aliases)
|
||||
: MAryInstruction<4>(classOpcode),
|
||||
offset_(uint32_t(offset)),
|
||||
aliases_(aliases) {
|
||||
MOZ_ASSERT(obj->type() == TargetWordMIRType() ||
|
||||
obj->type() == MIRType::Pointer ||
|
||||
obj->type() == MIRType::RefOrNull);
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
MOZ_ASSERT(value->type() == MIRType::RefOrNull);
|
||||
MOZ_ASSERT(
|
||||
aliases.flags() ==
|
||||
|
@ -11164,16 +11264,25 @@ class MWasmStoreFieldRefKA : public MAryInstruction<4>,
|
|||
aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
|
||||
initOperand(0, instance);
|
||||
initOperand(1, ka);
|
||||
initOperand(2, valueAddr);
|
||||
initOperand(2, obj);
|
||||
initOperand(3, value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(WasmStoreFieldRefKA)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, instance), (1, ka), (2, valueAddr), (3, value))
|
||||
NAMED_OPERANDS((0, instance), (1, ka), (2, obj), (3, value))
|
||||
|
||||
uint32_t offset() const { return offset_; }
|
||||
AliasSet getAliasSet() const override { return aliases_; }
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
void getExtras(ExtrasCollector* extras) override {
|
||||
char buf[64];
|
||||
SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_);
|
||||
extras->add(buf);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Tests if the WasmGcObject, `object`, is a subtype of `superTypeDef`. The
|
||||
|
|
|
@ -2425,6 +2425,8 @@ typedef int32_t (*Prototype_General_GeneralInt32General)(int32_t, int32_t,
|
|||
int32_t);
|
||||
typedef int32_t (*Prototype_General_GeneralInt32Int32GeneralInt32)(
|
||||
int32_t, int32_t, int32_t, int32_t, int32_t);
|
||||
typedef int32_t (*Prototype_Int32_GeneralGeneralInt32General)(int32_t, int32_t,
|
||||
int32_t, int32_t);
|
||||
typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)(
|
||||
int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
|
||||
typedef int64_t (*Prototype_Int64_General)(int32_t);
|
||||
|
@ -3140,6 +3142,15 @@ void Simulator::softwareInterrupt(SimInstruction* instr) {
|
|||
setCallResult(result);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int32_GeneralGeneralInt32General: {
|
||||
Prototype_Int32_GeneralGeneralInt32General target =
|
||||
reinterpret_cast<Prototype_Int32_GeneralGeneralInt32General>(
|
||||
external);
|
||||
int64_t result = target(arg0, arg1, arg2, arg3);
|
||||
scratchVolatileRegisters(/* scratchFloat = true */);
|
||||
setCallResult(result);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32: {
|
||||
Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 target =
|
||||
reinterpret_cast<
|
||||
|
|
|
@ -657,6 +657,10 @@ typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)(
|
|||
int32_t,
|
||||
int32_t,
|
||||
int32_t);
|
||||
typedef int32_t (*Prototype_Int32_GeneralGeneralInt32General)(int64_t,
|
||||
int64_t,
|
||||
int32_t,
|
||||
int64_t);
|
||||
typedef int64_t (*Prototype_Int64_General)(int64_t);
|
||||
typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t);
|
||||
|
||||
|
@ -1055,6 +1059,13 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
setGPR32Result(ret);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int32_GeneralGeneralInt32General: {
|
||||
int32_t ret =
|
||||
reinterpret_cast<Prototype_Int32_GeneralGeneralInt32General>(
|
||||
nativeFn)(x0, x1, x2, x3);
|
||||
setGPR32Result(ret);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int64_General: {
|
||||
int64_t ret =
|
||||
reinterpret_cast<Prototype_Int64_General>(
|
||||
|
|
|
@ -3265,7 +3265,7 @@ class LWasmDerivedPointer : public LInstructionHelper<1, 1, 0> {
|
|||
setOperand(0, base);
|
||||
}
|
||||
const LAllocation* base() { return getOperand(0); }
|
||||
size_t offset() { return mirRaw()->toWasmDerivedPointer()->offset(); }
|
||||
uint32_t offset() { return mirRaw()->toWasmDerivedPointer()->offset(); }
|
||||
};
|
||||
|
||||
class LWasmDerivedIndexPointer : public LInstructionHelper<1, 2, 0> {
|
||||
|
|
|
@ -6206,7 +6206,8 @@ void BaseCompiler::emitPreBarrier(RegPtr valueAddr) {
|
|||
fr.loadInstancePtr(instance);
|
||||
#endif
|
||||
|
||||
EmitWasmPreBarrierGuard(masm, instance, scratch, valueAddr, &skipBarrier);
|
||||
EmitWasmPreBarrierGuard(masm, instance, scratch, valueAddr,
|
||||
/*valueOffset=*/0, &skipBarrier);
|
||||
|
||||
#ifndef RABALDR_PIN_INSTANCE
|
||||
fr.loadInstancePtr(instance);
|
||||
|
@ -6219,7 +6220,7 @@ void BaseCompiler::emitPreBarrier(RegPtr valueAddr) {
|
|||
masm.Mov(x28, sp);
|
||||
#endif
|
||||
// The prebarrier call preserves all volatile registers
|
||||
EmitWasmPreBarrierCall(masm, instance, scratch, valueAddr);
|
||||
EmitWasmPreBarrierCall(masm, instance, scratch, valueAddr, /*valueOffset=*/0);
|
||||
|
||||
masm.bind(&skipBarrier);
|
||||
}
|
||||
|
|
|
@ -268,6 +268,12 @@ const SymbolicAddressSignature SASigPostBarrierPrecise = {
|
|||
_Infallible,
|
||||
3,
|
||||
{_PTR, _PTR, _RoN, _END}};
|
||||
const SymbolicAddressSignature SASigPostBarrierPreciseWithOffset = {
|
||||
SymbolicAddress::PostBarrierPreciseWithOffset,
|
||||
_VOID,
|
||||
_Infallible,
|
||||
4,
|
||||
{_PTR, _PTR, _I32, _RoN, _END}};
|
||||
const SymbolicAddressSignature SASigPostBarrierFiltering = {
|
||||
SymbolicAddress::PostBarrierFiltering,
|
||||
_VOID,
|
||||
|
@ -1258,6 +1264,10 @@ void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
|
|||
*abiType = Args_Int32_GeneralGeneralGeneral;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrierPrecise));
|
||||
return FuncCast(Instance::postBarrierPrecise, *abiType);
|
||||
case SymbolicAddress::PostBarrierPreciseWithOffset:
|
||||
*abiType = Args_Int32_GeneralGeneralInt32General;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrierPreciseWithOffset));
|
||||
return FuncCast(Instance::postBarrierPreciseWithOffset, *abiType);
|
||||
case SymbolicAddress::PreBarrierFiltering:
|
||||
*abiType = Args_Int32_GeneralGeneral;
|
||||
MOZ_ASSERT(*abiType == ToABIType(SASigPreBarrierFiltering));
|
||||
|
@ -1460,6 +1470,7 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
|
|||
case SymbolicAddress::PreBarrierFiltering:
|
||||
case SymbolicAddress::PostBarrier:
|
||||
case SymbolicAddress::PostBarrierPrecise:
|
||||
case SymbolicAddress::PostBarrierPreciseWithOffset:
|
||||
case SymbolicAddress::PostBarrierFiltering:
|
||||
case SymbolicAddress::StructNew:
|
||||
case SymbolicAddress::ExceptionNew:
|
||||
|
|
|
@ -122,6 +122,7 @@ enum class SymbolicAddress {
|
|||
PreBarrierFiltering,
|
||||
PostBarrier,
|
||||
PostBarrierPrecise,
|
||||
PostBarrierPreciseWithOffset,
|
||||
PostBarrierFiltering,
|
||||
StructNew,
|
||||
ExceptionNew,
|
||||
|
@ -245,6 +246,7 @@ extern const SymbolicAddressSignature SASigRefFunc;
|
|||
extern const SymbolicAddressSignature SASigPreBarrierFiltering;
|
||||
extern const SymbolicAddressSignature SASigPostBarrier;
|
||||
extern const SymbolicAddressSignature SASigPostBarrierPrecise;
|
||||
extern const SymbolicAddressSignature SASigPostBarrierPreciseWithOffset;
|
||||
extern const SymbolicAddressSignature SASigPostBarrierFiltering;
|
||||
extern const SymbolicAddressSignature SASigStructNew;
|
||||
extern const SymbolicAddressSignature SASigExceptionNew;
|
||||
|
|
|
@ -1593,6 +1593,7 @@ static const char* ThunkedNativeToDescription(SymbolicAddress func) {
|
|||
return "call to native filtering GC prebarrier (in wasm)";
|
||||
case SymbolicAddress::PostBarrier:
|
||||
case SymbolicAddress::PostBarrierPrecise:
|
||||
case SymbolicAddress::PostBarrierPreciseWithOffset:
|
||||
case SymbolicAddress::PostBarrierFiltering:
|
||||
return "call to native GC postbarrier (in wasm)";
|
||||
case SymbolicAddress::StructNew:
|
||||
|
|
|
@ -204,7 +204,7 @@ bool wasm::GenerateStackmapEntriesForTrapExit(
|
|||
|
||||
void wasm::EmitWasmPreBarrierGuard(MacroAssembler& masm, Register instance,
|
||||
Register scratch, Register valueAddr,
|
||||
Label* skipBarrier) {
|
||||
size_t valueOffset, Label* skipBarrier) {
|
||||
// If no incremental GC has started, we don't need the barrier.
|
||||
masm.loadPtr(
|
||||
Address(instance, Instance::offsetOfAddressOfNeedsIncrementalBarrier()),
|
||||
|
@ -213,12 +213,13 @@ void wasm::EmitWasmPreBarrierGuard(MacroAssembler& masm, Register instance,
|
|||
skipBarrier);
|
||||
|
||||
// If the previous value is null, we don't need the barrier.
|
||||
masm.loadPtr(Address(valueAddr, 0), scratch);
|
||||
masm.loadPtr(Address(valueAddr, valueOffset), scratch);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch, scratch, skipBarrier);
|
||||
}
|
||||
|
||||
void wasm::EmitWasmPreBarrierCall(MacroAssembler& masm, Register instance,
|
||||
Register scratch, Register valueAddr) {
|
||||
Register scratch, Register valueAddr,
|
||||
size_t valueOffset) {
|
||||
MOZ_ASSERT(valueAddr == PreBarrierReg);
|
||||
|
||||
masm.loadPtr(Address(instance, Instance::offsetOfPreBarrierCode()), scratch);
|
||||
|
|
|
@ -449,7 +449,7 @@ wasm::StackMap* ConvertStackMapBoolVectorToStackMap(
|
|||
|
||||
void EmitWasmPreBarrierGuard(jit::MacroAssembler& masm, Register instance,
|
||||
Register scratch, Register valueAddr,
|
||||
Label* skipBarrier);
|
||||
size_t valueOffset, Label* skipBarrier);
|
||||
|
||||
// Before storing a GC pointer value in memory, call out-of-line prebarrier
|
||||
// code. This assumes `PreBarrierReg` contains the address that will be updated.
|
||||
|
@ -460,7 +460,8 @@ void EmitWasmPreBarrierGuard(jit::MacroAssembler& masm, Register instance,
|
|||
// It is OK for `instance` and `scratch` to be the same register.
|
||||
|
||||
void EmitWasmPreBarrierCall(jit::MacroAssembler& masm, Register instance,
|
||||
Register scratch, Register valueAddr);
|
||||
Register scratch, Register valueAddr,
|
||||
size_t valueOffset);
|
||||
|
||||
// After storing a GC pointer value in memory, skip to `skipBarrier` if a
|
||||
// postbarrier is not needed. If the location being set is in an heap-allocated
|
||||
|
|
|
@ -1119,7 +1119,17 @@ bool Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
JSObject** location,
|
||||
JSObject* prev) {
|
||||
MOZ_ASSERT(SASigPostBarrierPrecise.failureMode == FailureMode::Infallible);
|
||||
MOZ_ASSERT(location);
|
||||
postBarrierPreciseWithOffset(instance, location, /*offset=*/0, prev);
|
||||
}
|
||||
|
||||
/* static */ void Instance::postBarrierPreciseWithOffset(Instance* instance,
|
||||
JSObject** base,
|
||||
uint32_t offset,
|
||||
JSObject* prev) {
|
||||
MOZ_ASSERT(SASigPostBarrierPreciseWithOffset.failureMode ==
|
||||
FailureMode::Infallible);
|
||||
MOZ_ASSERT(base);
|
||||
JSObject** location = (JSObject**)(uintptr_t(base) + size_t(offset));
|
||||
JSObject* next = *location;
|
||||
JSObject::postWriteBarrier(location, prev, next);
|
||||
}
|
||||
|
|
|
@ -443,6 +443,8 @@ class alignas(16) Instance {
|
|||
static void postBarrier(Instance* instance, gc::Cell** location);
|
||||
static void postBarrierPrecise(Instance* instance, JSObject** location,
|
||||
JSObject* prev);
|
||||
static void postBarrierPreciseWithOffset(Instance* instance, JSObject** base,
|
||||
uint32_t offset, JSObject* prev);
|
||||
static void postBarrierFiltering(Instance* instance, gc::Cell** location);
|
||||
static void* structNew(Instance* instance, const wasm::TypeDef* typeDef);
|
||||
static void* exceptionNew(Instance* instance, JSObject* tag);
|
||||
|
|
|
@ -1695,8 +1695,9 @@ class FunctionCompiler {
|
|||
curBlock_->add(prevValue);
|
||||
|
||||
// Store the new value
|
||||
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
|
||||
v, AliasSet::WasmGlobalCell);
|
||||
auto* store =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
|
||||
/*valueOffset=*/0, v, AliasSet::WasmGlobalCell);
|
||||
curBlock_->add(store);
|
||||
|
||||
// Call the post-write barrier
|
||||
|
@ -1723,8 +1724,9 @@ class FunctionCompiler {
|
|||
curBlock_->add(prevValue);
|
||||
|
||||
// Store the new value
|
||||
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, valueAddr, v,
|
||||
AliasSet::WasmGlobalVar);
|
||||
auto* store =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
|
||||
/*valueOffset=*/0, v, AliasSet::WasmGlobalVar);
|
||||
curBlock_->add(store);
|
||||
|
||||
// Call the post-write barrier
|
||||
|
@ -1802,8 +1804,9 @@ class FunctionCompiler {
|
|||
curBlock_->add(loc);
|
||||
|
||||
// Store the new value
|
||||
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, loc, value,
|
||||
AliasSet::WasmTableElement);
|
||||
auto* store =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, loc, /*valueOffset=*/0,
|
||||
value, AliasSet::WasmTableElement);
|
||||
curBlock_->add(store);
|
||||
|
||||
// Perform the post barrier
|
||||
|
@ -1825,6 +1828,18 @@ class FunctionCompiler {
|
|||
value);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool postBarrierPreciseWithOffset(uint32_t lineOrBytecode,
|
||||
MDefinition* valueBase,
|
||||
uint32_t valueOffset,
|
||||
MDefinition* value) {
|
||||
MDefinition* valueOffsetDef = constantI32(int32_t(valueOffset));
|
||||
if (!valueOffsetDef) {
|
||||
return false;
|
||||
}
|
||||
return emitInstanceCall3(lineOrBytecode, SASigPostBarrierPreciseWithOffset,
|
||||
valueBase, valueOffsetDef, value);
|
||||
}
|
||||
|
||||
/***************************************************************** Calls */
|
||||
|
||||
// The IonMonkey backend maintains a single stack offset (from the stack
|
||||
|
@ -2286,12 +2301,9 @@ class FunctionCompiler {
|
|||
if (result.onStack()) {
|
||||
MOZ_ASSERT(iter.remaining() > 1);
|
||||
if (result.type().isRefRepr()) {
|
||||
auto* loc = MWasmDerivedPointer::New(alloc(), stackResultPointer_,
|
||||
result.stackOffset());
|
||||
curBlock_->add(loc);
|
||||
auto* store =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, loc, values[i],
|
||||
AliasSet::WasmStackResult);
|
||||
auto* store = MWasmStoreRef::New(
|
||||
alloc(), instancePointer_, stackResultPointer_,
|
||||
result.stackOffset(), values[i], AliasSet::WasmStackResult);
|
||||
curBlock_->add(store);
|
||||
} else {
|
||||
auto* store = MWasmStoreStackResult::New(
|
||||
|
@ -2819,11 +2831,11 @@ class FunctionCompiler {
|
|||
auto* exceptionAddr = MWasmDerivedPointer::New(
|
||||
alloc(), instancePointer_, Instance::offsetOfPendingException());
|
||||
curBlock_->add(exceptionAddr);
|
||||
auto* setException =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, exceptionAddr, exception,
|
||||
AliasSet::WasmPendingException);
|
||||
auto* setException = MWasmStoreRef::New(
|
||||
alloc(), instancePointer_, exceptionAddr, /*valueOffset=*/0, exception,
|
||||
AliasSet::WasmPendingException);
|
||||
curBlock_->add(setException);
|
||||
if (!postBarrierPrecise(0, exceptionAddr, exception)) {
|
||||
if (!postBarrierPrecise(/*lineOrBytecode=*/0, exceptionAddr, exception)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2831,11 +2843,11 @@ class FunctionCompiler {
|
|||
auto* exceptionTagAddr = MWasmDerivedPointer::New(
|
||||
alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag());
|
||||
curBlock_->add(exceptionTagAddr);
|
||||
auto* setExceptionTag =
|
||||
MWasmStoreRef::New(alloc(), instancePointer_, exceptionTagAddr, tag,
|
||||
AliasSet::WasmPendingException);
|
||||
auto* setExceptionTag = MWasmStoreRef::New(
|
||||
alloc(), instancePointer_, exceptionTagAddr, /*valueOffset=*/0, tag,
|
||||
AliasSet::WasmPendingException);
|
||||
curBlock_->add(setExceptionTag);
|
||||
return postBarrierPrecise(0, exceptionTagAddr, tag);
|
||||
return postBarrierPrecise(/*lineOrBytecode=*/0, exceptionTagAddr, tag);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool addPadPatch(MControlInstruction* ins,
|
||||
|
@ -3265,13 +3277,6 @@ class FunctionCompiler {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Compute the address of the field
|
||||
auto* fieldAddr = MWasmDerivedPointer::New(alloc(), data, offset);
|
||||
if (!fieldAddr) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(fieldAddr);
|
||||
|
||||
// Load the previous value
|
||||
auto* prevValue = MWasmLoadFieldKA::New(
|
||||
alloc(), exception, data, offset, type.toMIRType(), MWideningOp::None,
|
||||
|
@ -3283,7 +3288,7 @@ class FunctionCompiler {
|
|||
|
||||
// Store the new value
|
||||
auto* store = MWasmStoreFieldRefKA::New(
|
||||
alloc(), instancePointer_, exception, fieldAddr, argValues[i],
|
||||
alloc(), instancePointer_, exception, data, offset, argValues[i],
|
||||
AliasSet::Store(AliasSet::Any));
|
||||
if (!store) {
|
||||
return false;
|
||||
|
@ -3291,7 +3296,8 @@ class FunctionCompiler {
|
|||
curBlock_->add(store);
|
||||
|
||||
// Call the post-write barrier
|
||||
if (!postBarrierPrecise(bytecodeOffset, fieldAddr, prevValue)) {
|
||||
if (!postBarrierPreciseWithOffset(bytecodeOffset, data, offset,
|
||||
prevValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3533,64 +3539,6 @@ class FunctionCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
// Generate a write of `value` at address `address`. If the written value
|
||||
// is a reftype, the previous value at `address` will be retrieved and
|
||||
// handed off to the post-write barrier. `keepAlive` will be referenced by
|
||||
// the instruction so as to hold it live (from the GC's point of view).
|
||||
[[nodiscard]] bool writeGcValueAtAddress(
|
||||
uint32_t lineOrBytecode, FieldType fieldType, MDefinition* keepAlive,
|
||||
AliasSet::Flag aliasBitset, MDefinition* value, MDefinition* address,
|
||||
bool needsTrapInfo) {
|
||||
MOZ_ASSERT(aliasBitset != 0);
|
||||
MOZ_ASSERT(keepAlive->type() == MIRType::RefOrNull);
|
||||
MOZ_ASSERT(fieldType.widenToValType().toMIRType() == value->type());
|
||||
MNarrowingOp narrowingOp = fieldStoreInfoToMIR(fieldType);
|
||||
|
||||
if (!fieldType.isRefRepr()) {
|
||||
MaybeTrapSiteInfo maybeTrap;
|
||||
if (needsTrapInfo) {
|
||||
maybeTrap.emplace(getTrapSiteInfo());
|
||||
}
|
||||
auto* store =
|
||||
MWasmStoreFieldKA::New(alloc(), keepAlive, address,
|
||||
/*offset=*/0, value, narrowingOp,
|
||||
AliasSet::Store(aliasBitset), maybeTrap);
|
||||
if (!store) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(store);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise it's a ref store. Load the previous value so we can show it
|
||||
// to the post-write barrier.
|
||||
//
|
||||
// Optimisation opportunity: for the case where this field write results
|
||||
// from struct.new, the old value is always zero. So we should synthesise
|
||||
// a suitable zero constant rather than reading it from the object. See
|
||||
// also bug 1799999.
|
||||
auto* prevValue = MWasmLoadFieldKA::New(
|
||||
alloc(), keepAlive, address, /*offset=*/0,
|
||||
fieldType.valType().toMIRType(), MWideningOp::None,
|
||||
AliasSet::Load(aliasBitset), mozilla::Some(getTrapSiteInfo()));
|
||||
if (!prevValue) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(prevValue);
|
||||
|
||||
// Store the new value
|
||||
auto* store =
|
||||
MWasmStoreFieldRefKA::New(alloc(), instancePointer_, keepAlive, address,
|
||||
value, AliasSet::Store(aliasBitset));
|
||||
if (!store) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(store);
|
||||
|
||||
// Call the post-write barrier
|
||||
return postBarrierPrecise(lineOrBytecode, address, prevValue);
|
||||
}
|
||||
|
||||
// Generate a write of `value` at address `base + offset`, where `offset` is
|
||||
// known at JIT time. If the written value is a reftype, the previous value
|
||||
// at `base + offset` will be retrieved and handed off to the post-write
|
||||
|
@ -3620,22 +3568,37 @@ class FunctionCompiler {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Otherwise it's a ref store, for which we can't use a
|
||||
// base-plus-constant-offset address form. So roll the offset into the
|
||||
// address at this point.
|
||||
// Otherwise it's a ref store. Load the previous value so we can show it
|
||||
// to the post-write barrier.
|
||||
//
|
||||
// Optimisation opportunity: for the case where this field write results
|
||||
// from struct.new, the old value is always zero. So we should synthesise
|
||||
// a suitable zero constant rather than reading it from the object. See
|
||||
// also bug 1799999.
|
||||
MOZ_ASSERT(narrowingOp == MNarrowingOp::None);
|
||||
MOZ_ASSERT(fieldType.widenToValType() == fieldType.valType());
|
||||
if (offset != 0) {
|
||||
auto* derived = MWasmDerivedPointer::New(alloc(), base, offset);
|
||||
if (!derived) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(derived);
|
||||
base = derived;
|
||||
}
|
||||
|
||||
return writeGcValueAtAddress(lineOrBytecode, fieldType, keepAlive,
|
||||
aliasBitset, value, base, needsTrapInfo);
|
||||
auto* prevValue = MWasmLoadFieldKA::New(
|
||||
alloc(), keepAlive, base, offset, fieldType.valType().toMIRType(),
|
||||
MWideningOp::None, AliasSet::Load(aliasBitset),
|
||||
mozilla::Some(getTrapSiteInfo()));
|
||||
if (!prevValue) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(prevValue);
|
||||
|
||||
// Store the new value
|
||||
auto* store =
|
||||
MWasmStoreFieldRefKA::New(alloc(), instancePointer_, keepAlive, base,
|
||||
offset, value, AliasSet::Store(aliasBitset));
|
||||
if (!store) {
|
||||
return false;
|
||||
}
|
||||
curBlock_->add(store);
|
||||
|
||||
// Call the post-write barrier
|
||||
return postBarrierPreciseWithOffset(lineOrBytecode, base, offset,
|
||||
prevValue);
|
||||
}
|
||||
|
||||
// Generate a write of `value` at address `base + index * scale`, where
|
||||
|
@ -3665,8 +3628,10 @@ class FunctionCompiler {
|
|||
return false;
|
||||
}
|
||||
|
||||
return writeGcValueAtAddress(lineOrBytecode, fieldType, keepAlive,
|
||||
aliasBitset, value, finalAddr, false);
|
||||
return writeGcValueAtBasePlusOffset(lineOrBytecode, fieldType, keepAlive,
|
||||
aliasBitset, value, finalAddr,
|
||||
/*offset=*/0,
|
||||
/*needsTrapInfo=*/false);
|
||||
}
|
||||
|
||||
// Generate a read from address `base + offset`, where `offset` is known at
|
||||
|
@ -4148,11 +4113,13 @@ class FunctionCompiler {
|
|||
curBlock_->addPhi(ptrPhi);
|
||||
curBlock_->setLoopDepth(loopDepth_ + 1);
|
||||
|
||||
// Because we have the exact address to hand, use `writeGcValueAtBase`
|
||||
// rather than `writeGcValueAtBasePlusScaledIndex` to do the store.
|
||||
if (!writeGcValueAtAddress(lineOrBytecode, fillValueFieldType, arrayObject,
|
||||
AliasSet::WasmArrayDataArea, fillValue, ptrPhi,
|
||||
false)) {
|
||||
// Because we have the exact address to hand, use
|
||||
// `writeGcValueAtBasePlusOffset` rather than
|
||||
// `writeGcValueAtBasePlusScaledIndex` to do the store.
|
||||
if (!writeGcValueAtBasePlusOffset(lineOrBytecode, fillValueFieldType,
|
||||
arrayObject, AliasSet::WasmArrayDataArea,
|
||||
fillValue, ptrPhi, /*offset=*/0,
|
||||
/*needsTrapInfo=*/false)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -847,7 +847,7 @@ template <CoderMode mode>
|
|||
CoderResult CodeSymbolicLinkArray(
|
||||
Coder<mode>& coder,
|
||||
CoderArg<mode, wasm::LinkData::SymbolicLinkArray> item) {
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData::SymbolicLinkArray, 7056);
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData::SymbolicLinkArray, 7128);
|
||||
for (SymbolicAddress address :
|
||||
mozilla::MakeEnumeratedRange(SymbolicAddress::Limit)) {
|
||||
MOZ_TRY(CodePodVector(coder, &(*item)[address]));
|
||||
|
@ -858,7 +858,7 @@ CoderResult CodeSymbolicLinkArray(
|
|||
template <CoderMode mode>
|
||||
CoderResult CodeLinkData(Coder<mode>& coder,
|
||||
CoderArg<mode, wasm::LinkData> item) {
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 7104);
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::LinkData, 7176);
|
||||
if constexpr (mode == MODE_ENCODE) {
|
||||
MOZ_ASSERT(item->tier == Tier::Serialized);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче