зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1651037 part 8 - Support nursery-allocated objects in the transpiler. r=iain
Depends on D82673 Differential Revision: https://phabricator.services.mozilla.com/D82674
This commit is contained in:
Родитель
506eea20ec
Коммит
c18a778773
|
@ -721,68 +721,6 @@ void ICStub::trace(JSTracer* trc) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ICStub::stubDataHasNurseryPointers(const CacheIRStubInfo* stubInfo) {
|
||||
MOZ_ASSERT(IsCacheIRKind(kind()));
|
||||
|
||||
uint32_t field = 0;
|
||||
size_t offset = 0;
|
||||
while (true) {
|
||||
StubField::Type fieldType = stubInfo->fieldType(field);
|
||||
switch (fieldType) {
|
||||
case StubField::Type::RawWord:
|
||||
case StubField::Type::RawInt64:
|
||||
case StubField::Type::DOMExpandoGeneration:
|
||||
break;
|
||||
case StubField::Type::Shape:
|
||||
static_assert(std::is_convertible_v<Shape*, gc::TenuredCell*>,
|
||||
"Code assumes shapes are tenured");
|
||||
break;
|
||||
case StubField::Type::ObjectGroup:
|
||||
static_assert(std::is_convertible_v<ObjectGroup*, gc::TenuredCell*>,
|
||||
"Code assumes groups are tenured");
|
||||
break;
|
||||
case StubField::Type::Symbol:
|
||||
static_assert(std::is_convertible_v<JS::Symbol*, gc::TenuredCell*>,
|
||||
"Code assumes symbols are tenured");
|
||||
break;
|
||||
case StubField::Type::JSObject: {
|
||||
JSObject* obj = stubInfo->getStubField<ICStub, JSObject*>(this, offset);
|
||||
if (IsInsideNursery(obj)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StubField::Type::String: {
|
||||
JSString* str = stubInfo->getStubField<ICStub, JSString*>(this, offset);
|
||||
if (IsInsideNursery(str)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Id: {
|
||||
#ifdef DEBUG
|
||||
// jsid never contains nursery-allocated things.
|
||||
jsid id = stubInfo->getStubField<ICStub, jsid>(this, offset);
|
||||
MOZ_ASSERT_IF(id.isGCThing(),
|
||||
!IsInsideNursery(id.toGCCellPtr().asCell()));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Value: {
|
||||
Value v = stubInfo->getStubField<ICStub, JS::Value>(this, offset);
|
||||
if (v.isGCThing() && IsInsideNursery(v.toGCThing())) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Limit:
|
||||
return false; // Done. Didn't find any nursery pointers.
|
||||
}
|
||||
field++;
|
||||
offset += StubField::sizeInBytes(fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
// This helper handles ICState updates/transitions while attaching CacheIR
|
||||
// stubs.
|
||||
template <typename IRGenerator, typename... Args>
|
||||
|
|
|
@ -420,8 +420,6 @@ class ICStub {
|
|||
void updateCode(JitCode* stubCode);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
bool stubDataHasNurseryPointers(const CacheIRStubInfo* stubInfo);
|
||||
|
||||
static const uint16_t EXPECTED_TRACE_MAGIC = 0b1100011;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
|
|
|
@ -1000,6 +1000,15 @@ int64_t CacheIRStubInfo::getStubRawInt64(ICStub* stub, uint32_t offset) const {
|
|||
return getStubRawInt64(stubData, offset);
|
||||
}
|
||||
|
||||
void CacheIRStubInfo::replaceStubRawWord(uint8_t* stubData, uint32_t offset,
|
||||
uintptr_t oldWord,
|
||||
uintptr_t newWord) const {
|
||||
MOZ_ASSERT(uintptr_t(stubData) % sizeof(uintptr_t) == 0);
|
||||
uintptr_t* addr = reinterpret_cast<uintptr_t*>(stubData + offset);
|
||||
MOZ_ASSERT(*addr == oldWord);
|
||||
*addr = newWord;
|
||||
}
|
||||
|
||||
template <class Stub, class T>
|
||||
GCPtr<T>& CacheIRStubInfo::getStubField(Stub* stub, uint32_t offset) const {
|
||||
uint8_t* stubData = (uint8_t*)stub + stubDataOffset_;
|
||||
|
|
|
@ -1208,6 +1208,9 @@ class CacheIRStubInfo {
|
|||
|
||||
int64_t getStubRawInt64(const uint8_t* stubData, uint32_t offset) const;
|
||||
int64_t getStubRawInt64(ICStub* stub, uint32_t offset) const;
|
||||
|
||||
void replaceStubRawWord(uint8_t* stubData, uint32_t offset, uintptr_t oldWord,
|
||||
uintptr_t newWord) const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -93,9 +93,6 @@ class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
|
|||
JS::Symbol* symbolStubField(uint32_t offset) {
|
||||
return reinterpret_cast<JS::Symbol*>(readStubWord(offset));
|
||||
}
|
||||
JSObject* objectStubField(uint32_t offset) {
|
||||
return reinterpret_cast<JSObject*>(readStubWord(offset));
|
||||
}
|
||||
const void* rawPointerField(uint32_t offset) {
|
||||
return reinterpret_cast<const void*>(readStubWord(offset));
|
||||
}
|
||||
|
@ -106,6 +103,16 @@ class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
|
|||
return static_cast<uint32_t>(readStubWord(offset));
|
||||
}
|
||||
|
||||
// This must only be called when the caller knows the object is tenured and
|
||||
// not a nursery index.
|
||||
JSObject* tenuredObjectStubField(uint32_t offset) {
|
||||
WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
|
||||
return field.toObject();
|
||||
}
|
||||
|
||||
// Returns either MConstant or MNurseryIndex. See WarpObjectField.
|
||||
MInstruction* objectStubField(uint32_t offset);
|
||||
|
||||
MOZ_MUST_USE bool emitGuardTo(ValOperandId inputId, MIRType type);
|
||||
|
||||
MOZ_MUST_USE bool emitToString(OperandId inputId, StringOperandId resultId);
|
||||
|
@ -178,6 +185,20 @@ bool WarpCacheIRTranspiler::transpile(const MDefinitionStackVector& inputs) {
|
|||
return true;
|
||||
}
|
||||
|
||||
MInstruction* WarpCacheIRTranspiler::objectStubField(uint32_t offset) {
|
||||
WarpObjectField field = WarpObjectField::fromData(readStubWord(offset));
|
||||
|
||||
if (field.isNurseryIndex()) {
|
||||
auto* ins = MNurseryObject::New(alloc(), field.toNurseryIndex());
|
||||
add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
||||
auto* ins = MConstant::NewConstraintlessObject(alloc(), field.toObject());
|
||||
add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitGuardClass(ObjOperandId objId,
|
||||
GuardClassKind kind) {
|
||||
MDefinition* def = getOperand(objId);
|
||||
|
@ -235,11 +256,9 @@ bool WarpCacheIRTranspiler::emitGuardNullProto(ObjOperandId objId) {
|
|||
bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
|
||||
uint32_t protoOffset) {
|
||||
MDefinition* def = getOperand(objId);
|
||||
JSObject* proto = objectStubField(protoOffset);
|
||||
MDefinition* proto = objectStubField(protoOffset);
|
||||
|
||||
MConstant* protoConst = constant(ObjectValue(*proto));
|
||||
|
||||
auto* ins = MGuardProto::New(alloc(), def, protoConst);
|
||||
auto* ins = MGuardProto::New(alloc(), def, proto);
|
||||
add(ins);
|
||||
|
||||
setOperand(objId, ins);
|
||||
|
@ -295,12 +314,9 @@ bool WarpCacheIRTranspiler::emitGuardSpecificSymbol(SymbolOperandId symId,
|
|||
bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
|
||||
uint32_t expectedOffset) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
JSObject* expected = objectStubField(expectedOffset);
|
||||
MDefinition* expected = objectStubField(expectedOffset);
|
||||
|
||||
auto* constObj = MConstant::NewConstraintlessObject(alloc(), expected);
|
||||
add(constObj);
|
||||
|
||||
auto* ins = MGuardObjectIdentity::New(alloc(), obj, constObj,
|
||||
auto* ins = MGuardObjectIdentity::New(alloc(), obj, expected,
|
||||
/* bailOnEquality = */ false);
|
||||
add(ins);
|
||||
|
||||
|
@ -311,18 +327,13 @@ bool WarpCacheIRTranspiler::emitGuardSpecificObject(ObjOperandId objId,
|
|||
bool WarpCacheIRTranspiler::emitGuardSpecificFunction(
|
||||
ObjOperandId objId, uint32_t expectedOffset, uint32_t nargsAndFlagsOffset) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
JSObject* expected = objectStubField(expectedOffset);
|
||||
MDefinition* expected = objectStubField(expectedOffset);
|
||||
uint32_t nargsAndFlags = uint32StubField(nargsAndFlagsOffset);
|
||||
|
||||
MOZ_ASSERT(expected->is<JSFunction>());
|
||||
|
||||
auto* constObj = MConstant::NewConstraintlessObject(alloc(), expected);
|
||||
add(constObj);
|
||||
|
||||
uint16_t nargs = nargsAndFlags >> 16;
|
||||
FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
|
||||
|
||||
auto* ins = MGuardSpecificFunction::New(alloc(), obj, constObj, nargs, flags);
|
||||
auto* ins = MGuardSpecificFunction::New(alloc(), obj, expected, nargs, flags);
|
||||
add(ins);
|
||||
|
||||
setOperand(objId, ins);
|
||||
|
@ -617,10 +628,7 @@ bool WarpCacheIRTranspiler::emitLoadEnclosingEnvironment(
|
|||
|
||||
bool WarpCacheIRTranspiler::emitLoadObject(ObjOperandId resultId,
|
||||
uint32_t objOffset) {
|
||||
JSObject* obj = objectStubField(objOffset);
|
||||
|
||||
auto* ins = MConstant::NewConstraintlessObject(alloc(), obj);
|
||||
add(ins);
|
||||
MInstruction* ins = objectStubField(objOffset);
|
||||
|
||||
return defineOperand(resultId, ins);
|
||||
}
|
||||
|
@ -1758,7 +1766,7 @@ bool WarpCacheIRTranspiler::emitMetaTwoByte(MetaTwoByteKind kind,
|
|||
return true;
|
||||
}
|
||||
|
||||
JSObject* templateObj = objectStubField(templateObjectOffset);
|
||||
JSObject* templateObj = tenuredObjectStubField(templateObjectOffset);
|
||||
MConstant* templateConst = constant(ObjectValue(*templateObj));
|
||||
|
||||
// TODO: support pre-tenuring.
|
||||
|
|
|
@ -54,6 +54,9 @@ class MOZ_STACK_CLASS WarpScriptOracle {
|
|||
AbortReasonOr<WarpEnvironment> createEnvironment();
|
||||
AbortReasonOr<Ok> maybeInlineIC(WarpOpSnapshotList& snapshots,
|
||||
BytecodeLocation loc);
|
||||
MOZ_MUST_USE bool replaceNurseryPointers(ICStub* stub,
|
||||
const CacheIRStubInfo* stubInfo,
|
||||
uint8_t* stubDataCopy);
|
||||
|
||||
public:
|
||||
WarpScriptOracle(JSContext* cx, WarpOracle* oracle, HandleScript script)
|
||||
|
@ -115,6 +118,10 @@ AbortReasonOr<WarpSnapshot*> WarpOracle::createSnapshot() {
|
|||
return abort(outerScript_, AbortReason::Alloc);
|
||||
}
|
||||
|
||||
if (!snapshot->nurseryObjects().appendAll(nurseryObjects_)) {
|
||||
return abort(outerScript_, AbortReason::Alloc);
|
||||
}
|
||||
|
||||
#ifdef JS_JITSPEW
|
||||
if (JitSpewEnabled(JitSpew_WarpSnapshots)) {
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
|
@ -783,12 +790,6 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots,
|
|||
const CacheIRStubInfo* stubInfo = stub->cacheIRStubInfo();
|
||||
const uint8_t* stubData = stub->cacheIRStubData();
|
||||
|
||||
// TODO: we don't support stubs with nursery pointers for now. Handling this
|
||||
// well requires special machinery. See bug 1631267.
|
||||
if (stub->stubDataHasNurseryPointers(stubInfo)) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Only create a snapshots if all opcodes are supported by the transpiler.
|
||||
CacheIRReader reader(stubInfo);
|
||||
while (reader.more()) {
|
||||
|
@ -849,10 +850,14 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots,
|
|||
return abort(AbortReason::Alloc);
|
||||
}
|
||||
|
||||
// We don't need any GC barriers because the stub data does not contain
|
||||
// nursery pointers (checked above) so we can do a bitwise copy.
|
||||
// Note: nursery pointers are handled below so we don't need to trigger any GC
|
||||
// barriers and can do a bitwise copy.
|
||||
std::copy_n(stubData, bytesNeeded, stubDataCopy);
|
||||
|
||||
if (!replaceNurseryPointers(stub, stubInfo, stubDataCopy)) {
|
||||
return abort(AbortReason::Alloc);
|
||||
}
|
||||
|
||||
JitCode* jitCode = stub->jitCode();
|
||||
|
||||
if (!AddOpSnapshot<WarpCacheIR>(alloc_, snapshots, offset, jitCode, stubInfo,
|
||||
|
@ -864,3 +869,93 @@ AbortReasonOr<Ok> WarpScriptOracle::maybeInlineIC(WarpOpSnapshotList& snapshots,
|
|||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool WarpScriptOracle::replaceNurseryPointers(ICStub* stub,
|
||||
const CacheIRStubInfo* stubInfo,
|
||||
uint8_t* stubDataCopy) {
|
||||
// If the stub data contains nursery object pointers, replace them with the
|
||||
// corresponding nursery index. See WarpObjectField.
|
||||
//
|
||||
// Also asserts non-object fields don't contain nursery pointers.
|
||||
|
||||
uint32_t field = 0;
|
||||
size_t offset = 0;
|
||||
while (true) {
|
||||
StubField::Type fieldType = stubInfo->fieldType(field);
|
||||
switch (fieldType) {
|
||||
case StubField::Type::RawWord:
|
||||
case StubField::Type::RawInt64:
|
||||
case StubField::Type::DOMExpandoGeneration:
|
||||
break;
|
||||
case StubField::Type::Shape:
|
||||
static_assert(std::is_convertible_v<Shape*, gc::TenuredCell*>,
|
||||
"Code assumes shapes are tenured");
|
||||
break;
|
||||
case StubField::Type::ObjectGroup:
|
||||
static_assert(std::is_convertible_v<ObjectGroup*, gc::TenuredCell*>,
|
||||
"Code assumes groups are tenured");
|
||||
break;
|
||||
case StubField::Type::Symbol:
|
||||
static_assert(std::is_convertible_v<JS::Symbol*, gc::TenuredCell*>,
|
||||
"Code assumes symbols are tenured");
|
||||
break;
|
||||
case StubField::Type::JSObject: {
|
||||
JSObject* obj = stubInfo->getStubField<ICStub, JSObject*>(stub, offset);
|
||||
if (IsInsideNursery(obj)) {
|
||||
uint32_t nurseryIndex;
|
||||
if (!oracle_->registerNurseryObject(obj, &nurseryIndex)) {
|
||||
return false;
|
||||
}
|
||||
uintptr_t oldWord = WarpObjectField::fromObject(obj).rawData();
|
||||
uintptr_t newWord =
|
||||
WarpObjectField::fromNurseryIndex(nurseryIndex).rawData();
|
||||
stubInfo->replaceStubRawWord(stubDataCopy, offset, oldWord, newWord);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StubField::Type::String: {
|
||||
#ifdef DEBUG
|
||||
JSString* str = stubInfo->getStubField<ICStub, JSString*>(stub, offset);
|
||||
MOZ_ASSERT(!IsInsideNursery(str));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Id: {
|
||||
#ifdef DEBUG
|
||||
// jsid never contains nursery-allocated things.
|
||||
jsid id = stubInfo->getStubField<ICStub, jsid>(stub, offset);
|
||||
MOZ_ASSERT_IF(id.isGCThing(),
|
||||
!IsInsideNursery(id.toGCCellPtr().asCell()));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Value: {
|
||||
#ifdef DEBUG
|
||||
Value v = stubInfo->getStubField<ICStub, JS::Value>(stub, offset);
|
||||
MOZ_ASSERT_IF(v.isGCThing(), !IsInsideNursery(v.toGCThing()));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case StubField::Type::Limit:
|
||||
return true; // Done.
|
||||
}
|
||||
field++;
|
||||
offset += StubField::sizeInBytes(fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
bool WarpOracle::registerNurseryObject(JSObject* obj, uint32_t* nurseryIndex) {
|
||||
MOZ_ASSERT(IsInsideNursery(obj));
|
||||
|
||||
auto p = nurseryObjectsMap_.lookupForAdd(obj);
|
||||
if (p) {
|
||||
*nurseryIndex = p->value();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!nurseryObjects_.append(obj)) {
|
||||
return false;
|
||||
}
|
||||
*nurseryIndex = nurseryObjects_.length() - 1;
|
||||
return nurseryObjectsMap_.add(p, obj, *nurseryIndex);
|
||||
}
|
||||
|
|
|
@ -25,12 +25,24 @@ class MOZ_STACK_CLASS WarpOracle {
|
|||
HandleScript outerScript_;
|
||||
WarpBailoutInfo bailoutInfo_;
|
||||
|
||||
// List of nursery objects to copy to the snapshot. See WarpObjectField.
|
||||
// The HashMap is used to de-duplicate the Vector. It maps each object to the
|
||||
// corresponding nursery index (index into the Vector).
|
||||
// Note: this stores raw object pointers because WarpOracle can't GC.
|
||||
Vector<JSObject*, 8, SystemAllocPolicy> nurseryObjects_;
|
||||
using NurseryObjectsMap =
|
||||
HashMap<JSObject*, uint32_t, DefaultHasher<JSObject*>, SystemAllocPolicy>;
|
||||
NurseryObjectsMap nurseryObjectsMap_;
|
||||
|
||||
public:
|
||||
WarpOracle(JSContext* cx, MIRGenerator& mirGen, HandleScript outerScript);
|
||||
|
||||
MIRGenerator& mirGen() { return mirGen_; }
|
||||
WarpBailoutInfo& bailoutInfo() { return bailoutInfo_; }
|
||||
|
||||
MOZ_MUST_USE bool registerNurseryObject(JSObject* obj,
|
||||
uint32_t* nurseryIndex);
|
||||
|
||||
AbortReasonOr<WarpSnapshot*> createSnapshot();
|
||||
|
||||
mozilla::GenericErrorResult<AbortReason> abort(HandleScript script,
|
||||
|
|
|
@ -285,5 +285,7 @@ void WarpBailout::traceData(JSTracer* trc) {
|
|||
|
||||
void WarpCacheIR::traceData(JSTracer* trc) {
|
||||
TraceWarpGCPtr(trc, stubCode_, "warp-stub-code");
|
||||
// TODO: trace pointers in stub data.
|
||||
|
||||
// TODO: trace pointers in stub data. Beware of nursery indexes in the stub
|
||||
// data. See WarpObjectField.
|
||||
}
|
||||
|
|
|
@ -280,6 +280,63 @@ class WarpCacheIR : public WarpOpSnapshot {
|
|||
#endif
|
||||
};
|
||||
|
||||
// [SMDOC] Warp Nursery Object support
|
||||
//
|
||||
// CacheIR stub data can contain nursery allocated objects. This can happen for
|
||||
// example for GuardSpecificObject/GuardSpecificFunction or GuardProto.
|
||||
//
|
||||
// To support nursery GCs in parallel with off-thread compilation, we use the
|
||||
// following mechanism:
|
||||
//
|
||||
// * When WarpOracle copies stub data, it builds a Vector of nursery objects.
|
||||
// The nursery object pointers in the stub data are replaced with the
|
||||
// corresponding index into this Vector.
|
||||
// See WarpScriptOracle::replaceNurseryPointers.
|
||||
//
|
||||
// * The Vector is copied to the snapshot and, at the end of compilation, to
|
||||
// the IonScript. The Vector is only accessed on the main thread.
|
||||
//
|
||||
// * The MIR backend never accesses the raw JSObject*. Instead, it uses
|
||||
// MNurseryObject which will load the object at runtime from the IonScript.
|
||||
//
|
||||
// WarpObjectField is a helper class to encode/decode a stub data field that
|
||||
// either stores an object or a nursery index.
|
||||
class WarpObjectField {
|
||||
// This is a nursery index if the low bit is set. Else it's a JSObject*.
|
||||
static constexpr uintptr_t NurseryIndexTag = 0x1;
|
||||
static constexpr uintptr_t NurseryIndexShift = 1;
|
||||
|
||||
uintptr_t data_;
|
||||
|
||||
explicit WarpObjectField(uintptr_t data) : data_(data) {}
|
||||
|
||||
public:
|
||||
static WarpObjectField fromData(uintptr_t data) {
|
||||
return WarpObjectField(data);
|
||||
}
|
||||
static WarpObjectField fromObject(JSObject* obj) {
|
||||
return WarpObjectField(uintptr_t(obj));
|
||||
}
|
||||
static WarpObjectField fromNurseryIndex(uint32_t index) {
|
||||
uintptr_t data = (uintptr_t(index) << NurseryIndexShift) | NurseryIndexTag;
|
||||
return WarpObjectField(data);
|
||||
}
|
||||
|
||||
uintptr_t rawData() const { return data_; }
|
||||
|
||||
bool isNurseryIndex() const { return (data_ & NurseryIndexTag) != 0; }
|
||||
|
||||
uint32_t toNurseryIndex() const {
|
||||
MOZ_ASSERT(isNurseryIndex());
|
||||
return data_ >> NurseryIndexShift;
|
||||
}
|
||||
|
||||
JSObject* toObject() const {
|
||||
MOZ_ASSERT(!isNurseryIndex());
|
||||
return reinterpret_cast<JSObject*>(data_);
|
||||
}
|
||||
};
|
||||
|
||||
// Template object for JSOp::Rest.
|
||||
class WarpRest : public WarpOpSnapshot {
|
||||
WarpGCPtr<ArrayObject*> templateObject_;
|
||||
|
|
Загрузка…
Ссылка в новой задаче