mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1365518 - Fix polymorphic inlining to work correctly for functions that had their group/proto changed. r=shu
This commit is contained in:
parent
16741b1999
commit
a24f22ae22
13
js/src/jit-test/tests/ion/bug1365518.js
Normal file
13
js/src/jit-test/tests/ion/bug1365518.js
Normal file
@ -0,0 +1,13 @@
|
||||
function init() {
|
||||
foo = () => 1;
|
||||
bar = () => 2;
|
||||
foo.__proto__ = function() {};
|
||||
}
|
||||
function test() {
|
||||
var arr = [foo, bar];
|
||||
for (var i = 0; i < 1300; i++) {
|
||||
assertEq(arr[i % 2](), i % 2 + 1);
|
||||
}
|
||||
}
|
||||
init();
|
||||
test();
|
@ -273,7 +273,7 @@ IonBuilder::getSingleCallTarget(TemporaryTypeSet* calleeTypes)
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
|
||||
ObjectVector& targets, uint32_t maxTargets)
|
||||
InliningTargets& targets, uint32_t maxTargets)
|
||||
{
|
||||
MOZ_ASSERT(targets.empty());
|
||||
|
||||
@ -292,10 +292,11 @@ IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
|
||||
return abort(AbortReason::Alloc);
|
||||
for (unsigned i = 0; i < objCount; i++) {
|
||||
JSObject* obj = calleeTypes->getSingleton(i);
|
||||
ObjectGroup* group = nullptr;
|
||||
if (obj) {
|
||||
MOZ_ASSERT(obj->isSingleton());
|
||||
} else {
|
||||
ObjectGroup* group = calleeTypes->getGroup(i);
|
||||
group = calleeTypes->getGroup(i);
|
||||
if (!group)
|
||||
continue;
|
||||
|
||||
@ -316,7 +317,7 @@ IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
|
||||
return Ok();
|
||||
}
|
||||
|
||||
targets.infallibleAppend(obj);
|
||||
targets.infallibleAppend(InliningTarget(obj, group));
|
||||
}
|
||||
|
||||
return Ok();
|
||||
@ -4011,8 +4012,8 @@ IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::selectInliningTargets(const ObjectVector& targets, CallInfo& callInfo, BoolVector& choiceSet,
|
||||
uint32_t* numInlineable)
|
||||
IonBuilder::selectInliningTargets(const InliningTargets& targets, CallInfo& callInfo,
|
||||
BoolVector& choiceSet, uint32_t* numInlineable)
|
||||
{
|
||||
*numInlineable = 0;
|
||||
uint32_t totalSize = 0;
|
||||
@ -4027,7 +4028,7 @@ IonBuilder::selectInliningTargets(const ObjectVector& targets, CallInfo& callInf
|
||||
return Ok();
|
||||
|
||||
for (size_t i = 0; i < targets.length(); i++) {
|
||||
JSObject* target = targets[i];
|
||||
JSObject* target = targets[i].target;
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::Call_Inline);
|
||||
trackTypeInfo(TrackedTypeSite::Call_Target, target);
|
||||
@ -4071,7 +4072,7 @@ IonBuilder::selectInliningTargets(const ObjectVector& targets, CallInfo& callInf
|
||||
// depend on the types of the arguments and the return value.
|
||||
if (isOptimizationTrackingEnabled()) {
|
||||
for (size_t i = 0; i < targets.length(); i++) {
|
||||
if (choiceSet[i] && targets[i]->as<JSFunction>().isNative()) {
|
||||
if (choiceSet[i] && targets[i].target->as<JSFunction>().isNative()) {
|
||||
trackTypeInfo(callInfo);
|
||||
break;
|
||||
}
|
||||
@ -4223,7 +4224,7 @@ IonBuilder::inlineSingleCall(CallInfo& callInfo, JSObject* targetArg)
|
||||
}
|
||||
|
||||
IonBuilder::InliningResult
|
||||
IonBuilder::inlineCallsite(const ObjectVector& targets, CallInfo& callInfo)
|
||||
IonBuilder::inlineCallsite(const InliningTargets& targets, CallInfo& callInfo)
|
||||
{
|
||||
if (targets.empty()) {
|
||||
trackOptimizationAttempt(TrackedStrategy::Call_Inline);
|
||||
@ -4240,7 +4241,7 @@ IonBuilder::inlineCallsite(const ObjectVector& targets, CallInfo& callInfo)
|
||||
// Inline single targets -- unless they derive from a cache, in which case
|
||||
// avoiding the cache and guarding is still faster.
|
||||
if (!propCache.get() && targets.length() == 1) {
|
||||
JSObject* target = targets[0];
|
||||
JSObject* target = targets[0].target;
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::Call_Inline);
|
||||
trackTypeInfo(TrackedTypeSite::Call_Target, target);
|
||||
@ -4409,7 +4410,7 @@ IonBuilder::inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchB
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok>
|
||||
IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVector& choiceSet,
|
||||
IonBuilder::inlineCalls(CallInfo& callInfo, const InliningTargets& targets, BoolVector& choiceSet,
|
||||
MGetPropertyCache* maybeCache)
|
||||
{
|
||||
// Only handle polymorphic inlining.
|
||||
@ -4482,7 +4483,7 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec
|
||||
amendOptimizationAttempt(i);
|
||||
|
||||
// Target must be reachable by the MDispatchInstruction.
|
||||
JSFunction* target = &targets[i]->as<JSFunction>();
|
||||
JSFunction* target = &targets[i].target->as<JSFunction>();
|
||||
if (maybeCache && !maybeCache->propTable()->hasFunction(target)) {
|
||||
choiceSet[i] = false;
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNotInDispatch);
|
||||
@ -4548,8 +4549,7 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec
|
||||
setCurrent(dispatchBlock);
|
||||
|
||||
// Connect the inline path to the returnBlock.
|
||||
ObjectGroup* funcGroup = target->isSingleton() ? nullptr : target->group();
|
||||
if (!dispatch->addCase(target, funcGroup, inlineBlock))
|
||||
if (!dispatch->addCase(target, targets[i].group, inlineBlock))
|
||||
return abort(AbortReason::Alloc);
|
||||
|
||||
MDefinition* retVal = inlineReturnBlock->peek(-1);
|
||||
@ -4634,8 +4634,9 @@ IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVec
|
||||
continue;
|
||||
|
||||
MOZ_ASSERT(!remaining);
|
||||
if (targets[i]->is<JSFunction>() && targets[i]->as<JSFunction>().isSingleton())
|
||||
remaining = &targets[i]->as<JSFunction>();
|
||||
JSObject* target = targets[i].target;
|
||||
if (target->is<JSFunction>() && target->isSingleton())
|
||||
remaining = &target->as<JSFunction>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -5261,7 +5262,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
|
||||
int calleeDepth = -((int)argc + 2 + constructing);
|
||||
|
||||
// Acquire known call target if existent.
|
||||
ObjectVector targets(alloc());
|
||||
InliningTargets targets(alloc());
|
||||
TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
|
||||
if (calleeTypes)
|
||||
MOZ_TRY(getPolyCallTargets(calleeTypes, constructing, targets, 4));
|
||||
@ -5281,8 +5282,8 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
|
||||
|
||||
// No inline, just make the call.
|
||||
JSFunction* target = nullptr;
|
||||
if (targets.length() == 1 && targets[0]->is<JSFunction>())
|
||||
target = &targets[0]->as<JSFunction>();
|
||||
if (targets.length() == 1 && targets[0].target->is<JSFunction>())
|
||||
target = &targets[0].target->as<JSFunction>();
|
||||
|
||||
if (target && status == InliningStatus_WarmUpCountTooLow) {
|
||||
MRecompileCheck* check =
|
||||
|
@ -72,7 +72,7 @@ class IonBuilder
|
||||
|
||||
JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
|
||||
AbortReasonOr<Ok> getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
|
||||
ObjectVector& targets, uint32_t maxTargets);
|
||||
InliningTargets& targets, uint32_t maxTargets);
|
||||
|
||||
AbortReasonOr<Ok> analyzeNewLoopTypes(const CFGBlock* loopEntryBlock);
|
||||
|
||||
@ -615,7 +615,7 @@ class IonBuilder
|
||||
// Oracles.
|
||||
InliningDecision canInlineTarget(JSFunction* target, CallInfo& callInfo);
|
||||
InliningDecision makeInliningDecision(JSObject* target, CallInfo& callInfo);
|
||||
AbortReasonOr<Ok> selectInliningTargets(const ObjectVector& targets, CallInfo& callInfo,
|
||||
AbortReasonOr<Ok> selectInliningTargets(const InliningTargets& targets, CallInfo& callInfo,
|
||||
BoolVector& choiceSet, uint32_t* numInlineable);
|
||||
|
||||
// Native inlining helpers.
|
||||
@ -792,8 +792,8 @@ class IonBuilder
|
||||
InliningResult inlineSingleCall(CallInfo& callInfo, JSObject* target);
|
||||
|
||||
// Call functions
|
||||
InliningResult inlineCallsite(const ObjectVector& targets, CallInfo& callInfo);
|
||||
AbortReasonOr<Ok> inlineCalls(CallInfo& callInfo, const ObjectVector& targets,
|
||||
InliningResult inlineCallsite(const InliningTargets& targets, CallInfo& callInfo);
|
||||
AbortReasonOr<Ok> inlineCalls(CallInfo& callInfo, const InliningTargets& targets,
|
||||
BoolVector& choiceSet, MGetPropertyCache* maybeCache);
|
||||
|
||||
// Inlining helpers.
|
||||
|
@ -5444,7 +5444,7 @@ MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const
|
||||
}
|
||||
|
||||
void
|
||||
InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choiceSet)
|
||||
InlinePropertyTable::trimTo(const InliningTargets& targets, const BoolVector& choiceSet)
|
||||
{
|
||||
for (size_t i = 0; i < targets.length(); i++) {
|
||||
// If the target was inlined, don't erase the entry.
|
||||
@ -5453,10 +5453,10 @@ InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choic
|
||||
|
||||
// If the target wasn't a function we would have veto'ed it
|
||||
// and it will not be in the entries list.
|
||||
if (!targets[i]->is<JSFunction>())
|
||||
if (!targets[i].target->is<JSFunction>())
|
||||
continue;
|
||||
|
||||
JSFunction* target = &targets[i]->as<JSFunction>();
|
||||
JSFunction* target = &targets[i].target->as<JSFunction>();
|
||||
|
||||
// Eliminate all entries containing the vetoed function from the map.
|
||||
size_t j = 0;
|
||||
@ -5470,7 +5470,7 @@ InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choic
|
||||
}
|
||||
|
||||
void
|
||||
InlinePropertyTable::trimToTargets(const ObjectVector& targets)
|
||||
InlinePropertyTable::trimToTargets(const InliningTargets& targets)
|
||||
{
|
||||
JitSpew(JitSpew_Inlining, "Got inlineable property cache with %d cases",
|
||||
(int)numEntries());
|
||||
@ -5479,7 +5479,7 @@ InlinePropertyTable::trimToTargets(const ObjectVector& targets)
|
||||
while (i < numEntries()) {
|
||||
bool foundFunc = false;
|
||||
for (size_t j = 0; j < targets.length(); j++) {
|
||||
if (entries_[i]->func == targets[j]) {
|
||||
if (entries_[i]->func == targets[j].target) {
|
||||
foundFunc = true;
|
||||
break;
|
||||
}
|
||||
|
@ -10736,8 +10736,25 @@ class MStoreFixedSlot
|
||||
ALLOW_CLONE(MStoreFixedSlot)
|
||||
};
|
||||
|
||||
typedef Vector<JSObject*, 4, JitAllocPolicy> ObjectVector;
|
||||
typedef Vector<bool, 4, JitAllocPolicy> BoolVector;
|
||||
struct InliningTarget
|
||||
{
|
||||
JSObject* target;
|
||||
|
||||
// If target is a singleton, group is nullptr. If target is not a singleton,
|
||||
// this is the group we need to guard on when doing a polymorphic inlining
|
||||
// dispatch. Note that this can be different from target->group() due to
|
||||
// proto mutation.
|
||||
ObjectGroup* group;
|
||||
|
||||
InliningTarget(JSObject* target, ObjectGroup* group)
|
||||
: target(target), group(group)
|
||||
{
|
||||
MOZ_ASSERT(target->isSingleton() == !group);
|
||||
}
|
||||
};
|
||||
|
||||
using InliningTargets = Vector<InliningTarget, 4, JitAllocPolicy>;
|
||||
using BoolVector = Vector<bool, 8, JitAllocPolicy>;
|
||||
|
||||
class InlinePropertyTable : public TempObject
|
||||
{
|
||||
@ -10800,10 +10817,10 @@ class InlinePropertyTable : public TempObject
|
||||
TemporaryTypeSet* buildTypeSetForFunction(JSFunction* func) const;
|
||||
|
||||
// Remove targets that vetoed inlining from the InlinePropertyTable.
|
||||
void trimTo(const ObjectVector& targets, const BoolVector& choiceSet);
|
||||
void trimTo(const InliningTargets& targets, const BoolVector& choiceSet);
|
||||
|
||||
// Ensure that the InlinePropertyTable's domain is a subset of |targets|.
|
||||
void trimToTargets(const ObjectVector& targets);
|
||||
void trimToTargets(const InliningTargets& targets);
|
||||
|
||||
bool appendRoots(MRootList& roots) const;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user