From 988ea0a8d6d1f6eb50cc013803ea70900d528b73 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Wed, 12 Mar 2014 13:39:40 -0700 Subject: [PATCH] Backout 907d5bc3bd26 (Bug 969012) for bustage on a CLOSED TREE. --- .../tests/parallel/closure-nested-branch.js | 14 + .../tests/parallel/closure-nested-compute.js | 14 + js/src/jit/CodeGenerator.cpp | 153 ++++++----- js/src/jit/CodeGenerator.h | 1 + js/src/jit/Ion.cpp | 11 - js/src/jit/IonBuilder.cpp | 34 +-- js/src/jit/IonMacroAssembler.cpp | 243 ++++-------------- js/src/jit/IonMacroAssembler.h | 34 +-- js/src/jit/JitCompartment.h | 14 - js/src/jit/LIR-Common.h | 72 +++++- js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 31 ++- js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 53 +++- js/src/jit/MOpcodes.h | 1 + js/src/jit/ParallelSafetyAnalysis.cpp | 10 +- js/src/jit/VMFunctions.cpp | 20 +- js/src/jit/VMFunctions.h | 4 +- js/src/jsobj.h | 3 +- js/src/jsobjinlines.h | 17 +- js/src/vm/ScopeObject.cpp | 4 +- js/src/vm/ScopeObject.h | 2 +- 22 files changed, 362 insertions(+), 375 deletions(-) diff --git a/js/src/jit-test/tests/parallel/closure-nested-branch.js b/js/src/jit-test/tests/parallel/closure-nested-branch.js index b0043d931cbb..dc781b7759ff 100644 --- a/js/src/jit-test/tests/parallel/closure-nested-branch.js +++ b/js/src/jit-test/tests/parallel/closure-nested-branch.js @@ -3,6 +3,15 @@ load(libdir + "parallelarray-helpers.js"); function testClosureCreationAndInvocation() { var a = range(0, 64); function makeaddv(v) { + var u = 1; + var t = 2; + var s = 3; + var r = 4; + var q = 5; + var p = 6; + var o = 7; + var n = 8; + var m = 9; var l = 10; var k = 11; var j = 12; @@ -25,6 +34,11 @@ function testClosureCreationAndInvocation() { case 6: return g; case 7: return h; case 8: return i; case 9: return j; case 10: return k; case 11: return l; + case 12: return m; case 13: return n; + case 14: return o; case 15: return p; + case 16: return q; case 17: return r; + case 18: return s; case 19: return t; + case 20: return u; } }); } diff --git a/js/src/jit-test/tests/parallel/closure-nested-compute.js b/js/src/jit-test/tests/parallel/closure-nested-compute.js index fd7a3227f272..a85e5e0a70fe 100644 --- a/js/src/jit-test/tests/parallel/closure-nested-compute.js +++ b/js/src/jit-test/tests/parallel/closure-nested-compute.js @@ -3,6 +3,15 @@ load(libdir + "parallelarray-helpers.js"); function testClosureCreationAndInvocation() { var a = range(0, 64); function makeaddv(v) { + var u = 1; + var t = 2; + var s = 3; + var r = 4; + var q = 5; + var p = 6; + var o = 7; + var n = 8; + var m = 9; var l = 10; var k = 11; var j = 12; @@ -23,6 +32,11 @@ function testClosureCreationAndInvocation() { case 6: return g; case 7: return h; case 8: return i; case 9: return j; case 10: return k; case 11: return l; + case 12: return m; case 13: return n; + case 14: return o; case 15: return p; + case 16: return q; case 17: return r; + case 18: return s; case 19: return t; + case 20: return u; } }; }; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index e494aa29e491..a62ee82debc5 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -972,7 +972,8 @@ CodeGenerator::visitLambda(LLambda *lir) JS_ASSERT(!info.singletonType); - masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry()); + masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap); + masm.initGCThing(output, tempReg, info.fun); emitLambdaInit(output, scopeChain, info); @@ -3336,6 +3337,29 @@ CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir) return callVM(CreateDerivedTypedObjInfo, lir); } +bool +CodeGenerator::visitNewSlots(LNewSlots *lir) +{ + Register temp1 = ToRegister(lir->temp1()); + Register temp2 = ToRegister(lir->temp2()); + Register temp3 = ToRegister(lir->temp3()); + Register output = ToRegister(lir->output()); + + masm.mov(ImmPtr(GetIonContext()->runtime), temp1); + masm.mov(ImmWord(lir->mir()->nslots()), temp2); + + masm.setupUnalignedABICall(2, temp3); + masm.passABIArg(temp1); + masm.passABIArg(temp2); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots)); + + masm.testPtr(output, output); + if (!bailoutIf(Assembler::Zero, lir->snapshot())) + return false; + + return true; +} + bool CodeGenerator::visitAtan2D(LAtan2D *lir) { Register temp = ToRegister(lir->temp()); @@ -3384,7 +3408,8 @@ CodeGenerator::visitNewArray(LNewArray *lir) if (!addOutOfLineCode(ool)) return false; - masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry()); + masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); + masm.initGCThing(objReg, tempReg, templateObject); masm.bind(ool->rejoin()); return true; @@ -3470,7 +3495,8 @@ CodeGenerator::visitNewObject(LNewObject *lir) if (!addOutOfLineCode(ool)) return false; - masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry()); + masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); + masm.initGCThing(objReg, tempReg, templateObject); masm.bind(ool->rejoin()); return true; @@ -3505,13 +3531,14 @@ CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir) if (!ool) return false; - masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry()); - + masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap); + masm.initGCThing(objReg, tempReg, templateObj); masm.bind(ool->rejoin()); return true; } -typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape, HandleTypeObject); +typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape, + HandleTypeObject, HeapSlot *); static const VMFunction NewCallObjectInfo = FunctionInfo(NewCallObject); @@ -3524,11 +3551,22 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) JSObject *templateObj = lir->mir()->templateObject(); // If we have a template object, we can inline call object creation. - OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir, - (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), - ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type())), - StoreRegisterTo(objReg)); + OutOfLineCode *ool; + if (lir->slots()->isRegister()) { + ool = oolCallVM(NewCallObjectInfo, lir, + (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), + ImmGCPtr(templateObj->lastProperty()), + ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()), + ToRegister(lir->slots())), + StoreRegisterTo(objReg)); + } else { + ool = oolCallVM(NewCallObjectInfo, lir, + (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), + ImmGCPtr(templateObj->lastProperty()), + ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()), + ImmPtr(nullptr)), + StoreRegisterTo(objReg)); + } if (!ool) return false; @@ -3536,7 +3574,11 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) // Objects can only be given singleton types in VM calls. masm.jump(ool->entry()); } else { - masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry()); + masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap); + masm.initGCThing(objReg, tempReg, templateObj); + + if (lir->slots()->isRegister()) + masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots())); } masm.bind(ool->rejoin()); @@ -3553,6 +3595,17 @@ CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir) JSObject *templateObj = lir->mir()->templateObj(); emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj); + + // NB: !lir->slots()->isRegister() implies that there is no slots + // array at all, and the memory is already zeroed when copying + // from the template object + + if (lir->slots()->isRegister()) { + Register slotsReg = ToRegister(lir->slots()); + JS_ASSERT(slotsReg != resultReg); + masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots())); + } + return true; } @@ -3611,7 +3664,8 @@ CodeGenerator::visitNewStringObject(LNewStringObject *lir) if (!ool) return false; - masm.createGCObject(output, temp, templateObj, gc::DefaultHeap, ool->entry()); + masm.newGCThing(output, temp, templateObj, ool->entry(), gc::DefaultHeap); + masm.initGCThing(output, temp, templateObj); masm.loadStringLength(input, temp); @@ -3856,7 +3910,7 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir) return false; // Allocate. If the FreeList is empty, call to VM, which may GC. - masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry()); + masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); // Initialize based on the templateObject. masm.bind(ool->rejoin()); @@ -4925,70 +4979,6 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode) return code; } -JitCode * -JitRuntime::generateMallocStub(JSContext *cx) -{ - const Register regReturn = CallTempReg0; - const Register regNBytes = CallTempReg0; - const Register regRuntime = CallTempReg1; - const Register regTemp = CallTempReg1; - - MacroAssembler masm(cx); - - RegisterSet regs = RegisterSet::Volatile(); - regs.takeUnchecked(regNBytes); - masm.PushRegsInMask(regs); - - masm.setupUnalignedABICall(2, regTemp); - masm.movePtr(ImmPtr(cx->runtime()), regRuntime); - masm.passABIArg(regRuntime); - masm.passABIArg(regNBytes); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MallocWrapper)); - masm.storeCallResult(regReturn); - - masm.PopRegsInMask(regs); - masm.ret(); - - Linker linker(masm); - JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); - -#ifdef JS_ION_PERF - writePerfSpewerJitCodeProfile(code, "MallocStub"); -#endif - - return code; -} - -JitCode * -JitRuntime::generateFreeStub(JSContext *cx) -{ - const Register regSlots = CallTempReg0; - const Register regTemp = CallTempReg1; - - MacroAssembler masm(cx); - - RegisterSet regs = RegisterSet::Volatile(); - regs.takeUnchecked(regSlots); - masm.PushRegsInMask(regs); - - masm.setupUnalignedABICall(1, regTemp); - masm.passABIArg(regSlots); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js_free)); - - masm.PopRegsInMask(regs); - - masm.ret(); - - Linker linker(masm); - JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); - -#ifdef JS_ION_PERF - writePerfSpewerJitCodeProfile(code, "FreeStub"); -#endif - - return code; -} - typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *); static const VMFunction CharCodeAtInfo = FunctionInfo(jit::CharCodeAt); @@ -5642,7 +5632,9 @@ CodeGenerator::visitArrayConcat(LArrayConcat *lir) masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail); // Try to allocate an object. - masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail); + JSObject *templateObj = lir->mir()->templateObj(); + masm.newGCThing(temp1, temp2, templateObj, &fail, lir->mir()->initialHeap()); + masm.initGCThing(temp1, temp2, templateObj); masm.jump(&call); { masm.bind(&fail); @@ -6013,7 +6005,8 @@ CodeGenerator::visitRest(LRest *lir) JSObject *templateObject = lir->mir()->templateObject(); Label joinAlloc, failAlloc; - masm.createGCObject(temp2, temp0, templateObject, gc::DefaultHeap, &failAlloc); + masm.newGCThing(temp2, temp0, templateObject, &failAlloc, gc::DefaultHeap); + masm.initGCThing(temp2, temp0, templateObject); masm.jump(&joinAlloc); { masm.bind(&failAlloc); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 244bc66b6a52..e675d7cd1fe8 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -131,6 +131,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitCallDirectEvalV(LCallDirectEvalV *lir); bool visitDoubleToInt32(LDoubleToInt32 *lir); bool visitFloat32ToInt32(LFloat32ToInt32 *lir); + bool visitNewSlots(LNewSlots *lir); bool visitNewArrayCallVM(LNewArray *lir); bool visitNewArray(LNewArray *lir); bool visitOutOfLineNewArray(OutOfLineNewArray *ool); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 8921f5685c0f..777729448e18 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -29,7 +29,6 @@ #include "jit/ExecutionModeInlines.h" #include "jit/IonAnalysis.h" #include "jit/IonBuilder.h" -#include "jit/IonLinker.h" #include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/JitCommon.h" @@ -269,16 +268,6 @@ JitRuntime::initialize(JSContext *cx) if (!shapePreBarrier_) return false; - IonSpew(IonSpew_Codegen, "# Emitting malloc stub"); - mallocStub_ = generateMallocStub(cx); - if (!mallocStub_) - return false; - - IonSpew(IonSpew_Codegen, "# Emitting free stub"); - freeStub_ = generateFreeStub(cx); - if (!freeStub_) - return false; - IonSpew(IonSpew_Codegen, "# Emitting VM function wrappers"); for (VMFunction *fun = VMFunction::functions; fun; fun = fun->next) { if (!generateVMWrapper(cx, *fun)) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 09cda1f0a5fd..2a94b4c75952 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -4597,9 +4597,23 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope) // creation. CallObject *templateObj = inspector->templateCallObject(); - // Allocate the object. Run-once scripts need a singleton type, so always do - // a VM call in such cases. - MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce()); + // If the CallObject needs dynamic slots, allocate those now. + MInstruction *slots; + if (templateObj->hasDynamicSlots()) { + size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(), + templateObj->lastProperty()->slotSpan(templateObj->getClass()), + templateObj->getClass()); + slots = MNewSlots::New(alloc(), nslots); + } else { + slots = MConstant::New(alloc(), NullValue()); + } + current->add(slots); + + // Allocate the actual object. It is important that no intervening + // instructions could potentially bailout, thus leaking the dynamic slots + // pointer. Run-once scripts need a singleton type, so always do a VM call + // in such cases. + MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce(), slots); current->add(callObj); // Initialize the object's reserved slots. No post barrier is needed here, @@ -4608,20 +4622,14 @@ IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope) current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee)); // Initialize argument slots. - MSlots *slots = nullptr; for (AliasedFormalIter i(script()); i; i++) { unsigned slot = i.scopeSlot(); unsigned formal = i.frameIndex(); MDefinition *param = current->getSlot(info().argSlotUnchecked(formal)); - if (slot >= templateObj->numFixedSlots()) { - if (!slots) { - slots = MSlots::New(alloc(), callObj); - current->add(slots); - } + if (slot >= templateObj->numFixedSlots()) current->add(MStoreSlot::New(alloc(), slots, slot - templateObj->numFixedSlots(), param)); - } else { + else current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param)); - } } return callObj; @@ -7484,10 +7492,6 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object, return true; TypedArrayObject *tarr = &tarrObj->as(); - - if (gc::IsInsideNursery(tarr->runtimeFromMainThread(), tarr->viewData())) - return true; - ArrayBufferView::ViewType viewType = (ArrayBufferView::ViewType) tarr->type(); MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType); diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index 44ab72777dbf..a201e15eb1cf 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -633,11 +633,14 @@ MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) #endif } -// Inlined version of gc::CheckAllocatorState that checks the bare essentials -// and bails for anything that cannot be handled with our jit allocators. void -MacroAssembler::checkAllocatorState(Label *fail) +MacroAssembler::newGCThing(Register result, Register temp, gc::AllocKind allocKind, Label *fail, + gc::InitialHeap initialHeap /* = gc::DefaultHeap */) { + // Inlined equivalent of js::gc::NewGCThing() without failure case handling. + + int thingSize = int(gc::Arena::thingSize(allocKind)); + #ifdef JS_GC_ZEAL // Don't execute the inline path if gcZeal is active. branch32(Assembler::NotEqual, @@ -649,66 +652,29 @@ MacroAssembler::checkAllocatorState(Label *fail) // as the metadata to use for the object may vary between executions of the op. if (GetIonContext()->compartment->hasObjectMetadataCallback()) jump(fail); -} -// Inline version of ShouldNurseryAllocate. -bool -MacroAssembler::shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap) -{ #ifdef JSGC_GENERATIONAL - // Note that Ion elides barriers on writes to objects know to be in the - // nursery, so any allocation that can be made into the nursery must be made - // into the nursery, even if the nursery is disabled. At runtime these will - // take the out-of-line path, which is required to insert a barrier for the - // initializing writes. - return IsNurseryAllocable(allocKind) && initialHeap != gc::TenuredHeap; -#else - return false; -#endif -} - -// Inline version of Nursery::allocateObject. -void -MacroAssembler::nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind, - size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail) -{ -#ifdef JSGC_GENERATIONAL - JS_ASSERT(IsNurseryAllocable(allocKind)); - JS_ASSERT(initialHeap != gc::TenuredHeap); - - // This allocation site is requesting too many dynamic slots. Several - // allocation sites assume that nursery allocation will succeed to - // avoid needing barriers later. Ensure these sites limit their slot - // requests appropriately. - JS_ASSERT(nDynamicSlots < Nursery::MaxNurserySlots); - - // No explicit check for nursery.isEnabled() is needed, as the comparison - // with the nursery's end will always fail in such cases. - const Nursery &nursery = GetIonContext()->runtime->gcNursery(); - Register temp = slots; - int thingSize = int(gc::Arena::thingSize(allocKind)); - int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot); - loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result); - computeEffectiveAddress(Address(result, totalSize), temp); - branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail); - storePtr(temp, AbsoluteAddress(nursery.addressOfPosition())); - - if (nDynamicSlots) - computeEffectiveAddress(Address(result, thingSize), slots); + // Always use nursery allocation if it is possible to do so. The jit + // assumes a nursery pointer is returned to avoid barriers. + if (allocKind <= gc::FINALIZE_OBJECT_LAST && initialHeap != gc::TenuredHeap) { + // Inline Nursery::allocate. No explicit check for nursery.isEnabled() + // is needed, as the comparison with the nursery's end will always fail + // in such cases. + const Nursery &nursery = GetIonContext()->runtime->gcNursery(); + loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result); + computeEffectiveAddress(Address(result, thingSize), temp); + branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail); + storePtr(temp, AbsoluteAddress(nursery.addressOfPosition())); + return; + } #endif // JSGC_GENERATIONAL -} -// Inlined version of FreeSpan::allocate. -void -MacroAssembler::freeSpanAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail) -{ CompileZone *zone = GetIonContext()->compartment->zone(); - int thingSize = int(gc::Arena::thingSize(allocKind)); - // Load FreeSpan::first of |zone|'s freeLists for |allocKind|. If there is - // no room remaining in the span, we bail to finish the allocation. The - // interpreter will call |refillFreeLists|, setting up a new FreeSpan so - // that we can continue allocating in the jit. + // Inline FreeSpan::allocate. + // There is always exactly one FreeSpan per allocKind per JSCompartment. + // If a FreeSpan is replaced, its members are updated in the freeLists table, + // which the code below always re-reads. loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result); branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail); computeEffectiveAddress(Address(result, thingSize), temp); @@ -716,115 +682,25 @@ MacroAssembler::freeSpanAllocate(Register result, Register temp, gc::AllocKind a } void -MacroAssembler::callMallocStub(size_t nbytes, Register result, Label *fail) -{ - // This register must match the one in JitRuntime::generateMallocStub. - const Register regNBytes = CallTempReg0; - - JS_ASSERT(nbytes > 0); - JS_ASSERT(nbytes <= INT32_MAX); - - push(regNBytes); - move32(Imm32(nbytes), regNBytes); - call(GetIonContext()->runtime->jitRuntime()->mallocStub()); - movePtr(regNBytes, result); - pop(regNBytes); - branchTest32(Assembler::Zero, result, result, fail); -} - -void -MacroAssembler::callFreeStub(Register slots) -{ - // This register must match the one in JitRuntime::generateFreeStub. - const Register regSlots = CallTempReg0; - - push(regSlots); - movePtr(slots, regSlots); - call(GetIonContext()->runtime->jitRuntime()->freeStub()); - pop(regSlots); -} - -// Inlined equivalent of gc::AllocateObject, without failure case handling. -void -MacroAssembler::allocateObject(Register result, Register slots, gc::AllocKind allocKind, - uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail) +MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObject, Label *fail, + gc::InitialHeap initialHeap) { + gc::AllocKind allocKind = templateObject->tenuredGetAllocKind(); JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); - checkAllocatorState(fail); - - if (shouldNurseryAllocate(allocKind, initialHeap)) - return nurseryAllocate(result, slots, allocKind, nDynamicSlots, initialHeap, fail); - - if (!nDynamicSlots) - return freeSpanAllocate(result, slots, allocKind, fail); - - callMallocStub(nDynamicSlots * sizeof(HeapValue), slots, fail); - - Label failAlloc; - Label success; - - push(slots); - freeSpanAllocate(result, slots, allocKind, &failAlloc); - pop(slots); - jump(&success); - - bind(&failAlloc); - pop(slots); - callFreeStub(slots); - jump(fail); - - bind(&success); -} - -void -MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj, - gc::InitialHeap initialHeap, Label *fail) -{ - // This method does not initialize the object: if external slots get - // allocated into |temp|, there is no easy way for us to ensure the caller - // frees them. Instead just assert this case does not happen. - JS_ASSERT(!templateObj->numDynamicSlots()); - - gc::AllocKind allocKind = templateObj->tenuredGetAllocKind(); - JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); - - allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail); -} - -void -MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj, - gc::InitialHeap initialHeap, Label *fail) -{ - uint32_t nDynamicSlots = templateObj->numDynamicSlots(); - gc::AllocKind allocKind = templateObj->tenuredGetAllocKind(); - JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); - - allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail); - initGCThing(obj, temp, templateObj); -} - - -// Inlined equivalent of gc::AllocateNonObject, without failure case handling. -// Non-object allocation does not need to worry about slots, so can take a -// simpler path. -void -MacroAssembler::allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail) -{ - checkAllocatorState(fail); - freeSpanAllocate(result, temp, allocKind, fail); + newGCThing(result, temp, allocKind, fail, initialHeap); } void MacroAssembler::newGCString(Register result, Register temp, Label *fail) { - allocateNonObject(result, temp, js::gc::FINALIZE_STRING, fail); + newGCThing(result, temp, js::gc::FINALIZE_STRING, fail); } void MacroAssembler::newGCShortString(Register result, Register temp, Label *fail) { - allocateNonObject(result, temp, js::gc::FINALIZE_SHORT_STRING, fail); + newGCThing(result, temp, js::gc::FINALIZE_SHORT_STRING, fail); } void @@ -899,7 +775,7 @@ MacroAssembler::newGCShortStringPar(Register result, Register cx, Register tempR } void -MacroAssembler::copySlotsFromTemplate(Register obj, const JSObject *templateObj, +MacroAssembler::copySlotsFromTemplate(Register obj, Register temp, const JSObject *templateObj, uint32_t start, uint32_t end) { uint32_t nfixed = Min(templateObj->numFixedSlots(), end); @@ -908,26 +784,27 @@ MacroAssembler::copySlotsFromTemplate(Register obj, const JSObject *templateObj, } void -MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end) +MacroAssembler::fillSlotsWithUndefined(Register obj, Register temp, const JSObject *templateObj, + uint32_t start, uint32_t end) { #ifdef JS_NUNBOX32 // We only have a single spare register, so do the initialization as two // strided writes of the tag and body. jsval_layout jv = JSVAL_TO_IMPL(UndefinedValue()); + uint32_t nfixed = Min(templateObj->numFixedSlots(), end); - Address addr = base; - move32(Imm32(jv.s.payload.i32), temp); - for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue)) - store32(temp, ToPayload(addr)); + mov(ImmWord(jv.s.tag), temp); + for (unsigned i = start; i < nfixed; i++) + store32(temp, ToType(Address(obj, JSObject::getFixedSlotOffset(i)))); - addr = base; - move32(Imm32(jv.s.tag), temp); - for (unsigned i = start; i < end; ++i, addr.offset += sizeof(HeapValue)) - store32(temp, ToType(addr)); + mov(ImmWord(jv.s.payload.i32), temp); + for (unsigned i = start; i < nfixed; i++) + store32(temp, ToPayload(Address(obj, JSObject::getFixedSlotOffset(i)))); #else moveValue(UndefinedValue(), temp); - for (uint32_t i = start; i < end; ++i, base.offset += sizeof(HeapValue)) - storePtr(temp, base); + uint32_t nfixed = Min(templateObj->numFixedSlots(), end); + for (unsigned i = start; i < nfixed; i++) + storePtr(temp, Address(obj, JSObject::getFixedSlotOffset(i))); #endif } @@ -944,7 +821,7 @@ FindStartOfUndefinedSlots(JSObject *templateObj, uint32_t nslots) } void -MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj) +MacroAssembler::initGCSlots(Register obj, Register temp, JSObject *templateObj) { // Slots of non-array objects are required to be initialized. // Use the values currently in the template object. @@ -952,9 +829,6 @@ MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj) if (nslots == 0) return; - uint32_t nfixed = templateObj->numFixedSlots(); - uint32_t ndynamic = templateObj->numDynamicSlots(); - // Attempt to group slot writes such that we minimize the amount of // duplicated data we need to embed in code and load into registers. In // general, most template object slots will be undefined except for any @@ -963,41 +837,22 @@ MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj) // duplicated writes of UndefinedValue to the tail. For the majority of // objects, the "tail" will be the entire slot range. uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots); - JS_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed. - - // Copy over any preserved reserved slots. - copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined); - - // Fill the rest of the fixed slots with undefined. - fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots, - startOfUndefined, nfixed); - - if (ndynamic) { - // We are short one register to do this elegantly. Borrow the obj - // register briefly for our slots base address. - push(obj); - loadPtr(Address(obj, JSObject::offsetOfSlots()), obj); - fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic); - pop(obj); - } + copySlotsFromTemplate(obj, temp, templateObj, 0, startOfUndefined); + fillSlotsWithUndefined(obj, temp, templateObj, startOfUndefined, nslots); } void -MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj) +MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj) { - // Fast initialization of an empty object returned by allocateObject(). + // Fast initialization of an empty object returned by NewGCThing(). JS_ASSERT(!templateObj->hasDynamicElements()); storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape())); storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType())); - if (templateObj->hasDynamicSlots()) - storePtr(slots, Address(obj, JSObject::offsetOfSlots())); - else - storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots())); + storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots())); if (templateObj->is()) { - Register temp = slots; JS_ASSERT(!templateObj->getDenseInitializedLength()); int elementsOffset = JSObject::offsetOfFixedElements(); @@ -1020,7 +875,7 @@ MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj) } else { storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements())); - initGCSlots(obj, slots, templateObj); + initGCSlots(obj, temp, templateObj); if (templateObj->hasPrivate()) { uint32_t nfixed = templateObj->numFixedSlots(); diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index 0f7c260de953..7e889d447dbe 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -786,30 +786,10 @@ class MacroAssembler : public MacroAssemblerSpecific void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, Register tag, Label *label); // Inline allocation. - private: - void checkAllocatorState(Label *fail); - bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap); - void nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind, - size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail); - void freeSpanAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail); - void allocateObject(Register result, Register slots, gc::AllocKind allocKind, - uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail); - void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail); - void copySlotsFromTemplate(Register obj, const JSObject *templateObj, - uint32_t start, uint32_t end); - void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end); - void initGCSlots(Register obj, Register temp, JSObject *templateObj); - - public: - void callMallocStub(size_t nbytes, Register result, Label *fail); - void callFreeStub(Register slots); - void createGCObject(Register result, Register temp, JSObject *templateObj, - gc::InitialHeap initialHeap, Label *fail); - - void newGCThing(Register result, Register temp, JSObject *templateObj, - gc::InitialHeap initialHeap, Label *fail); - void initGCThing(Register obj, Register temp, JSObject *templateObj); - + void newGCThing(Register result, Register temp, gc::AllocKind allocKind, Label *fail, + gc::InitialHeap initialHeap = gc::DefaultHeap); + void newGCThing(Register result, Register temp, JSObject *templateObject, Label *fail, + gc::InitialHeap initialHeap); void newGCString(Register result, Register temp, Label *fail); void newGCShortString(Register result, Register temp, Label *fail); @@ -822,6 +802,12 @@ class MacroAssembler : public MacroAssemblerSpecific void newGCShortStringPar(Register result, Register cx, Register tempReg1, Register tempReg2, Label *fail); + void copySlotsFromTemplate(Register obj, Register temp, const JSObject *templateObj, + uint32_t start, uint32_t end); + void fillSlotsWithUndefined(Register obj, Register temp, const JSObject *templateObj, + uint32_t start, uint32_t end); + void initGCSlots(Register obj, Register temp, JSObject *templateObj); + void initGCThing(Register obj, Register temp, JSObject *templateObj); // Compares two strings for equality based on the JSOP. // This checks for identical pointers, atoms and length and fails for everything else. diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h index 0ea210f63cd2..da0e09bf8928 100644 --- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -186,10 +186,6 @@ class JitRuntime JitCode *valuePreBarrier_; JitCode *shapePreBarrier_; - // Thunk to call malloc/free. - JitCode *mallocStub_; - JitCode *freeStub_; - // Thunk used by the debugger for breakpoint and step mode. JitCode *debugTrapHandler_; @@ -225,8 +221,6 @@ class JitRuntime JitCode *generateBailoutHandler(JSContext *cx); JitCode *generateInvalidator(JSContext *cx); JitCode *generatePreBarrier(JSContext *cx, MIRType type); - JitCode *generateMallocStub(JSContext *cx); - JitCode *generateFreeStub(JSContext *cx); JitCode *generateDebugTrapHandler(JSContext *cx); JitCode *generateForkJoinGetSliceStub(JSContext *cx); JitCode *generateVMWrapper(JSContext *cx, const VMFunction &f); @@ -332,14 +326,6 @@ class JitRuntime return shapePreBarrier_; } - JitCode *mallocStub() const { - return mallocStub_; - } - - JitCode *freeStub() const { - return freeStub_; - } - bool ensureForkJoinGetSliceStubExists(JSContext *cx); JitCode *forkJoinGetSliceStub() const { return forkJoinGetSliceStub_; diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 22eb4c143429..1b8d8a6590f4 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -302,6 +302,32 @@ class LGoto : public LControlInstructionHelper<1, 0, 0> } }; +class LNewSlots : public LCallInstructionHelper<1, 0, 3> +{ + public: + LIR_HEADER(NewSlots) + + LNewSlots(const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3) { + setTemp(0, temp1); + setTemp(1, temp2); + setTemp(2, temp3); + } + + const LDefinition *temp1() { + return getTemp(0); + } + const LDefinition *temp2() { + return getTemp(1); + } + const LDefinition *temp3() { + return getTemp(2); + } + + MNewSlots *mir() const { + return mir_->toNewSlots(); + } +}; + class LNewArray : public LInstructionHelper<1, 0, 1> { public: @@ -438,19 +464,22 @@ class LNewDeclEnvObject : public LInstructionHelper<1, 0, 1> } }; -// Allocates a new CallObject. +// Allocates a new CallObject. The inputs are: +// slots: either a reg representing a HeapSlot *, or a placeholder +// meaning that no slots pointer is needed. // // This instruction generates two possible instruction sets: // (1) If the call object is extensible, this is a callVM to create the // call object. // (2) Otherwise, an inline allocation of the call object is attempted. // -class LNewCallObject : public LInstructionHelper<1, 0, 1> +class LNewCallObject : public LInstructionHelper<1, 1, 1> { public: LIR_HEADER(NewCallObject) - LNewCallObject(const LDefinition &temp) { + LNewCallObject(const LAllocation &slots, const LDefinition &temp) { + setOperand(0, slots); setTemp(0, temp); } @@ -458,15 +487,21 @@ class LNewCallObject : public LInstructionHelper<1, 0, 1> return getTemp(0); } + const LAllocation *slots() { + return getOperand(0); + } MNewCallObject *mir() const { return mir_->toNewCallObject(); } }; -class LNewCallObjectPar : public LInstructionHelper<1, 1, 2> +class LNewCallObjectPar : public LInstructionHelper<1, 2, 2> { - LNewCallObjectPar(const LAllocation &cx, const LDefinition &temp1, const LDefinition &temp2) { + LNewCallObjectPar(const LAllocation &cx, const LAllocation &slots, + const LDefinition &temp1, const LDefinition &temp2) + { setOperand(0, cx); + setOperand(1, slots); setTemp(0, temp1); setTemp(1, temp2); } @@ -474,16 +509,37 @@ class LNewCallObjectPar : public LInstructionHelper<1, 1, 2> public: LIR_HEADER(NewCallObjectPar); - static LNewCallObjectPar *New(TempAllocator &alloc, const LAllocation &cx, - const LDefinition &temp1, const LDefinition &temp2) + static LNewCallObjectPar *NewWithSlots(TempAllocator &alloc, + const LAllocation &cx, const LAllocation &slots, + const LDefinition &temp1, const LDefinition &temp2) { - return new(alloc) LNewCallObjectPar(cx, temp1, temp2); + return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2); + } + + static LNewCallObjectPar *NewSansSlots(TempAllocator &alloc, + const LAllocation &cx, + const LDefinition &temp1, const LDefinition &temp2) + { + LAllocation slots = LConstantIndex::Bogus(); + return new(alloc) LNewCallObjectPar(cx, slots, temp1, temp2); } const LAllocation *forkJoinContext() { return getOperand(0); } + const LAllocation *slots() { + return getOperand(1); + } + + const bool hasDynamicSlots() { + // TO INVESTIGATE: Felix tried using isRegister() method here, + // but for useFixed(_, CallTempN), isRegister() is false (and + // isUse() is true). So for now ignore that and try to match + // the LConstantIndex::Bogus() generated above instead. + return slots() && ! slots()->isConstant(); + } + const MNewCallObjectPar *mir() const { return mir_->toNewCallObjectPar(); } diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index fde736b40e8e..d5fa461b0ef0 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -25,6 +25,7 @@ _(Goto) \ _(NewArray) \ _(NewObject) \ + _(NewSlots) \ _(NewDeclEnvObject) \ _(NewCallObject) \ _(NewStringObject) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index afbadab9a72c..bff96c4ddec8 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -160,6 +160,17 @@ LIRGenerator::visitDefFun(MDefFun *ins) return add(lir, ins) && assignSafepoint(lir, ins); } +bool +LIRGenerator::visitNewSlots(MNewSlots *ins) +{ + // No safepoint needed, since we don't pass a cx. + LNewSlots *lir = new(alloc()) LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1), + tempFixed(CallTempReg2)); + if (!assignSnapshot(lir)) + return false; + return defineReturn(lir, ins); +} + bool LIRGenerator::visitNewArray(MNewArray *ins) { @@ -184,7 +195,13 @@ LIRGenerator::visitNewDeclEnvObject(MNewDeclEnvObject *ins) bool LIRGenerator::visitNewCallObject(MNewCallObject *ins) { - LNewCallObject *lir = new(alloc()) LNewCallObject(temp()); + LAllocation slots; + if (ins->slots()->type() == MIRType_Slots) + slots = useRegister(ins->slots()); + else + slots = LConstantIndex::Bogus(); + + LNewCallObject *lir = new(alloc()) LNewCallObject(slots, temp()); if (!define(lir, ins)) return false; @@ -208,7 +225,17 @@ bool LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins) { const LAllocation &parThreadContext = useRegister(ins->forkJoinContext()); - LNewCallObjectPar *lir = LNewCallObjectPar::New(alloc(), parThreadContext, temp(), temp()); + const LDefinition &temp1 = temp(); + const LDefinition &temp2 = temp(); + + LNewCallObjectPar *lir; + if (ins->slots()->type() == MIRType_Slots) { + const LAllocation &slots = useRegister(ins->slots()); + lir = LNewCallObjectPar::NewWithSlots(alloc(), parThreadContext, slots, temp1, temp2); + } else { + lir = LNewCallObjectPar::NewSansSlots(alloc(), parThreadContext, temp1, temp2); + } + return define(lir, ins); } diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 6a94ce31e020..494c03ba8308 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -66,6 +66,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitCallee(MCallee *callee); bool visitGoto(MGoto *ins); bool visitTableSwitch(MTableSwitch *tableswitch); + bool visitNewSlots(MNewSlots *ins); bool visitNewArray(MNewArray *ins); bool visitNewObject(MNewObject *ins); bool visitNewDeclEnvObject(MNewDeclEnvObject *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 3b3c6eddb4cd..35927a836a8b 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8932,6 +8932,33 @@ class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0> #endif }; +class MNewSlots : public MNullaryInstruction +{ + unsigned nslots_; + + MNewSlots(unsigned nslots) + : nslots_(nslots) + { + setResultType(MIRType_Slots); + } + + public: + INSTRUCTION_HEADER(NewSlots) + + static MNewSlots *New(TempAllocator &alloc, unsigned nslots) { + return new(alloc) MNewSlots(nslots); + } + unsigned nslots() const { + return nslots_; + } + AliasSet getAliasSet() const { + return AliasSet::None(); + } + bool possiblyCalls() const { + return true; + } +}; + class MNewDeclEnvObject : public MNullaryInstruction { CompilerRootObject templateObj_; @@ -8958,13 +8985,13 @@ class MNewDeclEnvObject : public MNullaryInstruction } }; -class MNewCallObject : public MNullaryInstruction +class MNewCallObject : public MUnaryInstruction { CompilerRootObject templateObj_; bool needsSingletonType_; - MNewCallObject(JSObject *templateObj, bool needsSingletonType) - : MNullaryInstruction(), + MNewCallObject(JSObject *templateObj, bool needsSingletonType, MDefinition *slots) + : MUnaryInstruction(slots), templateObj_(templateObj), needsSingletonType_(needsSingletonType) { @@ -8974,11 +9001,15 @@ class MNewCallObject : public MNullaryInstruction public: INSTRUCTION_HEADER(NewCallObject) - static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType) + static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType, + MDefinition *slots) { - return new(alloc) MNewCallObject(templateObj, needsSingletonType); + return new(alloc) MNewCallObject(templateObj, needsSingletonType, slots); } + MDefinition *slots() { + return getOperand(0); + } JSObject *templateObject() { return templateObj_; } @@ -8990,12 +9021,12 @@ class MNewCallObject : public MNullaryInstruction } }; -class MNewCallObjectPar : public MUnaryInstruction +class MNewCallObjectPar : public MBinaryInstruction { CompilerRootObject templateObj_; - MNewCallObjectPar(MDefinition *cx, JSObject *templateObj) - : MUnaryInstruction(cx), + MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots) + : MBinaryInstruction(cx, slots), templateObj_(templateObj) { setResultType(MIRType_Object); @@ -9005,13 +9036,17 @@ class MNewCallObjectPar : public MUnaryInstruction INSTRUCTION_HEADER(NewCallObjectPar); static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObject *callObj) { - return new(alloc) MNewCallObjectPar(cx, callObj->templateObject()); + return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots()); } MDefinition *forkJoinContext() const { return getOperand(0); } + MDefinition *slots() const { + return getOperand(1); + } + JSObject *templateObj() const { return templateObj_; } diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 94cf89744253..e598a711a5e2 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -85,6 +85,7 @@ namespace jit { _(ToInt32) \ _(TruncateToInt32) \ _(ToString) \ + _(NewSlots) \ _(NewArray) \ _(NewObject) \ _(NewDeclEnvObject) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 8eb92a64fb23..791b38614cf0 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -179,6 +179,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor SAFE_OP(TruncateToInt32) SAFE_OP(MaybeToDoubleElement) CUSTOM_OP(ToString) + SAFE_OP(NewSlots) CUSTOM_OP(NewArray) CUSTOM_OP(NewObject) CUSTOM_OP(NewCallObject) @@ -525,10 +526,6 @@ ParallelSafetyVisitor::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins) bool ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins) { - if (ins->templateObject()->hasDynamicSlots()) { - SpewMIR(ins, "call with dynamic slots"); - return markUnsafe(); - } replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins)); return true; } @@ -643,6 +640,11 @@ ParallelSafetyVisitor::insertWriteGuard(MInstruction *writeInstruction, object = valueBeingWritten->toSlots()->object(); break; + case MDefinition::Op_NewSlots: + // Values produced by new slots will ALWAYS be + // thread-local. + return true; + default: SpewMIR(writeInstruction, "cannot insert write guard for %s", valueBeingWritten->opName()); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index daac4d0cc743..71bdbc296f32 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -509,16 +509,26 @@ InterruptCheck(JSContext *cx) return CheckForInterrupt(cx); } -void * -MallocWrapper(JSRuntime *rt, size_t nbytes) +HeapSlot * +NewSlots(JSRuntime *rt, unsigned nslots) { - return rt->pod_malloc(nbytes); + JS_STATIC_ASSERT(sizeof(Value) == sizeof(HeapSlot)); + + Value *slots = reinterpret_cast(rt->malloc_(nslots * sizeof(Value))); + if (!slots) + return nullptr; + + for (unsigned i = 0; i < nslots; i++) + slots[i] = UndefinedValue(); + + return reinterpret_cast(slots); } JSObject * -NewCallObject(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type) +NewCallObject(JSContext *cx, HandleScript script, + HandleShape shape, HandleTypeObject type, HeapSlot *slots) { - JSObject *obj = CallObject::create(cx, script, shape, type); + JSObject *obj = CallObject::create(cx, script, shape, type, slots); if (!obj) return nullptr; diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index b1d3c11390d3..86e0a03ad4c1 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -617,9 +617,9 @@ bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, Handl bool InterruptCheck(JSContext *cx); -void *MallocWrapper(JSRuntime *rt, size_t nbytes); +HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots); JSObject *NewCallObject(JSContext *cx, HandleScript script, - HandleShape shape, HandleTypeObject type); + HandleShape shape, HandleTypeObject type, HeapSlot *slots); JSObject *NewStringObject(JSContext *cx, HandleString str); bool SPSEnter(JSContext *cx, HandleScript script); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 7420ac09e956..5d546ac6d15e 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -252,7 +252,8 @@ class JSObject : public js::ObjectImpl js::gc::AllocKind kind, js::gc::InitialHeap heap, js::HandleShape shape, - js::HandleTypeObject type); + js::HandleTypeObject type, + js::HeapSlot *extantSlots = nullptr); /* Make an array object with the specified initial state. */ static inline js::ArrayObject *createArray(js::ExclusiveContext *cx, diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d5976da40026..f4d55f755e89 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -497,7 +497,8 @@ inline bool JSObject::isVarObj() /* static */ inline JSObject * JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, - js::HandleShape shape, js::HandleTypeObject type) + js::HandleShape shape, js::HandleTypeObject type, + js::HeapSlot *extantSlots /* = nullptr */) { /* * Callers must use dynamicSlotsCount to size the initial slot array of the @@ -510,9 +511,13 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi JS_ASSERT(js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots()); JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind)); JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap); + JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), + type->clasp())); const js::Class *clasp = type->clasp(); - size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp); + size_t nDynamicSlots = 0; + if (!extantSlots) + nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp); JSObject *obj = js::NewGCObject(cx, kind, nDynamicSlots, heap); if (!obj) @@ -520,7 +525,13 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi obj->shape_.init(shape); obj->type_.init(type); - // Note: slots are created and assigned internally by NewGCObject. + if (extantSlots) { +#ifdef JSGC_GENERATIONAL + if (cx->isJSContext()) + cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, extantSlots); +#endif + obj->slots = extantSlots; + } obj->elements = js::emptyObjectElements; if (clasp->hasPrivate()) diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index b2127a070842..fc85588c5dd0 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -141,14 +141,14 @@ ScopeObject::setEnclosingScope(HandleObject obj) * The call object must be further initialized to be usable. */ CallObject * -CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type) +CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots) { gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); JS_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); kind = gc::GetBackgroundAllocKind(kind); gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap; - JSObject *obj = JSObject::create(cx, kind, heap, shape, type); + JSObject *obj = JSObject::create(cx, kind, heap, shape, type, slots); if (!obj) return nullptr; diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index cb056f121b62..f3a83d2ed29d 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -237,7 +237,7 @@ class CallObject : public ScopeObject /* These functions are internal and are exposed only for JITs. */ static CallObject * - create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type); + create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots); static CallObject * createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);