From 43d93ff145087fb34c07e6043c01a40694e38a74 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Thu, 8 May 2014 16:30:52 -0700 Subject: [PATCH] Backed out 2 changesets (bug 969012) for gc bustage Backed out changeset 01f27ad85b1b (bug 969012) Backed out changeset f844291b895b (bug 969012) --- js/src/jit-test/tests/gc/bug-969012.js | 60 ----- js/src/jit-test/tests/gc/bug-985732.js | 83 ------ .../tests/parallel/closure-nested-branch.js | 14 + .../tests/parallel/closure-nested-compute.js | 14 + js/src/jit/CodeGenerator.cpp | 178 +++++++------ js/src/jit/CodeGenerator.h | 1 + js/src/jit/Ion.cpp | 10 - js/src/jit/IonBuilder.cpp | 38 +-- js/src/jit/IonMacroAssembler.cpp | 245 ++++-------------- js/src/jit/IonMacroAssembler.h | 34 +-- js/src/jit/JitCompartment.h | 14 - js/src/jit/LIR-Common.h | 94 +++++-- js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 41 ++- js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 64 +++-- js/src/jit/MOpcodes.h | 1 + js/src/jit/ParallelSafetyAnalysis.cpp | 14 +- js/src/jit/VMFunctions.cpp | 23 +- js/src/jit/VMFunctions.h | 6 +- js/src/jit/shared/IonAssemblerBuffer.h | 4 +- js/src/jsobj.h | 3 +- js/src/jsobjinlines.h | 22 +- js/src/vm/ScopeObject.cpp | 8 +- js/src/vm/ScopeObject.h | 14 +- 25 files changed, 422 insertions(+), 565 deletions(-) delete mode 100644 js/src/jit-test/tests/gc/bug-969012.js delete mode 100644 js/src/jit-test/tests/gc/bug-985732.js diff --git a/js/src/jit-test/tests/gc/bug-969012.js b/js/src/jit-test/tests/gc/bug-969012.js deleted file mode 100644 index ed03f66a7392..000000000000 --- a/js/src/jit-test/tests/gc/bug-969012.js +++ /dev/null @@ -1,60 +0,0 @@ -function testClosureCreationAndInvocation() { - var a = 'foobar'; - function makeaddv(vvvv) { - var z = -4 * vvvv; - var y = -3 * vvvv; - var x = -2 * vvvv; - var w = -1 * vvvv; - var v = 0 * vvvv; - var u = 1 * vvvv; - var t = 2 * vvvv; - var s = 3 * vvvv; - var r = 4 * vvvv; - var q = 5 * vvvv; - var p = 6 * vvvv; - var o = 7 * vvvv; - var n = 8 * vvvv; - var m = 9 * vvvv; - var l = 10 * vvvv; - var k = 11 * vvvv; - var j = 12 * vvvv; - var i = 13 * vvvv; - var h = 14 * vvvv; - var g = 15 * vvvv; - var f = 16 * vvvv; - var e = 17 * vvvv; - var d = 18 * vvvv; - var c = 19 * vvvv; - var b = 20 * vvvv; - var a = 21 * vvvv; - return function (x) { - switch (x) { - case 0: return a; case 1: return b; - case 2: return c; case 3: return d; - case 4: return e; case 5: return f; - 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; case 21: return v; - case 22: return w; case 23: return x; - case 24: return y; case 25: return z; - } - }; - } - var a = Array(); - for (var i = 0; i < 26; ++i) { - a.push(makeaddv(Math.random())); - } - return a; -} - -var a = testClosureCreationAndInvocation(); -for (var i = 0; i < 26; ++i) { - print(a[i](i)); -} - - diff --git a/js/src/jit-test/tests/gc/bug-985732.js b/js/src/jit-test/tests/gc/bug-985732.js deleted file mode 100644 index df37f97005ba..000000000000 --- a/js/src/jit-test/tests/gc/bug-985732.js +++ /dev/null @@ -1,83 +0,0 @@ -function testx() { -function compareArray(aExpected, aActual) {} - for (var i = 0; i < expected.length; i++) {} -var supportsArrayIndexGettersOnArrays = undefined; -function fnSupportsArrayIndexGettersOnArrays() {} -var supportsArrayIndexGettersOnObjects = undefined; -function fnSupportsArrayIndexGettersOnObjects() {} -function ConvertToFileUrl(pathStr) { -} -function fnExists() {} -var __globalObject = Function("return this;")(); -function fnGlobalObject() {} -function fnSupportsStrict() { - eval('with ({}) {}'); -} -function dataPropertyAttributesAreCorrect(obj, configurable) {} -function accessorPropertyAttributesAreCorrect(obj, configurable) {} -var NotEarlyErrorString = "NotEarlyError"; -var EarlyErrorRePat = "^((?!" + NotEarlyErrorString + ").)*$"; -var NotEarlyError = new Error(NotEarlyErrorString); -function Test262Error(message) {}; -function testFailed(message) {} -function testPrint(message) {} -function $PRINT(message) {} -function $INCLUDE(message) { } -function $ERROR(message) {} -function $FAIL(message) {} -function getPrecision(num) {} -var prec; -function isEqual(num1, num2) {} -function ToInteger(p) {} -var HoursPerDay = 24; -var MinutesPerHour = 60; -var SecondsPerMinute = 60; -var msPerDay = 86400000; -var msPerSecond = 1000; -var msPerMinute = 60000; -var msPerHour = 3600000; -var date_1899_end = -2208988800001; -var date_1900_start = -2208988800000; -var date_1969_end = -1; -var date_1970_start = 0; -var date_1999_end = 946684799999; -var date_2000_start = 946684800000; -var date_2099_end = 4102444799999; -var date_2100_start = 4102444800000; -var $LocalTZ, - $DST_start_month, - $DST_start_sunday, - $DST_start_hour, - $DST_start_minutes, - $DST_end_month, - $DST_end_sunday, - $DST_end_hour, - $DST_end_minutes; -function Day(t) {} -function TimeWithinDay(t) {} -function DaysInYear(y){} -function DayFromYear(y) {} -function TimeFromYear(y){} -function YearFromTime(t) {} -function InLeapYear(t){} -function DayWithinYear(t) {} -function MonthFromTime(t){} -function DateFromTime(t) {} -function WeekDay(t) {} -var LocalTZA = $LocalTZ*msPerHour; -function DaysInMonth(m, leap) {} -function GetSundayInMonth(t, m, count){} -function DaylightSavingTA(t) {} -function LocalTime(t){} -function UTC(t) {} -function HourFromTime(t){} -function MinFromTime(t){} -function SecFromTime(t){} -function msFromTime(t){} -function MakeTime(hour, min, sec, ms){} -function MakeDay(year, month, date) {} -function MakeDate( day, time ) {} -function TimeClip(time) {} -function ConstructDate(year, month, date, hours, minutes, seconds, ms){} -function runTestCase(testcase) {} -} testx(); 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 5076b153384d..c57f8e476821 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1044,7 +1044,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); @@ -1080,7 +1081,8 @@ CodeGenerator::visitLambdaArrow(LLambdaArrow *lir) return true; } - 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); @@ -3504,6 +3506,28 @@ 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)); + + if (!bailoutTestPtr(Assembler::Zero, output, output, lir->snapshot())) + return false; + + return true; +} + bool CodeGenerator::visitAtan2D(LAtan2D *lir) { Register temp = ToRegister(lir->temp()); @@ -3552,7 +3576,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; @@ -3638,7 +3663,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; @@ -3673,13 +3699,13 @@ 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 *, HandleShape, HandleTypeObject); +typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *); static const VMFunction NewCallObjectInfo = FunctionInfo(NewCallObject); @@ -3691,21 +3717,44 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) JSObject *templateObj = lir->mir()->templateObject(); - OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObj->lastProperty()), - ImmGCPtr(templateObj->type())), - StoreRegisterTo(objReg)); + OutOfLineCode *ool; + if (lir->slots()->isRegister()) { + ool = oolCallVM(NewCallObjectInfo, lir, + (ArgList(), ImmGCPtr(templateObj->lastProperty()), + ImmGCPtr(templateObj->type()), + ToRegister(lir->slots())), + StoreRegisterTo(objReg)); + } else { + ool = oolCallVM(NewCallObjectInfo, lir, + (ArgList(), ImmGCPtr(templateObj->lastProperty()), + ImmGCPtr(templateObj->type()), + ImmPtr(nullptr)), + StoreRegisterTo(objReg)); + } if (!ool) return false; - // Inline call object creation, using the OOL path only for tricky cases. - masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry()); +#ifdef JSGC_GENERATIONAL + if (templateObj->hasDynamicSlots()) { + // Slot initialization is unbarriered in this case, so we must either + // allocate in the nursery or bail if that is not possible. + masm.jump(ool->entry()); + } else +#endif + { + // Inline call object creation, using the OOL path only for tricky cases. + 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()); return true; } -typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape); +typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, HeapSlot *); static const VMFunction NewSingletonCallObjectInfo = FunctionInfo(NewSingletonCallObject); @@ -3717,9 +3766,17 @@ CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject *lir) JSObject *templateObj = lir->mir()->templateObject(); OutOfLineCode *ool; - ool = oolCallVM(NewSingletonCallObjectInfo, lir, - (ArgList(), ImmGCPtr(templateObj->lastProperty())), - StoreRegisterTo(objReg)); + if (lir->slots()->isRegister()) { + ool = oolCallVM(NewSingletonCallObjectInfo, lir, + (ArgList(), ImmGCPtr(templateObj->lastProperty()), + ToRegister(lir->slots())), + StoreRegisterTo(objReg)); + } else { + ool = oolCallVM(NewSingletonCallObjectInfo, lir, + (ArgList(), ImmGCPtr(templateObj->lastProperty()), + ImmPtr(nullptr)), + StoreRegisterTo(objReg)); + } if (!ool) return false; @@ -3742,6 +3799,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; } @@ -3800,7 +3868,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); @@ -4045,7 +4114,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()); @@ -5174,70 +5243,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); @@ -5909,7 +5914,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); @@ -6280,7 +6287,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 70d5488abdaf..899e6ef8e05f 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -135,6 +135,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 6bb2b4e9e2ce..2f6df6bac7ed 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -270,16 +270,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 d3a728355c4c..04965acbb6d4 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -4738,13 +4738,27 @@ 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. - MNullaryInstruction *callObj; + // 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. + MUnaryInstruction *callObj; if (script()->treatAsRunOnce()) - callObj = MNewRunOnceCallObject::New(alloc(), templateObj); + callObj = MNewRunOnceCallObject::New(alloc(), templateObj, slots); else - callObj = MNewCallObject::New(alloc(), templateObj); + callObj = MNewCallObject::New(alloc(), templateObj, slots); current->add(callObj); // Initialize the object's reserved slots. No post barrier is needed here, @@ -4753,20 +4767,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; @@ -7740,10 +7748,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 77f8be8c4301..ea7611cd9224 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -425,11 +425,14 @@ template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &s template void MacroAssembler::loadFromTypedArray(int arrayType, const BaseIndex &src, const ValueOperand &dest, bool allowDouble, Register temp, Label *fail); -// 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, @@ -441,68 +444,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 known 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); - - // We still need to allocate in the nursery, per the comment in - // shouldNurseryAllocate; however, we need to insert into hugeSlots, so - // bail to do the nursery allocation in the interpreter. - if (nDynamicSlots >= Nursery::MaxNurserySlots) { - jump(fail); + // 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; } - - // 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); #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); @@ -510,118 +474,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); - - if (regNBytes != result) - push(regNBytes); - move32(Imm32(nbytes), regNBytes); - call(GetIonContext()->runtime->jitRuntime()->mallocStub()); - if (regNBytes != result) { - 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::newGCFatInlineString(Register result, Register temp, Label *fail) { - allocateNonObject(result, temp, js::gc::FINALIZE_FAT_INLINE_STRING, fail); + newGCThing(result, temp, js::gc::FINALIZE_FAT_INLINE_STRING, fail); } void @@ -677,7 +548,6 @@ MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, R { gc::AllocKind allocKind = templateObject->tenuredGetAllocKind(); JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); - JS_ASSERT(!templateObject->numDynamicSlots()); newGCThingPar(result, cx, tempReg1, tempReg2, allocKind, fail); } @@ -697,7 +567,7 @@ MacroAssembler::newGCFatInlineStringPar(Register result, Register cx, Register t } 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); @@ -706,26 +576,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 } @@ -742,7 +613,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. @@ -750,9 +621,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 @@ -761,41 +629,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(); @@ -818,7 +667,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 4c87d1eec3ef..f5eee3cf1951 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -790,30 +790,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 newGCFatInlineString(Register result, Register temp, Label *fail); @@ -826,6 +806,12 @@ class MacroAssembler : public MacroAssemblerSpecific void newGCFatInlineStringPar(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 4401539aff65..89e22c9ce4b1 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_; @@ -243,8 +239,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 *generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut); @@ -361,14 +355,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 da7ded31c864..57adb53ff5e4 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,42 +487,46 @@ class LNewCallObject : public LInstructionHelper<1, 0, 1> return getTemp(0); } + const LAllocation *slots() { + return getOperand(0); + } MNewCallObject *mir() const { return mir_->toNewCallObject(); } }; -// Allocates a new CallObject with singleton type. +// Allocates a new CallObject with singleton type through an out-of-line VM +// call. 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 LNewSingletonCallObject : public LInstructionHelper<1, 0, 1> +class LNewSingletonCallObject : public LInstructionHelper<1, 1, 0> { public: LIR_HEADER(NewSingletonCallObject) - LNewSingletonCallObject(const LDefinition &temp) { - setTemp(0, temp); + LNewSingletonCallObject(const LAllocation &slots) { + setOperand(0, slots); } - const LDefinition *temp() { - return getTemp(0); + const LAllocation *slots() { + return getOperand(0); } - MNewCallObjectBase *mir() const { + MNewCallObjectBase * mir() const { MOZ_ASSERT(mir_->isNewCallObject() || mir_->isNewRunOnceCallObject()); return static_cast(mir_); } }; -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); } @@ -501,16 +534,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 ef40d50188fe..a46c7a5bed20 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -26,6 +26,7 @@ _(NewArray) \ _(ArraySplice) \ _(NewObject) \ + _(NewSlots) \ _(NewDeclEnvObject) \ _(NewCallObject) \ _(NewSingletonCallObject) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 8246e41f3d1e..10e20d05ab5d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -158,6 +158,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) { @@ -182,14 +193,20 @@ LIRGenerator::visitNewDeclEnvObject(MNewDeclEnvObject *ins) bool LIRGenerator::visitNewCallObject(MNewCallObject *ins) { + LAllocation slots; + if (ins->slots()->type() == MIRType_Slots) + slots = useRegister(ins->slots()); + else + slots = LConstantIndex::Bogus(); + LInstruction *lir; if (ins->templateObject()->hasSingletonType()) { - LNewSingletonCallObject *singletonLir = new(alloc()) LNewSingletonCallObject(temp()); + LNewSingletonCallObject *singletonLir = new(alloc()) LNewSingletonCallObject(slots); if (!define(singletonLir, ins)) return false; lir = singletonLir; } else { - LNewCallObject *normalLir = new(alloc()) LNewCallObject(temp()); + LNewCallObject *normalLir = new(alloc()) LNewCallObject(slots, temp()); if (!define(normalLir, ins)) return false; lir = normalLir; @@ -204,7 +221,13 @@ LIRGenerator::visitNewCallObject(MNewCallObject *ins) bool LIRGenerator::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins) { - LNewSingletonCallObject *lir = new(alloc()) LNewSingletonCallObject(temp()); + LAllocation slots; + if (ins->slots()->type() == MIRType_Slots) + slots = useRegister(ins->slots()); + else + slots = LConstantIndex::Bogus(); + + LNewSingletonCallObject *lir = new(alloc()) LNewSingletonCallObject(slots); if (!define(lir, ins)) return false; @@ -228,7 +251,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 238b77646c03..c87644d87f39 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -68,6 +68,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 67bca51fbb64..ddd02406f1ba 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9319,6 +9319,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_; @@ -9345,19 +9372,22 @@ class MNewDeclEnvObject : public MNullaryInstruction } }; -class MNewCallObjectBase : public MNullaryInstruction +class MNewCallObjectBase : public MUnaryInstruction { CompilerRootObject templateObj_; protected: - MNewCallObjectBase(JSObject *templateObj) - : MNullaryInstruction(), + MNewCallObjectBase(JSObject *templateObj, MDefinition *slots) + : MUnaryInstruction(slots), templateObj_(templateObj) { setResultType(MIRType_Object); } public: + MDefinition *slots() { + return getOperand(0); + } JSObject *templateObject() { return templateObj_; } @@ -9371,14 +9401,14 @@ class MNewCallObject : public MNewCallObjectBase public: INSTRUCTION_HEADER(NewCallObject) - MNewCallObject(JSObject *templateObj) - : MNewCallObjectBase(templateObj) + MNewCallObject(JSObject *templateObj, MDefinition *slots) + : MNewCallObjectBase(templateObj, slots) {} static MNewCallObject * - New(TempAllocator &alloc, JSObject *templateObj) + New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) { - return new(alloc) MNewCallObject(templateObj); + return new(alloc) MNewCallObject(templateObj, slots); } }; @@ -9387,23 +9417,23 @@ class MNewRunOnceCallObject : public MNewCallObjectBase public: INSTRUCTION_HEADER(NewRunOnceCallObject) - MNewRunOnceCallObject(JSObject *templateObj) - : MNewCallObjectBase(templateObj) + MNewRunOnceCallObject(JSObject *templateObj, MDefinition *slots) + : MNewCallObjectBase(templateObj, slots) {} static MNewRunOnceCallObject * - New(TempAllocator &alloc, JSObject *templateObj) + New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) { - return new(alloc) MNewRunOnceCallObject(templateObj); + return new(alloc) MNewRunOnceCallObject(templateObj, slots); } }; -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); @@ -9413,13 +9443,17 @@ class MNewCallObjectPar : public MUnaryInstruction INSTRUCTION_HEADER(NewCallObjectPar); static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObjectBase *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 00de788a4f75..a27bb54fd188 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -87,6 +87,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 480e548f3862..0017f0ea1a71 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -181,6 +181,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) @@ -532,10 +533,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; } @@ -543,10 +540,6 @@ ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins) bool ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins) { - if (ins->templateObject()->hasDynamicSlots()) { - SpewMIR(ins, "call with dynamic slots"); - return markUnsafe(); - } replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins)); return true; } @@ -675,6 +668,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 0f0c5aff06ab..14ab8f7d3a78 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -525,16 +525,25 @@ 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, HandleShape shape, HandleTypeObject type) +NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots) { - JSObject *obj = CallObject::create(cx, shape, type); + JSObject *obj = CallObject::create(cx, shape, type, slots); if (!obj) return nullptr; @@ -550,9 +559,9 @@ NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type) } JSObject * -NewSingletonCallObject(JSContext *cx, HandleShape shape) +NewSingletonCallObject(JSContext *cx, HandleShape shape, HeapSlot *slots) { - JSObject *obj = CallObject::createSingleton(cx, shape); + JSObject *obj = CallObject::createSingleton(cx, shape, slots); if (!obj) return nullptr; diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 26b287b42072..e343b3186409 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -619,9 +619,9 @@ bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, Handl bool InterruptCheck(JSContext *cx); -void *MallocWrapper(JSRuntime *rt, size_t nbytes); -JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type); -JSObject *NewSingletonCallObject(JSContext *cx, HandleShape shape); +HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots); +JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots); +JSObject *NewSingletonCallObject(JSContext *cx, HandleShape shape, HeapSlot *slots); JSObject *NewStringObject(JSContext *cx, HandleString str); bool SPSEnter(JSContext *cx, HandleScript script); diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h index 1fdb2ae850e5..100d7cb614e6 100644 --- a/js/src/jit/shared/IonAssemblerBuffer.h +++ b/js/src/jit/shared/IonAssemblerBuffer.h @@ -245,10 +245,8 @@ struct AssemblerBuffer // Break the instruction stream so we can go back and edit it at this point void perforate() { Slice *tmp = newSlice(LifoAlloc_); - if (!tmp) { + if (!tmp) m_oom = true; - return; - } bufferSize += tail->size(); tail->setNext(tmp); tail = tmp; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 3ff4ba011a84..7b333a9e8683 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -236,7 +236,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 4a185c8aa922..34b0b07a262b 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -486,8 +486,14 @@ 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 + * object. We can't check the allocated capacity of the dynamic slots, but + * make sure their presence is consistent with the shape. + */ JS_ASSERT(shape && type); JS_ASSERT(type->clasp() == shape->getObjectClass()); JS_ASSERT(type->clasp() != &js::ArrayObject::class_); @@ -495,9 +501,13 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi 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) @@ -505,7 +515,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()->gc.nursery.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 6437cf4c44c0..1e8305525e54 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -137,7 +137,7 @@ ScopeObject::setEnclosingScope(HandleObject obj) } CallObject * -CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type) +CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots) { MOZ_ASSERT(!type->singleton(), "passed a singleton type to create() (use createSingleton() " @@ -146,7 +146,7 @@ CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type) MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); kind = gc::GetBackgroundAllocKind(kind); - JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type); + JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type, slots); if (!obj) return nullptr; @@ -154,7 +154,7 @@ CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type) } CallObject * -CallObject::createSingleton(JSContext *cx, HandleShape shape) +CallObject::createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots) { gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); @@ -163,7 +163,7 @@ CallObject::createSingleton(JSContext *cx, HandleShape shape) RootedTypeObject type(cx, cx->getSingletonType(&class_, nullptr)); if (!type) return nullptr; - RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type)); + RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type, slots)); if (!obj) return nullptr; diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index e3fd77148183..217b676eb395 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -238,18 +238,20 @@ class CallObject : public ScopeObject /* These functions are internal and are exposed only for JITs. */ /* - * Construct a bare-bones call object given a shape and a non-singleton - * type. The call object must be further initialized to be usable. + * Construct a bare-bones call object given a shape, a non-singleton type, + * and slots pointer. The call object must be further initialized to be + * usable. */ static CallObject * - create(JSContext *cx, HandleShape shape, HandleTypeObject type); + create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots); /* - * Construct a bare-bones call object given a shape and make it have - * singleton type. The call object must be initialized to be usable. + * Construct a bare-bones call object given a shape and slots pointer, and + * make it have singleton type. The call object must be initialized to be + * usable. */ static CallObject * - createSingleton(JSContext *cx, HandleShape shape); + createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots); static CallObject * createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);