Bug 1418966 - Pin TypeScripts better when doing Baseline debug mode OSR. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 13 Dec 2017 14:13:43 -0600
changeset 396313 f5ea86116d56e8e90689ec325061c948869159ba
parent 396312 b2b7b46c8ad0ed72ebfc2a192e841f90e8620c91
child 396314 ccd7b237c4337a00e45bf18ac2d0218c61e1134d
push id56975
push userdluca@mozilla.com
push dateThu, 14 Dec 2017 09:59:07 +0000
treeherderautoland@16bcfaad13e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1418966
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1418966 - Pin TypeScripts better when doing Baseline debug mode OSR. r=bhackett
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineDebugModeOSR.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/VMFunctions.cpp
js/src/jsscript.h
js/src/vm/EnvironmentObject.cpp
js/src/vm/TypeInference-inl.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -31,16 +31,17 @@
 #include "vtune/VTuneWrapper.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/BaselineFrameInfo-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
   : BaselineCompilerSpecific(cx, alloc, script),
@@ -93,17 +94,18 @@ BaselineCompiler::compile()
     JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%zu",
             script->filename(), script->lineno());
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     TraceLoggerEvent scriptEvent(TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, scriptEvent);
     AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
 
-    if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
+    AutoKeepTypeScripts keepTypes(cx);
+    if (!script->ensureHasTypes(cx, keepTypes) || !script->ensureHasAnalyzedArgsUsage(cx))
         return Method_Error;
 
     // When code coverage is only enabled for optimizations, or when a Debugger
     // set the collectCoverageInfo flag, we have to create the ScriptCounts if
     // they do not exist.
     if (!script->hasScriptCounts() && cx->compartment()->collectCoverage()) {
         if (!script->initScriptCounts(cx))
             return Method_Error;
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -11,16 +11,17 @@
 #include "jit/BaselineIC.h"
 #include "jit/JitcodeMap.h"
 #include "jit/Linker.h"
 #include "jit/PerfSpewer.h"
 
 #include "jit/JitFrames-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Stack-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 struct DebugModeOSREntry
 {
@@ -668,16 +669,17 @@ RecompileBaselineScriptForDebugMode(JSCo
     // If a script is on the stack multiple times, it may have already
     // been recompiled.
     if (oldBaselineScript->hasDebugInstrumentation() == observing)
         return true;
 
     JitSpew(JitSpew_BaselineDebugModeOSR, "Recompiling (%s:%zu) for %s",
             script->filename(), script->lineno(), observing ? "DEBUGGING" : "NORMAL EXECUTION");
 
+    AutoKeepTypeScripts keepTypes(cx);
     script->setBaselineScript(cx->runtime(), nullptr);
 
     MethodStatus status = BaselineCompile(cx, script, /* forceDebugMode = */ observing);
     if (status != Method_Compiled) {
         // We will only fail to recompile for debug mode due to OOM. Restore
         // the old baseline script in case something doesn't properly
         // propagate OOM.
         MOZ_ASSERT(status == Method_Error);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4425,17 +4425,18 @@ jit::AnalyzeArgumentsUsage(JSContext* cx
 
     if (!jit::IsIonEnabled(cx))
         return true;
 
     static const uint32_t MAX_SCRIPT_SIZE = 10000;
     if (script->length() > MAX_SCRIPT_SIZE)
         return true;
 
-    if (!script->ensureHasTypes(cx))
+    AutoKeepTypeScripts keepTypes(cx);
+    if (!script->ensureHasTypes(cx, keepTypes))
         return false;
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     TraceLoggerEvent event(TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, event);
     AutoTraceLog logCompile(logger, TraceLogger_IonAnalysis);
 
     LifoAlloc alloc(TempAllocator::PreferredLifoChunkSize);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -648,17 +648,18 @@ bool
 CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval)
 {
     rval.set(MagicValue(JS_IS_CONSTRUCTING));
 
     if (callee->is<JSFunction>()) {
         RootedFunction fun(cx, &callee->as<JSFunction>());
         if (fun->isInterpreted() && fun->isConstructor()) {
             JSScript* script = JSFunction::getOrCreateScript(cx, fun);
-            if (!script || !script->ensureHasTypes(cx))
+            AutoKeepTypeScripts keepTypes(cx);
+            if (!script || !script->ensureHasTypes(cx, keepTypes))
                 return false;
             if (!js::CreateThis(cx, fun, script, newTarget, GenericObject, rval))
                 return false;
         }
     }
 
     return true;
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1690,17 +1690,17 @@ class JSScript : public js::gc::TenuredC
      * |code()| filters out this kind of scripts.
      *
      * If this script has a function associated to it, then it is not the
      * top-level of a file.
      */
     bool isTopLevel() { return code() && !functionNonDelazifying(); }
 
     /* Ensure the script has a TypeScript. */
-    inline bool ensureHasTypes(JSContext* cx);
+    inline bool ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&);
 
     inline js::TypeScript* types();
 
     void maybeSweepTypes(js::AutoClearTypeInferenceStateOnOOM* oom);
 
     inline js::GlobalObject& global() const;
     js::GlobalObject& uninlinedGlobal() const;
 
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -23,16 +23,17 @@
 #include "wasm/WasmInstance.h"
 
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
 
 #include "gc/Marking-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::PodZero;
 using mozilla::Maybe;
 using mozilla::Some;
 using mozilla::Nothing;
@@ -1499,17 +1500,18 @@ class DebugEnvironmentProxyHandler : pub
             return true;
         }
 
         /* Handle unaliased formals, vars, lets, and consts at function scope. */
         if (env->is<CallObject>()) {
             CallObject& callobj = env->as<CallObject>();
             RootedFunction fun(cx, &callobj.callee());
             RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
-            if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
+            AutoKeepTypeScripts keepTypes(cx);
+            if (!script->ensureHasTypes(cx, keepTypes) || !script->ensureHasAnalyzedArgsUsage(cx))
                 return false;
 
             BindingIter bi(script);
             while (bi && NameToId(bi.name()->asPropertyName()) != id)
                 bi++;
             if (!bi)
                 return true;
 
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -678,16 +678,31 @@ TypeScript::SetArgument(JSContext* cx, J
 }
 
 /* static */ inline void
 TypeScript::SetArgument(JSContext* cx, JSScript* script, unsigned arg, const js::Value& value)
 {
     SetArgument(cx, script, arg, TypeSet::GetValueType(value));
 }
 
+inline
+AutoKeepTypeScripts::AutoKeepTypeScripts(JSContext* cx)
+  : zone_(cx->zone()->types),
+    prev_(zone_.keepTypeScripts)
+{
+    zone_.keepTypeScripts = true;
+}
+
+inline
+AutoKeepTypeScripts::~AutoKeepTypeScripts()
+{
+    MOZ_ASSERT(zone_.keepTypeScripts);
+    zone_.keepTypeScripts = prev_;
+}
+
 /////////////////////////////////////////////////////////////////////
 // TypeHashSet
 /////////////////////////////////////////////////////////////////////
 
 // Hashing code shared by objects in TypeSets and properties in ObjectGroups.
 struct TypeHashSet
 {
     // The sets of objects in a type set grow monotonically, are usually empty,
@@ -1197,14 +1212,14 @@ ObjectGroup::getProperty(unsigned i)
 inline js::TypeScript*
 JSScript::types()
 {
     maybeSweepTypes(nullptr);
     return types_;
 }
 
 inline bool
-JSScript::ensureHasTypes(JSContext* cx)
+JSScript::ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&)
 {
     return types() || makeTypes(cx);
 }
 
 #endif /* vm_TypeInference_inl_h */
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4437,16 +4437,17 @@ JSScript::maybeSweepTypes(AutoClearTypeI
         }
         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.
     if (types.sweepReleaseTypes &&
+        !types.keepTypeScripts &&
         !hasBaselineScript() &&
         !hasIonScript())
     {
         types_->destroy();
         types_ = nullptr;
 
         // Freeze constraints on stack type sets need to be regenerated the
         // next time the script is analyzed.
@@ -4504,25 +4505,27 @@ TypeZone::TypeZone(Zone* zone)
   : zone_(zone),
     typeLifoAlloc_(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     generation(zone->group(), 0),
     compilerOutputs(zone->group(), nullptr),
     sweepTypeLifoAlloc(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepCompilerOutputs(zone->group(), nullptr),
     sweepReleaseTypes(zone->group(), false),
     sweepingTypes(zone->group(), false),
+    keepTypeScripts(zone->group(), false),
     activeAnalysis(zone->group(), nullptr)
 {
 }
 
 TypeZone::~TypeZone()
 {
     js_delete(compilerOutputs.ref());
     js_delete(sweepCompilerOutputs.ref());
     MOZ_RELEASE_ASSERT(!sweepingTypes);
+    MOZ_ASSERT(!keepTypeScripts);
 }
 
 void
 TypeZone::beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
     MOZ_ASSERT(!sweepCompilerOutputs);
     MOZ_ASSERT(!sweepReleaseTypes);
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1281,16 +1281,30 @@ class TypeScript
         return mallocSizeOf(this);
     }
 
 #ifdef DEBUG
     void printTypes(JSContext* cx, HandleScript script) const;
 #endif
 };
 
+// Ensures no TypeScripts are purged in the current zone.
+class MOZ_RAII AutoKeepTypeScripts
+{
+    TypeZone& zone_;
+    bool prev_;
+
+    AutoKeepTypeScripts(const AutoKeepTypeScripts&) = delete;
+    void operator=(const AutoKeepTypeScripts&) = delete;
+
+  public:
+    explicit inline AutoKeepTypeScripts(JSContext* cx);
+    inline ~AutoKeepTypeScripts();
+};
+
 void
 FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);
 
 class RecompileInfo;
 
 // Allocate a CompilerOutput for a finished compilation and generate the type
 // constraints for the compilation. Sets |isValidOut| based on whether the type
 // constraints still hold.
@@ -1355,16 +1369,19 @@ struct AutoEnterAnalysis;
 class TypeZone
 {
     JS::Zone* const zone_;
 
     /* Pool for type information in this zone. */
     static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
     ZoneGroupData<LifoAlloc> typeLifoAlloc_;
 
+    TypeZone(const TypeZone&) = delete;
+    void operator=(const TypeZone&) = delete;
+
   public:
     // Current generation for sweeping.
     ZoneGroupOrGCTaskOrIonCompileData<uint32_t> generation;
 
     /*
      * All Ion compilations that have occured in this zone, for indexing via
      * RecompileInfo. This includes both valid and invalid compilations, though
      * invalidated compilations are swept on GC.
@@ -1381,16 +1398,18 @@ class TypeZone
     ZoneGroupData<CompilerOutputVector*> sweepCompilerOutputs;
 
     // During incremental sweeping, whether to try to destroy all type
     // information attached to scripts.
     ZoneGroupData<bool> sweepReleaseTypes;
 
     ZoneGroupData<bool> sweepingTypes;
 
+    ZoneGroupData<bool> keepTypeScripts;
+
     // The topmost AutoEnterAnalysis on the stack, if there is one.
     ZoneGroupData<AutoEnterAnalysis*> activeAnalysis;
 
     explicit TypeZone(JS::Zone* zone);
     ~TypeZone();
 
     JS::Zone* zone() const { return zone_; }