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:
Julian Seward 2023-01-18 10:56:09 +00:00
Родитель 2de4d5e5b0
Коммит 114a9dd6fc
18 изменённых файлов: 305 добавлений и 168 удалений

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

@ -8373,17 +8373,18 @@ void CodeGenerator::visitWasmDerivedIndexPointer(
void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) { void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
Register instance = ToRegister(ins->instance()); 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 value = ToRegister(ins->value());
Register temp = ToRegister(ins->temp0()); Register temp = ToRegister(ins->temp0());
Label skipPreBarrier; Label skipPreBarrier;
wasm::EmitWasmPreBarrierGuard(masm, instance, temp, valueAddr, wasm::EmitWasmPreBarrierGuard(masm, instance, temp, valueBase, offset,
&skipPreBarrier); &skipPreBarrier);
wasm::EmitWasmPreBarrierCall(masm, instance, temp, valueAddr); wasm::EmitWasmPreBarrierCall(masm, instance, temp, valueBase, offset);
masm.bind(&skipPreBarrier); masm.bind(&skipPreBarrier);
masm.storePtr(value, Address(valueAddr, 0)); masm.storePtr(value, Address(valueBase, offset));
// The postbarrier is handled separately. // The postbarrier is handled separately.
} }

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

@ -898,6 +898,9 @@ enum ABIFunctionType : uint64_t {
Args_Int32_GeneralInt32Int32Int32Int32General = detail::MakeABIFunctionType( Args_Int32_GeneralInt32Int32Int32Int32General = detail::MakeABIFunctionType(
ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32, ArgType_Int32, {ArgType_General, ArgType_Int32, ArgType_Int32,
ArgType_Int32, ArgType_Int32, ArgType_General}), ArgType_Int32, ArgType_Int32, ArgType_General}),
Args_Int32_GeneralGeneralInt32General = detail::MakeABIFunctionType(
ArgType_Int32,
{ArgType_General, ArgType_General, ArgType_Int32, ArgType_General}),
Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 = Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 =
detail::MakeABIFunctionType( detail::MakeABIFunctionType(
ArgType_Int32, ArgType_Int32,

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

@ -2919,8 +2919,10 @@
- name: WasmStoreRef - name: WasmStoreRef
operands: operands:
instance: WordSized instance: WordSized
valueAddr: WordSized valueBase: WordSized
value: WordSized value: WordSized
arguments:
offset: uint32_t
num_temps: 1 num_temps: 1
mir_op: true mir_op: true

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

@ -5483,9 +5483,12 @@ void LIRGenerator::visitWasmDerivedIndexPointer(MWasmDerivedIndexPointer* ins) {
void LIRGenerator::visitWasmStoreRef(MWasmStoreRef* ins) { void LIRGenerator::visitWasmStoreRef(MWasmStoreRef* ins) {
LAllocation instance = useRegister(ins->instance()); LAllocation instance = useRegister(ins->instance());
LAllocation valueAddr = useFixed(ins->valueAddr(), PreBarrierReg); LAllocation valueBase = useFixed(ins->valueBase(), PreBarrierReg);
LAllocation value = useRegister(ins->value()); 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) { void LIRGenerator::visitWasmParameter(MWasmParameter* ins) {
@ -6884,7 +6887,7 @@ void LIRGenerator::visitWasmFence(MWasmFence* ins) {
} }
void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) { void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) {
size_t offs = ins->offset(); uint32_t offs = ins->offset();
LAllocation obj = useRegister(ins->obj()); LAllocation obj = useRegister(ins->obj());
MWideningOp wideningOp = ins->wideningOp(); MWideningOp wideningOp = ins->wideningOp();
if (ins->type() == MIRType::Int64) { if (ins->type() == MIRType::Int64) {
@ -6899,7 +6902,7 @@ void LIRGenerator::visitWasmLoadField(MWasmLoadField* ins) {
} }
void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) { void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) {
size_t offs = ins->offset(); uint32_t offs = ins->offset();
LAllocation obj = useRegister(ins->obj()); LAllocation obj = useRegister(ins->obj());
MWideningOp wideningOp = ins->wideningOp(); MWideningOp wideningOp = ins->wideningOp();
if (ins->type() == MIRType::Int64) { if (ins->type() == MIRType::Int64) {
@ -6916,7 +6919,7 @@ void LIRGenerator::visitWasmLoadFieldKA(MWasmLoadFieldKA* ins) {
void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) { void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) {
MDefinition* value = ins->value(); MDefinition* value = ins->value();
size_t offs = ins->offset(); uint32_t offs = ins->offset();
MNarrowingOp narrowingOp = ins->narrowingOp(); MNarrowingOp narrowingOp = ins->narrowingOp();
LAllocation obj = useRegister(ins->obj()); LAllocation obj = useRegister(ins->obj());
LInstruction* lir; LInstruction* lir;
@ -6935,9 +6938,10 @@ void LIRGenerator::visitWasmStoreFieldKA(MWasmStoreFieldKA* ins) {
void LIRGenerator::visitWasmStoreFieldRefKA(MWasmStoreFieldRefKA* ins) { void LIRGenerator::visitWasmStoreFieldRefKA(MWasmStoreFieldRefKA* ins) {
LAllocation instance = useRegister(ins->instance()); LAllocation instance = useRegister(ins->instance());
LAllocation valueAddr = useFixed(ins->valueAddr(), PreBarrierReg); LAllocation obj = useFixed(ins->obj(), PreBarrierReg);
LAllocation value = useRegister(ins->value()); 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); add(new (alloc()) LKeepAliveObject(useKeepalive(ins->ka())), ins);
} }

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

@ -10003,25 +10003,33 @@ class MWasmStoreStackResult : public MBinaryInstruction,
// Represents a known-good derived pointer into an object or memory region (in // 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 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`; // 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, class MWasmDerivedPointer : public MUnaryInstruction,
public NoTypePolicy::Data { public NoTypePolicy::Data {
MWasmDerivedPointer(MDefinition* base, size_t offset) MWasmDerivedPointer(MDefinition* base, size_t offset)
: MUnaryInstruction(classOpcode, base), offset_(offset) { : MUnaryInstruction(classOpcode, base), offset_(uint32_t(offset)) {
MOZ_ASSERT(offset <= INT32_MAX); 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); setResultType(MIRType::Pointer);
setMovable(); setMovable();
} }
size_t offset_; uint32_t offset_;
public: public:
INSTRUCTION_HEADER(WasmDerivedPointer) INSTRUCTION_HEADER(WasmDerivedPointer)
TRIVIAL_NEW_WRAPPERS TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, base)) NAMED_OPERANDS((0, base))
size_t offset() const { return offset_; } uint32_t offset() const { return offset_; }
AliasSet getAliasSet() const override { return AliasSet::None(); } AliasSet getAliasSet() const override { return AliasSet::None(); }
@ -10041,10 +10049,14 @@ class MWasmDerivedPointer : public MUnaryInstruction,
ALLOW_CLONE(MWasmDerivedPointer) 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, class MWasmDerivedIndexPointer : public MBinaryInstruction,
public NoTypePolicy::Data { public NoTypePolicy::Data {
MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale) MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale)
: MBinaryInstruction(classOpcode, base, 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); setResultType(MIRType::Pointer);
setMovable(); setMovable();
} }
@ -10071,27 +10083,42 @@ class MWasmDerivedIndexPointer : public MBinaryInstruction,
// Stores a reference to an address. This performs a pre-barrier on the address, // 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 // 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 { class MWasmStoreRef : public MAryInstruction<3>, public NoTypePolicy::Data {
uint32_t offset_;
AliasSet::Flag aliasSet_; AliasSet::Flag aliasSet_;
MWasmStoreRef(MDefinition* instance, MDefinition* valueAddr, MWasmStoreRef(MDefinition* instance, MDefinition* valueBase,
MDefinition* value, AliasSet::Flag aliasSet) size_t valueOffset, MDefinition* value, AliasSet::Flag aliasSet)
: MAryInstruction<3>(classOpcode), aliasSet_(aliasSet) { : MAryInstruction<3>(classOpcode),
MOZ_ASSERT(valueAddr->type() == MIRType::Pointer); 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); MOZ_ASSERT(value->type() == MIRType::RefOrNull);
initOperand(0, instance); initOperand(0, instance);
initOperand(1, valueAddr); initOperand(1, valueBase);
initOperand(2, value); initOperand(2, value);
} }
public: public:
INSTRUCTION_HEADER(WasmStoreRef) INSTRUCTION_HEADER(WasmStoreRef)
TRIVIAL_NEW_WRAPPERS 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_); } 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 { 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). // 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 }; 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 // Indicates how to narrow a 32-bit value (when it is written to memory). The
// operation is a simple truncate. // operation is a simple truncate.
enum class MNarrowingOp : uint8_t { None, To16, To8 }; 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, // Provide information about potential trap at the instruction machine code,
// e.g. null pointer dereference. // e.g. null pointer dereference.
struct TrapSiteInfo { 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 // 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 // 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 { class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
uint32_t offset_; uint32_t offset_;
MWideningOp wideningOp_; MWideningOp wideningOp_;
@ -10986,10 +11049,11 @@ class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
MWideningOp wideningOp, AliasSet aliases, MWideningOp wideningOp, AliasSet aliases,
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing()) MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
: MUnaryInstruction(classOpcode, obj), : MUnaryInstruction(classOpcode, obj),
offset_(offset), offset_(uint32_t(offset)),
wideningOp_(wideningOp), wideningOp_(wideningOp),
aliases_(aliases), aliases_(aliases),
maybeTrap_(maybeTrap) { maybeTrap_(maybeTrap) {
MOZ_ASSERT(offset <= INT32_MAX);
// "if you want to widen the value when it is loaded, the destination type // "if you want to widen the value when it is loaded, the destination type
// must be Int32". // must be Int32".
MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::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_; } uint32_t offset() const { return offset_; }
MWideningOp wideningOp() const { return wideningOp_; } MWideningOp wideningOp() const { return wideningOp_; }
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
AliasSet getAliasSet() const override { return aliases_; } AliasSet getAliasSet() const override { return aliases_; }
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; }
bool congruentTo(const MDefinition* ins) const override { bool congruentTo(const MDefinition* ins) const override {
// In the limited case where this insn is used to read // In the limited case where this insn is used to read
// WasmStructObject::outlineData_ (the field itself, not what it points // WasmStructObject::outlineData_ (the field itself, not what it points
@ -11032,32 +11097,43 @@ class MWasmLoadField : public MUnaryInstruction, public NoTypePolicy::Data {
getAliasSet().flags() == getAliasSet().flags() ==
AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).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 // Loads a value from a location, denoted as a fixed offset from a base
// field may be any value type, including references. No barriers are // pointer, which (it is assumed) is within a wasm object. This field may be
// performed. // any value type, including references. No barriers are performed.
// //
// This instruction takes a pointer to a second object `ka`, which it is // 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 // 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`. // `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`, // 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 // 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 { class MWasmLoadFieldKA : public MBinaryInstruction, public NoTypePolicy::Data {
uint32_t offset_; uint32_t offset_;
MWideningOp wideningOp_; MWideningOp wideningOp_;
AliasSet aliases_; AliasSet aliases_;
MaybeTrapSiteInfo maybeTrap_; MaybeTrapSiteInfo maybeTrap_;
MWasmLoadFieldKA(MDefinition* ka, MDefinition* obj, uint32_t offset, MWasmLoadFieldKA(MDefinition* ka, MDefinition* obj, size_t offset,
MIRType type, MWideningOp wideningOp, AliasSet aliases, MIRType type, MWideningOp wideningOp, AliasSet aliases,
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing()) MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
: MBinaryInstruction(classOpcode, ka, obj), : MBinaryInstruction(classOpcode, ka, obj),
offset_(offset), offset_(uint32_t(offset)),
wideningOp_(wideningOp), wideningOp_(wideningOp),
aliases_(aliases), aliases_(aliases),
maybeTrap_(maybeTrap) { maybeTrap_(maybeTrap) {
MOZ_ASSERT(offset <= INT32_MAX);
MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32); MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32);
MOZ_ASSERT( MOZ_ASSERT(
aliases.flags() == aliases.flags() ==
@ -11080,14 +11156,24 @@ class MWasmLoadFieldKA : public MBinaryInstruction, public NoTypePolicy::Data {
uint32_t offset() const { return offset_; } uint32_t offset() const { return offset_; }
MWideningOp wideningOp() const { return wideningOp_; } MWideningOp wideningOp() const { return wideningOp_; }
AliasSet getAliasSet() const override { return aliases_; }
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; } 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. // Stores a non-reference value to anlocation, denoted as a fixed offset from
// This field may be any value type, _excluding_ references. References // a base pointer, which (it is assumed) is within a wasm object. This field
// _must_ use the 'Ref' variant of this instruction. // 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 // This instruction takes a second object `ka` that must be kept alive, as
// described for MWasmLoadFieldKA above. // described for MWasmLoadFieldKA above.
@ -11098,15 +11184,16 @@ class MWasmStoreFieldKA : public MTernaryInstruction,
AliasSet aliases_; AliasSet aliases_;
MaybeTrapSiteInfo maybeTrap_; MaybeTrapSiteInfo maybeTrap_;
MWasmStoreFieldKA(MDefinition* ka, MDefinition* obj, uint32_t offset, MWasmStoreFieldKA(MDefinition* ka, MDefinition* obj, size_t offset,
MDefinition* value, MNarrowingOp narrowingOp, MDefinition* value, MNarrowingOp narrowingOp,
AliasSet aliases, AliasSet aliases,
MaybeTrapSiteInfo maybeTrap = mozilla::Nothing()) MaybeTrapSiteInfo maybeTrap = mozilla::Nothing())
: MTernaryInstruction(classOpcode, ka, obj, value), : MTernaryInstruction(classOpcode, ka, obj, value),
offset_(offset), offset_(uint32_t(offset)),
narrowingOp_(narrowingOp), narrowingOp_(narrowingOp),
aliases_(aliases), aliases_(aliases),
maybeTrap_(maybeTrap) { maybeTrap_(maybeTrap) {
MOZ_ASSERT(offset <= INT32_MAX);
MOZ_ASSERT(value->type() != MIRType::RefOrNull); MOZ_ASSERT(value->type() != MIRType::RefOrNull);
// "if you want to narrow the value when it is stored, the source type // "if you want to narrow the value when it is stored, the source type
// must be Int32". // must be Int32".
@ -11132,27 +11219,40 @@ class MWasmStoreFieldKA : public MTernaryInstruction,
uint32_t offset() const { return offset_; } uint32_t offset() const { return offset_; }
MNarrowingOp narrowingOp() const { return narrowingOp_; } MNarrowingOp narrowingOp() const { return narrowingOp_; }
AliasSet getAliasSet() const override { return aliases_; }
MaybeTrapSiteInfo maybeTrap() const { return maybeTrap_; } 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 // Stores a reference value to a location, denoted as a fixed offset from a
// wasm object. This instruction emits a pre-barrier. A post barrier _must_ // base pointer, which (it is assumed) is within a wasm object. This
// be performed separately. // 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 // This instruction takes a second object `ka` that must be kept alive, as
// described for MWasmLoadFieldKA above. // described for MWasmLoadFieldKA above.
class MWasmStoreFieldRefKA : public MAryInstruction<4>, class MWasmStoreFieldRefKA : public MAryInstruction<4>,
public NoTypePolicy::Data { public NoTypePolicy::Data {
uint32_t offset_;
AliasSet aliases_; AliasSet aliases_;
MWasmStoreFieldRefKA(MDefinition* instance, MDefinition* ka, MWasmStoreFieldRefKA(MDefinition* instance, MDefinition* ka, MDefinition* obj,
MDefinition* valueAddr, MDefinition* value, size_t offset, MDefinition* value, AliasSet aliases)
AliasSet aliases) : MAryInstruction<4>(classOpcode),
: MAryInstruction<4>(classOpcode), aliases_(aliases) { offset_(uint32_t(offset)),
MOZ_ASSERT(valueAddr->type() == MIRType::Pointer || aliases_(aliases) {
valueAddr->type() == TargetWordMIRType()); 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(value->type() == MIRType::RefOrNull);
MOZ_ASSERT( MOZ_ASSERT(
aliases.flags() == aliases.flags() ==
@ -11164,16 +11264,25 @@ class MWasmStoreFieldRefKA : public MAryInstruction<4>,
aliases.flags() == AliasSet::Store(AliasSet::Any).flags()); aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
initOperand(0, instance); initOperand(0, instance);
initOperand(1, ka); initOperand(1, ka);
initOperand(2, valueAddr); initOperand(2, obj);
initOperand(3, value); initOperand(3, value);
} }
public: public:
INSTRUCTION_HEADER(WasmStoreFieldRefKA) INSTRUCTION_HEADER(WasmStoreFieldRefKA)
TRIVIAL_NEW_WRAPPERS 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_; } 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 // 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); int32_t);
typedef int32_t (*Prototype_General_GeneralInt32Int32GeneralInt32)( typedef int32_t (*Prototype_General_GeneralInt32Int32GeneralInt32)(
int32_t, int32_t, int32_t, int32_t, int32_t); 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)( typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)(
int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
typedef int64_t (*Prototype_Int64_General)(int32_t); typedef int64_t (*Prototype_Int64_General)(int32_t);
@ -3140,6 +3142,15 @@ void Simulator::softwareInterrupt(SimInstruction* instr) {
setCallResult(result); setCallResult(result);
break; 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: { case js::jit::Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32: {
Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 target = Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32 target =
reinterpret_cast< reinterpret_cast<

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

@ -657,6 +657,10 @@ typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)(
int32_t, int32_t,
int32_t, 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_General)(int64_t);
typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t); typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t);
@ -1055,6 +1059,13 @@ Simulator::VisitCallRedirection(const Instruction* instr)
setGPR32Result(ret); setGPR32Result(ret);
break; 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: { case js::jit::Args_Int64_General: {
int64_t ret = int64_t ret =
reinterpret_cast<Prototype_Int64_General>( reinterpret_cast<Prototype_Int64_General>(

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

@ -3265,7 +3265,7 @@ class LWasmDerivedPointer : public LInstructionHelper<1, 1, 0> {
setOperand(0, base); setOperand(0, base);
} }
const LAllocation* base() { return getOperand(0); } 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> { class LWasmDerivedIndexPointer : public LInstructionHelper<1, 2, 0> {

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

@ -6206,7 +6206,8 @@ void BaseCompiler::emitPreBarrier(RegPtr valueAddr) {
fr.loadInstancePtr(instance); fr.loadInstancePtr(instance);
#endif #endif
EmitWasmPreBarrierGuard(masm, instance, scratch, valueAddr, &skipBarrier); EmitWasmPreBarrierGuard(masm, instance, scratch, valueAddr,
/*valueOffset=*/0, &skipBarrier);
#ifndef RABALDR_PIN_INSTANCE #ifndef RABALDR_PIN_INSTANCE
fr.loadInstancePtr(instance); fr.loadInstancePtr(instance);
@ -6219,7 +6220,7 @@ void BaseCompiler::emitPreBarrier(RegPtr valueAddr) {
masm.Mov(x28, sp); masm.Mov(x28, sp);
#endif #endif
// The prebarrier call preserves all volatile registers // The prebarrier call preserves all volatile registers
EmitWasmPreBarrierCall(masm, instance, scratch, valueAddr); EmitWasmPreBarrierCall(masm, instance, scratch, valueAddr, /*valueOffset=*/0);
masm.bind(&skipBarrier); masm.bind(&skipBarrier);
} }

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

@ -268,6 +268,12 @@ const SymbolicAddressSignature SASigPostBarrierPrecise = {
_Infallible, _Infallible,
3, 3,
{_PTR, _PTR, _RoN, _END}}; {_PTR, _PTR, _RoN, _END}};
const SymbolicAddressSignature SASigPostBarrierPreciseWithOffset = {
SymbolicAddress::PostBarrierPreciseWithOffset,
_VOID,
_Infallible,
4,
{_PTR, _PTR, _I32, _RoN, _END}};
const SymbolicAddressSignature SASigPostBarrierFiltering = { const SymbolicAddressSignature SASigPostBarrierFiltering = {
SymbolicAddress::PostBarrierFiltering, SymbolicAddress::PostBarrierFiltering,
_VOID, _VOID,
@ -1258,6 +1264,10 @@ void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
*abiType = Args_Int32_GeneralGeneralGeneral; *abiType = Args_Int32_GeneralGeneralGeneral;
MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrierPrecise)); MOZ_ASSERT(*abiType == ToABIType(SASigPostBarrierPrecise));
return FuncCast(Instance::postBarrierPrecise, *abiType); 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: case SymbolicAddress::PreBarrierFiltering:
*abiType = Args_Int32_GeneralGeneral; *abiType = Args_Int32_GeneralGeneral;
MOZ_ASSERT(*abiType == ToABIType(SASigPreBarrierFiltering)); MOZ_ASSERT(*abiType == ToABIType(SASigPreBarrierFiltering));
@ -1460,6 +1470,7 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
case SymbolicAddress::PreBarrierFiltering: case SymbolicAddress::PreBarrierFiltering:
case SymbolicAddress::PostBarrier: case SymbolicAddress::PostBarrier:
case SymbolicAddress::PostBarrierPrecise: case SymbolicAddress::PostBarrierPrecise:
case SymbolicAddress::PostBarrierPreciseWithOffset:
case SymbolicAddress::PostBarrierFiltering: case SymbolicAddress::PostBarrierFiltering:
case SymbolicAddress::StructNew: case SymbolicAddress::StructNew:
case SymbolicAddress::ExceptionNew: case SymbolicAddress::ExceptionNew:

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

@ -122,6 +122,7 @@ enum class SymbolicAddress {
PreBarrierFiltering, PreBarrierFiltering,
PostBarrier, PostBarrier,
PostBarrierPrecise, PostBarrierPrecise,
PostBarrierPreciseWithOffset,
PostBarrierFiltering, PostBarrierFiltering,
StructNew, StructNew,
ExceptionNew, ExceptionNew,
@ -245,6 +246,7 @@ extern const SymbolicAddressSignature SASigRefFunc;
extern const SymbolicAddressSignature SASigPreBarrierFiltering; extern const SymbolicAddressSignature SASigPreBarrierFiltering;
extern const SymbolicAddressSignature SASigPostBarrier; extern const SymbolicAddressSignature SASigPostBarrier;
extern const SymbolicAddressSignature SASigPostBarrierPrecise; extern const SymbolicAddressSignature SASigPostBarrierPrecise;
extern const SymbolicAddressSignature SASigPostBarrierPreciseWithOffset;
extern const SymbolicAddressSignature SASigPostBarrierFiltering; extern const SymbolicAddressSignature SASigPostBarrierFiltering;
extern const SymbolicAddressSignature SASigStructNew; extern const SymbolicAddressSignature SASigStructNew;
extern const SymbolicAddressSignature SASigExceptionNew; extern const SymbolicAddressSignature SASigExceptionNew;

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

@ -1593,6 +1593,7 @@ static const char* ThunkedNativeToDescription(SymbolicAddress func) {
return "call to native filtering GC prebarrier (in wasm)"; return "call to native filtering GC prebarrier (in wasm)";
case SymbolicAddress::PostBarrier: case SymbolicAddress::PostBarrier:
case SymbolicAddress::PostBarrierPrecise: case SymbolicAddress::PostBarrierPrecise:
case SymbolicAddress::PostBarrierPreciseWithOffset:
case SymbolicAddress::PostBarrierFiltering: case SymbolicAddress::PostBarrierFiltering:
return "call to native GC postbarrier (in wasm)"; return "call to native GC postbarrier (in wasm)";
case SymbolicAddress::StructNew: case SymbolicAddress::StructNew:

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

@ -204,7 +204,7 @@ bool wasm::GenerateStackmapEntriesForTrapExit(
void wasm::EmitWasmPreBarrierGuard(MacroAssembler& masm, Register instance, void wasm::EmitWasmPreBarrierGuard(MacroAssembler& masm, Register instance,
Register scratch, Register valueAddr, Register scratch, Register valueAddr,
Label* skipBarrier) { size_t valueOffset, Label* skipBarrier) {
// If no incremental GC has started, we don't need the barrier. // If no incremental GC has started, we don't need the barrier.
masm.loadPtr( masm.loadPtr(
Address(instance, Instance::offsetOfAddressOfNeedsIncrementalBarrier()), Address(instance, Instance::offsetOfAddressOfNeedsIncrementalBarrier()),
@ -213,12 +213,13 @@ void wasm::EmitWasmPreBarrierGuard(MacroAssembler& masm, Register instance,
skipBarrier); skipBarrier);
// If the previous value is null, we don't need the barrier. // 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); masm.branchTestPtr(Assembler::Zero, scratch, scratch, skipBarrier);
} }
void wasm::EmitWasmPreBarrierCall(MacroAssembler& masm, Register instance, void wasm::EmitWasmPreBarrierCall(MacroAssembler& masm, Register instance,
Register scratch, Register valueAddr) { Register scratch, Register valueAddr,
size_t valueOffset) {
MOZ_ASSERT(valueAddr == PreBarrierReg); MOZ_ASSERT(valueAddr == PreBarrierReg);
masm.loadPtr(Address(instance, Instance::offsetOfPreBarrierCode()), scratch); masm.loadPtr(Address(instance, Instance::offsetOfPreBarrierCode()), scratch);

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

@ -449,7 +449,7 @@ wasm::StackMap* ConvertStackMapBoolVectorToStackMap(
void EmitWasmPreBarrierGuard(jit::MacroAssembler& masm, Register instance, void EmitWasmPreBarrierGuard(jit::MacroAssembler& masm, Register instance,
Register scratch, Register valueAddr, Register scratch, Register valueAddr,
Label* skipBarrier); size_t valueOffset, Label* skipBarrier);
// Before storing a GC pointer value in memory, call out-of-line prebarrier // Before storing a GC pointer value in memory, call out-of-line prebarrier
// code. This assumes `PreBarrierReg` contains the address that will be updated. // 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. // It is OK for `instance` and `scratch` to be the same register.
void EmitWasmPreBarrierCall(jit::MacroAssembler& masm, Register instance, 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 // 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 // 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** location,
JSObject* prev) { JSObject* prev) {
MOZ_ASSERT(SASigPostBarrierPrecise.failureMode == FailureMode::Infallible); 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* next = *location;
JSObject::postWriteBarrier(location, prev, next); JSObject::postWriteBarrier(location, prev, next);
} }

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

@ -443,6 +443,8 @@ class alignas(16) Instance {
static void postBarrier(Instance* instance, gc::Cell** location); static void postBarrier(Instance* instance, gc::Cell** location);
static void postBarrierPrecise(Instance* instance, JSObject** location, static void postBarrierPrecise(Instance* instance, JSObject** location,
JSObject* prev); JSObject* prev);
static void postBarrierPreciseWithOffset(Instance* instance, JSObject** base,
uint32_t offset, JSObject* prev);
static void postBarrierFiltering(Instance* instance, gc::Cell** location); static void postBarrierFiltering(Instance* instance, gc::Cell** location);
static void* structNew(Instance* instance, const wasm::TypeDef* typeDef); static void* structNew(Instance* instance, const wasm::TypeDef* typeDef);
static void* exceptionNew(Instance* instance, JSObject* tag); static void* exceptionNew(Instance* instance, JSObject* tag);

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

@ -1695,8 +1695,9 @@ class FunctionCompiler {
curBlock_->add(prevValue); curBlock_->add(prevValue);
// Store the new value // Store the new value
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, valueAddr, auto* store =
v, AliasSet::WasmGlobalCell); MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
/*valueOffset=*/0, v, AliasSet::WasmGlobalCell);
curBlock_->add(store); curBlock_->add(store);
// Call the post-write barrier // Call the post-write barrier
@ -1723,8 +1724,9 @@ class FunctionCompiler {
curBlock_->add(prevValue); curBlock_->add(prevValue);
// Store the new value // Store the new value
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, valueAddr, v, auto* store =
AliasSet::WasmGlobalVar); MWasmStoreRef::New(alloc(), instancePointer_, valueAddr,
/*valueOffset=*/0, v, AliasSet::WasmGlobalVar);
curBlock_->add(store); curBlock_->add(store);
// Call the post-write barrier // Call the post-write barrier
@ -1802,8 +1804,9 @@ class FunctionCompiler {
curBlock_->add(loc); curBlock_->add(loc);
// Store the new value // Store the new value
auto* store = MWasmStoreRef::New(alloc(), instancePointer_, loc, value, auto* store =
AliasSet::WasmTableElement); MWasmStoreRef::New(alloc(), instancePointer_, loc, /*valueOffset=*/0,
value, AliasSet::WasmTableElement);
curBlock_->add(store); curBlock_->add(store);
// Perform the post barrier // Perform the post barrier
@ -1825,6 +1828,18 @@ class FunctionCompiler {
value); 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 */ /***************************************************************** Calls */
// The IonMonkey backend maintains a single stack offset (from the stack // The IonMonkey backend maintains a single stack offset (from the stack
@ -2286,12 +2301,9 @@ class FunctionCompiler {
if (result.onStack()) { if (result.onStack()) {
MOZ_ASSERT(iter.remaining() > 1); MOZ_ASSERT(iter.remaining() > 1);
if (result.type().isRefRepr()) { if (result.type().isRefRepr()) {
auto* loc = MWasmDerivedPointer::New(alloc(), stackResultPointer_, auto* store = MWasmStoreRef::New(
result.stackOffset()); alloc(), instancePointer_, stackResultPointer_,
curBlock_->add(loc); result.stackOffset(), values[i], AliasSet::WasmStackResult);
auto* store =
MWasmStoreRef::New(alloc(), instancePointer_, loc, values[i],
AliasSet::WasmStackResult);
curBlock_->add(store); curBlock_->add(store);
} else { } else {
auto* store = MWasmStoreStackResult::New( auto* store = MWasmStoreStackResult::New(
@ -2819,11 +2831,11 @@ class FunctionCompiler {
auto* exceptionAddr = MWasmDerivedPointer::New( auto* exceptionAddr = MWasmDerivedPointer::New(
alloc(), instancePointer_, Instance::offsetOfPendingException()); alloc(), instancePointer_, Instance::offsetOfPendingException());
curBlock_->add(exceptionAddr); curBlock_->add(exceptionAddr);
auto* setException = auto* setException = MWasmStoreRef::New(
MWasmStoreRef::New(alloc(), instancePointer_, exceptionAddr, exception, alloc(), instancePointer_, exceptionAddr, /*valueOffset=*/0, exception,
AliasSet::WasmPendingException); AliasSet::WasmPendingException);
curBlock_->add(setException); curBlock_->add(setException);
if (!postBarrierPrecise(0, exceptionAddr, exception)) { if (!postBarrierPrecise(/*lineOrBytecode=*/0, exceptionAddr, exception)) {
return false; return false;
} }
@ -2831,11 +2843,11 @@ class FunctionCompiler {
auto* exceptionTagAddr = MWasmDerivedPointer::New( auto* exceptionTagAddr = MWasmDerivedPointer::New(
alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag()); alloc(), instancePointer_, Instance::offsetOfPendingExceptionTag());
curBlock_->add(exceptionTagAddr); curBlock_->add(exceptionTagAddr);
auto* setExceptionTag = auto* setExceptionTag = MWasmStoreRef::New(
MWasmStoreRef::New(alloc(), instancePointer_, exceptionTagAddr, tag, alloc(), instancePointer_, exceptionTagAddr, /*valueOffset=*/0, tag,
AliasSet::WasmPendingException); AliasSet::WasmPendingException);
curBlock_->add(setExceptionTag); curBlock_->add(setExceptionTag);
return postBarrierPrecise(0, exceptionTagAddr, tag); return postBarrierPrecise(/*lineOrBytecode=*/0, exceptionTagAddr, tag);
} }
[[nodiscard]] bool addPadPatch(MControlInstruction* ins, [[nodiscard]] bool addPadPatch(MControlInstruction* ins,
@ -3265,13 +3277,6 @@ class FunctionCompiler {
continue; 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 // Load the previous value
auto* prevValue = MWasmLoadFieldKA::New( auto* prevValue = MWasmLoadFieldKA::New(
alloc(), exception, data, offset, type.toMIRType(), MWideningOp::None, alloc(), exception, data, offset, type.toMIRType(), MWideningOp::None,
@ -3283,7 +3288,7 @@ class FunctionCompiler {
// Store the new value // Store the new value
auto* store = MWasmStoreFieldRefKA::New( auto* store = MWasmStoreFieldRefKA::New(
alloc(), instancePointer_, exception, fieldAddr, argValues[i], alloc(), instancePointer_, exception, data, offset, argValues[i],
AliasSet::Store(AliasSet::Any)); AliasSet::Store(AliasSet::Any));
if (!store) { if (!store) {
return false; return false;
@ -3291,7 +3296,8 @@ class FunctionCompiler {
curBlock_->add(store); curBlock_->add(store);
// Call the post-write barrier // Call the post-write barrier
if (!postBarrierPrecise(bytecodeOffset, fieldAddr, prevValue)) { if (!postBarrierPreciseWithOffset(bytecodeOffset, data, offset,
prevValue)) {
return false; 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 // 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 // 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 // at `base + offset` will be retrieved and handed off to the post-write
@ -3620,22 +3568,37 @@ class FunctionCompiler {
return true; return true;
} }
// Otherwise it's a ref store, for which we can't use a // Otherwise it's a ref store. Load the previous value so we can show it
// base-plus-constant-offset address form. So roll the offset into the // to the post-write barrier.
// address at this point. //
// 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(narrowingOp == MNarrowingOp::None);
MOZ_ASSERT(fieldType.widenToValType() == fieldType.valType()); 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, auto* prevValue = MWasmLoadFieldKA::New(
aliasBitset, value, base, needsTrapInfo); 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 // Generate a write of `value` at address `base + index * scale`, where
@ -3665,8 +3628,10 @@ class FunctionCompiler {
return false; return false;
} }
return writeGcValueAtAddress(lineOrBytecode, fieldType, keepAlive, return writeGcValueAtBasePlusOffset(lineOrBytecode, fieldType, keepAlive,
aliasBitset, value, finalAddr, false); aliasBitset, value, finalAddr,
/*offset=*/0,
/*needsTrapInfo=*/false);
} }
// Generate a read from address `base + offset`, where `offset` is known at // Generate a read from address `base + offset`, where `offset` is known at
@ -4148,11 +4113,13 @@ class FunctionCompiler {
curBlock_->addPhi(ptrPhi); curBlock_->addPhi(ptrPhi);
curBlock_->setLoopDepth(loopDepth_ + 1); curBlock_->setLoopDepth(loopDepth_ + 1);
// Because we have the exact address to hand, use `writeGcValueAtBase` // Because we have the exact address to hand, use
// rather than `writeGcValueAtBasePlusScaledIndex` to do the store. // `writeGcValueAtBasePlusOffset` rather than
if (!writeGcValueAtAddress(lineOrBytecode, fillValueFieldType, arrayObject, // `writeGcValueAtBasePlusScaledIndex` to do the store.
AliasSet::WasmArrayDataArea, fillValue, ptrPhi, if (!writeGcValueAtBasePlusOffset(lineOrBytecode, fillValueFieldType,
false)) { arrayObject, AliasSet::WasmArrayDataArea,
fillValue, ptrPhi, /*offset=*/0,
/*needsTrapInfo=*/false)) {
return nullptr; return nullptr;
} }

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

@ -847,7 +847,7 @@ template <CoderMode mode>
CoderResult CodeSymbolicLinkArray( CoderResult CodeSymbolicLinkArray(
Coder<mode>& coder, Coder<mode>& coder,
CoderArg<mode, wasm::LinkData::SymbolicLinkArray> item) { 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 : for (SymbolicAddress address :
mozilla::MakeEnumeratedRange(SymbolicAddress::Limit)) { mozilla::MakeEnumeratedRange(SymbolicAddress::Limit)) {
MOZ_TRY(CodePodVector(coder, &(*item)[address])); MOZ_TRY(CodePodVector(coder, &(*item)[address]));
@ -858,7 +858,7 @@ CoderResult CodeSymbolicLinkArray(
template <CoderMode mode> template <CoderMode mode>
CoderResult CodeLinkData(Coder<mode>& coder, CoderResult CodeLinkData(Coder<mode>& coder,
CoderArg<mode, wasm::LinkData> item) { 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) { if constexpr (mode == MODE_ENCODE) {
MOZ_ASSERT(item->tier == Tier::Serialized); MOZ_ASSERT(item->tier == Tier::Serialized);
} }