Bug 1533890: Add native constructor call support to CacheIR r=mgaudet
authorIain Ireland <iireland@mozilla.com>
Tue, 19 Mar 2019 23:41:09 +0000
changeset 465212 b641ee647a01b1a05bd7775c7ce6e4fac0564917
parent 465211 9fe35422fa326fc61e8f8049eb3291d0a898e821
child 465213 d808eb8f5dd52d49348586d0764ecf8a419d1e9f
push id35735
push usershindli@mozilla.com
push dateThu, 21 Mar 2019 04:34:45 +0000
treeherdermozilla-central@ac0cd1a710f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgaudet
bugs1533890
milestone68.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 1533890: Add native constructor call support to CacheIR r=mgaudet This patch uses the infrastructure from the previous patch to add constructor support for native calls. Comparison points: - CallIRGenerator::getTemplateObjectForNative corresponds to GetTemplateObjectForNative in BaselineIC.cpp Differential Revision: https://phabricator.services.mozilla.com/D22781
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -904,16 +904,29 @@ JSObject* BaselineInspector::getTemplate
   }
 
   const ICEntry& entry = icEntryFromPC(pc);
   for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
     if (stub->isCall_Native() &&
         stub->toCall_Native()->callee()->native() == native) {
       return stub->toCall_Native()->templateObject();
     }
+    if (ICStub::IsCacheIRKind(stub->kind())) {
+      auto filter = [stub, native](CacheIRReader& reader,
+                                   const CacheIRStubInfo* info) {
+        JSFunction* callee =
+            info->getStubField<JSFunction*>(stub, reader.stubOffset());
+        return callee->native() == native;
+      };
+      JSObject* result = MaybeTemplateObject(
+          stub, MetaTwoByteKind::NativeTemplateObject, filter);
+      if (result) {
+        return result;
+      }
+    }
   }
 
   return nullptr;
 }
 
 bool BaselineInspector::isOptimizableConstStringSplit(jsbytecode* pc,
                                                       JSString** strOut,
                                                       JSString** sepOut,
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -7,23 +7,25 @@
 #include "jit/CacheIR.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "jit/BaselineCacheIRCompiler.h"
 #include "jit/BaselineIC.h"
 #include "jit/CacheIRSpewer.h"
+#include "jit/InlinableNatives.h"
 #include "vm/SelfHosting.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
+#include "vm/StringObject-inl.h"
 #include "vm/TypeInference-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
@@ -5359,54 +5361,151 @@ bool CallIRGenerator::tryAttachCallScrip
   writer.typeMonitorResult();
 
   cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
   trackAttached("Call scripted func");
 
   return true;
 }
 
+bool CallIRGenerator::getTemplateObjectForNative(HandleFunction calleeFunc,
+                                                 MutableHandleObject res) {
+  if (!calleeFunc->hasJitInfo() ||
+      calleeFunc->jitInfo()->type() != JSJitInfo::InlinableNative) {
+    return true;
+  }
+
+  // Check for natives to which template objects can be attached. This is
+  // done to provide templates to Ion for inlining these natives later on.
+  switch (calleeFunc->jitInfo()->inlinableNative) {
+    case InlinableNative::Array: {
+      // Note: the template array won't be used if its length is inaccurately
+      // computed here.  (We allocate here because compilation may occur on a
+      // separate thread where allocation is impossible.)
+      size_t count = 0;
+      if (args_.length() != 1) {
+        count = args_.length();
+      } else if (args_.length() == 1 && args_[0].isInt32() &&
+                 args_[0].toInt32() >= 0) {
+        count = args_[0].toInt32();
+      }
+
+      if (count > ArrayObject::EagerAllocationMaxLength) {
+        return true;
+      }
+
+      // With this and other array templates, analyze the group so that
+      // we don't end up with a template whose structure might change later.
+      res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx_, count,
+                                                             TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::ArraySlice: {
+      if (!thisval_.isObject()) {
+        return true;
+      }
+
+      RootedObject obj(cx_, &thisval_.toObject());
+      if (obj->isSingleton()) {
+        return true;
+      }
+
+      res.set(NewFullyAllocatedArrayTryReuseGroup(cx_, obj, 0, TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::String: {
+      RootedString emptyString(cx_, cx_->runtime()->emptyString);
+      res.set(StringObject::create(cx_, emptyString, /* proto = */ nullptr,
+                                   TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::ObjectCreate: {
+      if (args_.length() != 1 || !args_[0].isObjectOrNull()) {
+        return true;
+      }
+      RootedObject proto(cx_, args_[0].toObjectOrNull());
+      res.set(ObjectCreateImpl(cx_, proto, TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::IntrinsicNewArrayIterator: {
+      res.set(NewArrayIteratorObject(cx_, TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::IntrinsicNewStringIterator: {
+      res.set(NewStringIteratorObject(cx_, TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::IntrinsicNewRegExpStringIterator: {
+      res.set(NewRegExpStringIteratorObject(cx_, TenuredObject));
+      return !!res;
+    }
+
+    case InlinableNative::TypedArrayConstructor: {
+      return TypedArrayObject::GetTemplateObjectForNative(cx_, calleeFunc->native(),
+                                                          args_, res);
+    }
+
+    default:
+      return true;
+  }
+}
+
 bool CallIRGenerator::tryAttachCallNative(HandleFunction calleeFunc) {
   MOZ_ASSERT(mode_ == ICState::Mode::Specialized);
   MOZ_ASSERT(calleeFunc->isNative());
 
   bool isSpread = IsSpreadCallPC(pc_);
 
   bool isConstructing = IsConstructorCallPC(pc_);
-  // TODO: Support constructors
-  if (isConstructing) {
+  if (isConstructing && !calleeFunc->isConstructor()) {
     return false;
   }
 
   // Check for specific native-function optimizations.
   if (tryAttachSpecialCaseCallNative(calleeFunc)) {
     return true;
   }
   if (JitOptions.disableCacheIRCalls) {
     return false;
   }
 
+  RootedObject templateObj(cx_);
+  if (isConstructing && !getTemplateObjectForNative(calleeFunc, &templateObj)) {
+    return false;
+  }
+
   // Load argc.
   Int32OperandId argcId(writer.setInputOperandId(0));
 
   // Load the callee
   uint32_t calleeSlot = calleeStackSlot(isSpread, isConstructing);
   ValOperandId calleeValId = writer.loadStackValue(calleeSlot);
   ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
 
   // Ensure callee matches this stub's callee
-  writer.guardSpecificObject(calleeObjId, calleeFunc);
+  FieldOffset calleeOffset = writer.guardSpecificObject(calleeObjId, calleeFunc);
 
   // Enforce limits on spread call length, and update argc.
   if (isSpread) {
     writer.guardAndUpdateSpreadArgc(argcId, isConstructing);
   }
-  writer.callNativeFunction(calleeObjId, argcId, op_, calleeFunc, isSpread);
+  writer.callNativeFunction(calleeObjId, argcId, op_, calleeFunc, isSpread,
+                            isConstructing);
   writer.typeMonitorResult();
 
+  if (templateObj) {
+    writer.metaNativeTemplateObject(templateObj, calleeOffset);
+  }
+
   cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
   trackAttached("Call native func");
 
   return true;
 }
 
 bool CallIRGenerator::getTemplateObjectForClassHook(
     HandleObject calleeObj, MutableHandleObject result) {
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -485,16 +485,17 @@ enum TypedThingLayout {
   Layout_InlineTypedObject
 };
 
 void LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst,
                               Label* failure);
 
 enum class MetaTwoByteKind : uint8_t {
   ClassTemplateObject,
+  NativeTemplateObject,
 };
 
 // Class to record CacheIR + some additional metadata for code generation.
 class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
   JSContext* cx_;
   CompactBufferWriter buffer_;
 
   uint32_t nextOperandId_;
@@ -799,20 +800,20 @@ class MOZ_RAII CacheIRWriter : public JS
   }
   void guardHasProxyHandler(ObjOperandId obj, const void* handler) {
     writeOpWithOperandId(CacheOp::GuardHasProxyHandler, obj);
     addStubField(uintptr_t(handler), StubField::Type::RawWord);
   }
   void guardNotDOMProxy(ObjOperandId obj) {
     writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj);
   }
-  void guardSpecificObject(ObjOperandId obj, JSObject* expected) {
+  FieldOffset guardSpecificObject(ObjOperandId obj, JSObject* expected) {
     assertSameCompartment(expected);
     writeOpWithOperandId(CacheOp::GuardSpecificObject, obj);
-    addStubField(uintptr_t(expected), StubField::Type::JSObject);
+    return addStubField(uintptr_t(expected), StubField::Type::JSObject);
   }
   void guardSpecificAtom(StringOperandId str, JSAtom* expected) {
     writeOpWithOperandId(CacheOp::GuardSpecificAtom, str);
     addStubField(uintptr_t(expected), StubField::Type::String);
   }
   void guardSpecificSymbol(SymbolOperandId sym, JS::Symbol* expected) {
     writeOpWithOperandId(CacheOp::GuardSpecificSymbol, sym);
     addStubField(uintptr_t(expected), StubField::Type::Symbol);
@@ -1153,23 +1154,24 @@ class MOZ_RAII CacheIRWriter : public JS
                             bool isCrossRealm, bool isSpread) {
     writeOpWithOperandId(CacheOp::CallScriptedFunction, calleeId);
     writeOperandId(argc);
     buffer_.writeByte(uint32_t(isCrossRealm));
     buffer_.writeByte(uint32_t(isSpread));
     buffer_.writeByte(uint32_t(false));  // isConstructing
   }
   void callNativeFunction(ObjOperandId calleeId, Int32OperandId argc, JSOp op,
-                          HandleFunction calleeFunc, bool isSpread) {
+                          HandleFunction calleeFunc, bool isSpread,
+                          bool isConstructing) {
     writeOpWithOperandId(CacheOp::CallNativeFunction, calleeId);
     writeOperandId(argc);
     bool isCrossRealm = cx_->realm() != calleeFunc->realm();
     buffer_.writeByte(uint32_t(isCrossRealm));
     buffer_.writeByte(uint32_t(isSpread));
-    buffer_.writeByte(uint32_t(false));  // isConstructing
+    buffer_.writeByte(uint32_t(isConstructing));
 
     // Some native functions can be implemented faster if we know that
     // the return value is ignored.
     bool ignoresReturnValue =
         op == JSOP_CALL_IGNORES_RV && calleeFunc->hasJitInfo() &&
         calleeFunc->jitInfo()->type() == JSJitInfo::IgnoresReturnValueNative;
 
 #ifdef JS_SIMULATOR
@@ -1206,18 +1208,25 @@ class MOZ_RAII CacheIRWriter : public JS
     // swi instruction to handle them, so we store the redirected
     // pointer in the stub and use that instead of the original one.
     target = Simulator::RedirectNativeFunction(target, Args_General3);
 #endif
 
     addStubField(uintptr_t(target), StubField::Type::RawWord);
   }
 
-  // This generates no code, but saves the template object in a stub
+  // These generate no code, but save the template object in a stub
   // field for BaselineInspector.
+  void metaNativeTemplateObject(JSObject* templateObject,
+                                FieldOffset calleeOffset) {
+    writeOp(CacheOp::MetaTwoByte);
+    buffer_.writeByte(uint32_t(MetaTwoByteKind::NativeTemplateObject));
+    reuseStubField(calleeOffset);
+    addStubField(uintptr_t(templateObject), StubField::Type::JSObject);
+  }
   void metaClassTemplateObject(JSObject* templateObject,
                                FieldOffset classOffset) {
     writeOp(CacheOp::MetaTwoByte);
     buffer_.writeByte(uint32_t(MetaTwoByteKind::ClassTemplateObject));
     reuseStubField(classOffset);
     addStubField(uintptr_t(templateObject), StubField::Type::JSObject);
   }
 
@@ -2111,16 +2120,18 @@ class MOZ_RAII CallIRGenerator : public 
   uint32_t argc_;
   HandleValue callee_;
   HandleValue thisval_;
   HandleValueArray args_;
   PropertyTypeCheckInfo typeCheckInfo_;
   BaselineCacheIRStubKind cacheIRStubKind_;
 
   uint32_t calleeStackSlot(bool isSpread, bool isConstructing);
+  bool getTemplateObjectForNative(HandleFunction calleeFunc,
+                                  MutableHandleObject result);
   bool getTemplateObjectForClassHook(HandleObject calleeObj,
                                      MutableHandleObject result);
 
   bool tryAttachStringSplit();
   bool tryAttachArrayPush();
   bool tryAttachArrayJoin();
   bool tryAttachIsSuspendedGenerator();
   bool tryAttachCallScripted(HandleFunction calleeFunc);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -944,16 +944,18 @@ GCPtr<T>& CacheIRStubInfo::getStubField(
 template GCPtr<Shape*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<ObjectGroup*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<JSObject*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<JSString*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
+template GCPtr<JSFunction*>& CacheIRStubInfo::getStubField<ICStub>(
+    ICStub* stub, uint32_t offset) const;
 template GCPtr<JS::Symbol*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<JS::Value>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<jsid>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<Class*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1389,17 +1389,18 @@ bool TypedArrayConstructor(JSContext* cx
   CallArgs args = CallArgsFromVp(argc, vp);
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT,
                             args.isConstructing() ? "construct" : "call");
   return false;
 }
 
 template <typename T>
-static bool GetTemplateObjectForNative(JSContext* cx, const CallArgs& args,
+static bool GetTemplateObjectForNative(JSContext* cx,
+                                       const JS::HandleValueArray args,
                                        MutableHandleObject res) {
   if (args.length() == 0) {
     return true;
   }
 
   HandleValue arg = args[0];
   if (arg.isInt32()) {
     uint32_t len = 0;
@@ -1428,20 +1429,19 @@ static bool GetTemplateObjectForNative(J
     uint32_t len = 0;
     res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
     return !!res;
   }
 
   return true;
 }
 
-/* static */
-bool TypedArrayObject::GetTemplateObjectForNative(JSContext* cx, Native native,
-                                                  const CallArgs& args,
-                                                  MutableHandleObject res) {
+/* static */ bool TypedArrayObject::GetTemplateObjectForNative(
+    JSContext* cx, Native native, const JS::HandleValueArray args,
+    MutableHandleObject res) {
   MOZ_ASSERT(!res);
 #define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N)                        \
   if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
     return ::GetTemplateObjectForNative<T>(cx, args, res);         \
   }
   JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
 #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
   return true;
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -134,17 +134,17 @@ class TypedArrayObject : public ArrayBuf
 
   /*
    * Copy all elements from this typed array to vp. vp must point to rooted
    * memory.
    */
   void getElements(Value* vp);
 
   static bool GetTemplateObjectForNative(JSContext* cx, Native native,
-                                         const CallArgs& args,
+                                         const JS::HandleValueArray args,
                                          MutableHandleObject res);
 
   /*
    * Byte length above which created typed arrays will have singleton types.
    * This only applies to typed arrays created with an existing ArrayBuffer and
    * when not inlined from Ion.
    */
   static constexpr uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;