зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1673553 part 34 - Replace JitScript's inlinedCompilations vector with a HashMap. r=iain,jonco
At this point JitScript::sweepTypes is only used to sweep the inlinedCompilations Vector, a list of Ion/Warp compilations that inlined the script. This patch replaces that with a HashMap per JitZone so that we can then remove the type sweeping mechanism in the next patch. This also moves the TI invalidation code into jit/. This can be cleaned up more later. Differential Revision: https://phabricator.services.mozilla.com/D97579
This commit is contained in:
Родитель
33fc7fee2e
Коммит
b75bc546b1
|
@ -156,6 +156,24 @@ class GCVector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool traceWeak(JSTracer* trc) {
|
||||||
|
T* src = begin();
|
||||||
|
T* dst = begin();
|
||||||
|
while (src != end()) {
|
||||||
|
if (GCPolicy<T>::traceWeak(trc, src)) {
|
||||||
|
if (src != dst) {
|
||||||
|
*dst = std::move(*src);
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(dst <= end());
|
||||||
|
shrinkBy(end() - dst);
|
||||||
|
return !empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool needsSweep() const { return !this->empty(); }
|
bool needsSweep() const { return !this->empty(); }
|
||||||
|
|
||||||
void sweep() {
|
void sweep() {
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
|
#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
|
||||||
#include "jit/BaselineDebugModeOSR.h" // for RecompileOnStackBaselineScriptsForDebugMode
|
#include "jit/BaselineDebugModeOSR.h" // for RecompileOnStackBaselineScriptsForDebugMode
|
||||||
#include "jit/BaselineJIT.h" // for FinishDiscardBaselineScript
|
#include "jit/BaselineJIT.h" // for FinishDiscardBaselineScript
|
||||||
|
#include "jit/Invalidation.h" // for RecompileInfoVector
|
||||||
#include "jit/Ion.h" // for JitContext
|
#include "jit/Ion.h" // for JitContext
|
||||||
#include "jit/JitScript.h" // for JitScript
|
#include "jit/JitScript.h" // for JitScript
|
||||||
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
|
#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
|
||||||
|
@ -3129,13 +3130,14 @@ static inline void MarkJitScriptActiveIfObservable(
|
||||||
|
|
||||||
static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
|
static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
|
||||||
JSScript* script,
|
JSScript* script,
|
||||||
|
jit::RecompileInfoVector& invalid,
|
||||||
Vector<JSScript*>& scripts) {
|
Vector<JSScript*>& scripts) {
|
||||||
// Enter the script's realm as addPendingRecompile attempts to
|
// Enter the script's realm as AddPendingInvalidation attempts to
|
||||||
// cancel off-thread compilations, whose books are kept on the
|
// cancel off-thread compilations, whose books are kept on the
|
||||||
// script's realm.
|
// script's realm.
|
||||||
MOZ_ASSERT(script->zone() == zone);
|
MOZ_ASSERT(script->zone() == zone);
|
||||||
AutoRealm ar(cx, script);
|
AutoRealm ar(cx, script);
|
||||||
zone->types.addPendingRecompile(cx, script);
|
AddPendingInvalidation(invalid, script);
|
||||||
return scripts.append(script);
|
return scripts.append(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3153,10 +3155,10 @@ static bool UpdateExecutionObservabilityOfScriptsInZone(
|
||||||
// Iterate through observable scripts, invalidating their Ion scripts and
|
// Iterate through observable scripts, invalidating their Ion scripts and
|
||||||
// appending them to a vector for discarding their baseline scripts later.
|
// appending them to a vector for discarding their baseline scripts later.
|
||||||
{
|
{
|
||||||
AutoEnterAnalysis enter(fop, zone);
|
RecompileInfoVector invalid;
|
||||||
if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
|
if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
|
||||||
if (obs.shouldRecompileOrInvalidate(script)) {
|
if (obs.shouldRecompileOrInvalidate(script)) {
|
||||||
if (!AppendAndInvalidateScript(cx, zone, script, scripts)) {
|
if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3168,12 +3170,13 @@ static bool UpdateExecutionObservabilityOfScriptsInZone(
|
||||||
}
|
}
|
||||||
JSScript* script = base->asJSScript();
|
JSScript* script = base->asJSScript();
|
||||||
if (obs.shouldRecompileOrInvalidate(script)) {
|
if (obs.shouldRecompileOrInvalidate(script)) {
|
||||||
if (!AppendAndInvalidateScript(cx, zone, script, scripts)) {
|
if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Invalidate(cx, invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code below this point must be infallible to ensure the active bit of
|
// Code below this point must be infallible to ensure the active bit of
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "gc/PublicIterators.h"
|
#include "gc/PublicIterators.h"
|
||||||
#include "jit/BaselineIC.h"
|
#include "jit/BaselineIC.h"
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "jit/JitZone.h"
|
#include "jit/JitZone.h"
|
||||||
#include "vm/Runtime.h"
|
#include "vm/Runtime.h"
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "jit/BaselineIC.h"
|
#include "jit/BaselineIC.h"
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
#include "jit/CalleeToken.h"
|
#include "jit/CalleeToken.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "jit/IonScript.h"
|
#include "jit/IonScript.h"
|
||||||
#include "jit/JitFrames.h"
|
#include "jit/JitFrames.h"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "jit/BaselineFrame.h"
|
#include "jit/BaselineFrame.h"
|
||||||
#include "jit/BaselineIC.h"
|
#include "jit/BaselineIC.h"
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "jit/JitcodeMap.h"
|
#include "jit/JitcodeMap.h"
|
||||||
#include "jit/JitFrames.h"
|
#include "jit/JitFrames.h"
|
||||||
|
@ -463,7 +464,7 @@ static bool InvalidateScriptsInZone(JSContext* cx, Zone* zone,
|
||||||
|
|
||||||
// No need to cancel off-thread Ion compiles again, we already did it
|
// No need to cancel off-thread Ion compiles again, we already did it
|
||||||
// above.
|
// above.
|
||||||
Invalidate(zone->types, cx->runtime()->defaultFreeOp(), invalid,
|
Invalidate(cx, invalid,
|
||||||
/* resetUses = */ true, /* cancelOffThread = */ false);
|
/* resetUses = */ true, /* cancelOffThread = */ false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "jit/BaselineCodeGen.h"
|
#include "jit/BaselineCodeGen.h"
|
||||||
#include "jit/CompileInfo.h"
|
#include "jit/CompileInfo.h"
|
||||||
#include "jit/InlineScriptTree.h"
|
#include "jit/InlineScriptTree.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/IonIC.h"
|
#include "jit/IonIC.h"
|
||||||
#include "jit/IonOptimizationLevels.h"
|
#include "jit/IonOptimizationLevels.h"
|
||||||
#include "jit/IonScript.h"
|
#include "jit/IonScript.h"
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
#include "jit/JitRealm.h"
|
#include "jit/JitRealm.h"
|
||||||
#include "jit/JitRuntime.h"
|
#include "jit/JitRuntime.h"
|
||||||
#include "jit/JitSpewer.h"
|
#include "jit/JitSpewer.h"
|
||||||
|
#include "jit/JitZone.h"
|
||||||
#include "jit/Linker.h"
|
#include "jit/Linker.h"
|
||||||
#include "jit/Lowering.h"
|
#include "jit/Lowering.h"
|
||||||
#include "jit/MIRGenerator.h"
|
#include "jit/MIRGenerator.h"
|
||||||
|
@ -11245,13 +11247,15 @@ bool CodeGenerator::generate() {
|
||||||
return !masm.oom();
|
return !masm.oom();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AddInlinedCompilations(HandleScript script,
|
static bool AddInlinedCompilations(JSContext* cx, HandleScript script,
|
||||||
IonCompilationId compilationId,
|
IonCompilationId compilationId,
|
||||||
const WarpSnapshot* snapshot,
|
const WarpSnapshot* snapshot,
|
||||||
bool* isValid) {
|
bool* isValid) {
|
||||||
MOZ_ASSERT(!*isValid);
|
MOZ_ASSERT(!*isValid);
|
||||||
RecompileInfo recompileInfo(script, compilationId);
|
RecompileInfo recompileInfo(script, compilationId);
|
||||||
|
|
||||||
|
JitZone* jitZone = cx->zone()->jitZone();
|
||||||
|
|
||||||
for (const auto* scriptSnapshot : snapshot->scripts()) {
|
for (const auto* scriptSnapshot : snapshot->scripts()) {
|
||||||
JSScript* inlinedScript = scriptSnapshot->script();
|
JSScript* inlinedScript = scriptSnapshot->script();
|
||||||
if (inlinedScript == script) {
|
if (inlinedScript == script) {
|
||||||
|
@ -11269,9 +11273,7 @@ static bool AddInlinedCompilations(HandleScript script,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoSweepJitScript sweep(inlinedScript);
|
if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) {
|
||||||
if (!inlinedScript->jitScript()->addInlinedCompilation(sweep,
|
|
||||||
recompileInfo)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11325,7 +11327,7 @@ bool CodeGenerator::link(JSContext* cx, const WarpSnapshot* snapshot) {
|
||||||
|
|
||||||
// If an inlined script is invalidated (for example, by attaching
|
// If an inlined script is invalidated (for example, by attaching
|
||||||
// a debugger), we must also invalidate the parent IonScript.
|
// a debugger), we must also invalidate the parent IonScript.
|
||||||
if (!AddInlinedCompilations(script, compilationId, snapshot, &isValid)) {
|
if (!AddInlinedCompilations(cx, script, compilationId, snapshot, &isValid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef jit_Invalidation_h
|
||||||
|
#define jit_Invalidation_h
|
||||||
|
|
||||||
|
#include "jit/IonTypes.h"
|
||||||
|
#include "js/AllocPolicy.h"
|
||||||
|
#include "js/GCVector.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
class IonScript;
|
||||||
|
|
||||||
|
class RecompileInfo {
|
||||||
|
JSScript* script_;
|
||||||
|
IonCompilationId id_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RecompileInfo(JSScript* script, IonCompilationId id)
|
||||||
|
: script_(script), id_(id) {}
|
||||||
|
|
||||||
|
JSScript* script() const { return script_; }
|
||||||
|
|
||||||
|
IonScript* maybeIonScriptToInvalidate() const;
|
||||||
|
|
||||||
|
bool traceWeak(JSTracer* trc);
|
||||||
|
|
||||||
|
bool operator==(const RecompileInfo& other) const {
|
||||||
|
return script_ == other.script_ && id_ == other.id_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
|
||||||
|
// single IonScript doesn't require an allocation.
|
||||||
|
using RecompileInfoVector = JS::GCVector<RecompileInfo, 1, SystemAllocPolicy>;
|
||||||
|
|
||||||
|
// Called from Zone::discardJitCode().
|
||||||
|
void InvalidateAll(JSFreeOp* fop, JS::Zone* zone);
|
||||||
|
void FinishInvalidation(JSFreeOp* fop, JSScript* script);
|
||||||
|
|
||||||
|
// Add compilations involving |script| (outer script or inlined) to the vector.
|
||||||
|
void AddPendingInvalidation(jit::RecompileInfoVector& invalid,
|
||||||
|
JSScript* script);
|
||||||
|
|
||||||
|
// Walk the stack and invalidate active Ion frames for the invalid scripts.
|
||||||
|
void Invalidate(JSContext* cx, const RecompileInfoVector& invalid,
|
||||||
|
bool resetUses = true, bool cancelOffThread = true);
|
||||||
|
void Invalidate(JSContext* cx, JSScript* script, bool resetUses = true,
|
||||||
|
bool cancelOffThread = true);
|
||||||
|
|
||||||
|
} // namespace jit
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* jit_Invalidation_h */
|
|
@ -31,6 +31,7 @@
|
||||||
#include "jit/FoldLinearArithConstants.h"
|
#include "jit/FoldLinearArithConstants.h"
|
||||||
#include "jit/InlineScriptTree.h"
|
#include "jit/InlineScriptTree.h"
|
||||||
#include "jit/InstructionReordering.h"
|
#include "jit/InstructionReordering.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/IonAnalysis.h"
|
#include "jit/IonAnalysis.h"
|
||||||
#include "jit/IonCompileTask.h"
|
#include "jit/IonCompileTask.h"
|
||||||
#include "jit/IonIC.h"
|
#include "jit/IonIC.h"
|
||||||
|
@ -451,8 +452,96 @@ void JitRealm::traceWeak(JSTracer* trc, JS::Realm* realm) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JitZone::addInlinedCompilation(const RecompileInfo& info,
|
||||||
|
JSScript* inlined) {
|
||||||
|
MOZ_ASSERT(inlined != info.script());
|
||||||
|
|
||||||
|
auto p = inlinedCompilations_.lookupForAdd(inlined);
|
||||||
|
if (p) {
|
||||||
|
auto& compilations = p->value();
|
||||||
|
if (!compilations.empty() && compilations.back() == info) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return compilations.append(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecompileInfoVector compilations;
|
||||||
|
if (!compilations.append(info)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return inlinedCompilations_.add(p, inlined, std::move(compilations));
|
||||||
|
}
|
||||||
|
|
||||||
|
void jit::AddPendingInvalidation(RecompileInfoVector& invalid,
|
||||||
|
JSScript* script) {
|
||||||
|
MOZ_ASSERT(script);
|
||||||
|
|
||||||
|
CancelOffThreadIonCompile(script);
|
||||||
|
|
||||||
|
// Let the script warm up again before attempting another compile.
|
||||||
|
script->resetWarmUpCounterToDelayIonCompilation();
|
||||||
|
|
||||||
|
JitScript* jitScript = script->maybeJitScript();
|
||||||
|
if (!jitScript) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto addPendingInvalidation = [&invalid](const RecompileInfo& info) {
|
||||||
|
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||||
|
if (!invalid.append(info)) {
|
||||||
|
// BUG 1536159: For diagnostics, compute the size of the failed
|
||||||
|
// allocation. This presumes the vector growth strategy is to double. This
|
||||||
|
// is only used for crash reporting so not a problem if we get it wrong.
|
||||||
|
size_t allocSize = 2 * sizeof(RecompileInfo) * invalid.capacity();
|
||||||
|
oomUnsafe.crash(allocSize, "Could not update RecompileInfoVector");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trigger invalidation of the IonScript.
|
||||||
|
if (jitScript->hasIonScript()) {
|
||||||
|
RecompileInfo info(script, jitScript->ionScript()->compilationId());
|
||||||
|
addPendingInvalidation(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger invalidation of any callers inlining this script.
|
||||||
|
auto* inlinedCompilations =
|
||||||
|
script->zone()->jitZone()->maybeInlinedCompilations(script);
|
||||||
|
if (inlinedCompilations) {
|
||||||
|
for (const RecompileInfo& info : *inlinedCompilations) {
|
||||||
|
addPendingInvalidation(info);
|
||||||
|
}
|
||||||
|
script->zone()->jitZone()->removeInlinedCompilations(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IonScript* RecompileInfo::maybeIonScriptToInvalidate() const {
|
||||||
|
// Make sure this is not called under CodeGenerator::link (before the
|
||||||
|
// IonScript is created).
|
||||||
|
MOZ_ASSERT_IF(script_->zone()->types.currentCompilationId(),
|
||||||
|
script_->zone()->types.currentCompilationId().ref() != id_);
|
||||||
|
|
||||||
|
if (!script_->hasIonScript() ||
|
||||||
|
script_->ionScript()->compilationId() != id_) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return script_->ionScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RecompileInfo::traceWeak(JSTracer* trc) {
|
||||||
|
// Sweep the RecompileInfo if either the script is dead or the IonScript has
|
||||||
|
// been invalidated.
|
||||||
|
|
||||||
|
if (!TraceManuallyBarrieredWeakEdge(trc, &script_, "RecompileInfo::script")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maybeIonScriptToInvalidate() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void JitZone::traceWeak(JSTracer* trc) {
|
void JitZone::traceWeak(JSTracer* trc) {
|
||||||
baselineCacheIRStubCodes_.traceWeak(trc);
|
baselineCacheIRStubCodes_.traceWeak(trc);
|
||||||
|
inlinedCompilations_.traceWeak(trc);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t JitRealm::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
size_t JitRealm::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||||
|
@ -2512,9 +2601,8 @@ static void ClearIonScriptAfterInvalidation(JSContext* cx, JSScript* script,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void jit::Invalidate(TypeZone& types, JSFreeOp* fop,
|
void jit::Invalidate(JSContext* cx, const RecompileInfoVector& invalid,
|
||||||
const RecompileInfoVector& invalid, bool resetUses,
|
bool resetUses, bool cancelOffThread) {
|
||||||
bool cancelOffThread) {
|
|
||||||
JitSpew(JitSpew_IonInvalidate, "Start invalidation.");
|
JitSpew(JitSpew_IonInvalidate, "Start invalidation.");
|
||||||
|
|
||||||
// Add an invalidation reference to all invalidated IonScripts to indicate
|
// Add an invalidation reference to all invalidated IonScripts to indicate
|
||||||
|
@ -2525,7 +2613,7 @@ void jit::Invalidate(TypeZone& types, JSFreeOp* fop,
|
||||||
CancelOffThreadIonCompile(info.script());
|
CancelOffThreadIonCompile(info.script());
|
||||||
}
|
}
|
||||||
|
|
||||||
IonScript* ionScript = info.maybeIonScriptToInvalidate(types);
|
IonScript* ionScript = info.maybeIonScriptToInvalidate();
|
||||||
if (!ionScript) {
|
if (!ionScript) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2546,7 +2634,7 @@ void jit::Invalidate(TypeZone& types, JSFreeOp* fop,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSContext* cx = TlsContext.get();
|
JSFreeOp* fop = cx->defaultFreeOp();
|
||||||
for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
|
for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
|
||||||
InvalidateActivation(fop, iter, false);
|
InvalidateActivation(fop, iter, false);
|
||||||
}
|
}
|
||||||
|
@ -2555,7 +2643,7 @@ void jit::Invalidate(TypeZone& types, JSFreeOp* fop,
|
||||||
// IonScript will be immediately destroyed. Otherwise, it will be held live
|
// IonScript will be immediately destroyed. Otherwise, it will be held live
|
||||||
// until its last invalidated frame is destroyed.
|
// until its last invalidated frame is destroyed.
|
||||||
for (const RecompileInfo& info : invalid) {
|
for (const RecompileInfo& info : invalid) {
|
||||||
IonScript* ionScript = info.maybeIonScriptToInvalidate(types);
|
IonScript* ionScript = info.maybeIonScriptToInvalidate();
|
||||||
if (!ionScript) {
|
if (!ionScript) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2579,18 +2667,12 @@ void jit::Invalidate(TypeZone& types, JSFreeOp* fop,
|
||||||
// Finally, null out jitScript->ionScript_ for IonScripts that are still on
|
// Finally, null out jitScript->ionScript_ for IonScripts that are still on
|
||||||
// the stack.
|
// the stack.
|
||||||
for (const RecompileInfo& info : invalid) {
|
for (const RecompileInfo& info : invalid) {
|
||||||
if (IonScript* ionScript = info.maybeIonScriptToInvalidate(types)) {
|
if (IonScript* ionScript = info.maybeIonScriptToInvalidate()) {
|
||||||
ClearIonScriptAfterInvalidation(cx, info.script(), ionScript, resetUses);
|
ClearIonScriptAfterInvalidation(cx, info.script(), ionScript, resetUses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void jit::Invalidate(JSContext* cx, const RecompileInfoVector& invalid,
|
|
||||||
bool resetUses, bool cancelOffThread) {
|
|
||||||
jit::Invalidate(cx->zone()->types, cx->runtime()->defaultFreeOp(), invalid,
|
|
||||||
resetUses, cancelOffThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jit::IonScript::invalidate(JSContext* cx, JSScript* script, bool resetUses,
|
void jit::IonScript::invalidate(JSContext* cx, JSScript* script, bool resetUses,
|
||||||
const char* reason) {
|
const char* reason) {
|
||||||
// Note: we could short circuit here if we already invalidated this
|
// Note: we could short circuit here if we already invalidated this
|
||||||
|
|
|
@ -66,15 +66,6 @@ MethodStatus CanEnterIon(JSContext* cx, RunState& state);
|
||||||
|
|
||||||
MethodStatus Recompile(JSContext* cx, HandleScript script, bool force);
|
MethodStatus Recompile(JSContext* cx, HandleScript script, bool force);
|
||||||
|
|
||||||
// Walk the stack and invalidate active Ion frames for the invalid scripts.
|
|
||||||
void Invalidate(TypeZone& types, JSFreeOp* fop,
|
|
||||||
const RecompileInfoVector& invalid, bool resetUses = true,
|
|
||||||
bool cancelOffThread = true);
|
|
||||||
void Invalidate(JSContext* cx, const RecompileInfoVector& invalid,
|
|
||||||
bool resetUses = true, bool cancelOffThread = true);
|
|
||||||
void Invalidate(JSContext* cx, JSScript* script, bool resetUses = true,
|
|
||||||
bool cancelOffThread = true);
|
|
||||||
|
|
||||||
class MIRGenerator;
|
class MIRGenerator;
|
||||||
class LIRGraph;
|
class LIRGraph;
|
||||||
class CodeGenerator;
|
class CodeGenerator;
|
||||||
|
|
|
@ -245,12 +245,6 @@ class alignas(uintptr_t) JitScript final : public TrailingArray {
|
||||||
// analyzed by IonBuilder. This is done lazily to improve performance and
|
// analyzed by IonBuilder. This is done lazily to improve performance and
|
||||||
// memory usage as most scripts are never Ion-compiled.
|
// memory usage as most scripts are never Ion-compiled.
|
||||||
struct CachedIonData {
|
struct CachedIonData {
|
||||||
// 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_;
|
|
||||||
|
|
||||||
// For functions with a call object, template objects to use for the call
|
// For functions with a call object, template objects to use for the call
|
||||||
// object and decl env object (linked via the call object's enclosing
|
// object and decl env object (linked via the call object's enclosing
|
||||||
// scope).
|
// scope).
|
||||||
|
@ -343,24 +337,6 @@ class alignas(uintptr_t) JitScript final : public TrailingArray {
|
||||||
void setHadIonOSR() { flags_.hadIonOSR = true; }
|
void setHadIonOSR() { flags_.hadIonOSR = true; }
|
||||||
bool hadIonOSR() const { return flags_.hadIonOSR; }
|
bool hadIonOSR() const { return flags_.hadIonOSR; }
|
||||||
|
|
||||||
RecompileInfoVector* maybeInlinedCompilations(
|
|
||||||
const js::AutoSweepJitScript& sweep) {
|
|
||||||
MOZ_ASSERT(sweep.jitScript() == this);
|
|
||||||
if (!hasCachedIonData()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &cachedIonData().inlinedCompilations_;
|
|
||||||
}
|
|
||||||
MOZ_MUST_USE bool addInlinedCompilation(const js::AutoSweepJitScript& sweep,
|
|
||||||
RecompileInfo info) {
|
|
||||||
MOZ_ASSERT(sweep.jitScript() == this);
|
|
||||||
auto& inlinedCompilations = cachedIonData().inlinedCompilations_;
|
|
||||||
if (!inlinedCompilations.empty() && inlinedCompilations.back() == info) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return inlinedCompilations.append(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t numICEntries() const { return icScript_.numICEntries(); }
|
uint32_t numICEntries() const { return icScript_.numICEntries(); }
|
||||||
|
|
||||||
bool active() const { return flags_.active; }
|
bool active() const { return flags_.active; }
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "gc/Barrier.h"
|
#include "gc/Barrier.h"
|
||||||
#include "jit/ExecutableAllocator.h"
|
#include "jit/ExecutableAllocator.h"
|
||||||
#include "jit/ICStubSpace.h"
|
#include "jit/ICStubSpace.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "js/AllocPolicy.h"
|
#include "js/AllocPolicy.h"
|
||||||
#include "js/GCHashTable.h"
|
#include "js/GCHashTable.h"
|
||||||
#include "js/HashTable.h"
|
#include "js/HashTable.h"
|
||||||
|
@ -98,6 +99,12 @@ class JitZone {
|
||||||
// Executable allocator for all code except wasm code.
|
// Executable allocator for all code except wasm code.
|
||||||
MainThreadData<ExecutableAllocator> execAlloc_;
|
MainThreadData<ExecutableAllocator> execAlloc_;
|
||||||
|
|
||||||
|
// HashMap that maps scripts to compilations inlining those scripts.
|
||||||
|
using InlinedScriptMap =
|
||||||
|
GCHashMap<WeakHeapPtr<BaseScript*>, RecompileInfoVector,
|
||||||
|
MovableCellHasher<WeakHeapPtr<BaseScript*>>, SystemAllocPolicy>;
|
||||||
|
InlinedScriptMap inlinedCompilations_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void traceWeak(JSTracer* trc);
|
void traceWeak(JSTracer* trc);
|
||||||
|
|
||||||
|
@ -140,11 +147,19 @@ class JitZone {
|
||||||
|
|
||||||
ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
|
ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
|
||||||
const ExecutableAllocator& execAlloc() const { return execAlloc_.ref(); }
|
const ExecutableAllocator& execAlloc() const { return execAlloc_.ref(); }
|
||||||
};
|
|
||||||
|
|
||||||
// Called from Zone::discardJitCode().
|
MOZ_MUST_USE bool addInlinedCompilation(const RecompileInfo& info,
|
||||||
void InvalidateAll(JSFreeOp* fop, JS::Zone* zone);
|
JSScript* inlined);
|
||||||
void FinishInvalidation(JSFreeOp* fop, JSScript* script);
|
|
||||||
|
RecompileInfoVector* maybeInlinedCompilations(JSScript* inlined) {
|
||||||
|
auto p = inlinedCompilations_.lookup(inlined);
|
||||||
|
return p ? &p->value() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeInlinedCompilations(JSScript* inlined) {
|
||||||
|
inlinedCompilations_.remove(inlined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "frontend/StencilXdr.h" // frontend::StencilXdr::SharedData
|
#include "frontend/StencilXdr.h" // frontend::StencilXdr::SharedData
|
||||||
#include "gc/FreeOp.h"
|
#include "gc/FreeOp.h"
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
|
#include "jit/Invalidation.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "jit/IonScript.h"
|
#include "jit/IonScript.h"
|
||||||
#include "jit/JitCode.h"
|
#include "jit/JitCode.h"
|
||||||
|
@ -4753,8 +4754,9 @@ void JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script) {
|
||||||
// Warp code depends on the NeedsArgsObj flag so invalidate the script
|
// Warp code depends on the NeedsArgsObj flag so invalidate the script
|
||||||
// (including compilations inlining the script).
|
// (including compilations inlining the script).
|
||||||
if (jit::JitOptions.warpBuilder) {
|
if (jit::JitOptions.warpBuilder) {
|
||||||
AutoEnterAnalysis enter(cx->runtime()->defaultFreeOp(), script->zone());
|
jit::RecompileInfoVector invalid;
|
||||||
script->zone()->types.addPendingRecompile(cx, script);
|
AddPendingInvalidation(invalid, script);
|
||||||
|
Invalidate(cx, invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
#include "jit/IonScript.h"
|
#include "jit/IonScript.h"
|
||||||
#include "jit/JitScript.h"
|
#include "jit/JitScript.h"
|
||||||
|
#include "jit/JitZone.h"
|
||||||
#include "js/HeapAPI.h"
|
#include "js/HeapAPI.h"
|
||||||
#include "util/DiagnosticAssertions.h"
|
#include "util/DiagnosticAssertions.h"
|
||||||
#include "vm/ArrayObject.h"
|
#include "vm/ArrayObject.h"
|
||||||
|
@ -43,43 +44,6 @@
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// RecompileInfo
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
jit::IonScript* RecompileInfo::maybeIonScriptToInvalidate(
|
|
||||||
const TypeZone& zone) const {
|
|
||||||
MOZ_ASSERT(script_->zone() == zone.zone());
|
|
||||||
|
|
||||||
// Make sure this is not called under CodeGenerator::link (before the
|
|
||||||
// IonScript is created).
|
|
||||||
MOZ_ASSERT_IF(zone.currentCompilationId(),
|
|
||||||
zone.currentCompilationId().ref() != id_);
|
|
||||||
|
|
||||||
if (!script_->hasIonScript() ||
|
|
||||||
script_->ionScript()->compilationId() != id_) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return script_->ionScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool RecompileInfo::shouldSweep(const TypeZone& zone) {
|
|
||||||
if (IsAboutToBeFinalizedUnbarriered(&script_)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(script_->zone() == zone.zone());
|
|
||||||
|
|
||||||
// Don't sweep if we're called under CodeGenerator::link, before the
|
|
||||||
// IonScript is created.
|
|
||||||
if (zone.currentCompilationId() && zone.currentCompilationId().ref() == id_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return maybeIonScriptToInvalidate(zone) == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
|
class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
|
||||||
JS::Zone* zone;
|
JS::Zone* zone;
|
||||||
bool saved;
|
bool saved;
|
||||||
|
@ -111,9 +75,6 @@ struct MOZ_RAII AutoEnterAnalysis {
|
||||||
// Prevent GC activity in the middle of analysis.
|
// Prevent GC activity in the middle of analysis.
|
||||||
gc::AutoSuppressGC suppressGC;
|
gc::AutoSuppressGC suppressGC;
|
||||||
|
|
||||||
// Pending recompilations to perform before execution of JIT code can resume.
|
|
||||||
RecompileInfoVector pendingRecompiles;
|
|
||||||
|
|
||||||
// Prevent us from calling the objectMetadataCallback.
|
// Prevent us from calling the objectMetadataCallback.
|
||||||
js::AutoSuppressAllocationMetadataBuilder suppressMetadata;
|
js::AutoSuppressAllocationMetadataBuilder suppressMetadata;
|
||||||
|
|
||||||
|
@ -136,10 +97,6 @@ struct MOZ_RAII AutoEnterAnalysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
zone->types.activeAnalysis = nullptr;
|
zone->types.activeAnalysis = nullptr;
|
||||||
|
|
||||||
if (!pendingRecompiles.empty()) {
|
|
||||||
zone->types.processPendingRecompiles(freeOp, pendingRecompiles);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
#include "jit/Ion.h"
|
#include "jit/Ion.h"
|
||||||
#include "jit/IonAnalysis.h"
|
#include "jit/IonAnalysis.h"
|
||||||
|
#include "jit/JitZone.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
#include "js/ScalarType.h" // js::Scalar::Type
|
#include "js/ScalarType.h" // js::Scalar::Type
|
||||||
#include "js/UniquePtr.h"
|
#include "js/UniquePtr.h"
|
||||||
|
@ -61,62 +62,6 @@ bool js::ClassCanHaveExtraProperties(const JSClass* clasp) {
|
||||||
clasp->getOpsGetProperty() || IsTypedArrayClass(clasp);
|
clasp->getOpsGetProperty() || IsTypedArrayClass(clasp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeZone::processPendingRecompiles(JSFreeOp* fop,
|
|
||||||
RecompileInfoVector& recompiles) {
|
|
||||||
MOZ_ASSERT(!recompiles.empty());
|
|
||||||
|
|
||||||
// Steal the list of scripts to recompile, to make sure we don't try to
|
|
||||||
// recursively recompile them. Note: the move constructor will not reset the
|
|
||||||
// length if the Vector is using inline storage, so we also use clear().
|
|
||||||
RecompileInfoVector pending(std::move(recompiles));
|
|
||||||
recompiles.clear();
|
|
||||||
|
|
||||||
jit::Invalidate(*this, fop, pending);
|
|
||||||
|
|
||||||
MOZ_ASSERT(recompiles.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypeZone::addPendingRecompile(JSContext* cx, const RecompileInfo& info) {
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
RecompileInfoVector& vector =
|
|
||||||
cx->zone()->types.activeAnalysis->pendingRecompiles;
|
|
||||||
if (!vector.append(info)) {
|
|
||||||
// BUG 1536159: For diagnostics, compute the size of the failed allocation.
|
|
||||||
// This presumes the vector growth strategy is to double. This is only used
|
|
||||||
// for crash reporting so not a problem if we get it wrong.
|
|
||||||
size_t allocSize = 2 * sizeof(RecompileInfo) * vector.capacity();
|
|
||||||
oomUnsafe.crash(allocSize, "Could not update pendingRecompiles");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) {
|
|
||||||
MOZ_ASSERT(script);
|
|
||||||
|
|
||||||
CancelOffThreadIonCompile(script);
|
|
||||||
|
|
||||||
// Let the script warm up again before attempting another compile.
|
|
||||||
script->resetWarmUpCounterToDelayIonCompilation();
|
|
||||||
|
|
||||||
if (JitScript* jitScript = script->maybeJitScript()) {
|
|
||||||
// Trigger recompilation of the IonScript.
|
|
||||||
if (jitScript->hasIonScript()) {
|
|
||||||
addPendingRecompile(
|
|
||||||
cx, RecompileInfo(script, jitScript->ionScript()->compilationId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger recompilation of any callers inlining this script.
|
|
||||||
AutoSweepJitScript sweep(script);
|
|
||||||
RecompileInfoVector* inlinedCompilations =
|
|
||||||
jitScript->maybeInlinedCompilations(sweep);
|
|
||||||
if (inlinedCompilations) {
|
|
||||||
for (const RecompileInfo& info : *inlinedCompilations) {
|
|
||||||
addPendingRecompile(cx, info);
|
|
||||||
}
|
|
||||||
inlinedCompilations->clearAndFree();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// Tracing
|
// Tracing
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
@ -135,22 +80,6 @@ void JitScript::sweepTypes(const js::AutoSweepJitScript& sweep, Zone* zone) {
|
||||||
setTypesGeneration(zone->types.generation);
|
setTypesGeneration(zone->types.generation);
|
||||||
|
|
||||||
AssertGCStateForSweep(zone);
|
AssertGCStateForSweep(zone);
|
||||||
|
|
||||||
TypeZone& types = zone->types;
|
|
||||||
|
|
||||||
// Sweep the inlinedCompilations Vector.
|
|
||||||
if (maybeInlinedCompilations(sweep)) {
|
|
||||||
RecompileInfoVector& inlinedCompilations = *maybeInlinedCompilations(sweep);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeZone::TypeZone(Zone* zone)
|
TypeZone::TypeZone(Zone* zone)
|
||||||
|
|
|
@ -71,29 +71,6 @@ inline bool isInlinableCall(jsbytecode* pc);
|
||||||
|
|
||||||
bool ClassCanHaveExtraProperties(const JSClass* clasp);
|
bool ClassCanHaveExtraProperties(const JSClass* clasp);
|
||||||
|
|
||||||
class RecompileInfo {
|
|
||||||
JSScript* script_;
|
|
||||||
IonCompilationId id_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
RecompileInfo(JSScript* script, IonCompilationId id)
|
|
||||||
: script_(script), id_(id) {}
|
|
||||||
|
|
||||||
JSScript* script() const { return script_; }
|
|
||||||
|
|
||||||
inline jit::IonScript* maybeIonScriptToInvalidate(const TypeZone& zone) const;
|
|
||||||
|
|
||||||
inline bool shouldSweep(const TypeZone& zone);
|
|
||||||
|
|
||||||
bool operator==(const RecompileInfo& other) const {
|
|
||||||
return script_ == other.script_ && id_ == other.id_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
struct AutoEnterAnalysis;
|
||||||
|
|
||||||
class TypeZone {
|
class TypeZone {
|
||||||
|
@ -139,12 +116,6 @@ class TypeZone {
|
||||||
void beginSweep();
|
void beginSweep();
|
||||||
void endSweep(JSRuntime* rt);
|
void endSweep(JSRuntime* rt);
|
||||||
|
|
||||||
/* Mark a script as needing recompilation once inference has finished. */
|
|
||||||
void addPendingRecompile(JSContext* cx, const RecompileInfo& info);
|
|
||||||
void addPendingRecompile(JSContext* cx, JSScript* script);
|
|
||||||
|
|
||||||
void processPendingRecompiles(JSFreeOp* fop, RecompileInfoVector& recompiles);
|
|
||||||
|
|
||||||
bool isSweepingTypes() const { return sweepingTypes; }
|
bool isSweepingTypes() const { return sweepingTypes; }
|
||||||
void setSweepingTypes(bool sweeping) {
|
void setSweepingTypes(bool sweeping) {
|
||||||
MOZ_RELEASE_ASSERT(sweepingTypes != sweeping);
|
MOZ_RELEASE_ASSERT(sweepingTypes != sweeping);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче