Bug 1566607 - Add selfHostingLazy_ field to JSFunction. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Wed, 17 Jul 2019 13:48:05 +0000
changeset 483312 b88fad4496c51d4e63a5ba14d1a3534bb2c8e31f
parent 483311 e4bf7a4c4bbfafdf2a973b8d541cba0c50308f43
child 483313 afc58591c3fcbafd85ee4eaddd1b88e7fe30f7fe
push id90304
push usertcampbell@mozilla.com
push dateThu, 18 Jul 2019 12:45:49 +0000
treeherderautoland@afc58591c3fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1566607
milestone70.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 1566607 - Add selfHostingLazy_ field to JSFunction. r=jandem This union arm is used by lazy self-hosted functions to point to the runtime SelfHostedLazyScript object. Previously this pointer was null for these types of functions. This will make these types of functions more JIT-friendly. Also avoid setting the INTERPRETED_LAZY flag except for initLazyScript and initSelfHostedLazyScript. We remove some dead code paths from NewFunctionFromSpec / DefineFunction. Differential Revision: https://phabricator.services.mozilla.com/D38254
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSFunction.h
js/src/vm/SelfHosting.cpp
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2019,16 +2019,17 @@ JSFunction* AllocNewFunction(JSContext* 
 
   fun = NewFunctionWithProto(cx, nullptr, 0, flags, nullptr, atom, proto,
                              allocKind, TenuredObject);
   if (!fun) {
     return nullptr;
   }
   if (isSelfHosting) {
     fun->setIsSelfHostedBuiltin();
+    MOZ_ASSERT(!fun->isInterpretedLazy());
   }
   return fun;
 }
 
 JSFunction* ParserBase::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
                                     GeneratorKind generatorKind,
                                     FunctionAsyncKind asyncKind,
                                     HandleObject proto /* = nullptr */) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3241,21 +3241,20 @@ JS_PUBLIC_API JSFunction* JS::NewFunctio
     return &funVal.toObject().as<JSFunction>();
   }
 
   RootedAtom atom(cx, IdToFunctionName(cx, id));
   if (!atom) {
     return nullptr;
   }
 
+  MOZ_ASSERT(fs->call.op);
+
   JSFunction* fun;
-  if (!fs->call.op) {
-    fun =
-        NewScriptedFunction(cx, fs->nargs, JSFunction::INTERPRETED_LAZY, atom);
-  } else if (fs->flags & JSFUN_CONSTRUCTOR) {
+  if (fs->flags & JSFUN_CONSTRUCTOR) {
     fun = NewNativeConstructor(cx, fs->call.op, fs->nargs, atom);
   } else {
     fun = NewNativeFunction(cx, fs->call.op, fs->nargs, atom);
   }
   if (!fun) {
     return nullptr;
   }
 
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -772,16 +772,17 @@ inline void JSFunction::trace(JSTracer* 
     // Functions can be be marked as interpreted despite having no script
     // yet at some points when parsing, and can be lazy with no lazy script
     // for self-hosted code.
     if (hasScript() && !hasUncompletedScript()) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
     } else if (hasLazyScript() && u.scripted.s.lazy_) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
     }
+    // NOTE: The u.scripted.s.selfHostedLazy_ does not point to GC things.
 
     if (u.scripted.env_) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
     }
   }
 }
 
 static void fun_trace(JSTracer* trc, JSObject* obj) {
@@ -1738,20 +1739,23 @@ void JSFunction::maybeRelazify(JSRuntime
     return;
   }
 
   JSScript* script = nonLazyScript();
 
   flags_ &= ~INTERPRETED;
   flags_ |= INTERPRETED_LAZY;
   LazyScript* lazy = script->maybeLazyScript();
-  u.scripted.s.lazy_ = lazy;
   if (lazy) {
+    u.scripted.s.lazy_ = lazy;
     MOZ_ASSERT(!isSelfHostedBuiltin());
   } else {
+    // Lazy self-hosted builtins point to a SelfHostedLazyScript that may be
+    // called from JIT scripted calls.
+    u.scripted.s.selfHostedLazy_ = &rt->selfHostedLazyScript.ref();
     MOZ_ASSERT(isSelfHostedBuiltin());
     MOZ_ASSERT(isExtended());
     MOZ_ASSERT(GetClonedSelfHostedFunctionName(this));
   }
 
   realm->scheduleDelazificationForDebugger();
 }
 
@@ -2247,19 +2251,20 @@ JSFunction* js::CloneFunctionReuseScript
     clone->initScript(fun->nonLazyScript());
     clone->initEnvironment(enclosingEnv);
   } else if (fun->hasLazyScript()) {
     MOZ_ASSERT(fun->compartment() == clone->compartment());
     LazyScript* lazy = fun->lazyScript();
     clone->initLazyScript(lazy);
     clone->initEnvironment(enclosingEnv);
   } else {
-    MOZ_ASSERT(fun->isSelfHostedBuiltin());
+    MOZ_ASSERT(fun->hasSelfHostedLazyScript());
     MOZ_ASSERT(fun->compartment() == clone->compartment());
-    clone->initLazyScript(nullptr);
+    SelfHostedLazyScript* lazy = fun->selfHostedLazyScript();
+    clone->initSelfHostLazyScript(lazy);
     clone->initEnvironment(enclosingEnv);
   }
 
   /*
    * Clone the function, reusing its script. We can use the same group as
    * the original function provided that its prototype is correct.
    */
   if (fun->staticPrototype() == clone->staticPrototype()) {
@@ -2488,22 +2493,20 @@ bool js::SetFunctionName(JSContext* cx, 
 JSFunction* js::DefineFunction(
     JSContext* cx, HandleObject obj, HandleId id, Native native, unsigned nargs,
     unsigned flags, gc::AllocKind allocKind /* = AllocKind::FUNCTION */) {
   RootedAtom atom(cx, IdToFunctionName(cx, id));
   if (!atom) {
     return nullptr;
   }
 
+  MOZ_ASSERT(native);
+
   RootedFunction fun(cx);
-  if (!native) {
-    fun = NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY, atom,
-                              /* proto = */ nullptr, allocKind, GenericObject,
-                              obj);
-  } else if (flags & JSFUN_CONSTRUCTOR) {
+  if (flags & JSFUN_CONSTRUCTOR) {
     fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
   } else {
     fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
   }
 
   if (!fun) {
     return nullptr;
   }
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -14,16 +14,17 @@
 #include "jstypes.h"
 
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 
 namespace js {
 
 class FunctionExtended;
+struct SelfHostedLazyScript;
 
 typedef JSNative Native;
 }  // namespace js
 
 static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 2;
 static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 3;
 static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4;
 
@@ -157,16 +158,17 @@ class JSFunction : public js::NativeObje
       } extra;
     } native;
     struct {
       JSObject* env_; /* environment for new activations */
       union {
         JSScript* script_;     /* interpreted bytecode descriptor or
                                   null; use the accessor! */
         js::LazyScript* lazy_; /* lazily compiled script, or nullptr */
+        js::SelfHostedLazyScript* selfHostedLazy_;
       } s;
     } scripted;
   } u;
 
   // The |atom_| field can have different meanings depending on the function
   // type and flags. It is used for diagnostics, decompiling, and
   //
   // 1. If the function is not a bound function:
@@ -298,16 +300,19 @@ class JSFunction : public js::NativeObje
   // These methods determine which of the u.scripted.s union arms are active.
   // For live JSFunctions the pointer values will always be non-null, but due
   // to partial initialization the GC (and other features that scan the heap
   // directly) may still return a null pointer.
   bool hasScript() const { return flags() & INTERPRETED; }
   bool hasLazyScript() const {
     return isInterpretedLazy() && !isSelfHostedOrIntrinsic();
   }
+  bool hasSelfHostedLazyScript() const {
+    return isInterpretedLazy() && isSelfHostedOrIntrinsic();
+  }
 
   // Arrow functions store their lexical new.target in the first extended slot.
   bool isArrow() const { return kind() == Arrow; }
   // Every class-constructor is also a method.
   bool isMethod() const {
     return kind() == Method || kind() == ClassConstructor;
   }
   bool isClassConstructor() const { return kind() == ClassConstructor; }
@@ -623,16 +628,21 @@ class JSFunction : public js::NativeObje
     MOZ_ASSERT(hasLazyScript() && u.scripted.s.lazy_);
     return u.scripted.s.lazy_;
   }
   js::LazyScript* maybeLazyScript() const {
     MOZ_ASSERT(hasLazyScript());
     return u.scripted.s.lazy_;
   }
 
+  js::SelfHostedLazyScript* selfHostedLazyScript() const {
+    MOZ_ASSERT(hasSelfHostedLazyScript() && u.scripted.s.selfHostedLazy_);
+    return u.scripted.s.selfHostedLazy_;
+  }
+
   js::GeneratorKind generatorKind() const {
     if (!isInterpreted()) {
       return js::GeneratorKind::NotGenerator;
     }
     if (hasScript()) {
       return nonLazyScript()->generatorKind();
     }
     if (hasLazyScript()) {
@@ -690,16 +700,24 @@ class JSFunction : public js::NativeObje
 
   void initLazyScript(js::LazyScript* lazy) {
     MOZ_ASSERT(isInterpreted());
     flags_ &= ~INTERPRETED;
     flags_ |= INTERPRETED_LAZY;
     u.scripted.s.lazy_ = lazy;
   }
 
+  void initSelfHostLazyScript(js::SelfHostedLazyScript* lazy) {
+    MOZ_ASSERT(isInterpreted());
+    MOZ_ASSERT(isSelfHostedBuiltin());
+    flags_ &= ~INTERPRETED;
+    flags_ |= INTERPRETED_LAZY;
+    u.scripted.s.selfHostedLazy_ = lazy;
+  }
+
   JSNative native() const {
     MOZ_ASSERT(isNative());
     return u.native.func_;
   }
 
   JSNative maybeNative() const { return isInterpreted() ? nullptr : native(); }
 
   void initNative(js::Native native, const JSJitInfo* jitInfo) {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -3459,23 +3459,24 @@ bool JSRuntime::createLazySelfHostedFunc
   if (!selfHostedFun->isClassConstructor() &&
       !selfHostedFun->hasGuessedAtom() &&
       selfHostedFun->explicitName() != selfHostedName) {
     MOZ_ASSERT(GetUnclonedSelfHostedFunctionName(selfHostedFun) ==
                selfHostedName);
     funName = selfHostedFun->explicitName();
   }
 
-  fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY, funName,
+  fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED, funName,
                               proto, gc::AllocKind::FUNCTION_EXTENDED,
                               newKind));
   if (!fun) {
     return false;
   }
   fun->setIsSelfHostedBuiltin();
+  fun->initSelfHostLazyScript(&cx->runtime()->selfHostedLazyScript.ref());
   SetClonedSelfHostedFunctionName(fun, selfHostedName);
   return true;
 }
 
 bool JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx,
                                               HandlePropertyName name,
                                               HandleFunction targetFun) {
   RootedFunction sourceFun(cx, getUnclonedSelfHostedFunction(cx, name));