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) {
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);
}

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

@ -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);
}