Bug 1619343 - Part 3: Prepare emitApplyGeneric to support constructor calls. r=jandem
authorAndré Bargull <andre.bargull@gmail.com>
Thu, 05 Mar 2020 16:01:44 +0000
changeset 517074 2f8f4267d3e7024be6324270ec4959478b540880
parent 517073 74c24cb3ac9a99e776ed265767342c8c504b1e1d
child 517075 2872f192769e2eb1e57d03ac1dd6cbf56bc9f738
push id37186
push usermalexandru@mozilla.com
push dateFri, 06 Mar 2020 09:47:39 +0000
treeherdermozilla-central@a1b3010b6b87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1619343
milestone75.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 1619343 - Part 3: Prepare emitApplyGeneric to support constructor calls. r=jandem SpreadNew and SpreadSuperCall will reuse this function, so prepare it to support constructor calls by adding the missing pieces based on what we perform in `visitCallGeneric()`. Differential Revision: https://phabricator.services.mozilla.com/D64980
js/src/jit/CodeGenerator.cpp
js/src/jit/MIR.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5270,20 +5270,20 @@ void CodeGenerator::emitCallInvokeFuncti
 
   // Push the space used by the arguments.
   masm.moveStackPtrTo(objreg);
   masm.Push(extraStackSize);
 
   pushArg(objreg);                                     // argv.
   pushArg(ToRegister(apply->getArgc()));               // argc.
   pushArg(Imm32(apply->mir()->ignoresReturnValue()));  // ignoresReturnValue.
-  pushArg(Imm32(false));                               // isConstrucing.
+  pushArg(Imm32(apply->mir()->isConstructing()));      // isConstructing.
   pushArg(ToRegister(apply->getFunction()));           // JSFunction*.
 
-  // This specialization og callVM restore the extraStackSize after the call.
+  // This specialization of callVM restores the extraStackSize after the call.
   using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
                       MutableHandleValue);
   callVM<Fn, jit::InvokeFunction>(apply, &extraStackSize);
 
   masm.Pop(extraStackSize);
 }
 
 // Do not bailout after the execution of this function since the stack no longer
@@ -5520,33 +5520,59 @@ void CodeGenerator::emitApplyGeneric(T* 
   //
   // objreg is dead across this call.
   //
   // extraStackSpace is garbage on entry and defined on exit.
   emitPushArguments(apply, extraStackSpace);
 
   masm.checkStackAlignment();
 
+  bool constructing = apply->mir()->isConstructing();
+
   // If the function is native, only emit the call to InvokeFunction.
   if (apply->hasSingleTarget() &&
       apply->getSingleTarget()->isNativeWithCppEntry()) {
     emitCallInvokeFunction(apply, extraStackSpace);
+
+#ifdef DEBUG
+    // Native constructors are guaranteed to return an Object value, so we never
+    // have to replace a primitive result with the previously allocated Object
+    // from CreateThis.
+    if (constructing) {
+      Label notPrimitive;
+      masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
+                               &notPrimitive);
+      masm.assumeUnreachable("native constructors don't return primitives");
+      masm.bind(&notPrimitive);
+    }
+#endif
+
     emitPopArguments(extraStackSpace);
     return;
   }
 
   Label end, invoke;
 
   // Guard that calleereg is an interpreted function with a JSScript.
-  masm.branchIfFunctionHasNoJitEntry(calleereg, /* constructing */ false,
-                                     &invoke);
-
-  // Guard that calleereg is not a class constrcuctor
-  masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
-                          calleereg, objreg, &invoke);
+  masm.branchIfFunctionHasNoJitEntry(calleereg, constructing, &invoke);
+
+  // Guard that callee allows the [[Call]] or [[Construct]] operation required.
+  if (constructing) {
+    masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR,
+                                 Assembler::Zero, &invoke);
+  } else {
+    masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
+                            calleereg, objreg, &invoke);
+  }
+
+  // Use the slow path if CreateThis was unable to create the |this| object.
+  if (constructing) {
+    Address thisAddr(masm.getStackPointer(), 0);
+    masm.branchTestNull(Assembler::Equal, thisAddr, &invoke);
+  }
 
   // Call with an Ion frame or a rectifier frame.
   {
     if (apply->mir()->maybeCrossRealm()) {
       masm.switchToObjectRealm(calleereg, objreg);
     }
 
     // Knowing that calleereg is a non-native function, load jitcode.
@@ -5555,17 +5581,17 @@ void CodeGenerator::emitApplyGeneric(T* 
     // Create the frame descriptor.
     unsigned pushed = masm.framePushed();
     Register stackSpace = extraStackSpace;
     masm.addPtr(Imm32(pushed), stackSpace);
     masm.makeFrameDescriptor(stackSpace, FrameType::IonJS,
                              JitFrameLayout::Size());
 
     masm.Push(argcreg);
-    masm.Push(calleereg);
+    masm.PushCalleeToken(calleereg, constructing);
     masm.Push(stackSpace);  // descriptor
 
     Label underflow, rejoin;
 
     // Check whether the provided arguments satisfy target argc.
     if (!apply->hasSingleTarget()) {
       Register nformals = extraStackSpace;
       masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()),
@@ -5616,18 +5642,29 @@ void CodeGenerator::emitApplyGeneric(T* 
   }
 
   // Handle uncompiled or native functions.
   {
     masm.bind(&invoke);
     emitCallInvokeFunction(apply, extraStackSpace);
   }
 
+  masm.bind(&end);
+
+  // If the return value of the constructing function is Primitive,
+  // replace the return value with the Object from CreateThis.
+  if (constructing) {
+    Label notPrimitive;
+    masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
+                             &notPrimitive);
+    masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand);
+    masm.bind(&notPrimitive);
+  }
+
   // Pop arguments and continue.
-  masm.bind(&end);
   emitPopArguments(extraStackSpace);
 }
 
 void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) {
   // Limit the number of parameters we can handle to a number that does not risk
   // us allocating too much stack, notably on Windows where there is a 4K guard
   // page that has to be touched to extend the stack.  See bug 1351278.  The
   // value "3000" is the size of the guard page minus an arbitrary, but large,
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2807,16 +2807,18 @@ class MApplyArgs : public MTernaryInstru
   WrappedFunction* getSingleTarget() const { return target_; }
 
   bool maybeCrossRealm() const { return maybeCrossRealm_; }
   void setNotCrossRealm() { maybeCrossRealm_ = false; }
 
   bool ignoresReturnValue() const { return ignoresReturnValue_; }
   void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
 
+  bool isConstructing() const { return false; }
+
   bool possiblyCalls() const override { return true; }
 
   bool appendRoots(MRootList& roots) const override {
     if (target_) {
       return target_->appendRoots(roots);
     }
     return true;
   }
@@ -2847,16 +2849,18 @@ class MApplyArray
   WrappedFunction* getSingleTarget() const { return target_; }
 
   bool maybeCrossRealm() const { return maybeCrossRealm_; }
   void setNotCrossRealm() { maybeCrossRealm_ = false; }
 
   bool ignoresReturnValue() const { return ignoresReturnValue_; }
   void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
 
+  bool isConstructing() const { return false; }
+
   bool possiblyCalls() const override { return true; }
 
   bool appendRoots(MRootList& roots) const override {
     if (target_) {
       return target_->appendRoots(roots);
     }
     return true;
   }