Bug 1480524 - [Part 1] Use CacheIR version of NewObject for Baseline r=jandem
authorMatthew Gaudet <mgaudet@mozilla.com>
Wed, 01 Aug 2018 11:49:43 -0400
changeset 486685 2a09332be9ac7cc7c2b3bd224ad4be8bb408af96
parent 486684 34f564520a234b4318cce5be5c86adcd2be8187b
child 486686 ab47d3f47325f9c3a1575a406c4b0748e0eed333
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1480524
milestone63.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 1480524 - [Part 1] Use CacheIR version of NewObject for Baseline r=jandem
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/IonIC.cpp
js/src/jit/JitRealm.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/SharedIC.cpp
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2077,16 +2077,17 @@ BaselineCacheIRCompiler::init(CacheKind 
     size_t numInputs = writer_.numInputOperands();
 
     // Baseline passes the first 2 inputs in R0/R1, other Values are stored on
     // the stack.
     size_t numInputsInRegs = std::min(numInputs, size_t(2));
     AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs));
 
     switch (kind) {
+      case CacheKind::NewObject:
       case CacheKind::GetIntrinsic:
         MOZ_ASSERT(numInputs == 0);
         break;
       case CacheKind::GetProp:
       case CacheKind::TypeOf:
       case CacheKind::GetIterator:
       case CacheKind::ToBool:
       case CacheKind::UnaryArith:
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -5593,8 +5593,60 @@ BinaryArithIRGenerator::tryAttachBoolean
         break;
       default:
         MOZ_CRASH("Unhandled op in tryAttachInt32");
     }
 
     writer.returnFromIC();
     return true;
 }
+
+NewObjectIRGenerator::NewObjectIRGenerator(JSContext* cx, HandleScript script,
+                                           jsbytecode* pc, ICState::Mode mode, JSOp op,
+                                           HandleObject templateObj)
+  : IRGenerator(cx, script, pc, CacheKind::NewObject, mode),
+    op_(op),
+    templateObject_(templateObj)
+{
+    MOZ_ASSERT(templateObject_);
+}
+
+void
+NewObjectIRGenerator::trackAttached(const char* name)
+{
+#ifdef JS_CACHEIR_SPEW
+    if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
+        sp.opcodeProperty("op", op_);
+    }
+#endif
+}
+
+bool
+NewObjectIRGenerator::tryAttachStub()
+{
+    if (!templateObject_->is<UnboxedPlainObject>() &&
+        templateObject_->as<PlainObject>().hasDynamicSlots())
+    {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+
+    // Don't attach stub if group is pretenured, as the stub
+    // won't succeed.
+    AutoSweepObjectGroup sweep(templateObject_->group());
+    if (templateObject_->group()->shouldPreTenure(sweep)) {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+    // Stub doesn't support metadata builder
+    if (cx_->realm()->hasAllocationMetadataBuilder()) {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+
+    writer.guardNoAllocationMetadataBuilder();
+    writer.guardObjectGroupNotPretenured(templateObject_->group());
+    writer.loadNewObjectFromTemplateResult(templateObject_);
+    writer.returnFromIC();
+
+    trackAttached("NewObjectWithTemplate");
+    return true;
+}
\ No newline at end of file
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -162,17 +162,18 @@ class TypedOperandId : public OperandId
     _(HasOwn)               \
     _(TypeOf)               \
     _(InstanceOf)           \
     _(GetIterator)          \
     _(Compare)              \
     _(ToBool)               \
     _(Call)                 \
     _(UnaryArith)           \
-    _(BinaryArith)
+    _(BinaryArith)          \
+    _(NewObject)
 
 enum class CacheKind : uint8_t
 {
 #define DEFINE_KIND(kind) kind,
     CACHE_IR_KINDS(DEFINE_KIND)
 #undef DEFINE_KIND
 };
 
@@ -216,16 +217,18 @@ extern const char* const CacheKindNames[
     _(GuardAndGetIndexFromString)         \
     _(GuardAndGetIterator)                \
     _(GuardHasGetterSetter)               \
     _(GuardGroupHasUnanalyzedNewScript)   \
     _(GuardIndexIsNonNegative)            \
     _(GuardTagNotEqual)                   \
     _(GuardXrayExpandoShapeAndDefaultProto) \
     _(GuardFunctionPrototype)             \
+    _(GuardNoAllocationMetadataBuilder)   \
+    _(GuardObjectGroupNotPretenured)      \
     _(LoadStackValue)                     \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
                                           \
     _(TruncateDoubleToUInt32)             \
@@ -314,16 +317,17 @@ extern const char* const CacheKindNames[
     _(Int32NotResult)                     \
     _(Int32NegationResult)                \
     _(DoubleNegationResult)               \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
     _(LoadValueResult)                    \
+    _(LoadNewObjectFromTemplateResult)    \
                                           \
     _(CallStringSplitResult)              \
     _(CallStringConcatResult)             \
     _(CallStringObjectConcatResult)       \
                                           \
     _(CompareStringResult)                \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
@@ -641,16 +645,23 @@ class MOZ_RAII CacheIRWriter : public JS
         addStubField(uintptr_t(shapeWrapper), StubField::Type::JSObject);
     }
     // Guard rhs[slot] == prototypeObject
     void guardFunctionPrototype(ObjOperandId rhs, uint32_t slot, ObjOperandId protoId) {
         writeOpWithOperandId(CacheOp::GuardFunctionPrototype, rhs);
         writeOperandId(protoId);
         addStubField(slot, StubField::Type::RawWord);
     }
+    void guardNoAllocationMetadataBuilder() {
+        writeOp(CacheOp::GuardNoAllocationMetadataBuilder);
+    }
+    void guardObjectGroupNotPretenured(ObjectGroup* group) {
+        writeOp(CacheOp::GuardObjectGroupNotPretenured);
+        addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
+    }
   private:
     // Use (or create) a specialization below to clarify what constaint the
     // group guard is implying.
     void guardGroup(ObjOperandId obj, ObjectGroup* group) {
         writeOpWithOperandId(CacheOp::GuardGroup, obj);
         addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
     }
   public:
@@ -1245,16 +1256,26 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     void loadObjectTruthyResult(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::LoadObjectTruthyResult, obj);
     }
     void loadValueResult(const Value& val) {
         writeOp(CacheOp::LoadValueResult);
         addStubField(val.asRawBits(), StubField::Type::Value);
     }
+    void loadNewObjectFromTemplateResult(JSObject* templateObj) {
+        writeOp(CacheOp::LoadNewObjectFromTemplateResult);
+        addStubField(uintptr_t(templateObj), StubField::Type::JSObject);
+        // Bake in a monotonically increasing number to ensure we differentiate
+        // between different baseline stubs that otherwise might share
+        // stub code.
+        uint64_t id = cx_->runtime()->jitRuntime()->nextDisambiguationId();
+        writeUint32Immediate(id & UINT32_MAX);
+        writeUint32Immediate(id >> 32);
+    }
     void callStringConcatResult(StringOperandId lhs, StringOperandId rhs) {
         writeOpWithOperandId(CacheOp::CallStringConcatResult, lhs);
         writeOperandId(rhs);
     }
     void callStringObjectConcatResult(ValOperandId lhs, ValOperandId rhs) {
         writeOpWithOperandId(CacheOp::CallStringObjectConcatResult, lhs);
         writeOperandId(rhs);
     }
@@ -1933,12 +1954,26 @@ class MOZ_RAII BinaryArithIRGenerator : 
   public:
     BinaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
                            JSOp op, HandleValue lhs, HandleValue rhs, HandleValue res);
 
     bool tryAttachStub();
 
 };
 
+class MOZ_RAII NewObjectIRGenerator : public IRGenerator
+{
+    JSOp op_;
+    HandleObject templateObject_;
+
+    void trackAttached(const char* name);
+
+  public:
+    NewObjectIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
+                         JSOp op, HandleObject templateObj);
+
+    bool tryAttachStub();
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2678,16 +2678,46 @@ CacheIRCompiler::emitGuardTagNotEqual()
     masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
     masm.jump(failure->label());
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardNoAllocationMetadataBuilder()
+{
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx_->realm()->addressOfMetadataBuilder()),
+                   ImmWord(0), failure->label());
+
+    return true;
+}
+
+bool
+CacheIRCompiler::emitGuardObjectGroupNotPretenured()
+{
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    StubFieldOffset group(reader.stubOffset(), StubField::Type::ObjectGroup);
+    emitLoadStubField(group, scratch);
+
+    masm.branchIfPretenuredGroup(scratch, failure->label());
+    return true;
+}
+
+bool
 CacheIRCompiler::emitLoadDenseElementHoleResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegisterMaybeOutput scratch2(allocator, masm, output);
 
@@ -3107,16 +3137,38 @@ CacheIRCompiler::emitLoadObjectTruthyRes
     masm.xor32(Imm32(1), ReturnReg);
     masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, output.valueReg());
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CacheIRCompiler::emitLoadNewObjectFromTemplateResult()
+{
+    AutoOutputRegister output(*this);
+    AutoScratchRegister obj(allocator, masm);
+    AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+    TemplateObject templateObj(objectStubFieldUnchecked(reader.stubOffset()));
+
+    // Consume the disambiguation id (2 halves)
+    mozilla::Unused << reader.uint32Immediate();
+    mozilla::Unused << reader.uint32Immediate();
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.createGCObject(obj, scratch, templateObj, gc::DefaultHeap, failure->label());
+    masm.tagValue(JSVAL_TYPE_OBJECT, obj, output.valueReg());
+    return true;
+}
+
+bool
 CacheIRCompiler::emitComparePointerResultShared(bool symbol)
 {
     AutoOutputRegister output(*this);
 
     Register left = symbol ? allocator.useRegister(masm, reader.symbolOperandId())
                            : allocator.useRegister(masm, reader.objOperandId());
     Register right = symbol ? allocator.useRegister(masm, reader.symbolOperandId())
                             : allocator.useRegister(masm, reader.objOperandId());
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -41,16 +41,18 @@ namespace jit {
     _(GuardMagicValue)                    \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoDenseElements)               \
     _(GuardAndGetIndexFromString)         \
     _(GuardIndexIsNonNegative)            \
     _(GuardTagNotEqual)                   \
+    _(GuardNoAllocationMetadataBuilder)   \
+    _(GuardObjectGroupNotPretenured)           \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(LoadUndefinedResult)                \
@@ -89,16 +91,17 @@ namespace jit {
     _(LoadTypedElementExistsResult)       \
     _(LoadTypedElementResult)             \
     _(LoadObjectResult)                   \
     _(LoadTypeOfObjectResult)             \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
+    _(LoadNewObjectFromTemplateResult)    \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
     _(CompareInt32Result)                 \
     _(CompareDoubleResult)                \
     _(CompareObjectUndefinedNullResult)   \
     _(ArrayJoinResult)                    \
     _(CallPrintString)                    \
     _(Breakpoint)                         \
@@ -735,16 +738,23 @@ class MOZ_RAII CacheIRCompiler
     Shape* shapeStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (Shape*)readStubWord(offset, StubField::Type::Shape);
     }
     JSObject* objectStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JSObject*)readStubWord(offset, StubField::Type::JSObject);
     }
+    // This accessor is for cases where the stubField policy is
+    // being respected through other means, so we don't check the
+    // policy here. (see LoadNewObjectFromTemplateResult)
+    JSObject* objectStubFieldUnchecked(uint32_t offset) {
+        return (JSObject*)writer_.readStubFieldForIon(offset,
+                                                      StubField::Type::JSObject).asWord();
+    }
     JSString* stringStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JSString*)readStubWord(offset, StubField::Type::String);
     }
     JS::Symbol* symbolStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JS::Symbol*)readStubWord(offset, StubField::Type::Symbol);
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -423,16 +423,17 @@ CodeGenerator::visitOutOfLineICFallback(
 
         masm.jump(ool->rejoin());
         return;
       }
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
 {
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -550,16 +550,17 @@ IonCacheIRCompiler::init()
         allocator.initInputLocation(0, ic->lhs());
         allocator.initInputLocation(1, ic->rhs());
         break;
       }
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
 
     allocator.initAvailableRegs(available);
     allocator.initAvailableRegsAfterSpill();
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -61,16 +61,17 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::BinaryArith:
         return asBinaryArithIC()->output().scratchReg();
       case CacheKind::Compare:
         return asCompareIC()->output().scratchReg();
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 IonIC::discardStubs(Zone* zone)
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -158,16 +158,19 @@ class JitRuntime
                             mozilla::recordreplay::Behavior::DontPreserve> NumFinishedBuildersType;
     NumFinishedBuildersType numFinishedBuilders_;
 
     // List of Ion compilation waiting to get linked.
     using IonBuilderList = mozilla::LinkedList<js::jit::IonBuilder>;
     MainThreadData<IonBuilderList> ionLazyLinkList_;
     MainThreadData<size_t> ionLazyLinkListSize_;
 
+    // Counter used to help dismbiguate stubs in CacheIR
+    MainThreadData<uint64_t> disambiguationId_;
+
   private:
     void generateLazyLinkStub(MacroAssembler& masm);
     void generateInterpreterStub(MacroAssembler& masm);
     void generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail);
     void generateExceptionTailStub(MacroAssembler& masm, void* handler, Label* profilerExitTail);
     void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
     void generateEnterJIT(JSContext* cx, MacroAssembler& masm);
     void generateArgumentsRectifier(MacroAssembler& masm);
@@ -324,16 +327,20 @@ class JitRuntime
     IonBuilderList& ionLazyLinkList(JSRuntime* rt);
 
     size_t ionLazyLinkListSize() const {
         return ionLazyLinkListSize_;
     }
 
     void ionLazyLinkListRemove(JSRuntime* rt, js::jit::IonBuilder* builder);
     void ionLazyLinkListAdd(JSRuntime* rt, js::jit::IonBuilder* builder);
+
+    uint64_t nextDisambiguationId() {
+        return disambiguationId_++;
+    }
 };
 
 enum class CacheKind : uint8_t;
 class CacheIRStubInfo;
 
 enum class ICStubEngine : uint8_t {
     // Baseline IC, see SharedIC.h and BaselineIC.h.
     Baseline = 0,
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3320,20 +3320,27 @@ MacroAssembler::branchIfObjGroupHasNoAdd
               ImmWord(0),
               label);
 }
 
 void
 MacroAssembler::branchIfPretenuredGroup(const ObjectGroup* group, Register scratch, Label* label)
 {
     movePtr(ImmGCPtr(group), scratch);
-    branchTest32(Assembler::NonZero, Address(scratch, ObjectGroup::offsetOfFlags()),
+    branchIfPretenuredGroup(scratch, label);
+}
+
+void
+MacroAssembler::branchIfPretenuredGroup(Register group, Label* label)
+{
+    branchTest32(Assembler::NonZero, Address(group, ObjectGroup::offsetOfFlags()),
                  Imm32(OBJECT_FLAG_PRE_TENURE), label);
 }
 
+
 void
 MacroAssembler::branchIfNonNativeObj(Register obj, Register scratch, Label* label)
 {
     loadObjClassUnsafe(obj, scratch);
     branchTest32(Assembler::NonZero, Address(scratch, Class::offsetOfFlags()),
                  Imm32(Class::NON_NATIVE), label);
 }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1186,16 +1186,17 @@ class MacroAssembler : public MacroAssem
     inline void branchTestObjGroupUnsafe(Condition cond, Register obj, const ObjectGroup* group,
                                          Label* label);
 
     void branchTestObjCompartment(Condition cond, Register obj, const Address& compartment,
                                   Register scratch, Label* label);
     void branchTestObjCompartment(Condition cond, Register obj, const JS::Compartment* compartment,
                                   Register scratch, Label* label);
     void branchIfObjGroupHasNoAddendum(Register obj, Register scratch, Label* label);
+    void branchIfPretenuredGroup(Register group, Label* label);
     void branchIfPretenuredGroup(const ObjectGroup* group, Register scratch, Label* label);
 
     void branchIfNonNativeObj(Register obj, Register scratch, Label* label);
 
     void branchIfInlineTypedObject(Register obj, Register scratch, Label* label);
 
     inline void branchTestClassIsProxy(bool proxy, Register clasp, Label* label);
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1235,38 +1235,51 @@ DoNewObject(JSContext* cx, void* payload
     } else {
         HandleScript script = info.script();
         jsbytecode* pc = info.pc();
         obj = NewObjectOperation(cx, script, pc);
 
         if (obj && !obj->isSingleton() &&
             !obj->group()->maybePreliminaryObjectsDontCheckGeneration())
         {
-            JSObject* templateObject = NewObjectOperation(cx, script, pc, TenuredObject);
+            templateObject = NewObjectOperation(cx, script, pc, TenuredObject);
             if (!templateObject)
                 return false;
 
-            if (!stub->invalid() &&
-                (templateObject->is<UnboxedPlainObject>() ||
-                 !templateObject->as<PlainObject>().hasDynamicSlots()))
-            {
-                JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
-                if (!code)
-                    return false;
+            ICStubCompiler::Engine engine = info.engine();
+            if (engine ==  ICStubEngine::Baseline && !JitOptions.disableCacheIR) {
+                bool attached = false;
+                RootedScript script(cx, info.outerScript(cx));
+                NewObjectIRGenerator gen(cx, script, pc, stub->state().mode(), JSOp(*pc), templateObject);
+                if (gen.tryAttachStub()) {
+                    ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                                BaselineCacheIRStubKind::Regular,
+                                                                ICStubEngine::Baseline , script, stub, &attached);
+                    if (newStub)
+                        JitSpew(JitSpew_BaselineIC, "  NewObject Attached CacheIR stub");
+                }
+            } else {
+                if (!stub->invalid() &&
+                    (templateObject->is<UnboxedPlainObject>() ||
+                    !templateObject->as<PlainObject>().hasDynamicSlots()))
+                {
+                    JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
+                    if (!code)
+                        return false;
 
-                ICStubSpace* space =
-                    ICStubCompiler::StubSpaceForStub(/* makesGCCalls = */ false, script,
-                                                     ICStubCompiler::Engine::Baseline);
-                ICStub* templateStub = ICStub::New<ICNewObject_WithTemplate>(cx, space, code);
-                if (!templateStub)
-                    return false;
+                    ICStubSpace* space =
+                        ICStubCompiler::StubSpaceForStub(/* makesGCCalls = */ false, script,
+                                                        ICStubCompiler::Engine::Baseline);
+                    ICStub* templateStub = ICStub::New<ICNewObject_WithTemplate>(cx, space, code);
+                    if (!templateStub)
+                        return false;
 
-                stub->addNewStub(templateStub);
+                    stub->addNewStub(templateStub);
+                }
             }
-
             stub->setTemplateObject(templateObject);
         }
     }
 
     if (!obj)
         return false;
 
     res.setObject(*obj);