From c8abcdcbb98cb57d6d5c438aaadf90e78c307206 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 25 Sep 2014 19:12:55 +0200 Subject: [PATCH] Bug 1064358 - Recover CreateThisWithTemplate. r=jandem --- js/src/jit-test/tests/ion/recover-objects.js | 17 ++++++++- js/src/jit/IonBuilder.cpp | 4 ++- js/src/jit/MIR.cpp | 8 +++++ js/src/jit/MIR.h | 20 ++++++----- js/src/jit/Recover.cpp | 38 ++++++++++++++++++++ js/src/jit/Recover.h | 16 +++++++++ js/src/jsobj.h | 5 +++ js/src/jsobjinlines.h | 29 +++++++++++++++ 8 files changed, 126 insertions(+), 11 deletions(-) diff --git a/js/src/jit-test/tests/ion/recover-objects.js b/js/src/jit-test/tests/ion/recover-objects.js index 9ad4086d37d..4643487108e 100644 --- a/js/src/jit-test/tests/ion/recover-objects.js +++ b/js/src/jit-test/tests/ion/recover-objects.js @@ -1,5 +1,5 @@ setJitCompilerOption("baseline.warmup.trigger", 10); -setJitCompilerOption("ion.warmup.trigger", 20); +setJitCompilerOption("ion.warmup.trigger", 30); var uceFault = function (i) { if (i > 98) @@ -112,6 +112,20 @@ function dynamicSlots(i) { assertEq(obj.p0 + obj.p10 + obj.p20 + obj.p30 + obj.p40, 5 * i + 100); } +// Check that we can correctly recover allocations of new objects. +function Point(x, y) +{ + this.x = x; + this.y = y; +} + +function createThisWithTemplate(i) +{ + var p = new Point(i - 1, i + 1); + bailout(); + assertEq(p.y - p.x, 2); +} + for (var i = 0; i < 100; i++) { notSoEmpty1(i); @@ -121,4 +135,5 @@ for (var i = 0; i < 100; i++) { withinIf(i); unknownLoad(i); dynamicSlots(i); + createThisWithTemplate(i); } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2381ca8f232..59a21604121 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -5171,9 +5171,11 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) // Generate an inline path to create a new |this| object with // the given singleton prototype. + MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); MCreateThisWithTemplate *createThis = - MCreateThisWithTemplate::New(alloc(), constraints(), templateObject, + MCreateThisWithTemplate::New(alloc(), constraints(), templateConst, templateObject->type()->initialHeap(constraints())); + current->add(templateConst); current->add(createThis); return createThis; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 7f2d7638e98..53c31aa9abe 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3167,6 +3167,14 @@ MNewObject::shouldUseVM() const return obj->hasSingletonType() || obj->hasDynamicSlots(); } +bool +MCreateThisWithTemplate::canRecoverOnBailout() const +{ + MOZ_ASSERT(!templateObject()->denseElementsAreCopyOnWrite()); + MOZ_ASSERT(!templateObject()->is()); + return true; +} + MObjectState::MObjectState(MDefinition *obj) { // This instruction is only used as a summary for bailout paths. diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 5521c0451ab..c814096a8bd 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3629,31 +3629,30 @@ class MAssertRange // Caller-side allocation of |this| for |new|: // Given a templateobject, construct |this| for JSOP_NEW class MCreateThisWithTemplate - : public MNullaryInstruction + : public MUnaryInstruction { - // Template for |this|, provided by TI - AlwaysTenuredObject templateObject_; gc::InitialHeap initialHeap_; - MCreateThisWithTemplate(types::CompilerConstraintList *constraints, JSObject *templateObject, + MCreateThisWithTemplate(types::CompilerConstraintList *constraints, MConstant *templateConst, gc::InitialHeap initialHeap) - : templateObject_(templateObject), + : MUnaryInstruction(templateConst), initialHeap_(initialHeap) { setResultType(MIRType_Object); - setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); + setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject())); } public: INSTRUCTION_HEADER(CreateThisWithTemplate); static MCreateThisWithTemplate *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, - JSObject *templateObject, gc::InitialHeap initialHeap) + MConstant *templateConst, gc::InitialHeap initialHeap) { - return new(alloc) MCreateThisWithTemplate(constraints, templateObject, initialHeap); + return new(alloc) MCreateThisWithTemplate(constraints, templateConst, initialHeap); } + // Template for |this|, provided by TI. JSObject *templateObject() const { - return templateObject_; + return &getOperand(0)->toConstant()->value().toObject(); } gc::InitialHeap initialHeap() const { @@ -3664,6 +3663,9 @@ class MCreateThisWithTemplate AliasSet getAliasSet() const { return AliasSet::None(); } + + bool writeRecoverData(CompactBufferWriter &writer) const; + bool canRecoverOnBailout() const; }; // Caller-side allocation of |this| for |new|: diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index 7a95c48815a..35a5e155043 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -14,6 +14,8 @@ #include "builtin/RegExp.h" #include "builtin/TypedObject.h" +#include "gc/Heap.h" + #include "jit/JitFrameIterator.h" #include "jit/JitSpewer.h" #include "jit/MIR.h" @@ -1094,6 +1096,42 @@ RNewDerivedTypedObject::recover(JSContext *cx, SnapshotIterator &iter) const return true; } +bool +MCreateThisWithTemplate::writeRecoverData(CompactBufferWriter &writer) const +{ + MOZ_ASSERT(canRecoverOnBailout()); + writer.writeUnsigned(uint32_t(RInstruction::Recover_CreateThisWithTemplate)); + writer.writeByte(bool(initialHeap() == gc::TenuredHeap)); + return true; +} + +RCreateThisWithTemplate::RCreateThisWithTemplate(CompactBufferReader &reader) +{ + tenuredHeap_ = reader.readByte(); +} + +bool +RCreateThisWithTemplate::recover(JSContext *cx, SnapshotIterator &iter) const +{ + RootedObject templateObject(cx, &iter.read().toObject()); + + // Use AutoEnterAnalysis to avoid invoking the object metadata callback + // while bailing out, which could try to walk the stack. + types::AutoEnterAnalysis enter(cx); + + // See CodeGenerator::visitCreateThisWithTemplate + gc::AllocKind allocKind = templateObject->asTenured()->getAllocKind(); + gc::InitialHeap initialHeap = tenuredHeap_ ? gc::TenuredHeap : gc::DefaultHeap; + JSObject *resultObject = JSObject::copy(cx, allocKind, initialHeap, templateObject); + if (!resultObject) + return false; + + RootedValue result(cx); + result.setObject(*resultObject); + iter.storeInstructionResult(result); + return true; +} + bool MObjectState::writeRecoverData(CompactBufferWriter &writer) const { diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index 22802f18abc..012021c2495 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -54,6 +54,7 @@ namespace jit { _(NewObject) \ _(NewArray) \ _(NewDerivedTypedObject) \ + _(CreateThisWithTemplate) \ _(ObjectState) \ _(ArrayState) @@ -557,6 +558,21 @@ class RNewDerivedTypedObject MOZ_FINAL : public RInstruction bool recover(JSContext *cx, SnapshotIterator &iter) const; }; +class RCreateThisWithTemplate MOZ_FINAL : public RInstruction +{ + private: + bool tenuredHeap_; + + public: + RINSTRUCTION_HEADER_(CreateThisWithTemplate) + + virtual uint32_t numOperands() const { + return 1; + } + + bool recover(JSContext *cx, SnapshotIterator &iter) const; +}; + class RObjectState MOZ_FINAL : public RInstruction { private: diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 3b350a4a669..988c5d0a296 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -241,6 +241,11 @@ class JSObject : public js::ObjectImpl js::HandleShape shape, js::HandleTypeObject type); + static inline JSObject *copy(js::ExclusiveContext *cx, + js::gc::AllocKind kind, + js::gc::InitialHeap heap, + js::HandleObject templateObject); + /* Make an array object with the specified initial state. */ static inline js::ArrayObject *createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 6ff4befae25..7e8d49cd87c 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -548,6 +548,35 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi return obj; } +/* static */ inline JSObject * +JSObject::copy(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, + js::HandleObject templateObject) +{ + js::RootedShape shape(cx, templateObject->lastProperty()); + js::RootedTypeObject type(cx, templateObject->type()); + MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite()); + + JSObject *obj = create(cx, kind, heap, shape, type); + if (!obj) + return nullptr; + + size_t span = shape->slotSpan(); + if (span) { + uint32_t numFixed = templateObject->numFixedSlots(); + const js::Value *fixed = &templateObject->getSlot(0); + MOZ_ASSERT(numFixed <= span); + obj->copySlotRange(0, fixed, numFixed); + + if (numFixed < span) { + uint32_t numSlots = span - numFixed; + const js::Value *slots = &templateObject->getSlot(numFixed); + obj->copySlotRange(numFixed, slots, numSlots); + } + } + + return obj; +} + /* static */ inline JSObject * JSObject::createArrayInternal(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, js::HandleShape shape, js::HandleTypeObject type)