Skip to content

Commit

Permalink
Bug 1664357: Refactor GetTemplateObjectForScripted r=jandem
Browse files Browse the repository at this point in the history
Rewriting this code to make it obvious that we only attach specialized constructor stubs if we have enough information about `this` to inline.

Note: the old code used `skipAttach` in more cases, but after looking at it more closely, the new script analysis is the only case where it makes sense.

This version matches the corresponding pre-CacheIR call IC code (https://searchfox.org/mozilla-central/rev/3c073ca1ae02fd52fd6be584aa343b78999842fa/js/src/jit/BaselineIC.cpp#3509-3545).

Differential Revision: https://phabricator.services.mozilla.com/D90197
  • Loading branch information
iainireland committed Sep 16, 2020
1 parent cf3b84d commit ee709e6
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 46 deletions.
76 changes: 36 additions & 40 deletions js/src/jit/CacheIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9106,37 +9106,31 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative(

// Remember the template object associated with any script being called
// as a constructor, for later use during Ion compilation.
bool CallIRGenerator::getTemplateObjectForScripted(HandleFunction calleeFunc,
MutableHandleObject result,
bool* skipAttach) {
MOZ_ASSERT(!*skipAttach);

ScriptedThisResult CallIRGenerator::getThisForScripted(
HandleFunction calleeFunc, MutableHandleObject result) {
// Some constructors allocate their own |this| object.
if (calleeFunc->constructorNeedsUninitializedThis()) {
return true;
return ScriptedThisResult::UninitializedThis;
}

// Only attach a stub if the newTarget is a function that already has a
// prototype and we can look it up without causing side effects.
// Only attach a stub if the newTarget is a function with a
// nonconfigurable prototype.
RootedValue protov(cx_);
RootedObject newTarget(cx_, &newTarget_.toObject());
if (!newTarget->is<JSFunction>() ||
!newTarget->as<JSFunction>().hasNonConfigurablePrototypeDataProperty()) {
trackAttached(IRGenerator::NotAttached);
*skipAttach = true;
return true;
return ScriptedThisResult::NoAction;
}

if (!GetPropertyPure(cx_, newTarget, NameToId(cx_->names().prototype),
protov.address())) {
// Can't purely lookup function prototype
trackAttached(IRGenerator::NotAttached);
*skipAttach = true;
return true;
// The lazy prototype property hasn't been resolved yet.
MOZ_ASSERT(newTarget->as<JSFunction>().needsPrototypeProperty());
return ScriptedThisResult::TemporarilyUnoptimizable;
}

if (!protov.isObject()) {
*skipAttach = true;
return true;
return ScriptedThisResult::NoAction;
}

{
Expand All @@ -9145,27 +9139,28 @@ bool CallIRGenerator::getTemplateObjectForScripted(HandleFunction calleeFunc,
ObjectGroup* group = ObjectGroup::defaultNewGroup(cx_, &PlainObject::class_,
proto, newTarget);
if (!group) {
return false;
cx_->clearPendingException();
return ScriptedThisResult::NoAction;
}

AutoSweepObjectGroup sweep(group);
if (group->newScript(sweep) && !group->newScript(sweep)->analyzed()) {
// Function newScript has not been analyzed
trackAttached(IRGenerator::NotAttached);
*skipAttach = true;
return true;
// The new script analysis has not been done on this function.
// Don't attach until after the analysis has been done.
return ScriptedThisResult::TemporarilyUnoptimizable;
}
}

PlainObject* thisObject =
CreateThisForFunction(cx_, calleeFunc, newTarget, TenuredObject);
if (!thisObject) {
return false;
cx_->clearPendingException();
return ScriptedThisResult::NoAction;
}

MOZ_ASSERT(thisObject->nonCCWRealm() == calleeFunc->realm());
result.set(thisObject);
return true;
return ScriptedThisResult::TemplateObject;
}

AttachDecision CallIRGenerator::tryAttachCallScripted(
Expand Down Expand Up @@ -9214,19 +9209,18 @@ AttachDecision CallIRGenerator::tryAttachCallScripted(
}

RootedObject templateObj(cx_);
bool skipAttach = false;
if (isConstructing && isSpecialized &&
!getTemplateObjectForScripted(calleeFunc, &templateObj, &skipAttach)) {
cx_->clearPendingException();
return AttachDecision::NoAction;
}
if (skipAttach) {
return AttachDecision::TemporarilyUnoptimizable;
}

if (isConstructing && isSpecialized &&
calleeFunc->constructorNeedsUninitializedThis()) {
flags.setNeedsUninitializedThis();
if (isConstructing && isSpecialized) {
switch (getThisForScripted(calleeFunc, &templateObj)) {
case ScriptedThisResult::TemplateObject:
break;
case ScriptedThisResult::UninitializedThis:
flags.setNeedsUninitializedThis();
break;
case ScriptedThisResult::TemporarilyUnoptimizable:
return AttachDecision::TemporarilyUnoptimizable;
case ScriptedThisResult::NoAction:
return AttachDecision::NoAction;
}
}

// Load argc.
Expand All @@ -9238,16 +9232,18 @@ AttachDecision CallIRGenerator::tryAttachCallScripted(
ObjOperandId calleeObjId = writer.guardToObject(calleeValId);

if (isSpecialized) {
MOZ_ASSERT_IF(isConstructing,
templateObj || flags.needsUninitializedThis());

// Ensure callee matches this stub's callee
emitCalleeGuard(calleeObjId, calleeFunc);
if (templateObj) {
// Call metaScriptedTemplateObject before emitting the call, so that Warp
// can use this template object before transpiling the call.
MOZ_ASSERT(!flags.needsUninitializedThis());
if (JitOptions.warpBuilder) {
// Emit guards to ensure the newTarget's .prototype property is what we
// expect. Note that getTemplateObjectForScripted checked newTarget is a
// function with a non-configurable .prototype data property.
// expect. Note that getThisForScripted checked newTarget is a function
// with a non-configurable .prototype data property.
JSFunction* newTarget = &newTarget_.toObject().as<JSFunction>();
Shape* shape = newTarget->lookupPure(cx_->names().prototype);
MOZ_ASSERT(shape);
Expand Down
11 changes: 8 additions & 3 deletions js/src/jit/CacheIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,12 @@ class MOZ_RAII OptimizeSpreadCallIRGenerator : public IRGenerator {
};

enum class StringChar { CodeAt, At };
enum class ScriptedThisResult {
NoAction,
TemporarilyUnoptimizable,
UninitializedThis,
TemplateObject
};

class MOZ_RAII CallIRGenerator : public IRGenerator {
private:
Expand All @@ -1701,9 +1707,8 @@ class MOZ_RAII CallIRGenerator : public IRGenerator {
PropertyTypeCheckInfo typeCheckInfo_;
BaselineCacheIRStubKind cacheIRStubKind_;

bool getTemplateObjectForScripted(HandleFunction calleeFunc,
MutableHandleObject result,
bool* skipAttach);
ScriptedThisResult getThisForScripted(HandleFunction calleeFunc,
MutableHandleObject result);
bool getTemplateObjectForNative(HandleFunction calleeFunc,
MutableHandleObject result);

Expand Down
6 changes: 3 additions & 3 deletions js/src/jit/WarpCacheIRTranspiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3419,9 +3419,9 @@ bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId,
MOZ_ASSERT(flags.isConstructing());

// We call maybeCreateThis to update |this|, but inlined constructors
// never need a VM call. CallIRGenerator::getTemplateObjectForScripted
// ensures that we don't attach a specialized stub unless we have a
// template object or know that the constructor needs uninitialized this.
// never need a VM call. CallIRGenerator::getThisForScripted ensures that
// we don't attach a specialized stub unless we have a template object or
// know that the constructor needs uninitialized this.
MOZ_ALWAYS_FALSE(maybeCreateThis(callee, flags, CallKind::Scripted));
mozilla::DebugOnly<MDefinition*> thisArg = callInfo_->thisArg();
MOZ_ASSERT(thisArg->isCreateThisWithTemplate() ||
Expand Down

0 comments on commit ee709e6

Please sign in to comment.