Bug 1357680 part 1 - Track Ion-inlined scripts explicitly so we can inline functions with unknown properties. r=bhackett

This commit is contained in:
Jan de Mooij 2017-04-21 10:05:12 +02:00
Родитель b8a6f4f2ab
Коммит ba166dc062
4 изменённых файлов: 137 добавлений и 148 удалений

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

@ -197,7 +197,6 @@ namespace JS {
_(CantInlineTooManyArgs) \
_(CantInlineNeedsArgsObj) \
_(CantInlineDebuggee) \
_(CantInlineUnknownProps) \
_(CantInlineExceededDepth) \
_(CantInlineExceededTotalBytecodeLength) \
_(CantInlineBigCaller) \

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

@ -502,12 +502,6 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
return DontInline(inlineScript, "Script is debuggee");
}
TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
if (targetKey->unknownProperties()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineUnknownProps);
return DontInline(inlineScript, "Target type has unknown properties");
}
return InliningDecision_Inline;
}
@ -4000,10 +3994,6 @@ IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
// End of heuristics, we will inline this function.
// TI calls ObjectStateChange to trigger invalidation of the caller.
TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
targetKey->watchStateChangeForInlinedCall(constraints());
outerBuilder->inlinedBytecodeLength_ += targetScript->length();
return InliningDecision_Inline;

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

@ -1494,6 +1494,15 @@ js::FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList
succeeded = false;
}
// Add this compilation to the inlinedCompilations list of each inlined
// script, so we can invalidate it on changes to stack type sets.
if (entry.script != script) {
if (!entry.script->types()->addInlinedCompilation(*precompileInfo)) {
ReportOutOfMemory(cx);
return false;
}
}
// If necessary, add constraints to trigger invalidation on the script
// after any future changes to the stack type sets.
if (entry.script->hasFreezeConstraints())
@ -1900,37 +1909,6 @@ ObjectGroup::initialHeap(CompilerConstraintList* constraints)
namespace {
// Constraint which triggers recompilation on any type change in an inlined
// script. The freeze constraints added to stack type sets will only directly
// invalidate the script containing those stack type sets. To invalidate code
// for scripts into which the base script was inlined, ObjectStateChange is used.
class ConstraintDataFreezeObjectForInlinedCall
{
public:
ConstraintDataFreezeObjectForInlinedCall()
{}
const char* kind() { return "freezeObjectForInlinedCall"; }
bool invalidateOnNewType(TypeSet::Type type) { return false; }
bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
bool invalidateOnNewObjectState(ObjectGroup* group) {
// We don't keep track of the exact dependencies the caller has on its
// inlined scripts' type sets, so always invalidate the caller.
return true;
}
bool constraintHolds(JSContext* cx,
const HeapTypeSetKey& property, TemporaryTypeSet* expected)
{
return true;
}
bool shouldSweep() { return false; }
JSCompartment* maybeCompartment() { return nullptr; }
};
// Constraint which triggers recompilation when a typed array's data becomes
// invalid.
class ConstraintDataFreezeObjectForTypedArrayData
@ -2004,16 +1982,6 @@ class ConstraintDataFreezeObjectForUnboxedConvertedToNative
} /* anonymous namespace */
void
TypeSet::ObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList* constraints)
{
HeapTypeSetKey objectProperty = property(JSID_EMPTY);
LifoAlloc* alloc = constraints->alloc();
typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T;
constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
}
void
TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* constraints)
{
@ -2609,11 +2577,12 @@ TypeZone::addPendingRecompile(JSContext* cx, JSScript* script)
if (script->hasIonScript())
addPendingRecompile(cx, script->ionScript()->recompileInfo());
// When one script is inlined into another the caller listens to state
// changes on the callee's script, so trigger these to force recompilation
// of any such callers.
if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup())
ObjectStateChange(cx, script->functionNonDelazifying()->group(), false);
// Trigger recompilation of any callers inlining this script.
if (TypeScript* types = script->types()) {
for (RecompileInfo info : types->inlinedCompilations())
addPendingRecompile(cx, info);
types->inlinedCompilations().clearAndFree();
}
}
#ifdef JS_CRASH_DIAGNOSTICS
@ -4437,6 +4406,19 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
TypeZone& types = zone()->types;
// Sweep the inlinedCompilations Vector.
{
RecompileInfoVector& inlinedCompilations = types_->inlinedCompilations();
size_t dest = 0;
for (size_t i = 0; i < inlinedCompilations.length(); i++) {
if (inlinedCompilations[i].shouldSweep(types))
continue;
inlinedCompilations[dest] = inlinedCompilations[i];
dest++;
}
inlinedCompilations.shrinkTo(dest);
}
// Destroy all type information attached to the script if desired. We can
// only do this if nothing has been compiled for the script, which will be
// the case unless the script has been compiled since we started sweeping.
@ -4475,7 +4457,7 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
void
TypeScript::destroy()
{
js_free(this);
js_delete(this);
}
void

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

@ -261,7 +261,6 @@ class TypeSet
bool unknownProperties();
bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
bool hasStableClassAndProto(CompilerConstraintList* constraints);
void watchStateChangeForInlinedCall(CompilerConstraintList* constraints);
void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
HeapTypeSetKey property(jsid id);
@ -1085,15 +1084,123 @@ inline bool isInlinableCall(jsbytecode* pc);
bool
ClassCanHaveExtraProperties(const Class* clasp);
/*
* Information about the result of the compilation of a script. This structure
* stored in the TypeCompartment is indexed by the RecompileInfo. This
* indirection enables the invalidation of all constraints related to the same
* compilation.
*/
class CompilerOutput
{
// If this compilation has not been invalidated, the associated script and
// kind of compilation being performed.
JSScript* script_;
// Whether this compilation is about to be invalidated.
bool pendingInvalidation_ : 1;
// During sweeping, the list of compiler outputs is compacted and invalidated
// outputs are removed. This gives the new index for a valid compiler output.
uint32_t sweepIndex_ : 31;
public:
static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
CompilerOutput()
: script_(nullptr),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
explicit CompilerOutput(JSScript* script)
: script_(script),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
JSScript* script() const { return script_; }
inline jit::IonScript* ion() const;
bool isValid() const {
return script_ != nullptr;
}
void invalidate() {
script_ = nullptr;
}
void setPendingInvalidation() {
pendingInvalidation_ = true;
}
bool pendingInvalidation() {
return pendingInvalidation_;
}
void setSweepIndex(uint32_t index) {
if (index >= INVALID_SWEEP_INDEX)
MOZ_CRASH();
sweepIndex_ = index;
}
uint32_t sweepIndex() {
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
return sweepIndex_;
}
};
class RecompileInfo
{
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
// depending on the generation value.
uint32_t outputIndex : 31;
// If out of sync with the TypeZone's generation, this index is for the
// zone's sweepCompilerOutputs rather than compilerOutputs.
uint32_t generation : 1;
public:
RecompileInfo(uint32_t outputIndex, uint32_t generation)
: outputIndex(outputIndex), generation(generation)
{}
RecompileInfo()
: outputIndex(JS_BITMASK(31)), generation(0)
{}
bool operator==(const RecompileInfo& other) const {
return outputIndex == other.outputIndex && generation == other.generation;
}
CompilerOutput* compilerOutput(TypeZone& types) const;
CompilerOutput* compilerOutput(JSContext* cx) const;
bool shouldSweep(TypeZone& types);
};
// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
// single IonScript doesn't require an allocation.
typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;
/* Persistent type information for a script, retained across GCs. */
class TypeScript
{
friend class ::JSScript;
// The freeze constraints added to stack type sets will only directly
// invalidate the script containing those stack type sets. This Vector
// contains compilations that inlined this script, so we can invalidate
// them as well.
RecompileInfoVector inlinedCompilations_;
// Variable-size array
StackTypeSet typeArray_[1];
public:
RecompileInfoVector& inlinedCompilations() {
return inlinedCompilations_;
}
MOZ_MUST_USE bool addInlinedCompilation(RecompileInfo info) {
if (!inlinedCompilations_.empty() && inlinedCompilations_.back() == info)
return true;
return inlinedCompilations_.append(info);
}
/* Array of type sets for variables and JOF_TYPESET ops. */
StackTypeSet* typeArray() const {
// Ensure typeArray_ is the last data member of TypeScript.
@ -1236,95 +1343,6 @@ class HeapTypeSetKey
bool couldBeConstant(CompilerConstraintList* constraints);
};
/*
* Information about the result of the compilation of a script. This structure
* stored in the TypeCompartment is indexed by the RecompileInfo. This
* indirection enables the invalidation of all constraints related to the same
* compilation.
*/
class CompilerOutput
{
// If this compilation has not been invalidated, the associated script and
// kind of compilation being performed.
JSScript* script_;
// Whether this compilation is about to be invalidated.
bool pendingInvalidation_ : 1;
// During sweeping, the list of compiler outputs is compacted and invalidated
// outputs are removed. This gives the new index for a valid compiler output.
uint32_t sweepIndex_ : 31;
public:
static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
CompilerOutput()
: script_(nullptr),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
explicit CompilerOutput(JSScript* script)
: script_(script),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
JSScript* script() const { return script_; }
inline jit::IonScript* ion() const;
bool isValid() const {
return script_ != nullptr;
}
void invalidate() {
script_ = nullptr;
}
void setPendingInvalidation() {
pendingInvalidation_ = true;
}
bool pendingInvalidation() {
return pendingInvalidation_;
}
void setSweepIndex(uint32_t index) {
if (index >= INVALID_SWEEP_INDEX)
MOZ_CRASH();
sweepIndex_ = index;
}
uint32_t sweepIndex() {
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
return sweepIndex_;
}
};
class RecompileInfo
{
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
// depending on the generation value.
uint32_t outputIndex : 31;
// If out of sync with the TypeZone's generation, this index is for the
// zone's sweepCompilerOutputs rather than compilerOutputs.
uint32_t generation : 1;
public:
RecompileInfo(uint32_t outputIndex, uint32_t generation)
: outputIndex(outputIndex), generation(generation)
{}
RecompileInfo()
: outputIndex(JS_BITMASK(31)), generation(0)
{}
CompilerOutput* compilerOutput(TypeZone& types) const;
CompilerOutput* compilerOutput(JSContext* cx) const;
bool shouldSweep(TypeZone& types);
};
// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
// single IonScript doesn't require an allocation.
typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;
struct AutoEnterAnalysis;
class TypeZone